SlideShare a Scribd company logo
Exceptions in Ruby
                         Tips & Tricks

                                       Vlad ZLOTEANU
     #ParisRB
     July 3, 2002             Software Engineer @ Dimelo
                                           @vladzloteanu

Copyright Dimelo SA                             www.dimelo.com
About me


           R&D Software Engineer @ Dimelo

           @vladzloteanu

           https://github.com/vladzloteanu




Copyright Dimelo SA                          www.dimelo.com
Exceptions (in Ruby)



         Failure: abnormal situation

      Exception: instance of Exception class (or instance of
     descendant)
          What: allows packaging info about the failure
             message string
             backtrace
          default behavior: program terminates (exits)


Copyright Dimelo SA                                  www.dimelo.com
Exceptions classes in Ruby

                      Exception
                         NoMemoryError
                         ScriptError
                            LoadError
                         SignalException
                            Interrupt
                         StandardError
                            ArgumentError
                            IOError ...
                            RuntimeError
                         SystemExit
Copyright Dimelo SA                           www.dimelo.com
Raising an exception

    class MyException < StandardError; end


    raise MyException.new


    raise # aka: raise RuntimeError.new


    raise “Boom!!” # aka raise RuntimeError.new(“Boom!!”)


    raise MyException, “Boom!!”, backtrace




Copyright Dimelo SA                              www.dimelo.com
Rescuing exceptions

    begin
      raise MyError.new("Booooom!")

    rescue MyError
      puts "MyError just hapened"

    rescue # Will rescue StandardError
      puts "a RuntimeError just happened"

    rescue Exception => e
      puts ”Exception: #{e.message}”
      e.backtrace.each{|line| puts “ > #{line}”}

    end




Copyright Dimelo SA                                www.dimelo.com
Intermezzo: Signals

                      Signals – used to alert threads/processes
                      (hardware faults, timer expiration, terminal
                      activity, kill command, etc)
                         SIGINT: CTRL+C in terminal (can be caught)
                         SIGTERM: Sent by kill command by default
                         (can be caught)
                         SIGKILL: Uncatchable exception (kill -9)

                      A SignalException is raised when a signal is
                      received


Copyright Dimelo SA                                          www.dimelo.com
What should we rescue?




Copyright Dimelo SA                       www.dimelo.com
Rescuing exceptions


                      TIP: DON’T RESCUE ‘EXCEPTION’ class!

    begin


      while(true) ; end
    rescue Exception
      puts "Rescued exception; Will retry"
      retry
    end

    ^CRescued exception; WIll retry
    ^CRescued exception; WIll retry
    ^CRescued exception; WIll retry

    # Kill -9, baby!
Copyright Dimelo SA                                          www.dimelo.com
Rescuing exceptions (third-party libraries)

    # You may think this will save you from all evil ..
    begin
      Net::HTTP.get_response(’external.resource', '/')
    rescue => e
      […]


    # Think again!

    # Excerpt from net/http.rb
    def connect
        s = timeout(@open_timeout)
             { TCPSocket.open(conn_address(), conn_port()) }

         Net::HTTP(Timeout library) may throw
         Timeout::Error < InterruptError
Copyright Dimelo SA                                     www.dimelo.com
Rescuing exceptions (third-party libraries)

    # Solution is easy:

    begin
      Net::HTTP.get_response(’external.resource', '/')
    rescue StandardError, Timeout::Error => e
      […]




         Tip: Net::HTTP (and many of the gems that
         depend on it) can throw Timeout::Error – don’t
         forget to rescue it


Copyright Dimelo SA                                     www.dimelo.com
Rescuing exceptions - cont


                      TIP: (rescue blocks): rescue most specific
                      errors first, most generic last

                      TIP: inline rescue
                         but avoid it because:
                                 No access to rescued exception
                                 Exceptions are slow (explained later)
    # Will return: DefaultDecorator
    @decorator = “foo”.constantize rescue DefaultDecorator




Copyright Dimelo SA                                           www.dimelo.com
What should we raise?




Copyright Dimelo SA                      www.dimelo.com
Raising exceptions


                      Tip: make your own exception class, do not
                      “raise string”
                         avoid using error message for making
                         discriminating error type
   begin
     if @avatar_url.empty?
       raise “Input should not be empty”
     elsif @avatar_url.valid?
       raise “Avatar URL invalid”
     end

   rescue RuntimeError => e
     # They are both instances of RuntimeError, can’t
     # rescue only the first one
   end
Copyright Dimelo SA                                        www.dimelo.com
Raising exceptions (cont)

    # excerpt from OpenSSL::SSLError
    # - invalid certificate, invalid hostname, protocol
    mismatch, etc.
    class SSLError
      def initialize(message)
        @message = message
      end
    […]

    # Client code
    rescue OpenSSL::SSL::SSLError => e
      if e.message =~ /sslv3 alert unexpected message/
        @conn.ssl_version="SSLv3“
        retry
      else
        raise


Copyright Dimelo SA                              www.dimelo.com
Getting latest exception



    begin
      raise ”Boom!"
    rescue
      puts $!.inspect
        # > #<RuntimeError: Boom!>
    end

    puts $!.inspect
      # > nil

    1 / 0 rescue nil
    puts $!.inspect
      # > nil


Copyright Dimelo SA                         www.dimelo.com
Intermezzo: caller method


                      TIP: ’caller’ gets you current execution
                      stacktrace

    def foo
        bar
    end

    def bar
        puts caller.inspect
    end

    foo
      # [”test_caller:2:in `foo'",
         ”test_caller.rb:9"]

Copyright Dimelo SA                                          www.dimelo.com
Re-raise exception – backtrace issue

    def foo
      Net::HTTP.get_response('does.not.exists', '/')
    rescue => e
      # some logging, or whatever..

      # raising a new error
      raise MyCustomException.new(‘Boom!’)
    end

    begin
      foo
    rescue => e
      puts e.backtrace.inspect
    end

    ["reraise_ex.rb:7:in `foo'", "reraise_ex.rb:11"]


Copyright Dimelo SA                                     www.dimelo.com
Re-raise exception – backtrace issue (2)


                      TIP: when reraising new exception, ensure you
                      don’t loose the backtrace

    def foo
       Net::HTTP.get_response('does.not.exists', '/')
    rescue => e

        # raising a new error, keeping old backtrace
        raise MyException, "And now.. boom!", e.backtrace

      # another option is to reraise same error:
      raise
    End



Copyright Dimelo SA                                        www.dimelo.com
Ensure
 begin
   puts '> always executed (even if there was
            normal flow’                        or there
   raise "error in normal flow”
   puts "< was not flow"
            normal an exception)
 rescue
           good place for code cleanup
   puts "> RESCUE - last error is <#{$!}>”
   raise "error in rescue”
   puts "< RESCUE"
 ensure
   puts "> ENSURE - last error is <#{$!}>”
   raise "error in ensure”
   puts "< ENSURE after raising"
 end

   ruby raise_rescue.rb
   > normal flow
   > RESCUE - last error is <error in normal flow>
   > ENSURE - last error is <error in rescue>
                                                 www.dimelo.com
   raise_rescue.rb:11: error in ensure (RuntimeError)
Copyright Dimelo SA
Catch/throw


            ruby ‘goto label’ implementation
            should be used on ‘expected’ situations

  # Sinatra code

  def last_modified(time)
    response['Last-Modified'] = time
    request.env['HTTP_IF_MODIFIED_SINCE'] > time
      throw :halt, response
    end
  end

    def invoke
         res = catch(:halt) { yield }
         ..
    end
Copyright Dimelo SA                               www.dimelo.com
Exceptions are slow ?

         https://github.com/vladzloteanu/ruby_exceptions_benchmark


  user_name = ““
                                                          ~ 0.3ns
  # Test with ‘if’
  nil if user_name.empty?

  # Test with raise/rescue
  begin                                                   ~ 74 ns
    raise "boom!" if user_name.empty?
  rescue => e
    nil
  end
                                                          ~ 2 ns
    # Test with try/catch
    catch(:error) do
       throw :error if user_name.empty?
    end
Copyright Dimelo SA                                                 www.dimelo.com
Exceptions are slow ? (2)


    class User < ActiveRecord::Base
      validates :email, :presence => true
    end

    user = User.new

    # Test with ‘if’
                                             ~ 1.5 ms
    nil unless user.save

    # Test with raise/rescue
                                             ~ 2 ms
    user.save! rescue nil




Copyright Dimelo SA                                   www.dimelo.com
Exceptions are slow ? (3)


                      TIP: Exceptions should be used only on
                      ‘exceptional’ situations
                         Use conditional constructs or ‘throw’ for
                         expected cases
                      TIP: Detect errors at low level, handle them at
                      high level




Copyright Dimelo SA                                          www.dimelo.com
Recap


                      Don’t rescue Exception!
                      Watch out for exceptions thrown by your libs
                      (Timeout::Error on Net::HTTP) Avoid putting
                      error type (only) in exception message
                      Avoid using exceptions for normal program
                      flow
                          Exceptions are slow(er)
                      Catch errors on the caller code, not on the
                      callee
                      When you reraise, ensure that you don’t
                      loose initial error’s backtrace
Copyright Dimelo SA                                        www.dimelo.com
Thank you!


                      Questions?




                      http://jobs.dimelo.com ;)



Copyright Dimelo SA                               www.dimelo.com

More Related Content

PPT
Ruby & Rails Error Handling
PDF
Ruby 程式語言綜覽簡介
PPTX
Linux System Administration
PPT
CPAP.com Introduction To Coding: Part 2
KEY
Code Fast, die() Early, Throw Structured Exceptions
PDF
Ruby Exceptions
PDF
21 ruby exceptions
PPT
Rails performance: Ruby GC tweaking
Ruby & Rails Error Handling
Ruby 程式語言綜覽簡介
Linux System Administration
CPAP.com Introduction To Coding: Part 2
Code Fast, die() Early, Throw Structured Exceptions
Ruby Exceptions
21 ruby exceptions
Rails performance: Ruby GC tweaking

Similar to Exceptions in Ruby - Tips and Tricks (20)

PPT
Enhance you APDEX.. naturally!
PDF
Exception Handling: Designing Robust Software in Ruby
ODP
Perl exceptions lightning talk
PPTX
Python Programming Essentials - M21 - Exception Handling
PDF
A exception ekon16
PPTX
Exception handling in c
PPTX
Exception handling in c
PDF
Vagrant for real
ODP
Troubleshooting Plone
PPTX
Exception handling.pptxnn h
PPTX
Error and exception in python
PDF
Getting modern with logging via log4perl
PDF
From dot net_to_rails
PPT
Exceptions in java
PDF
Exception Handling: Designing Robust Software in Ruby (with presentation note)
PDF
Course 102: Lecture 8: Composite Commands
PDF
Monkeybars in the Manor
PPTX
Namespaces
PDF
Questioning the status quo
ODP
Decompiling Java - SCAM2009 Presentation
Enhance you APDEX.. naturally!
Exception Handling: Designing Robust Software in Ruby
Perl exceptions lightning talk
Python Programming Essentials - M21 - Exception Handling
A exception ekon16
Exception handling in c
Exception handling in c
Vagrant for real
Troubleshooting Plone
Exception handling.pptxnn h
Error and exception in python
Getting modern with logging via log4perl
From dot net_to_rails
Exceptions in java
Exception Handling: Designing Robust Software in Ruby (with presentation note)
Course 102: Lecture 8: Composite Commands
Monkeybars in the Manor
Namespaces
Questioning the status quo
Decompiling Java - SCAM2009 Presentation
Ad

Recently uploaded (20)

PPTX
A Presentation on Artificial Intelligence
PPTX
Big Data Technologies - Introduction.pptx
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
Cloud computing and distributed systems.
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
sap open course for s4hana steps from ECC to s4
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Electronic commerce courselecture one. Pdf
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
A Presentation on Artificial Intelligence
Big Data Technologies - Introduction.pptx
Network Security Unit 5.pdf for BCA BBA.
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
20250228 LYD VKU AI Blended-Learning.pptx
Per capita expenditure prediction using model stacking based on satellite ima...
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Cloud computing and distributed systems.
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
MYSQL Presentation for SQL database connectivity
sap open course for s4hana steps from ECC to s4
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Spectral efficient network and resource selection model in 5G networks
Electronic commerce courselecture one. Pdf
Chapter 3 Spatial Domain Image Processing.pdf
Encapsulation_ Review paper, used for researhc scholars
Dropbox Q2 2025 Financial Results & Investor Presentation
Diabetes mellitus diagnosis method based random forest with bat algorithm
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Ad

Exceptions in Ruby - Tips and Tricks

  • 1. Exceptions in Ruby Tips & Tricks Vlad ZLOTEANU #ParisRB July 3, 2002 Software Engineer @ Dimelo @vladzloteanu Copyright Dimelo SA www.dimelo.com
  • 2. About me R&D Software Engineer @ Dimelo @vladzloteanu https://github.com/vladzloteanu Copyright Dimelo SA www.dimelo.com
  • 3. Exceptions (in Ruby) Failure: abnormal situation Exception: instance of Exception class (or instance of descendant) What: allows packaging info about the failure message string backtrace default behavior: program terminates (exits) Copyright Dimelo SA www.dimelo.com
  • 4. Exceptions classes in Ruby Exception NoMemoryError ScriptError LoadError SignalException Interrupt StandardError ArgumentError IOError ... RuntimeError SystemExit Copyright Dimelo SA www.dimelo.com
  • 5. Raising an exception class MyException < StandardError; end raise MyException.new raise # aka: raise RuntimeError.new raise “Boom!!” # aka raise RuntimeError.new(“Boom!!”) raise MyException, “Boom!!”, backtrace Copyright Dimelo SA www.dimelo.com
  • 6. Rescuing exceptions begin raise MyError.new("Booooom!") rescue MyError puts "MyError just hapened" rescue # Will rescue StandardError puts "a RuntimeError just happened" rescue Exception => e puts ”Exception: #{e.message}” e.backtrace.each{|line| puts “ > #{line}”} end Copyright Dimelo SA www.dimelo.com
  • 7. Intermezzo: Signals Signals – used to alert threads/processes (hardware faults, timer expiration, terminal activity, kill command, etc) SIGINT: CTRL+C in terminal (can be caught) SIGTERM: Sent by kill command by default (can be caught) SIGKILL: Uncatchable exception (kill -9) A SignalException is raised when a signal is received Copyright Dimelo SA www.dimelo.com
  • 8. What should we rescue? Copyright Dimelo SA www.dimelo.com
  • 9. Rescuing exceptions TIP: DON’T RESCUE ‘EXCEPTION’ class! begin while(true) ; end rescue Exception puts "Rescued exception; Will retry" retry end ^CRescued exception; WIll retry ^CRescued exception; WIll retry ^CRescued exception; WIll retry # Kill -9, baby! Copyright Dimelo SA www.dimelo.com
  • 10. Rescuing exceptions (third-party libraries) # You may think this will save you from all evil .. begin Net::HTTP.get_response(’external.resource', '/') rescue => e […] # Think again! # Excerpt from net/http.rb def connect s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) } Net::HTTP(Timeout library) may throw Timeout::Error < InterruptError Copyright Dimelo SA www.dimelo.com
  • 11. Rescuing exceptions (third-party libraries) # Solution is easy: begin Net::HTTP.get_response(’external.resource', '/') rescue StandardError, Timeout::Error => e […] Tip: Net::HTTP (and many of the gems that depend on it) can throw Timeout::Error – don’t forget to rescue it Copyright Dimelo SA www.dimelo.com
  • 12. Rescuing exceptions - cont TIP: (rescue blocks): rescue most specific errors first, most generic last TIP: inline rescue but avoid it because: No access to rescued exception Exceptions are slow (explained later) # Will return: DefaultDecorator @decorator = “foo”.constantize rescue DefaultDecorator Copyright Dimelo SA www.dimelo.com
  • 13. What should we raise? Copyright Dimelo SA www.dimelo.com
  • 14. Raising exceptions Tip: make your own exception class, do not “raise string” avoid using error message for making discriminating error type begin if @avatar_url.empty? raise “Input should not be empty” elsif @avatar_url.valid? raise “Avatar URL invalid” end rescue RuntimeError => e # They are both instances of RuntimeError, can’t # rescue only the first one end Copyright Dimelo SA www.dimelo.com
  • 15. Raising exceptions (cont) # excerpt from OpenSSL::SSLError # - invalid certificate, invalid hostname, protocol mismatch, etc. class SSLError def initialize(message) @message = message end […] # Client code rescue OpenSSL::SSL::SSLError => e if e.message =~ /sslv3 alert unexpected message/ @conn.ssl_version="SSLv3“ retry else raise Copyright Dimelo SA www.dimelo.com
  • 16. Getting latest exception begin raise ”Boom!" rescue puts $!.inspect # > #<RuntimeError: Boom!> end puts $!.inspect # > nil 1 / 0 rescue nil puts $!.inspect # > nil Copyright Dimelo SA www.dimelo.com
  • 17. Intermezzo: caller method TIP: ’caller’ gets you current execution stacktrace def foo bar end def bar puts caller.inspect end foo # [”test_caller:2:in `foo'", ”test_caller.rb:9"] Copyright Dimelo SA www.dimelo.com
  • 18. Re-raise exception – backtrace issue def foo Net::HTTP.get_response('does.not.exists', '/') rescue => e # some logging, or whatever.. # raising a new error raise MyCustomException.new(‘Boom!’) end begin foo rescue => e puts e.backtrace.inspect end ["reraise_ex.rb:7:in `foo'", "reraise_ex.rb:11"] Copyright Dimelo SA www.dimelo.com
  • 19. Re-raise exception – backtrace issue (2) TIP: when reraising new exception, ensure you don’t loose the backtrace def foo Net::HTTP.get_response('does.not.exists', '/') rescue => e # raising a new error, keeping old backtrace raise MyException, "And now.. boom!", e.backtrace # another option is to reraise same error: raise End Copyright Dimelo SA www.dimelo.com
  • 20. Ensure begin puts '> always executed (even if there was normal flow’ or there raise "error in normal flow” puts "< was not flow" normal an exception) rescue good place for code cleanup puts "> RESCUE - last error is <#{$!}>” raise "error in rescue” puts "< RESCUE" ensure puts "> ENSURE - last error is <#{$!}>” raise "error in ensure” puts "< ENSURE after raising" end ruby raise_rescue.rb > normal flow > RESCUE - last error is <error in normal flow> > ENSURE - last error is <error in rescue> www.dimelo.com raise_rescue.rb:11: error in ensure (RuntimeError) Copyright Dimelo SA
  • 21. Catch/throw ruby ‘goto label’ implementation should be used on ‘expected’ situations # Sinatra code def last_modified(time) response['Last-Modified'] = time request.env['HTTP_IF_MODIFIED_SINCE'] > time throw :halt, response end end def invoke res = catch(:halt) { yield } .. end Copyright Dimelo SA www.dimelo.com
  • 22. Exceptions are slow ? https://github.com/vladzloteanu/ruby_exceptions_benchmark user_name = ““ ~ 0.3ns # Test with ‘if’ nil if user_name.empty? # Test with raise/rescue begin ~ 74 ns raise "boom!" if user_name.empty? rescue => e nil end ~ 2 ns # Test with try/catch catch(:error) do throw :error if user_name.empty? end Copyright Dimelo SA www.dimelo.com
  • 23. Exceptions are slow ? (2) class User < ActiveRecord::Base validates :email, :presence => true end user = User.new # Test with ‘if’ ~ 1.5 ms nil unless user.save # Test with raise/rescue ~ 2 ms user.save! rescue nil Copyright Dimelo SA www.dimelo.com
  • 24. Exceptions are slow ? (3) TIP: Exceptions should be used only on ‘exceptional’ situations Use conditional constructs or ‘throw’ for expected cases TIP: Detect errors at low level, handle them at high level Copyright Dimelo SA www.dimelo.com
  • 25. Recap Don’t rescue Exception! Watch out for exceptions thrown by your libs (Timeout::Error on Net::HTTP) Avoid putting error type (only) in exception message Avoid using exceptions for normal program flow Exceptions are slow(er) Catch errors on the caller code, not on the callee When you reraise, ensure that you don’t loose initial error’s backtrace Copyright Dimelo SA www.dimelo.com
  • 26. Thank you! Questions? http://jobs.dimelo.com ;) Copyright Dimelo SA www.dimelo.com