Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Exceptions in Ruby - Tips and Tricks


Published on

Published in: Technology

Exceptions in Ruby - Tips and Tricks

  1. 1. Exceptions in Ruby Tips & Tricks Vlad ZLOTEANU #ParisRB July 3, 2002 Software Engineer @ Dimelo @vladzloteanuCopyright Dimelo SA
  2. 2. About me R&D Software Engineer @ Dimelo @vladzloteanu Dimelo SA
  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
  4. 4. Exceptions classes in Ruby Exception NoMemoryError ScriptError LoadError SignalException Interrupt StandardError ArgumentError IOError ... RuntimeError SystemExitCopyright Dimelo SA
  5. 5. Raising an exception class MyException < StandardError; end raise raise # aka: raise raise “Boom!!” # aka raise“Boom!!”) raise MyException, “Boom!!”, backtraceCopyright Dimelo SA
  6. 6. Rescuing exceptions begin raise"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}”} endCopyright Dimelo SA
  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 receivedCopyright Dimelo SA
  8. 8. What should we rescue?Copyright Dimelo SA
  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
  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) {, conn_port()) } Net::HTTP(Timeout library) may throw Timeout::Error < InterruptErrorCopyright Dimelo SA
  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 itCopyright Dimelo SA
  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 DefaultDecoratorCopyright Dimelo SA
  13. 13. What should we raise?Copyright Dimelo SA
  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 endCopyright Dimelo SA
  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 raiseCopyright Dimelo SA
  16. 16. Getting latest exception begin raise ”Boom!" rescue puts $!.inspect # > #<RuntimeError: Boom!> end puts $!.inspect # > nil 1 / 0 rescue nil puts $!.inspect # > nilCopyright Dimelo SA
  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
  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‘Boom!’) end begin foo rescue => e puts e.backtrace.inspect end ["reraise_ex.rb:7:in `foo", "reraise_ex.rb:11"]Copyright Dimelo SA
  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 EndCopyright Dimelo SA
  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> 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 } .. endCopyright Dimelo SA
  22. 22. Exceptions are slow ? 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? endCopyright Dimelo SA
  23. 23. Exceptions are slow ? (2) class User < ActiveRecord::Base validates :email, :presence => true end user = # Test with ‘if’ ~ 1.5 ms nil unless # Test with raise/rescue ~ 2 ms! rescue nilCopyright Dimelo SA
  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 levelCopyright Dimelo SA
  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 backtraceCopyright Dimelo SA
  26. 26. Thank you! Questions? ;)Copyright Dimelo SA