Successfully reported this slideshow.
Your SlideShare is downloading. ×

Exceptions in Ruby - Tips and Tricks

Ad

Exceptions in Ruby
                         Tips & Tricks

                                       Vlad ZLOTEANU
     #Pari...

Ad

About me


           R&D Software Engineer @ Dimelo

           @vladzloteanu

           https://github.com/vladzloteanu...

Ad

Exceptions (in Ruby)



         Failure: abnormal situation

      Exception: instance of Exception class (or instance of...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Upcoming SlideShare
Ruby & Rails Error Handling
Ruby & Rails Error Handling
Loading in …3
×

Check these out next

1 of 26 Ad
1 of 26 Ad

More Related Content

Exceptions in Ruby - Tips and Tricks

  1. 1. Exceptions in Ruby Tips & Tricks Vlad ZLOTEANU #ParisRB July 3, 2002 Software Engineer @ Dimelo @vladzloteanu Copyright Dimelo SA www.dimelo.com
  2. 2. About me R&D Software Engineer @ Dimelo @vladzloteanu https://github.com/vladzloteanu Copyright Dimelo SA www.dimelo.com
  3. 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. 4. Exceptions classes in Ruby Exception NoMemoryError ScriptError LoadError SignalException Interrupt StandardError ArgumentError IOError ... RuntimeError SystemExit Copyright Dimelo SA www.dimelo.com
  5. 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. 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. 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. 8. What should we rescue? Copyright Dimelo SA www.dimelo.com
  9. 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. 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. 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. 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. 13. What should we raise? Copyright Dimelo SA www.dimelo.com
  14. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 26. Thank you! Questions? http://jobs.dimelo.com ;) Copyright Dimelo SA www.dimelo.com

×