Slide 1

Slide 1 text

Exceptional Ruby

Slide 2

Slide 2 text

Tim Uruski timuruski.net

Slide 3

Slide 3 text

Avdi Grimm virtuouscode.com

Slide 4

Slide 4 text

• What is a failure? • How does Ruby handle errors? • What to do when code fails?

Slide 5

Slide 5 text

What is a failure?

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Human Fails Computer Fails Human Fails

Slide 10

Slide 10 text

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.”

Slide 11

Slide 11 text

begin do_work rescue handle_error end

Slide 12

Slide 12 text

begin do_work rescue puts $!.message end begin do_work rescue => my_error puts my_error.message end

Slide 13

Slide 13 text

begin do_work rescue # Handle StandardError end begin do_work rescue ArgumentError # Handle ArgumentError end

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

begin do_work rescue ErrorA # Handle ErrorA rescue ErrorB # Handle ErrorB end

Slide 17

Slide 17 text

parse(str) rescue 'parse failed'

Slide 18

Slide 18 text

def example do_work rescue => error puts error.message end def example begin do_work rescue => error puts error.message end end

Slide 19

Slide 19 text

begin do_work ensure cleanup end

Slide 20

Slide 20 text

begin file = File.new('greeting.txt', 'w') file << 'Hello, world!' ensure file.close end

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

File.open('greeting.txt', 'w') do |f| f << 'Hello, world!' end

Slide 23

Slide 23 text

begin do_work rescue => error warn error.message ensure cleanup end

Slide 24

Slide 24 text

begin raise 'Silent failure' ensure return end

Slide 25

Slide 25 text

begin do_work raise end

Slide 26

Slide 26 text

begin fail ErrorA rescue raise ErrorB end

Slide 27

Slide 27 text

raise #=> raise RuntimeError #=> raise RuntimeError.new #=>

Slide 28

Slide 28 text

raise "boom" #=> raise RuntimeError, "boom" #=> raise RuntimeError.new("boom") #=> raise RuntimeError.new("bada"), "boom" #=>

Slide 29

Slide 29 text

raise RuntimeError, "boom", ['./boom.rb:1'] raise RuntimeError, "boom", caller(1..10) raise RuntimeError, "boom", Thread.current.backtrace

Slide 30

Slide 30 text

Raise Internals

Slide 31

Slide 31 text

1. Get the exception 2. Set the backtrace and cause 3. Set global exception variable 4. Unwind the call stack

Slide 32

Slide 32 text

RuntimeError.exception #=> .exception #=> RuntimeError.exception('boom') #=> .exception('pow') #=>

Slide 33

Slide 33 text

exception.set_backtrace exception.cause

Slide 34

Slide 34 text

$! = exception require 'english' $ERROR_INFO = exception

Slide 35

Slide 35 text

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: # #=> t2: #

Slide 36

Slide 36 text

uncaught.rb:2:in `boom': unhandled exception from uncaught.rb:5:in `'

Slide 37

Slide 37 text

Kernel.raise

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

raise inside ensure? raise inside rescue?

Slide 40

Slide 40 text

begin do_work rescue Timeout handle_error retry end

Slide 41

Slide 41 text

tries = 0 begin do_work rescue TimeoutError tries += 1 retry if tries < 3 end

Slide 42

Slide 42 text

tries = 1 begin do_work rescue TimeoutError retry if tries < 3 tries += 1 # Never called... end

Slide 43

Slide 43 text

begin do_work rescue handle_failure else handle_success end

Slide 44

Slide 44 text

begin do_work puts 'success' rescue puts 'failure' end begin do_work rescue puts 'failure' else puts 'success' end

Slide 45

Slide 45 text

begin step_1 step_2 rescue revert_step_1 # Maybe? end

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

begin do_work rescue Timeout retry rescue Fatal log_failure else log_success ensure cleanup end

Slide 49

Slide 49 text

Exception

Slide 50

Slide 50 text

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 ...

Slide 51

Slide 51 text

class MyError < Exception # ... end

Slide 52

Slide 52 text

begin do_work rescue Exception handle_error end

Slide 53

Slide 53 text

Responding to failures

Slide 54

Slide 54 text

exit

Slide 55

Slide 55 text

$ ruby -e "sleep" ^C -e:1:in `sleep': Interrupt from -e:1:in `'

Slide 56

Slide 56 text

begin sleep rescue Interrupt exit end

Slide 57

Slide 57 text

begin do_work rescue exit 1 end Code = 0 -> Success Code = 1 -> Failure Code > 1 -> Custom Failure

Slide 58

Slide 58 text

Output an error message

Slide 59

Slide 59 text

begin do_work rescue => error puts error.message exit 1 end

Slide 60

Slide 60 text

begin do_work rescue => error warn error.message exit 1 end

Slide 61

Slide 61 text

begin do_work rescue => error abort error.message end

Slide 62

Slide 62 text

Exception Logging Service

Slide 63

Slide 63 text

class LogExceptions < Middlware def call(env) @app.call(env) rescue => error log_exception(error) end def log_exception(error) # ... end end

Slide 64

Slide 64 text

Errbit

Slide 65

Slide 65 text

exceptionalruby.com $15 USD $20 CAD