Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Exceptional Ruby

Exceptional Ruby

A tribute to Avdi Grimm's excellent book on error handling for the YYC Ruby group.

Tim Uruski

March 09, 2017
Tweet

More Decks by Tim Uruski

Other Decks in Programming

Transcript

  1. • What is a failure? • How does Ruby handle

    errors? • What to do when code fails?
  2. I realized that when a computer program had a fault,

    the machine could turn out errors millions of times faster than any human or group of humans. [...] I could actually program a machine to make more errors in a day than all human beings had made in the last 10,000 years. – Gerald M. Weinberg. “Errors.”
  3. begin do_work rescue ErrorA, ErrorB # Handle ErrorA or ErrorB

    end begin do_work rescue ErrorA # Handle ErrorA rescue ErrorB # Handle ErrorB end
  4. begin do_work rescue ErrorA, ErrorB # Handle ErrorA or ErrorB

    end begin do_work rescue ErrorA, ErrorB => error # Handle ErrorA or ErrorB as error end
  5. def example do_work rescue => error puts error.message end def

    example begin do_work rescue => error puts error.message end end
  6. def write_to(path) file = File.new(path, 'w') yield file ensure file.close

    end write_to('greeting.txt') do |file| file << 'Hello, world!' end
  7. raise "boom" #=> <RuntimeError "boom"> raise RuntimeError, "boom" #=> <RuntimeError

    "boom"> raise RuntimeError.new("boom") #=> <RuntimeError "boom"> raise RuntimeError.new("bada"), "boom" #=> <RuntimeError "boom">
  8. 1. Get the exception 2. Set the backtrace and cause

    3. Set global exception variable 4. Unwind the call stack
  9. t1 = Thread.new { raise 'boom' rescue $! } t2

    = Thread.new { raise 'pow' rescue $! } puts "main: #{$!}" puts "t1: #{t1.value.inspect}" puts "t2: #{t2.value.inspect}" #=> main: #=> t1: #<RuntimeError: boom> #=> t2: #<RuntimeError: pow>
  10. module RaiseExit
 def raise(msg_or_exc, msg=msg_or_exc, trace=caller) warn msg.to_s warn caller

    exit! end end module Kernel include RaiseExit if ENV['DEBUG'] end
  11. tries = 1 begin do_work rescue TimeoutError retry if tries

    < 3 tries += 1 # Never called... end
  12. begin do_work puts 'success' rescue puts 'failure' end begin do_work

    rescue puts 'failure' else puts 'success' end
  13. begin step_1 revert = false rescue revert = true end

    if revert revert_step_1 else step_2 end
  14. begin step_1 revert = false rescue revert = true end

    if revert revert_step_1 else step_2 end begin step_1 rescue revert_step_1 else step_2 end
  15. Exception NoMemoryError ScriptError LoadError NotImplementedError SyntaxError SecurityError SignalException Interrupt StandardError

    ArgumentError UncaughtThrowError EncodingError Encoding::CompatibilityError Encoding::ConverterNotFoundError Encoding::InvalidByteSequenceError Encoding::UndefinedConversionError FiberError IOError EOFError IndexError KeyError StopIteration ClosedQueueError LocalJumpError Math::DomainError NameError DidYouMean::Correctable NoMethodError RangeError FloatDomainError RegexpError RuntimeError SystemCallError Errno::EAGAIN IO::WaitReadable IO::EAGAINWaitReadable IO::WaitWritable IO::EAGAINWaitWritable Errno::EINPROGRESS IO::WaitReadable IO::EINPROGRESSWaitReadable IO::WaitWritable IO::EINPROGRESSWaitWritable ThreadError TypeError ZeroDivisionError SystemExit SystemStackError fatal Exception NoMemoryError ScriptError LoadError NotImplementedError SignalException StandardError ArgumentError LocalJumpError NameError NoMethodError RuntimeError SystemExit ...
  16. begin do_work rescue exit 1 end Code = 0 ->

    Success Code = 1 -> Failure Code > 1 -> Custom Failure
  17. class LogExceptions < Middlware def call(env) @app.call(env) rescue => error

    log_exception(error) end def log_exception(error) # ... end end