Slide 1

Slide 1 text

10 Things You Didn’t Know Ruby Could Do

Slide 2

Slide 2 text

James Edward Gray II ✤ I’m a regular on the Ruby Rogues podcast ✤ I’ve written a lot of documentation and code for Ruby ✤ This is my first time in Hawai’i

Slide 3

Slide 3 text

10 Things You Didn’t Know Rails Could Do RailsConf 2012

Slide 4

Slide 4 text

10 Things You Didn’t Know Rails Could Do RailsConf 2012 42

Slide 5

Slide 5 text

10 Things You Didn’t Know Ruby Could Do

Slide 6

Slide 6 text

10 Things You Didn’t Know Ruby Could Do 42

Slide 7

Slide 7 text

10 Things You Didn’t Know Ruby Could Do 42 101

Slide 8

Slide 8 text

MRI Ruby 1.9.3-p194…

Slide 9

Slide 9 text

Compiler Tricks

Slide 10

Slide 10 text

#1 Ruby Can Read Your Email Or any other text content with Ruby code in it Dear Rubyists: Did you know that Ruby can even read your emails? #!/usr/bin/env ruby -w puts "It's true." __END__ I told you it could. James Edward Gray II

Slide 11

Slide 11 text

#1 Ruby Can Read Your Email Or any other text content with Ruby code in it Dear Rubyists: Did you know that Ruby can even read your emails? #!/usr/bin/env ruby -w puts "It's true." __END__ I told you it could. James Edward Gray II $ ruby -x email.txt It's true.

Slide 12

Slide 12 text

#1 Ruby Can Read Your Email Or any other text content with Ruby code in it Dear Rubyists: Did you know that Ruby can even read your emails? #!/usr/bin/env ruby -w puts "It's true." __END__ I told you it could. James Edward Gray II $ ruby -x email.txt It's true.

Slide 13

Slide 13 text

#1 Ruby Can Read Your Email Or any other text content with Ruby code in it Dear Rubyists: Did you know that Ruby can even read your emails? #!/usr/bin/env ruby -w puts "It's true." __END__ I told you it could. James Edward Gray II $ ruby -x email.txt It's true.

Slide 14

Slide 14 text

#1 Ruby Can Read Your Email Or any other text content with Ruby code in it Dear Rubyists: Did you know that Ruby can even read your emails? #!/usr/bin/env ruby -w puts "It's true." __END__ I told you it could. James Edward Gray II $ ruby -x email.txt It's true.

Slide 15

Slide 15 text

puts DATA.read __END__ This is data! #2 From David Brady Storing Data in Your Code Or is it DATA?

Slide 16

Slide 16 text

puts DATA.read __END__ This is data! #2 From David Brady Storing Data in Your Code Or is it DATA?

Slide 17

Slide 17 text

puts DATA.read __END__ This is data! #2 From David Brady Storing Data in Your Code Or is it DATA?

Slide 18

Slide 18 text

puts DATA.read __END__ This is data! #2 From David Brady Storing Data in Your Code Or is it DATA? This is data!

Slide 19

Slide 19 text

DATA.rewind puts DATA.read __END__ #3 From David Brady A Cheat of a Quine Most quine rules disallow using IO

Slide 20

Slide 20 text

DATA.rewind puts DATA.read __END__ #3 From David Brady A Cheat of a Quine Most quine rules disallow using IO

Slide 21

Slide 21 text

DATA.rewind puts DATA.read __END__ #3 From David Brady A Cheat of a Quine Most quine rules disallow using IO DATA.rewind puts DATA.read __END__

Slide 22

Slide 22 text

#4 From Ara T. Howard There Can Be Only One Use a tricky lock to make your script exclusive DATA.flock(File::LOCK_EX | File::LOCK_NB) or abort "Already running." trap("INT", "EXIT") puts "Running..." loop do sleep end __END__ DO NOT DELETE: used for locking

Slide 23

Slide 23 text

#4 From Ara T. Howard There Can Be Only One Use a tricky lock to make your script exclusive DATA.flock(File::LOCK_EX | File::LOCK_NB) or abort "Already running." trap("INT", "EXIT") puts "Running..." loop do sleep end __END__ DO NOT DELETE: used for locking

Slide 24

Slide 24 text

#4 From Ara T. Howard There Can Be Only One Use a tricky lock to make your script exclusive DATA.flock(File::LOCK_EX | File::LOCK_NB) or abort "Already running." trap("INT", "EXIT") puts "Running..." loop do sleep end __END__ DO NOT DELETE: used for locking

Slide 25

Slide 25 text

#4 From Ara T. Howard There Can Be Only One Use a tricky lock to make your script exclusive DATA.flock(File::LOCK_EX | File::LOCK_NB) or abort "Already running." trap("INT", "EXIT") puts "Running..." loop do sleep end __END__ DO NOT DELETE: used for locking $ ruby lock.rb Running... ^Z [1]+ Stopped ruby lock.rb $ ruby lock.rb Already running. $ fg ruby lock.rb ^C$ ruby lock.rb Running...

Slide 26

Slide 26 text

#5 Your Source File, The Database A dirty trick to carry some data with the code pos = DATA.pos list = DATA.readlines if ARGV.empty? puts list.shift else list.push(*ARGV) end DATA.reopen(__FILE__, "r+") DATA.truncate(pos) DATA.seek(pos) DATA.puts list __END__ Service-Oriented Design with Ruby and Rails Practical Object-Oriented Design in Ruby

Slide 27

Slide 27 text

#5 Your Source File, The Database A dirty trick to carry some data with the code pos = DATA.pos list = DATA.readlines if ARGV.empty? puts list.shift else list.push(*ARGV) end DATA.reopen(__FILE__, "r+") DATA.truncate(pos) DATA.seek(pos) DATA.puts list __END__ Service-Oriented Design with Ruby and Rails Practical Object-Oriented Design in Ruby

Slide 28

Slide 28 text

#5 Your Source File, The Database A dirty trick to carry some data with the code pos = DATA.pos list = DATA.readlines if ARGV.empty? puts list.shift else list.push(*ARGV) end DATA.reopen(__FILE__, "r+") DATA.truncate(pos) DATA.seek(pos) DATA.puts list __END__ Service-Oriented Design with Ruby and Rails Practical Object-Oriented Design in Ruby $ ruby reading_list.rb \ 'Service-Oriented Design with Ruby and Rails' \ 'Practical Object-Oriented Design in Ruby' $ ruby reading_list.rb Service-Oriented Design with Ruby and Rails $ ruby reading_list.rb Practical Object-Oriented Design in Ruby

Slide 29

Slide 29 text

#6 From Eric Hodel See How Ruby Reads Your Code All you have to do is ask $ ruby -e 'puts { is_this_a_block }' --dump parsetree ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_ITER (line: 1) # +- nd_iter: # | @ NODE_FCALL (line: 1) # | +- nd_mid: :puts # | +- nd_args: # | (null node) …

Slide 30

Slide 30 text

#6 From Eric Hodel See How Ruby Reads Your Code All you have to do is ask $ ruby -e 'puts { is_this_a_block }' --dump parsetree ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_ITER (line: 1) # +- nd_iter: # | @ NODE_FCALL (line: 1) # | +- nd_mid: :puts # | +- nd_args: # | (null node) …

Slide 31

Slide 31 text

#6 From Eric Hodel See How Ruby Reads Your Code All you have to do is ask $ ruby -e 'puts { is_this_a_block }' --dump parsetree ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_ITER (line: 1) # +- nd_iter: # | @ NODE_FCALL (line: 1) # | +- nd_mid: :puts # | +- nd_args: # | (null node) …

Slide 32

Slide 32 text

#7 From Eric Hodel Available With Comments Ruby can even explain it to you $ ruby -e 'puts { is_this_a_block }' --dump parsetree_with_comment … # @ NODE_ITER (line: 1) # | # method call with block # | # format: [nd_iter] { [nd_body] } # | # example: 3.times { foo } # +- nd_iter (iteration receiver): # | @ NODE_FCALL (line: 1) # | | # function call # | | # format: [nd_mid]([nd_args]) # | | # example: foo(1) # | +- nd_mid (method id): :puts # | +- nd_args (arguments): # | (null node) …

Slide 33

Slide 33 text

#7 From Eric Hodel Available With Comments Ruby can even explain it to you $ ruby -e 'puts { is_this_a_block }' --dump parsetree_with_comment … # @ NODE_ITER (line: 1) # | # method call with block # | # format: [nd_iter] { [nd_body] } # | # example: 3.times { foo } # +- nd_iter (iteration receiver): # | @ NODE_FCALL (line: 1) # | | # function call # | | # format: [nd_mid]([nd_args]) # | | # example: foo(1) # | +- nd_mid (method id): :puts # | +- nd_args (arguments): # | (null node) …

Slide 34

Slide 34 text

#7 From Eric Hodel Available With Comments Ruby can even explain it to you $ ruby -e 'puts { is_this_a_block }' --dump parsetree_with_comment … # @ NODE_ITER (line: 1) # | # method call with block # | # format: [nd_iter] { [nd_body] } # | # example: 3.times { foo } # +- nd_iter (iteration receiver): # | @ NODE_FCALL (line: 1) # | | # function call # | | # format: [nd_mid]([nd_args]) # | | # example: foo(1) # | +- nd_mid (method id): :puts # | +- nd_args (arguments): # | (null node) …

Slide 35

Slide 35 text

#8 From Eric Hodel View the Machine Instructions Alternately, you can view the virtual machine instructions $ ruby -e 'ft = 40 + 2; p ft' --dump insns == disasm: @-e>====================== local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 2] ft 0000 trace 1 ( 1) 0002 putobject 40 0004 putobject 2 0006 opt_plus 0008 setdynamic ft, 0 0011 trace 1 0013 putself 0014 getdynamic ft, 0 0017 send :p, 1, nil, 8, 0023 leave

Slide 36

Slide 36 text

#8 From Eric Hodel View the Machine Instructions Alternately, you can view the virtual machine instructions $ ruby -e 'ft = 40 + 2; p ft' --dump insns == disasm: @-e>====================== local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1) [ 2] ft 0000 trace 1 ( 1) 0002 putobject 40 0004 putobject 2 0006 opt_plus 0008 setdynamic ft, 0 0011 trace 1 0013 putself 0014 getdynamic ft, 0 0017 send :p, 1, nil, 8, 0023 leave

Slide 37

Slide 37 text

#9 From Eric Hodel Watch Ruby’s Parser Work See how parse.y thinks $ ruby -e 'arg = ARGV.shift or abort "No arg"' --dump yydebug Starting parse Entering state 0 Reducing stack by rule 1 (line 782): -> $$ = nterm $@1 () Stack now 0 Entering state 2 Reading a token: Next token is token tIDENTIFIER () Shifting token tIDENTIFIER () Entering state 35 Reading a token: Next token is token '=' () Reducing stack by rule 474 (line 4275): $1 = token tIDENTIFIER () -> $$ = nterm user_variable () Stack now 0 2 …

Slide 38

Slide 38 text

#9 From Eric Hodel Watch Ruby’s Parser Work See how parse.y thinks $ ruby -e 'arg = ARGV.shift or abort "No arg"' --dump yydebug Starting parse Entering state 0 Reducing stack by rule 1 (line 782): -> $$ = nterm $@1 () Stack now 0 Entering state 2 Reading a token: Next token is token tIDENTIFIER () Shifting token tIDENTIFIER () Entering state 35 Reading a token: Next token is token '=' () Reducing stack by rule 474 (line 4275): $1 = token tIDENTIFIER () -> $$ = nterm user_variable () Stack now 0 2 …

Slide 39

Slide 39 text

#10 From Eric Hodel Inspecting the Source For those of us who belong to “The Ridiculous Church of 80-Character Lines” SCRIPT_LINES__ = { } require_relative "better_be_well_formed_code" # format: {"file_name.rb" => ["line 1", "line 2", ...]} if SCRIPT_LINES__.values.flatten.any? { |line| line.size > 80 } abort "Clean up your code first!" end

Slide 40

Slide 40 text

#10 From Eric Hodel Inspecting the Source For those of us who belong to “The Ridiculous Church of 80-Character Lines” SCRIPT_LINES__ = { } require_relative "better_be_well_formed_code" # format: {"file_name.rb" => ["line 1", "line 2", ...]} if SCRIPT_LINES__.values.flatten.any? { |line| line.size > 80 } abort "Clean up your code first!" end

Slide 41

Slide 41 text

#10 From Eric Hodel Inspecting the Source For those of us who belong to “The Ridiculous Church of 80-Character Lines” SCRIPT_LINES__ = { } require_relative "better_be_well_formed_code" # format: {"file_name.rb" => ["line 1", "line 2", ...]} if SCRIPT_LINES__.values.flatten.any? { |line| line.size > 80 } abort "Clean up your code first!" end

Slide 42

Slide 42 text

#10 From Eric Hodel Inspecting the Source For those of us who belong to “The Ridiculous Church of 80-Character Lines” SCRIPT_LINES__ = { } require_relative "better_be_well_formed_code" # format: {"file_name.rb" => ["line 1", "line 2", ...]} if SCRIPT_LINES__.values.flatten.any? { |line| line.size > 80 } abort "Clean up your code first!" end

Slide 43

Slide 43 text

#11 From Aaron Patterson Tail Call Optimization Yeah, it’s in there def factorial(n, result = 1) if n == 1 result else factorial(n - 1, n * result) end end p factorial(30_000)

Slide 44

Slide 44 text

#11 From Aaron Patterson Tail Call Optimization Yeah, it’s in there def factorial(n, result = 1) if n == 1 result else factorial(n - 1, n * result) end end p factorial(30_000) /…/factorial.rb:2: stack level too deep (SystemStackError)

Slide 45

Slide 45 text

RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false } eval <

Slide 46

Slide 46 text

RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false } eval <

Slide 47

Slide 47 text

RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false } eval <

Slide 48

Slide 48 text

RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false } eval <

Slide 49

Slide 49 text

Syntax

Slide 50

Slide 50 text

#12 Pass Some Arguments Up Helps add functionality to methods class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super(a, b) end end Child.new.show_args(:a, :b, :c)

Slide 51

Slide 51 text

#12 Pass Some Arguments Up Helps add functionality to methods class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super(a, b) end end Child.new.show_args(:a, :b, :c)

Slide 52

Slide 52 text

#12 Pass Some Arguments Up Helps add functionality to methods class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super(a, b) end end Child.new.show_args(:a, :b, :c) [:a, :b]

Slide 53

Slide 53 text

#13 From Daniel Azuma Pass The Same Arguments Up Remember, super is a magical keyword class Parent def show_args(*args, &block) p [*args, block] end end class Child < Parent def show_args(a, b, c) super end end Child.new.show_args(:a, :b, :c) { :block }

Slide 54

Slide 54 text

#13 From Daniel Azuma Pass The Same Arguments Up Remember, super is a magical keyword class Parent def show_args(*args, &block) p [*args, block] end end class Child < Parent def show_args(a, b, c) super end end Child.new.show_args(:a, :b, :c) { :block }

Slide 55

Slide 55 text

#13 From Daniel Azuma Pass The Same Arguments Up Remember, super is a magical keyword class Parent def show_args(*args, &block) p [*args, block] end end class Child < Parent def show_args(a, b, c) super end end Child.new.show_args(:a, :b, :c) { :block } [:a, :b, :c, #]

Slide 56

Slide 56 text

#14 From Daniel Azuma Pass Modified Arguments Up Scary magic class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) a.upcase! # not too surprising b = "Wow!" # very surprising super end end Child.new.show_args("a", "b", "c")

Slide 57

Slide 57 text

#14 From Daniel Azuma Pass Modified Arguments Up Scary magic class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) a.upcase! # not too surprising b = "Wow!" # very surprising super end end Child.new.show_args("a", "b", "c")

Slide 58

Slide 58 text

#14 From Daniel Azuma Pass Modified Arguments Up Scary magic class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) a.upcase! # not too surprising b = "Wow!" # very surprising super end end Child.new.show_args("a", "b", "c")

Slide 59

Slide 59 text

#14 From Daniel Azuma Pass Modified Arguments Up Scary magic class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) a.upcase! # not too surprising b = "Wow!" # very surprising super end end Child.new.show_args("a", "b", "c") ["A", "Wow!", "c"]

Slide 60

Slide 60 text

#15 From Daniel Azuma Pass No Arguments Up The parentheses are required class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super() end end Child.new.show_args(:a, :b, :c)

Slide 61

Slide 61 text

#15 From Daniel Azuma Pass No Arguments Up The parentheses are required class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super() end end Child.new.show_args(:a, :b, :c)

Slide 62

Slide 62 text

#15 From Daniel Azuma Pass No Arguments Up The parentheses are required class Parent def show_args(*args) p args end end class Child < Parent def show_args(a, b, c) super() end end Child.new.show_args(:a, :b, :c) []

Slide 63

Slide 63 text

#16 From Marten Veldthuis Pass No Block Up How to make a block disappear class Parent def show_block(&block) p block end end class Child < Parent def show_block super(&nil) end end Child.new.show_block { :block }

Slide 64

Slide 64 text

#16 From Marten Veldthuis Pass No Block Up How to make a block disappear class Parent def show_block(&block) p block end end class Child < Parent def show_block super(&nil) end end Child.new.show_block { :block }

Slide 65

Slide 65 text

#16 From Marten Veldthuis Pass No Block Up How to make a block disappear class Parent def show_block(&block) p block end end class Child < Parent def show_block super(&nil) end end Child.new.show_block { :block } nil

Slide 66

Slide 66 text

#17 From Avdi Grimm Asking If You Can Pass Up Ruby will tell you if a parent method is available to delegate to class DontDelegateToMe; end class DelegateToMe; def delegate; "DelegateToMe" end end module DelegateIfICan def delegate if defined? super "Modified: #{super}" else "DelegateIfICan" end end end puts DelegateToMe.new.extend(DelegateIfICan).delegate puts DontDelegateToMe.new.extend(DelegateIfICan).delegate

Slide 67

Slide 67 text

#17 From Avdi Grimm Asking If You Can Pass Up Ruby will tell you if a parent method is available to delegate to class DontDelegateToMe; end class DelegateToMe; def delegate; "DelegateToMe" end end module DelegateIfICan def delegate if defined? super "Modified: #{super}" else "DelegateIfICan" end end end puts DelegateToMe.new.extend(DelegateIfICan).delegate puts DontDelegateToMe.new.extend(DelegateIfICan).delegate

Slide 68

Slide 68 text

#17 From Avdi Grimm Asking If You Can Pass Up Ruby will tell you if a parent method is available to delegate to class DontDelegateToMe; end class DelegateToMe; def delegate; "DelegateToMe" end end module DelegateIfICan def delegate if defined? super "Modified: #{super}" else "DelegateIfICan" end end end puts DelegateToMe.new.extend(DelegateIfICan).delegate puts DontDelegateToMe.new.extend(DelegateIfICan).delegate Modified: DelegateToMe DelegateIfICan

Slide 69

Slide 69 text

#18 Lambda Literals Also known as the “stabby lambda” minimal = -> { p :called } minimal.call loaded = ->(arg, default = :default, &block) { p [arg, default, block] } loaded.call(:arg) { :block }

Slide 70

Slide 70 text

#18 Lambda Literals Also known as the “stabby lambda” minimal = -> { p :called } minimal.call loaded = ->(arg, default = :default, &block) { p [arg, default, block] } loaded.call(:arg) { :block }

Slide 71

Slide 71 text

#18 Lambda Literals Also known as the “stabby lambda” minimal = -> { p :called } minimal.call loaded = ->(arg, default = :default, &block) { p [arg, default, block] } loaded.call(:arg) { :block }

Slide 72

Slide 72 text

#18 Lambda Literals Also known as the “stabby lambda” minimal = -> { p :called } minimal.call loaded = ->(arg, default = :default, &block) { p [arg, default, block] } loaded.call(:arg) { :block } :called [:arg, :default, #]

Slide 73

Slide 73 text

#19 From Benjamin Fleischer Or Make Your Own UTF-8 Syntax Perl 6 has nothing on us # encoding: UTF-8 module Kernel alias_method :λ, :lambda end l = λ { p :called } l.call

Slide 74

Slide 74 text

#19 From Benjamin Fleischer Or Make Your Own UTF-8 Syntax Perl 6 has nothing on us # encoding: UTF-8 module Kernel alias_method :λ, :lambda end l = λ { p :called } l.call

Slide 75

Slide 75 text

#19 From Benjamin Fleischer Or Make Your Own UTF-8 Syntax Perl 6 has nothing on us # encoding: UTF-8 module Kernel alias_method :λ, :lambda end l = λ { p :called } l.call

Slide 76

Slide 76 text

#19 From Benjamin Fleischer Or Make Your Own UTF-8 Syntax Perl 6 has nothing on us # encoding: UTF-8 module Kernel alias_method :λ, :lambda end l = λ { p :called } l.call :called

Slide 77

Slide 77 text

#20 Blocks Can Now Take Blocks This trick helps with metaprogramming var = :var object = Object.new object.define_singleton_method(:show_var_and_block) do |&block| p [var, block] end object.show_var_and_block { :block }

Slide 78

Slide 78 text

#20 Blocks Can Now Take Blocks This trick helps with metaprogramming var = :var object = Object.new object.define_singleton_method(:show_var_and_block) do |&block| p [var, block] end object.show_var_and_block { :block }

Slide 79

Slide 79 text

#20 Blocks Can Now Take Blocks This trick helps with metaprogramming var = :var object = Object.new object.define_singleton_method(:show_var_and_block) do |&block| p [var, block] end object.show_var_and_block { :block } [:var, #]

Slide 80

Slide 80 text

#21 The New Call Anything Syntax Or should we call it the “Call Me Maybe” syntax? class Callable def call :my_own_class end end p -> { :lambda }.() p [ ].method(:class).() p Callable.new.()

Slide 81

Slide 81 text

#21 The New Call Anything Syntax Or should we call it the “Call Me Maybe” syntax? class Callable def call :my_own_class end end p -> { :lambda }.() p [ ].method(:class).() p Callable.new.()

Slide 82

Slide 82 text

#21 The New Call Anything Syntax Or should we call it the “Call Me Maybe” syntax? class Callable def call :my_own_class end end p -> { :lambda }.() p [ ].method(:class).() p Callable.new.() :lambda Array :my_own_class

Slide 83

Slide 83 text

#22 From David Brady Symbol#to_proc Takes Arguments This probably isn’t a great thing to abuse, but still… shiny to_s_proc = :to_s.to_proc # or: lambda(&:to_s) receiver = 255 arg = 16 puts to_s_proc[receiver, arg]

Slide 84

Slide 84 text

#22 From David Brady Symbol#to_proc Takes Arguments This probably isn’t a great thing to abuse, but still… shiny to_s_proc = :to_s.to_proc # or: lambda(&:to_s) receiver = 255 arg = 16 puts to_s_proc[receiver, arg]

Slide 85

Slide 85 text

#22 From David Brady Symbol#to_proc Takes Arguments This probably isn’t a great thing to abuse, but still… shiny to_s_proc = :to_s.to_proc # or: lambda(&:to_s) receiver = 255 arg = 16 puts to_s_proc[receiver, arg] ff

Slide 86

Slide 86 text

#23 inject() a Symbol This is even shorter than using Symbol#to_proc p (1..10).inject(:*) # instead of: inject(&:*)

Slide 87

Slide 87 text

#23 inject() a Symbol This is even shorter than using Symbol#to_proc p (1..10).inject(:*) # instead of: inject(&:*)

Slide 88

Slide 88 text

#23 inject() a Symbol This is even shorter than using Symbol#to_proc p (1..10).inject(:*) # instead of: inject(&:*) 3628800

Slide 89

Slide 89 text

#24 From Piotr Szotkowski Case on Ranges Case statements work with anything that defines ===, like Range age = rand(1..100) p age case age when -Float::INFINITY..20 puts "You're too young." when 21..64 puts "You are the right age." when 65..Float::INFINITY puts "You're too old." end

Slide 90

Slide 90 text

#24 From Piotr Szotkowski Case on Ranges Case statements work with anything that defines ===, like Range age = rand(1..100) p age case age when -Float::INFINITY..20 puts "You're too young." when 21..64 puts "You are the right age." when 65..Float::INFINITY puts "You're too old." end

Slide 91

Slide 91 text

#24 From Piotr Szotkowski Case on Ranges Case statements work with anything that defines ===, like Range age = rand(1..100) p age case age when -Float::INFINITY..20 puts "You're too young." when 21..64 puts "You are the right age." when 65..Float::INFINITY puts "You're too old." end 96 You're too old.

Slide 92

Slide 92 text

#25 From Piotr Szotkowski Case on Date Ranges Range objects work in a case and Date objects work as a Range endpoint require "date" start_of_aloha_ruby_conf = Date.new(2012, 10, 8) end_of_aloha_ruby_conf = Date.new(2012, 10, 9) case Date.today when Date.new...start_of_aloha_ruby_conf puts "Anticipation is building." when start_of_aloha_ruby_conf..end_of_aloha_ruby_conf puts "Mind being blown." when (end_of_aloha_ruby_conf + 1)..Date::Infinity puts "You've learned some Ruby while in paradise." end

Slide 93

Slide 93 text

#25 From Piotr Szotkowski Case on Date Ranges Range objects work in a case and Date objects work as a Range endpoint require "date" start_of_aloha_ruby_conf = Date.new(2012, 10, 8) end_of_aloha_ruby_conf = Date.new(2012, 10, 9) case Date.today when Date.new...start_of_aloha_ruby_conf puts "Anticipation is building." when start_of_aloha_ruby_conf..end_of_aloha_ruby_conf puts "Mind being blown." when (end_of_aloha_ruby_conf + 1)..Date::Infinity puts "You've learned some Ruby while in paradise." end

Slide 94

Slide 94 text

#25 From Piotr Szotkowski Case on Date Ranges Range objects work in a case and Date objects work as a Range endpoint require "date" start_of_aloha_ruby_conf = Date.new(2012, 10, 8) end_of_aloha_ruby_conf = Date.new(2012, 10, 9) case Date.today when Date.new...start_of_aloha_ruby_conf puts "Anticipation is building." when start_of_aloha_ruby_conf..end_of_aloha_ruby_conf puts "Mind being blown." when (end_of_aloha_ruby_conf + 1)..Date::Infinity puts "You've learned some Ruby while in paradise." end Mind being blown.

Slide 95

Slide 95 text

require "prime" n = rand(1..10) p n case n when lambda(&:prime?) puts "This number is prime." when lambda(&:even?) puts "This number is even." else puts "This number is odd." end #26 From Piotr Szotkowski Case on Lambdas As of Ruby 1.9, lambdas (Proc objects) also define ===

Slide 96

Slide 96 text

require "prime" n = rand(1..10) p n case n when lambda(&:prime?) puts "This number is prime." when lambda(&:even?) puts "This number is even." else puts "This number is odd." end #26 From Piotr Szotkowski Case on Lambdas As of Ruby 1.9, lambdas (Proc objects) also define ===

Slide 97

Slide 97 text

require "prime" n = rand(1..10) p n case n when lambda(&:prime?) puts "This number is prime." when lambda(&:even?) puts "This number is even." else puts "This number is odd." end #26 From Piotr Szotkowski Case on Lambdas As of Ruby 1.9, lambdas (Proc objects) also define === 9 This number is odd.

Slide 98

Slide 98 text

#27 From Benjamin Fleischer A Formatted Output Syntax Like sprintf(), but even shorter def debug(name, content) puts "%s: %p" % [name, content] end debug "Num", 42 debug "Objects", {"Grays" => %w[James Dana Summer]}

Slide 99

Slide 99 text

#27 From Benjamin Fleischer A Formatted Output Syntax Like sprintf(), but even shorter def debug(name, content) puts "%s: %p" % [name, content] end debug "Num", 42 debug "Objects", {"Grays" => %w[James Dana Summer]}

Slide 100

Slide 100 text

#27 From Benjamin Fleischer A Formatted Output Syntax Like sprintf(), but even shorter def debug(name, content) puts "%s: %p" % [name, content] end debug "Num", 42 debug "Objects", {"Grays" => %w[James Dana Summer]}

Slide 101

Slide 101 text

#27 From Benjamin Fleischer A Formatted Output Syntax Like sprintf(), but even shorter def debug(name, content) puts "%s: %p" % [name, content] end debug "Num", 42 debug "Objects", {"Grays" => %w[James Dana Summer]} Num: 42 Objects: {"Grays"=>["James", "Dana", "Summer"]}

Slide 102

Slide 102 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end

Slide 103

Slide 103 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end

Slide 104

Slide 104 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end

Slide 105

Slide 105 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end

Slide 106

Slide 106 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end

Slide 107

Slide 107 text

#28 Or By Name A Ruby 1.9 enhancement to these format patterns order = {"Item 1" => 10, "Item 2" => 19.99, "Item 3" => 4.50} item_size = (["Item"] + order.keys).map(&:size).max price_size = ( ["Price".size] + order.values.map { |price| ("$%.2f" % price).size } ).max puts "%-#{item_size}s | %#{price_size}s" % {item: "Item", price: "Price"} puts "-" * (item_size + price_size + 3) order.each do |item, price| puts "%-#{item_size}s | $%#{price_size - 1}.2f" % {item: item, price: price} end Item | Price --------------- Item 1 | $10.00 Item 2 | $19.99 Item 3 | $ 4.50

Slide 108

Slide 108 text

#29 Multiple HEREDOC’s Ruby is smart enough to pick several off the same line def create_post(title, summary, body) # ... end create_post("Aloha RubyConf", <

Slide 109

Slide 109 text

#29 Multiple HEREDOC’s Ruby is smart enough to pick several off the same line def create_post(title, summary, body) # ... end create_post("Aloha RubyConf", <

Slide 110

Slide 110 text

#29 Multiple HEREDOC’s Ruby is smart enough to pick several off the same line def create_post(title, summary, body) # ... end create_post("Aloha RubyConf", <

Slide 111

Slide 111 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var || 42 end end p WarnMe.new.var

Slide 112

Slide 112 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var || 42 end end p WarnMe.new.var

Slide 113

Slide 113 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var || 42 end end p WarnMe.new.var

Slide 114

Slide 114 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var || 42 end end p WarnMe.new.var /…/dodging_a_warning.rb:5: warning: instance variable @var not initialized 42

Slide 115

Slide 115 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var ||= 42 end end p WarnMe.new.var

Slide 116

Slide 116 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var ||= 42 end end p WarnMe.new.var

Slide 117

Slide 117 text

#30 Dodging a Warning The “or equals” operator includes a defined? check $VERBOSE = true class WarnMe def var @var ||= 42 end end p WarnMe.new.var 42

Slide 118

Slide 118 text

#31 From Pete Higgins Shortcut Variable Interpolation The braces are optional with a sigil toting variable @instance = :instance @@class = :class $global = :global puts "#@instance, #@@class, and #$global variables don't need braces."

Slide 119

Slide 119 text

#31 From Pete Higgins Shortcut Variable Interpolation The braces are optional with a sigil toting variable @instance = :instance @@class = :class $global = :global puts "#@instance, #@@class, and #$global variables don't need braces."

Slide 120

Slide 120 text

#31 From Pete Higgins Shortcut Variable Interpolation The braces are optional with a sigil toting variable @instance = :instance @@class = :class $global = :global puts "#@instance, #@@class, and #$global variables don't need braces." instance, class, and global variables don't need braces.

Slide 121

Slide 121 text

#32 From Michael Foley Variables From a Regex It must be a literal regex and it must be on the left side of the match operator if /\A(?\w+),\s*(?\w+)\z/ =~ "Gray, James" puts "#{first} #{last}" end

Slide 122

Slide 122 text

#32 From Michael Foley Variables From a Regex It must be a literal regex and it must be on the left side of the match operator if /\A(?\w+),\s*(?\w+)\z/ =~ "Gray, James" puts "#{first} #{last}" end

Slide 123

Slide 123 text

#32 From Michael Foley Variables From a Regex It must be a literal regex and it must be on the left side of the match operator if /\A(?\w+),\s*(?\w+)\z/ =~ "Gray, James" puts "#{first} #{last}" end James Gray

Slide 124

Slide 124 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, ignore, ignore| puts name end

Slide 125

Slide 125 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, ignore, ignore| puts name end

Slide 126

Slide 126 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, ignore, ignore| puts name end /…/the_unused_variable.rb:3: duplicated argument name

Slide 127

Slide 127 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, _, _| puts name end

Slide 128

Slide 128 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, _, _| puts name end

Slide 129

Slide 129 text

#33 From Paul Battley The Unused Variable Ruby supports the convention of _ as an unused variable name [ ["James", "Gray", 36], ["Dana", "Gray", 37], ["Summer", "Gray", 2] ].each do |name, _, _| puts name end James Dana Summer

Slide 130

Slide 130 text

Data Structures

Slide 131

Slide 131 text

#34 From Nathaniel Barnes Objects Can Contain Themselves Array Inception ring = [:one, [:two, [:three]]] ring.last.last << ring position = ring 4.times do puts position.first position = position.last end

Slide 132

Slide 132 text

#34 From Nathaniel Barnes Objects Can Contain Themselves Array Inception ring = [:one, [:two, [:three]]] ring.last.last << ring position = ring 4.times do puts position.first position = position.last end

Slide 133

Slide 133 text

#34 From Nathaniel Barnes Objects Can Contain Themselves Array Inception ring = [:one, [:two, [:three]]] ring.last.last << ring position = ring 4.times do puts position.first position = position.last end one two three one

Slide 134

Slide 134 text

#35 Or Just Use cycle() Ruby 1.9 has a nice new repeating iterator ring = %w[one two three].cycle puts ring.take(4)

Slide 135

Slide 135 text

#35 Or Just Use cycle() Ruby 1.9 has a nice new repeating iterator ring = %w[one two three].cycle puts ring.take(4)

Slide 136

Slide 136 text

#35 Or Just Use cycle() Ruby 1.9 has a nice new repeating iterator ring = %w[one two three].cycle puts ring.take(4)

Slide 137

Slide 137 text

#35 Or Just Use cycle() Ruby 1.9 has a nice new repeating iterator ring = %w[one two three].cycle puts ring.take(4) one two three one

Slide 138

Slide 138 text

#36 Associative Arrays An ordered (by your criteria) Hash-like thing aa = [ %w[James Gray], %w[Yukihiro Matsumoto] ] p aa.assoc("James") p aa.rassoc("Matsumoto")

Slide 139

Slide 139 text

#36 Associative Arrays An ordered (by your criteria) Hash-like thing aa = [ %w[James Gray], %w[Yukihiro Matsumoto] ] p aa.assoc("James") p aa.rassoc("Matsumoto")

Slide 140

Slide 140 text

#36 Associative Arrays An ordered (by your criteria) Hash-like thing aa = [ %w[James Gray], %w[Yukihiro Matsumoto] ] p aa.assoc("James") p aa.rassoc("Matsumoto") ["James", "Gray"] ["Yukihiro", "Matsumoto"]

Slide 141

Slide 141 text

#37 More Than Two Fields Sneak some extra data through on the end many_fields = [ %w[James Gray Developer JEG2], %w[Yukihiro Matsumoto Language\ Designer yukihiro_matz] ] first, last, title, twitter = many_fields.assoc("Yukihiro") puts "matz is a #{title}" first, last, title, twitter = many_fields.rassoc("Gray") puts "I am a #{title}"

Slide 142

Slide 142 text

#37 More Than Two Fields Sneak some extra data through on the end many_fields = [ %w[James Gray Developer JEG2], %w[Yukihiro Matsumoto Language\ Designer yukihiro_matz] ] first, last, title, twitter = many_fields.assoc("Yukihiro") puts "matz is a #{title}" first, last, title, twitter = many_fields.rassoc("Gray") puts "I am a #{title}"

Slide 143

Slide 143 text

#37 More Than Two Fields Sneak some extra data through on the end many_fields = [ %w[James Gray Developer JEG2], %w[Yukihiro Matsumoto Language\ Designer yukihiro_matz] ] first, last, title, twitter = many_fields.assoc("Yukihiro") puts "matz is a #{title}" first, last, title, twitter = many_fields.rassoc("Gray") puts "I am a #{title}"

Slide 144

Slide 144 text

#37 More Than Two Fields Sneak some extra data through on the end many_fields = [ %w[James Gray Developer JEG2], %w[Yukihiro Matsumoto Language\ Designer yukihiro_matz] ] first, last, title, twitter = many_fields.assoc("Yukihiro") puts "matz is a #{title}" first, last, title, twitter = many_fields.rassoc("Gray") puts "I am a #{title}" matz is a Language Designer I am a Developer

Slide 145

Slide 145 text

#38 From David Brady With Versioned Data This data structure naturally handles versioning dana = [[:first, "Dana"], [:last, "Payne"]] maiden = dana.assoc(:last).last puts "Dana's maiden name was #{maiden}." dana.unshift([:last, "Gray"]) married = dana.assoc(:last).last puts "Dana's married name is #{married}." current = dana.assoc(:last) previous = dana[(dana.index(current) + 1)..-1].assoc(:last).last puts "Dana's previous last name was #{previous}."

Slide 146

Slide 146 text

#38 From David Brady With Versioned Data This data structure naturally handles versioning dana = [[:first, "Dana"], [:last, "Payne"]] maiden = dana.assoc(:last).last puts "Dana's maiden name was #{maiden}." dana.unshift([:last, "Gray"]) married = dana.assoc(:last).last puts "Dana's married name is #{married}." current = dana.assoc(:last) previous = dana[(dana.index(current) + 1)..-1].assoc(:last).last puts "Dana's previous last name was #{previous}."

Slide 147

Slide 147 text

#38 From David Brady With Versioned Data This data structure naturally handles versioning dana = [[:first, "Dana"], [:last, "Payne"]] maiden = dana.assoc(:last).last puts "Dana's maiden name was #{maiden}." dana.unshift([:last, "Gray"]) married = dana.assoc(:last).last puts "Dana's married name is #{married}." current = dana.assoc(:last) previous = dana[(dana.index(current) + 1)..-1].assoc(:last).last puts "Dana's previous last name was #{previous}."

Slide 148

Slide 148 text

#38 From David Brady With Versioned Data This data structure naturally handles versioning dana = [[:first, "Dana"], [:last, "Payne"]] maiden = dana.assoc(:last).last puts "Dana's maiden name was #{maiden}." dana.unshift([:last, "Gray"]) married = dana.assoc(:last).last puts "Dana's married name is #{married}." current = dana.assoc(:last) previous = dana[(dana.index(current) + 1)..-1].assoc(:last).last puts "Dana's previous last name was #{previous}." Dana's maiden name was Payne. Dana's married name is Gray. Dana's previous last name was Payne.

Slide 149

Slide 149 text

#39 From Benjamin Fleischer Easy, Fast Memoization Ruby’s Hash is a memoization algorithm in disguise fibonacci = Hash.new { |numbers, index| numbers[index] = fibonacci[index - 2] + fibonacci[index - 1] }.update(0 => 0, 1 => 1) p fibonacci[300]

Slide 150

Slide 150 text

#39 From Benjamin Fleischer Easy, Fast Memoization Ruby’s Hash is a memoization algorithm in disguise fibonacci = Hash.new { |numbers, index| numbers[index] = fibonacci[index - 2] + fibonacci[index - 1] }.update(0 => 0, 1 => 1) p fibonacci[300]

Slide 151

Slide 151 text

#39 From Benjamin Fleischer Easy, Fast Memoization Ruby’s Hash is a memoization algorithm in disguise fibonacci = Hash.new { |numbers, index| numbers[index] = fibonacci[index - 2] + fibonacci[index - 1] }.update(0 => 0, 1 => 1) p fibonacci[300] 222232244629420445529739893461909967206666939096499764990979600

Slide 152

Slide 152 text

#40 From Adam Williams Autovivification It’s not just for Perl programs deep = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) } deep[:a][:b][:c] = 42 p deep

Slide 153

Slide 153 text

#40 From Adam Williams Autovivification It’s not just for Perl programs deep = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) } deep[:a][:b][:c] = 42 p deep

Slide 154

Slide 154 text

#40 From Adam Williams Autovivification It’s not just for Perl programs deep = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) } deep[:a][:b][:c] = 42 p deep

Slide 155

Slide 155 text

#40 From Adam Williams Autovivification It’s not just for Perl programs deep = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) } deep[:a][:b][:c] = 42 p deep {:a=>{:b=>{:c=>42}}}

Slide 156

Slide 156 text

#41 From Dave Copeland Rolling Your Own Dispatch Table Works great with the new Hash and lambda syntax operations = { number: ->(n) { n.to_i }, unary_op: ->(op, n) { n.send("#{op}@") }, binary_op: ->(op, l, r) { l.send(op, r) } } stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then :binary_op when %r{\An\Z} then :unary_op else :number end op = operations[type] stack << op[line.strip.tr('n', '-'), *stack.pop(op.arity - 1)] end

Slide 157

Slide 157 text

#41 From Dave Copeland Rolling Your Own Dispatch Table Works great with the new Hash and lambda syntax operations = { number: ->(n) { n.to_i }, unary_op: ->(op, n) { n.send("#{op}@") }, binary_op: ->(op, l, r) { l.send(op, r) } } stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then :binary_op when %r{\An\Z} then :unary_op else :number end op = operations[type] stack << op[line.strip.tr('n', '-'), *stack.pop(op.arity - 1)] end

Slide 158

Slide 158 text

#41 From Dave Copeland Rolling Your Own Dispatch Table Works great with the new Hash and lambda syntax operations = { number: ->(n) { n.to_i }, unary_op: ->(op, n) { n.send("#{op}@") }, binary_op: ->(op, l, r) { l.send(op, r) } } stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then :binary_op when %r{\An\Z} then :unary_op else :number end op = operations[type] stack << op[line.strip.tr('n', '-'), *stack.pop(op.arity - 1)] end

Slide 159

Slide 159 text

#41 From Dave Copeland Rolling Your Own Dispatch Table Works great with the new Hash and lambda syntax operations = { number: ->(n) { n.to_i }, unary_op: ->(op, n) { n.send("#{op}@") }, binary_op: ->(op, l, r) { l.send(op, r) } } stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then :binary_op when %r{\An\Z} then :unary_op else :number end op = operations[type] stack << op[line.strip.tr('n', '-'), *stack.pop(op.arity - 1)] end $ ruby rpn.rb >> 2 0: 2 >> 3 0: 2 1: 3 >> * 0: 6 >> 2 0: 6 1: 2 >> / 0: 3 >> n 0: -3

Slide 160

Slide 160 text

#42 From Josh Susser Or Just Use Ruby to Dispatch This can be faster than a case statement and calling a lambda class Input; def initialize(input) @input = input end end class Number < Input; def calculate() @input.to_i end end class UnaryOperation < Input; def calculate(n) n.send("#@input@") end end class BinaryOperation < Input; def calculate(l, r) l.send(@input, r) end end stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then BinaryOperation when %r{\An\Z} then UnaryOperation else Number end op = type.new(line.strip.tr('n', '-')) stack << op.calculate(*stack.pop(op.method(:calculate).arity)) end

Slide 161

Slide 161 text

#42 From Josh Susser Or Just Use Ruby to Dispatch This can be faster than a case statement and calling a lambda class Input; def initialize(input) @input = input end end class Number < Input; def calculate() @input.to_i end end class UnaryOperation < Input; def calculate(n) n.send("#@input@") end end class BinaryOperation < Input; def calculate(l, r) l.send(@input, r) end end stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then BinaryOperation when %r{\An\Z} then UnaryOperation else Number end op = type.new(line.strip.tr('n', '-')) stack << op.calculate(*stack.pop(op.method(:calculate).arity)) end

Slide 162

Slide 162 text

#42 From Josh Susser Or Just Use Ruby to Dispatch This can be faster than a case statement and calling a lambda class Input; def initialize(input) @input = input end end class Number < Input; def calculate() @input.to_i end end class UnaryOperation < Input; def calculate(n) n.send("#@input@") end end class BinaryOperation < Input; def calculate(l, r) l.send(@input, r) end end stack = [ ] loop do puts stack.map.with_index { |n, i| "#{i}: #{n}" } print ">> " line = $stdin.gets or break type = case line when %r{\A[-+*/]\Z} then BinaryOperation when %r{\An\Z} then UnaryOperation else Number end op = type.new(line.strip.tr('n', '-')) stack << op.calculate(*stack.pop(op.method(:calculate).arity)) end $ ruby rpn.rb >> 2 0: 2 >> 3 0: 2 1: 3 >> * 0: 6 >> 2 0: 6 1: 2 >> / 0: 3 >> n 0: -3

Slide 163

Slide 163 text

#43 From Michael Foley Fetching Data This one method packs so many tricks is hard not to love it params = {var: 42} p params.fetch(:var) p params.fetch(:missing, 42) p params.fetch(:missing) { 40 + 2 } params.fetch(:missing)

Slide 164

Slide 164 text

#43 From Michael Foley Fetching Data This one method packs so many tricks is hard not to love it params = {var: 42} p params.fetch(:var) p params.fetch(:missing, 42) p params.fetch(:missing) { 40 + 2 } params.fetch(:missing)

Slide 165

Slide 165 text

#43 From Michael Foley Fetching Data This one method packs so many tricks is hard not to love it params = {var: 42} p params.fetch(:var) p params.fetch(:missing, 42) p params.fetch(:missing) { 40 + 2 } params.fetch(:missing)

Slide 166

Slide 166 text

#43 From Michael Foley Fetching Data This one method packs so many tricks is hard not to love it params = {var: 42} p params.fetch(:var) p params.fetch(:missing, 42) p params.fetch(:missing) { 40 + 2 } params.fetch(:missing)

Slide 167

Slide 167 text

#43 From Michael Foley Fetching Data This one method packs so many tricks is hard not to love it params = {var: 42} p params.fetch(:var) p params.fetch(:missing, 42) p params.fetch(:missing) { 40 + 2 } params.fetch(:missing) 42 42 42 /…/fetching_data.rb:8:in `fetch': key not found: :missing (KeyError) ! from /…/fetching_data.rb:8:in `'

Slide 168

Slide 168 text

#44 From Jacob Tjørnholm Indexing Into a String by Regex I use this more often than the match operator str = "Price $24.95" p str[/\$\d+(?:\.\d+)?/] p str[/\$(\d+)(?:\.\d+)?/, 1] p str[/\$(?\d+)(?:\.\d+)?/, :dollars]

Slide 169

Slide 169 text

#44 From Jacob Tjørnholm Indexing Into a String by Regex I use this more often than the match operator str = "Price $24.95" p str[/\$\d+(?:\.\d+)?/] p str[/\$(\d+)(?:\.\d+)?/, 1] p str[/\$(?\d+)(?:\.\d+)?/, :dollars]

Slide 170

Slide 170 text

#44 From Jacob Tjørnholm Indexing Into a String by Regex I use this more often than the match operator str = "Price $24.95" p str[/\$\d+(?:\.\d+)?/] p str[/\$(\d+)(?:\.\d+)?/, 1] p str[/\$(?\d+)(?:\.\d+)?/, :dollars]

Slide 171

Slide 171 text

#44 From Jacob Tjørnholm Indexing Into a String by Regex I use this more often than the match operator str = "Price $24.95" p str[/\$\d+(?:\.\d+)?/] p str[/\$(\d+)(?:\.\d+)?/, 1] p str[/\$(?\d+)(?:\.\d+)?/, :dollars] "$24.95" "24" "24"

Slide 172

Slide 172 text

#46 From Jacob Tjørnholm You Can Even Assign by Regex I don’t tend to use this version str = "$24.95 per seat" str[/\$\d+(?:\.\d+)/] = "$9.99" puts str str[/\$\d+(?:\.\d+)(\b)/, 1] = " USD" puts str

Slide 173

Slide 173 text

#46 From Jacob Tjørnholm You Can Even Assign by Regex I don’t tend to use this version str = "$24.95 per seat" str[/\$\d+(?:\.\d+)/] = "$9.99" puts str str[/\$\d+(?:\.\d+)(\b)/, 1] = " USD" puts str

Slide 174

Slide 174 text

#46 From Jacob Tjørnholm You Can Even Assign by Regex I don’t tend to use this version str = "$24.95 per seat" str[/\$\d+(?:\.\d+)/] = "$9.99" puts str str[/\$\d+(?:\.\d+)(\b)/, 1] = " USD" puts str

Slide 175

Slide 175 text

#46 From Jacob Tjørnholm You Can Even Assign by Regex I don’t tend to use this version str = "$24.95 per seat" str[/\$\d+(?:\.\d+)/] = "$9.99" puts str str[/\$\d+(?:\.\d+)(\b)/, 1] = " USD" puts str $9.99 per seat $9.99 USD per seat

Slide 176

Slide 176 text

#45 Finding the Last Match Work from the right instead of the left expression = "40 + 2 = 42" p expression[expression.rindex(/\d+/)..-1] p expression[expression.rindex(/\b\d+/)..-1]

Slide 177

Slide 177 text

#45 Finding the Last Match Work from the right instead of the left expression = "40 + 2 = 42" p expression[expression.rindex(/\d+/)..-1] p expression[expression.rindex(/\b\d+/)..-1]

Slide 178

Slide 178 text

#45 Finding the Last Match Work from the right instead of the left expression = "40 + 2 = 42" p expression[expression.rindex(/\d+/)..-1] p expression[expression.rindex(/\b\d+/)..-1] "2" "42"

Slide 179

Slide 179 text

#47 From Benjamin Fleischer Chew Through Binary Data These methods are also useful on old “fixed width” data files width, height = ARGF.read(24).unpack("@16N2") puts " Width: #{width} pixels" puts "Height: #{height} pixels"

Slide 180

Slide 180 text

#47 From Benjamin Fleischer Chew Through Binary Data These methods are also useful on old “fixed width” data files width, height = ARGF.read(24).unpack("@16N2") puts " Width: #{width} pixels" puts "Height: #{height} pixels"

Slide 181

Slide 181 text

#47 From Benjamin Fleischer Chew Through Binary Data These methods are also useful on old “fixed width” data files width, height = ARGF.read(24).unpack("@16N2") puts " Width: #{width} pixels" puts "Height: #{height} pixels" $ ruby png_size.rb rails_project/public/images/rails.png Width: 50 pixels Height: 64 pixels

Slide 182

Slide 182 text

Iterators

Slide 183

Slide 183 text

#48 From Benjamin Fleischer Iterating in Lockstep This can walk through two or more collections at once letters = "a".."d" numbers = 1..3 letters.zip(numbers) do |letter, number| p(letter: letter, number: number) end

Slide 184

Slide 184 text

#48 From Benjamin Fleischer Iterating in Lockstep This can walk through two or more collections at once letters = "a".."d" numbers = 1..3 letters.zip(numbers) do |letter, number| p(letter: letter, number: number) end

Slide 185

Slide 185 text

#48 From Benjamin Fleischer Iterating in Lockstep This can walk through two or more collections at once letters = "a".."d" numbers = 1..3 letters.zip(numbers) do |letter, number| p(letter: letter, number: number) end {:letter=>"a", :number=>1} {:letter=>"b", :number=>2} {:letter=>"c", :number=>3} {:letter=>"d", :number=>nil}

Slide 186

Slide 186 text

#49 From Piotr Szotkowski Partition Your Data This iterator has been in Ruby a long time now, but I seldom see it used Person = Struct.new(:name, :gender) people = [ Person.new("James", :male), Person.new("Dana", :female), Person.new("Summer", :female) ] males, females = people.partition { |person| person.gender == :male } puts "Males:", males.map { |male| " #{male.name}" } puts "Females:", females.map { |female| " #{female.name}" }

Slide 187

Slide 187 text

#49 From Piotr Szotkowski Partition Your Data This iterator has been in Ruby a long time now, but I seldom see it used Person = Struct.new(:name, :gender) people = [ Person.new("James", :male), Person.new("Dana", :female), Person.new("Summer", :female) ] males, females = people.partition { |person| person.gender == :male } puts "Males:", males.map { |male| " #{male.name}" } puts "Females:", females.map { |female| " #{female.name}" }

Slide 188

Slide 188 text

#49 From Piotr Szotkowski Partition Your Data This iterator has been in Ruby a long time now, but I seldom see it used Person = Struct.new(:name, :gender) people = [ Person.new("James", :male), Person.new("Dana", :female), Person.new("Summer", :female) ] males, females = people.partition { |person| person.gender == :male } puts "Males:", males.map { |male| " #{male.name}" } puts "Females:", females.map { |female| " #{female.name}" } Males: James Females: Dana Summer

Slide 189

Slide 189 text

#50 From Piotr Szotkowski Take Data in Chunks A newer iterator for us to exploit headings = [ "1.1 Compiler Tricks", "1.2 Syntax", "1.3 Data Structures", "1.4 Iterators", "2.1 Core Ruby", "2.2 The Standard Library", "2.3 Tools", "2.4 Black Magic" ] headings.chunk { |heading| heading[/\A\d+/] } .each do |chapter, headings| puts "Chapter #{chapter}:" puts headings.map { |heading| " #{heading}" } end

Slide 190

Slide 190 text

#50 From Piotr Szotkowski Take Data in Chunks A newer iterator for us to exploit headings = [ "1.1 Compiler Tricks", "1.2 Syntax", "1.3 Data Structures", "1.4 Iterators", "2.1 Core Ruby", "2.2 The Standard Library", "2.3 Tools", "2.4 Black Magic" ] headings.chunk { |heading| heading[/\A\d+/] } .each do |chapter, headings| puts "Chapter #{chapter}:" puts headings.map { |heading| " #{heading}" } end

Slide 191

Slide 191 text

#50 From Piotr Szotkowski Take Data in Chunks A newer iterator for us to exploit headings = [ "1.1 Compiler Tricks", "1.2 Syntax", "1.3 Data Structures", "1.4 Iterators", "2.1 Core Ruby", "2.2 The Standard Library", "2.3 Tools", "2.4 Black Magic" ] headings.chunk { |heading| heading[/\A\d+/] } .each do |chapter, headings| puts "Chapter #{chapter}:" puts headings.map { |heading| " #{heading}" } end Chapter 1: 1.1 Compiler Tricks 1.2 Syntax 1.3 Data Structures 1.4 Iterators Chapter 2: 2.1 Core Ruby 2.2 The Standard Library 2.3 Tools 2.4 Black Magic

Slide 192

Slide 192 text

#51 map() + flatten() = flat_map() This can be a handy shortcut for working with nested collections require "pp" chess_squares = ("A".."H").flat_map { |column| (1..8).map { |row| "#{column}#{row}" } } pp chess_squares

Slide 193

Slide 193 text

#51 map() + flatten() = flat_map() This can be a handy shortcut for working with nested collections require "pp" chess_squares = ("A".."H").flat_map { |column| (1..8).map { |row| "#{column}#{row}" } } pp chess_squares

Slide 194

Slide 194 text

#51 map() + flatten() = flat_map() This can be a handy shortcut for working with nested collections require "pp" chess_squares = ("A".."H").flat_map { |column| (1..8).map { |row| "#{column}#{row}" } } pp chess_squares ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "C1", …]

Slide 195

Slide 195 text

#52 Replace Ugly inject() Calls This iterator was added to kill a bad yet common usage of inject() # instead of: (1..3).inject({ }) { |hash, n| hash[n] = true; hash } object = (1..3).each_with_object({ }) do |n, hash| hash[n] = true end p object

Slide 196

Slide 196 text

#52 Replace Ugly inject() Calls This iterator was added to kill a bad yet common usage of inject() # instead of: (1..3).inject({ }) { |hash, n| hash[n] = true; hash } object = (1..3).each_with_object({ }) do |n, hash| hash[n] = true end p object

Slide 197

Slide 197 text

#52 Replace Ugly inject() Calls This iterator was added to kill a bad yet common usage of inject() # instead of: (1..3).inject({ }) { |hash, n| hash[n] = true; hash } object = (1..3).each_with_object({ }) do |n, hash| hash[n] = true end p object

Slide 198

Slide 198 text

#52 Replace Ugly inject() Calls This iterator was added to kill a bad yet common usage of inject() # instead of: (1..3).inject({ }) { |hash, n| hash[n] = true; hash } object = (1..3).each_with_object({ }) do |n, hash| hash[n] = true end p object {1=>true, 2=>true, 3=>true}

Slide 199

Slide 199 text

#53 Take a Little Off the Top Pull or skip from the beginning of a list numbers = 1..10 p numbers.take(3) p numbers.drop(7) p numbers.take_while { |n| n <= 5 } p numbers.drop_while { |n| n <= 5 }

Slide 200

Slide 200 text

#53 Take a Little Off the Top Pull or skip from the beginning of a list numbers = 1..10 p numbers.take(3) p numbers.drop(7) p numbers.take_while { |n| n <= 5 } p numbers.drop_while { |n| n <= 5 }

Slide 201

Slide 201 text

#53 Take a Little Off the Top Pull or skip from the beginning of a list numbers = 1..10 p numbers.take(3) p numbers.drop(7) p numbers.take_while { |n| n <= 5 } p numbers.drop_while { |n| n <= 5 }

Slide 202

Slide 202 text

#53 Take a Little Off the Top Pull or skip from the beginning of a list numbers = 1..10 p numbers.take(3) p numbers.drop(7) p numbers.take_while { |n| n <= 5 } p numbers.drop_while { |n| n <= 5 }

Slide 203

Slide 203 text

#53 Take a Little Off the Top Pull or skip from the beginning of a list numbers = 1..10 p numbers.take(3) p numbers.drop(7) p numbers.take_while { |n| n <= 5 } p numbers.drop_while { |n| n <= 5 } [1, 2, 3] [8, 9, 10] [1, 2, 3, 4, 5] [6, 7, 8, 9, 10]

Slide 204

Slide 204 text

#54 Manual Iteration You can step through the new Enumerator objects by hand row = %w[odd even].cycle puts "" ("A".."E").each do |letter| puts %Q{ #{letter}} end puts ""

Slide 205

Slide 205 text

#54 Manual Iteration You can step through the new Enumerator objects by hand row = %w[odd even].cycle puts "" ("A".."E").each do |letter| puts %Q{ #{letter}} end puts ""

Slide 206

Slide 206 text

#54 Manual Iteration You can step through the new Enumerator objects by hand row = %w[odd even].cycle puts "" ("A".."E").each do |letter| puts %Q{ #{letter}} end puts ""

Slide 207

Slide 207 text

#54 Manual Iteration You can step through the new Enumerator objects by hand row = %w[odd even].cycle puts "" ("A".."E").each do |letter| puts %Q{ #{letter}} end puts "" A B C D E

Slide 208

Slide 208 text

#55 A Smarter loop() Or get some help from loop() animals = %w[cat bat rat] enum = animals.to_enum 3.times do enum.next end enum.next rescue puts "Error raised: #{$!.class}" enum.rewind loop do puts "Processing #{enum.next}..." end

Slide 209

Slide 209 text

#55 A Smarter loop() Or get some help from loop() animals = %w[cat bat rat] enum = animals.to_enum 3.times do enum.next end enum.next rescue puts "Error raised: #{$!.class}" enum.rewind loop do puts "Processing #{enum.next}..." end

Slide 210

Slide 210 text

#55 A Smarter loop() Or get some help from loop() animals = %w[cat bat rat] enum = animals.to_enum 3.times do enum.next end enum.next rescue puts "Error raised: #{$!.class}" enum.rewind loop do puts "Processing #{enum.next}..." end

Slide 211

Slide 211 text

#55 A Smarter loop() Or get some help from loop() animals = %w[cat bat rat] enum = animals.to_enum 3.times do enum.next end enum.next rescue puts "Error raised: #{$!.class}" enum.rewind loop do puts "Processing #{enum.next}..." end Error raised: StopIteration Processing cat... Processing bat... Processing rat...

Slide 212

Slide 212 text

#56 From David Brady Chaining Iterators Enumerators also allow for the chaining of iteration p ("a".."z").each_cons(3) .map(&:join) .select { |letters| letters =~ /[aeiouy]/ }

Slide 213

Slide 213 text

#56 From David Brady Chaining Iterators Enumerators also allow for the chaining of iteration p ("a".."z").each_cons(3) .map(&:join) .select { |letters| letters =~ /[aeiouy]/ }

Slide 214

Slide 214 text

#56 From David Brady Chaining Iterators Enumerators also allow for the chaining of iteration p ("a".."z").each_cons(3) .map(&:join) .select { |letters| letters =~ /[aeiouy]/ } ["abc", "cde", "def", "efg", "ghi", "hij", "ijk", …]

Slide 215

Slide 215 text

#57 Add an Index to Any Iterator Enumerator supports this special method just for chaining votes = { "Josh" => %w[SBPP POODR GOOS], "Avdi" => %w[POODR SBPP GOOS], "James" => %w[POODR GOOS SBPP], "David" => %w[GOOS SBPP POODR], "Chuck" => %w[GOOS POODR SBPP] } tally = Hash.new(0) votes.values.each do |personal_selections| personal_selections.each_with_object(tally).with_index do |(vote, totals), i| totals[vote] += personal_selections.size - i end end p tally

Slide 216

Slide 216 text

#57 Add an Index to Any Iterator Enumerator supports this special method just for chaining votes = { "Josh" => %w[SBPP POODR GOOS], "Avdi" => %w[POODR SBPP GOOS], "James" => %w[POODR GOOS SBPP], "David" => %w[GOOS SBPP POODR], "Chuck" => %w[GOOS POODR SBPP] } tally = Hash.new(0) votes.values.each do |personal_selections| personal_selections.each_with_object(tally).with_index do |(vote, totals), i| totals[vote] += personal_selections.size - i end end p tally

Slide 217

Slide 217 text

#57 Add an Index to Any Iterator Enumerator supports this special method just for chaining votes = { "Josh" => %w[SBPP POODR GOOS], "Avdi" => %w[POODR SBPP GOOS], "James" => %w[POODR GOOS SBPP], "David" => %w[GOOS SBPP POODR], "Chuck" => %w[GOOS POODR SBPP] } tally = Hash.new(0) votes.values.each do |personal_selections| personal_selections.each_with_object(tally).with_index do |(vote, totals), i| totals[vote] += personal_selections.size - i end end p tally

Slide 218

Slide 218 text

#57 Add an Index to Any Iterator Enumerator supports this special method just for chaining votes = { "Josh" => %w[SBPP POODR GOOS], "Avdi" => %w[POODR SBPP GOOS], "James" => %w[POODR GOOS SBPP], "David" => %w[GOOS SBPP POODR], "Chuck" => %w[GOOS POODR SBPP] } tally = Hash.new(0) votes.values.each do |personal_selections| personal_selections.each_with_object(tally).with_index do |(vote, totals), i| totals[vote] += personal_selections.size - i end end p tally {"SBPP"=>9, "POODR"=>11, "GOOS"=>10}

Slide 219

Slide 219 text

Intermission

Slide 220

Slide 220 text

Why Do This? ✤ We trade in the currency of ideas ✤ A bad plan is better than no plan ✤ It’s good to have an appreciation for how rich Ruby is, as a language

Slide 221

Slide 221 text

I Don’t Have Time for This!

Slide 222

Slide 222 text

Core Ruby

Slide 223

Slide 223 text

#58 From Benjamin Fleischer Programmatically Build Classes This allows your code to construct what is needed def build_class(parent, extra_methods = { }) Class.new(parent) do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Thingy = build_class(Object, value: 42, dynamic: -> { :called }) thingy = Thingy.new p thingy.value p thingy.dynamic

Slide 224

Slide 224 text

#58 From Benjamin Fleischer Programmatically Build Classes This allows your code to construct what is needed def build_class(parent, extra_methods = { }) Class.new(parent) do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Thingy = build_class(Object, value: 42, dynamic: -> { :called }) thingy = Thingy.new p thingy.value p thingy.dynamic

Slide 225

Slide 225 text

#58 From Benjamin Fleischer Programmatically Build Classes This allows your code to construct what is needed def build_class(parent, extra_methods = { }) Class.new(parent) do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Thingy = build_class(Object, value: 42, dynamic: -> { :called }) thingy = Thingy.new p thingy.value p thingy.dynamic 42 :called

Slide 226

Slide 226 text

#59 From Benjamin Fleischer Programmatically Build Modules Modules can also be constructed by code def build_module(extra_methods = { }) Module.new do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Mixin = build_module(value: 42, dynamic: -> { :called }) thingy = Object.new.extend(Mixin) p thingy.value p thingy.dynamic

Slide 227

Slide 227 text

#59 From Benjamin Fleischer Programmatically Build Modules Modules can also be constructed by code def build_module(extra_methods = { }) Module.new do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Mixin = build_module(value: 42, dynamic: -> { :called }) thingy = Object.new.extend(Mixin) p thingy.value p thingy.dynamic

Slide 228

Slide 228 text

#59 From Benjamin Fleischer Programmatically Build Modules Modules can also be constructed by code def build_module(extra_methods = { }) Module.new do extra_methods.each do |name, result| body = result.is_a?(Proc) ? result : -> { result } define_method(name, &body) end end end Mixin = build_module(value: 42, dynamic: -> { :called }) thingy = Object.new.extend(Mixin) p thingy.value p thingy.dynamic 42 :called

Slide 229

Slide 229 text

#60 From Benjamin Fleischer Inherit From an Expression Programmatically build parent classes def Value(*fields) Class.new do define_method(:initialize) do |*args| fail ArgumentError, "wrong argument count" unless args.size == fields.size fields.zip(args) do |field, arg| instance_variable_set("@#{field}", arg) end end fields.each do |field| define_method(field) { instance_variable_get("@#{field}") } end end end class Name < Value(:first, :last) def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full

Slide 230

Slide 230 text

#60 From Benjamin Fleischer Inherit From an Expression Programmatically build parent classes def Value(*fields) Class.new do define_method(:initialize) do |*args| fail ArgumentError, "wrong argument count" unless args.size == fields.size fields.zip(args) do |field, arg| instance_variable_set("@#{field}", arg) end end fields.each do |field| define_method(field) { instance_variable_get("@#{field}") } end end end class Name < Value(:first, :last) def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full

Slide 231

Slide 231 text

#60 From Benjamin Fleischer Inherit From an Expression Programmatically build parent classes def Value(*fields) Class.new do define_method(:initialize) do |*args| fail ArgumentError, "wrong argument count" unless args.size == fields.size fields.zip(args) do |field, arg| instance_variable_set("@#{field}", arg) end end fields.each do |field| define_method(field) { instance_variable_get("@#{field}") } end end end class Name < Value(:first, :last) def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full James Gray

Slide 232

Slide 232 text

#61 Mix-in an Expression Module inclusion works the same way def LimitedUse(limit) Module.new do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end class ShortLived include LimitedUse(3) end limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s

Slide 233

Slide 233 text

#61 Mix-in an Expression Module inclusion works the same way def LimitedUse(limit) Module.new do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end class ShortLived include LimitedUse(3) end limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s

Slide 234

Slide 234 text

#61 Mix-in an Expression Module inclusion works the same way def LimitedUse(limit) Module.new do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end class ShortLived include LimitedUse(3) end limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s # # # /…/mixin_an_expression.rb:7:in `block (4 levels) in LimitedUse': Over use limit (RuntimeError) ! from /…/mixin_an_expression.rb:21:in `'

Slide 235

Slide 235 text

#62 From Avdi Grimm Subclass Module It’s possible to create modules with state class LimitedUse < Module def initialize(limit) @limit = limit super() do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end def to_s; "LimitedUse.new(#{@limit})" end end class ShortLived; include LimitedUse.new(3) end p ShortLived.ancestors limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s

Slide 236

Slide 236 text

#62 From Avdi Grimm Subclass Module It’s possible to create modules with state class LimitedUse < Module def initialize(limit) @limit = limit super() do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end def to_s; "LimitedUse.new(#{@limit})" end end class ShortLived; include LimitedUse.new(3) end p ShortLived.ancestors limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s

Slide 237

Slide 237 text

#62 From Avdi Grimm Subclass Module It’s possible to create modules with state class LimitedUse < Module def initialize(limit) @limit = limit super() do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end def to_s; "LimitedUse.new(#{@limit})" end end class ShortLived; include LimitedUse.new(3) end p ShortLived.ancestors limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s

Slide 238

Slide 238 text

#62 From Avdi Grimm Subclass Module It’s possible to create modules with state class LimitedUse < Module def initialize(limit) @limit = limit super() do define_singleton_method(:included) do |parent| count = 0 parent.public_instance_methods.each do |method| define_method(method) do |*args| fail "Over use limit" if (count += 1) > limit super(*args) end end end end end def to_s; "LimitedUse.new(#{@limit})" end end class ShortLived; include LimitedUse.new(3) end p ShortLived.ancestors limited = ShortLived.new puts Array.new(3) { limited.to_s } limited.to_s [ShortLived, LimitedUse.new(3), Object, Kernel, BasicObject] # # # /…/subclass_module.rb:9:in `block (4 levels) in initialize': Over use limit (RuntimeError) ! from /…/subclass_module.rb:22:in `'

Slide 239

Slide 239 text

#63 From Steve Klabnik Empty Types You don’t need a full declaration to define a class or module module MyNamespace module Errors # instead of: class MyNamespaceError < RuntimeError; end MyNamespaceError = Class.new(RuntimeError) WhateverError = Class.new(MyNamespaceError) end end p MyNamespace::Errors::WhateverError.ancestors

Slide 240

Slide 240 text

#63 From Steve Klabnik Empty Types You don’t need a full declaration to define a class or module module MyNamespace module Errors # instead of: class MyNamespaceError < RuntimeError; end MyNamespaceError = Class.new(RuntimeError) WhateverError = Class.new(MyNamespaceError) end end p MyNamespace::Errors::WhateverError.ancestors

Slide 241

Slide 241 text

#63 From Steve Klabnik Empty Types You don’t need a full declaration to define a class or module [MyNamespace::Errors::WhateverError, MyNamespace::Errors::MyNamespaceError, RuntimeError, StandardError, Exception, Object, Kernel, BasicObject] module MyNamespace module Errors # instead of: class MyNamespaceError < RuntimeError; end MyNamespaceError = Class.new(RuntimeError) WhateverError = Class.new(MyNamespaceError) end end p MyNamespace::Errors::WhateverError.ancestors

Slide 242

Slide 242 text

#64 From Chris Hunt Don’t Inherit From Struct Struct takes a block # instead of: class Name < Struct.new(:first, :last) ... end Name = Struct.new(:first, :last) do def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full

Slide 243

Slide 243 text

#64 From Chris Hunt Don’t Inherit From Struct Struct takes a block # instead of: class Name < Struct.new(:first, :last) ... end Name = Struct.new(:first, :last) do def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full

Slide 244

Slide 244 text

#64 From Chris Hunt Don’t Inherit From Struct Struct takes a block # instead of: class Name < Struct.new(:first, :last) ... end Name = Struct.new(:first, :last) do def full "#{first} #{last}" end end james = Name.new("James", "Gray") puts james.full James Gray

Slide 245

Slide 245 text

#65 Struct Without the Assignment Subclasses of Struct can be placed under that namespace # instead of: class Name < Struct.new(:first, :last) ... end Struct.new("Name", :first, :last) do def full "#{first} #{last}" end end james = Struct::Name.new("James", "Gray") puts james.full

Slide 246

Slide 246 text

#65 Struct Without the Assignment Subclasses of Struct can be placed under that namespace # instead of: class Name < Struct.new(:first, :last) ... end Struct.new("Name", :first, :last) do def full "#{first} #{last}" end end james = Struct::Name.new("James", "Gray") puts james.full

Slide 247

Slide 247 text

#65 Struct Without the Assignment Subclasses of Struct can be placed under that namespace # instead of: class Name < Struct.new(:first, :last) ... end Struct.new("Name", :first, :last) do def full "#{first} #{last}" end end james = Struct::Name.new("James", "Gray") puts james.full

Slide 248

Slide 248 text

#65 Struct Without the Assignment Subclasses of Struct can be placed under that namespace # instead of: class Name < Struct.new(:first, :last) ... end Struct.new("Name", :first, :last) do def full "#{first} #{last}" end end james = Struct::Name.new("James", "Gray") puts james.full James Gray

Slide 249

Slide 249 text

#66 From Mike Burns Be a Good method_missing() User Fix respond_to?() and method() for your method_missing() usage class Greeter GREETING_REGEX = /\Aaloha_\w+\z/ def method_missing(method, *args, &block) if method =~ GREETING_REGEX "Aloha #{method.to_s.split("_")[1..-1].map(&:capitalize).join(" ")}" else super end end def respond_to_missing?(method, include_private = false) method =~ GREETING_REGEX end end greeter = Greeter.new p greeter.respond_to?(:aloha_james_gray) puts greeter.method(:aloha_james_gray).call

Slide 250

Slide 250 text

#66 From Mike Burns Be a Good method_missing() User Fix respond_to?() and method() for your method_missing() usage class Greeter GREETING_REGEX = /\Aaloha_\w+\z/ def method_missing(method, *args, &block) if method =~ GREETING_REGEX "Aloha #{method.to_s.split("_")[1..-1].map(&:capitalize).join(" ")}" else super end end def respond_to_missing?(method, include_private = false) method =~ GREETING_REGEX end end greeter = Greeter.new p greeter.respond_to?(:aloha_james_gray) puts greeter.method(:aloha_james_gray).call

Slide 251

Slide 251 text

#66 From Mike Burns Be a Good method_missing() User Fix respond_to?() and method() for your method_missing() usage class Greeter GREETING_REGEX = /\Aaloha_\w+\z/ def method_missing(method, *args, &block) if method =~ GREETING_REGEX "Aloha #{method.to_s.split("_")[1..-1].map(&:capitalize).join(" ")}" else super end end def respond_to_missing?(method, include_private = false) method =~ GREETING_REGEX end end greeter = Greeter.new p greeter.respond_to?(:aloha_james_gray) puts greeter.method(:aloha_james_gray).call

Slide 252

Slide 252 text

#66 From Mike Burns Be a Good method_missing() User Fix respond_to?() and method() for your method_missing() usage class Greeter GREETING_REGEX = /\Aaloha_\w+\z/ def method_missing(method, *args, &block) if method =~ GREETING_REGEX "Aloha #{method.to_s.split("_")[1..-1].map(&:capitalize).join(" ")}" else super end end def respond_to_missing?(method, include_private = false) method =~ GREETING_REGEX end end greeter = Greeter.new p greeter.respond_to?(:aloha_james_gray) puts greeter.method(:aloha_james_gray).call true Aloha James Gray

Slide 253

Slide 253 text

#67 instance_eval() In Disguise This trick is faster than the real thing class Whatever def self.peek_inside(&block) define_method(:peek, &block) method = instance_method(:peek) remove_method(:peek) method end def initialize(secret) @secret = secret end end magic_key = Whatever.peek_inside { @secret } meaning = Whatever.new(42) other = Whatever.new(:other) p magic_key.bind(meaning).call p magic_key.bind(other).call

Slide 254

Slide 254 text

#67 instance_eval() In Disguise This trick is faster than the real thing class Whatever def self.peek_inside(&block) define_method(:peek, &block) method = instance_method(:peek) remove_method(:peek) method end def initialize(secret) @secret = secret end end magic_key = Whatever.peek_inside { @secret } meaning = Whatever.new(42) other = Whatever.new(:other) p magic_key.bind(meaning).call p magic_key.bind(other).call

Slide 255

Slide 255 text

#67 instance_eval() In Disguise This trick is faster than the real thing class Whatever def self.peek_inside(&block) define_method(:peek, &block) method = instance_method(:peek) remove_method(:peek) method end def initialize(secret) @secret = secret end end magic_key = Whatever.peek_inside { @secret } meaning = Whatever.new(42) other = Whatever.new(:other) p magic_key.bind(meaning).call p magic_key.bind(other).call

Slide 256

Slide 256 text

#67 instance_eval() In Disguise This trick is faster than the real thing class Whatever def self.peek_inside(&block) define_method(:peek, &block) method = instance_method(:peek) remove_method(:peek) method end def initialize(secret) @secret = secret end end magic_key = Whatever.peek_inside { @secret } meaning = Whatever.new(42) other = Whatever.new(:other) p magic_key.bind(meaning).call p magic_key.bind(other).call 42 :other

Slide 257

Slide 257 text

#68 From Benjamin Fleischer Iterating Over Each Object Remember, we’re talking about MRI here ObjectSpace.each_object do |object| puts object if object.is_a? String end

Slide 258

Slide 258 text

#68 From Benjamin Fleischer Iterating Over Each Object Remember, we’re talking about MRI here ObjectSpace.each_object do |object| puts object if object.is_a? String end

Slide 259

Slide 259 text

#68 From Benjamin Fleischer Iterating Over Each Object Remember, we’re talking about MRI here ObjectSpace.each_object do |object| puts object if object.is_a? String end .ext CONFIG["PREP"] = "miniruby$(EXEEXT)" EXTOUT no LIBRUBY_RELATIVE ARCHFILE EXECUTABLE_EXTS nodoc …

Slide 260

Slide 260 text

#69 From Benjamin Fleischer Iterating Over Specific Types Focus in on specific objects ObjectSpace.each_object(String) do |object| puts object end

Slide 261

Slide 261 text

#69 From Benjamin Fleischer Iterating Over Specific Types Focus in on specific objects ObjectSpace.each_object(String) do |object| puts object end

Slide 262

Slide 262 text

#69 From Benjamin Fleischer Iterating Over Specific Types Focus in on specific objects .ext CONFIG["PREP"] = "miniruby$(EXEEXT)" EXTOUT no LIBRUBY_RELATIVE ARCHFILE EXECUTABLE_EXTS nodoc … ObjectSpace.each_object(String) do |object| puts object end

Slide 263

Slide 263 text

#70 Benjamin Fleischer Count All Objects You don’t need to iterate if you just want counts require "pp" pp ObjectSpace.count_objects

Slide 264

Slide 264 text

#70 Benjamin Fleischer Count All Objects You don’t need to iterate if you just want counts require "pp" pp ObjectSpace.count_objects

Slide 265

Slide 265 text

#70 Benjamin Fleischer Count All Objects You don’t need to iterate if you just want counts require "pp" pp ObjectSpace.count_objects {:TOTAL=>17579, :FREE=>98, :T_OBJECT=>9, :T_CLASS=>496, :T_MODULE=>23, :T_FLOAT=>7, :T_STRING=>6466, :T_REGEXP=>28, :T_ARRAY=>1222, :T_HASH=>15, :T_BIGNUM=>6, :T_FILE=>10, :T_DATA=>557, :T_MATCH=>109, :T_COMPLEX=>1, :T_NODE=>8510, :T_ICLASS=>22}

Slide 266

Slide 266 text

#71 From Peter Cooper Profile the Garbage Collector This can help find memory leaks GC::Profiler.enable 10.times do array = Array.new(1_000_000) { |i| i.to_s } end puts GC::Profiler.result

Slide 267

Slide 267 text

#71 From Peter Cooper Profile the Garbage Collector This can help find memory leaks GC::Profiler.enable 10.times do array = Array.new(1_000_000) { |i| i.to_s } end puts GC::Profiler.result

Slide 268

Slide 268 text

#71 From Peter Cooper Profile the Garbage Collector This can help find memory leaks GC::Profiler.enable 10.times do array = Array.new(1_000_000) { |i| i.to_s } end puts GC::Profiler.result

Slide 269

Slide 269 text

#71 From Peter Cooper Profile the Garbage Collector This can help find memory leaks GC::Profiler.enable 10.times do array = Array.new(1_000_000) { |i| i.to_s } end puts GC::Profiler.result GC 21 invokes. Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms) 1 0.010 305000 703480 17587 0.52899999999999991473 2 0.013 588840 719840 17996 0.30400000000000043654 3 0.016 1258720 1276080 31902 0.50300000000000000266 4 0.022 2256520 2274040 56851 0.77800000000000091305 5 0.032 4055240 4073640 101841 1.40099999999999935696 6 0.050 7292960 7312920 182823 2.63900000000000245493 7 0.084 13114280 13137080 328427 4.41699999999999004530 8 0.144 23595920 23623840 590596 7.70400000000001661249 9 0.253 2466120 42486920 1062173 1.61999999999995480948 10 0.410 30496440 42486920 1062173 13.21600000000000463274 11 0.423 30496480 30544120 763603 10.43900000000003203127 12 0.568 14909960 54936880 1373422 7.15099999999990743049 13 0.770 11018240 54936880 1373422 3.72700000000003583978 14 0.932 40182760 46773240 1169331 22.61799999999991683808 15 1.169 40182800 80229440 2005736 26.77799999999997027089 16 1.398 40182840 80229440 2005736 27.82100000000009742962 17 1.628 40182840 80229440 2005736 27.38399999999985112709 18 1.858 40182840 80229440 2005736 27.53000000000005442757 19 2.089 40182840 80229440 2005736 27.83699999999988961008 20 2.322 40182760 80229440 2005736 27.59699999999964958874

Slide 270

Slide 270 text

#72 From David Brady Tapping Into a Call Chain Also used to mutate without affecting the return value puts "sssssssssssemaj".sub(/\A(\w)\1+/, '\1') .reverse .capitalize

Slide 271

Slide 271 text

#72 From David Brady Tapping Into a Call Chain Also used to mutate without affecting the return value puts "sssssssssssemaj".sub(/\A(\w)\1+/, '\1') .reverse .capitalize James

Slide 272

Slide 272 text

#72 From David Brady Tapping Into a Call Chain Also used to mutate without affecting the return value puts "sssssssssssemaj".sub(/\A(\w)\1+/, '\1') .tap { |str| p str.size } .reverse .capitalize

Slide 273

Slide 273 text

#72 From David Brady Tapping Into a Call Chain Also used to mutate without affecting the return value puts "sssssssssssemaj".sub(/\A(\w)\1+/, '\1') .tap { |str| p str.size } .reverse .capitalize

Slide 274

Slide 274 text

#72 From David Brady Tapping Into a Call Chain Also used to mutate without affecting the return value puts "sssssssssssemaj".sub(/\A(\w)\1+/, '\1') .tap { |str| p str.size } .reverse .capitalize 5 James

Slide 275

Slide 275 text

#73 Sneaking in a p() Without tap() In Ruby 1.9, p() returns its argument puts p("sssssssssssemaj".sub(/\A(\w)\1+/, '\1')).reverse.capitalize

Slide 276

Slide 276 text

#73 Sneaking in a p() Without tap() In Ruby 1.9, p() returns its argument puts p("sssssssssssemaj".sub(/\A(\w)\1+/, '\1')).reverse.capitalize

Slide 277

Slide 277 text

#73 Sneaking in a p() Without tap() In Ruby 1.9, p() returns its argument puts p("sssssssssssemaj".sub(/\A(\w)\1+/, '\1')).reverse.capitalize "semaj" James

Slide 278

Slide 278 text

#74 Bubbling Up Thread Errors This can be quite helpful when debugging threaded code Thread.abort_on_exception = true Thread.new do fail "Oops, we can't continue" end loop do sleep end

Slide 279

Slide 279 text

#74 Bubbling Up Thread Errors This can be quite helpful when debugging threaded code Thread.abort_on_exception = true Thread.new do fail "Oops, we can't continue" end loop do sleep end

Slide 280

Slide 280 text

#74 Bubbling Up Thread Errors This can be quite helpful when debugging threaded code Thread.abort_on_exception = true Thread.new do fail "Oops, we can't continue" end loop do sleep end /…/bubbling_up_thread_errors.rb:4:in `block in ': Oops, we can't continue (RuntimeError)

Slide 281

Slide 281 text

#75 The $DEBUG Flag Turn your debugging code on and off as needed def var @var || 40 end if $DEBUG puts "var is %p" % var end p var + 2

Slide 282

Slide 282 text

#75 The $DEBUG Flag Turn your debugging code on and off as needed def var @var || 40 end if $DEBUG puts "var is %p" % var end p var + 2

Slide 283

Slide 283 text

#75 The $DEBUG Flag Turn your debugging code on and off as needed def var @var || 40 end if $DEBUG puts "var is %p" % var end p var + 2 $ ruby the_debug_flag.rb 42 $ ruby -d the_debug_flag.rb Exception `LoadError' at /…/rubygems.rb:1264 - cannot load such file -- rubygems/defaults/operating_system Exception `LoadError' at /…/rubygems.rb:1273 - cannot load such file -- rubygems/defaults/ruby the_debug_flag.rb:2: warning: instance variable @var not initialized var is 40 the_debug_flag.rb:2: warning: instance variable @var not initialized 42

Slide 284

Slide 284 text

#75 The $DEBUG Flag Turn your debugging code on and off as needed def var @var || 40 end if $DEBUG puts "var is %p" % var end p var + 2 $ ruby the_debug_flag.rb 42 $ ruby -d the_debug_flag.rb Exception `LoadError' at /…/rubygems.rb:1264 - cannot load such file -- rubygems/defaults/operating_system Exception `LoadError' at /…/rubygems.rb:1273 - cannot load such file -- rubygems/defaults/ruby the_debug_flag.rb:2: warning: instance variable @var not initialized var is 40 the_debug_flag.rb:2: warning: instance variable @var not initialized 42

Slide 285

Slide 285 text

#76 Two Tests in One This works on any Comparable object p 2.between?(1, 10) p "cat".between?("bat", "rat")

Slide 286

Slide 286 text

#76 Two Tests in One This works on any Comparable object p 2.between?(1, 10) p "cat".between?("bat", "rat")

Slide 287

Slide 287 text

#76 Two Tests in One This works on any Comparable object p 2.between?(1, 10) p "cat".between?("bat", "rat") true false

Slide 288

Slide 288 text

#77 From Benjamin Fleischer Line-by-line Reading foreach() = open() + each() File.foreach(__FILE__) do |line| p line end

Slide 289

Slide 289 text

#77 From Benjamin Fleischer Line-by-line Reading foreach() = open() + each() File.foreach(__FILE__) do |line| p line end

Slide 290

Slide 290 text

#77 From Benjamin Fleischer Line-by-line Reading foreach() = open() + each() File.foreach(__FILE__) do |line| p line end "File.foreach(__FILE__) do |line|\n" " p line\n" "end\n"

Slide 291

Slide 291 text

#78 The Write-a-file Shortcut You can even dump some data without a call to open() File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt") puts File.write("output.txt", "one and a half\ntwo\n", 4) puts File.read("output.txt") puts File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt")

Slide 292

Slide 292 text

#78 The Write-a-file Shortcut You can even dump some data without a call to open() File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt") puts File.write("output.txt", "one and a half\ntwo\n", 4) puts File.read("output.txt") puts File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt")

Slide 293

Slide 293 text

#78 The Write-a-file Shortcut You can even dump some data without a call to open() File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt") puts File.write("output.txt", "one and a half\ntwo\n", 4) puts File.read("output.txt") puts File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt")

Slide 294

Slide 294 text

#78 The Write-a-file Shortcut You can even dump some data without a call to open() File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt") puts File.write("output.txt", "one and a half\ntwo\n", 4) puts File.read("output.txt") puts File.write("output.txt", "one\ntwo\nthree\n") puts File.read("output.txt") one two three one one and a half two one two three

Slide 295

Slide 295 text

#79 Daemonize Your Process I often see Ruby code roll their own, but it’s built-in now Process.daemon loop do sleep end

Slide 296

Slide 296 text

#79 Daemonize Your Process I often see Ruby code roll their own, but it’s built-in now Process.daemon loop do sleep end

Slide 297

Slide 297 text

#79 Daemonize Your Process I often see Ruby code roll their own, but it’s built-in now Process.daemon loop do sleep end $ ruby daemonize_your_process.rb $ ps auxww | grep daemonize james 27071 … grep daemonize james 27044 … ruby daemonize_your_process.rb james 26564 … emacs daemonize_your_process.rb $ kill 27044 $ ps auxww | grep daemonize james 26564 … emacs daemonize_your_process.rb james 27153 … grep daemonize

Slide 298

Slide 298 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read

Slide 299

Slide 299 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read

Slide 300

Slide 300 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read

Slide 301

Slide 301 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read

Slide 302

Slide 302 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read

Slide 303

Slide 303 text

#80 Process Launching on Steroids If you only learn one way to launch a process, let it be spawn() pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid) #!/usr/bin/env ruby -w puts "SOME_VAR=%p" % ENV["SOME_VAR"] puts "$stdin.read:", $stdin.read $ ruby process_launching_on_steroids.rb SOME_VAR="42" $stdin.read: pid = spawn( {"SOME_VAR" => "42"}, # env "./child_process.rb", # the command in: open(__FILE__) ) # options Process.wait(pid)

Slide 304

Slide 304 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 305

Slide 305 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 306

Slide 306 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 307

Slide 307 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 308

Slide 308 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 309

Slide 309 text

#81 Seriously, spawn() This sucker has all the features you need spawn({"A_VAR" => "Whatever"}, cmd) # set an environment variable spawn({"A_VAR" => nil}, cmd) # clear an environment variable spawn(env, cmd, unsetenv_others: true) # clear unset environment variables spawn("rm *.txt") # normal shell expansion spawn("rm", "*.txt") # bypass the shell, no expansion spawn(["rm", "sweeper"], "*.txt") # rename command in process list spawn(cmd, pgroup: 0) # change the process group spawn(cmd, rlimit_cpu: Process.getrlimit(:CPU).last) # raise resource limits spawn(cmd, chdir: path) # change working directory spawn(cmd, umask: 0222) # change permissions spawn(cmd, in: io) # redirect IO spawn(cmd, io => [open, args]) spawn(cmd, io => :close) # close an IO spawn(cmd, close_others: true) # close unset IO pid = spawn(cmd); Process.detach(pid) # asynchronous pid = spawn(cmd); Process.wait(pid) # synchronous

Slide 310

Slide 310 text

The Standard Library

Slide 311

Slide 311 text

#82 Get Random Data Ruby can generate random data for you require "securerandom" p SecureRandom.random_number p SecureRandom.random_number(100) puts p SecureRandom.hex(20) p SecureRandom.base64(20) p SecureRandom.urlsafe_base64(20) p SecureRandom.random_bytes(20) puts p SecureRandom.uuid

Slide 312

Slide 312 text

#82 Get Random Data Ruby can generate random data for you require "securerandom" p SecureRandom.random_number p SecureRandom.random_number(100) puts p SecureRandom.hex(20) p SecureRandom.base64(20) p SecureRandom.urlsafe_base64(20) p SecureRandom.random_bytes(20) puts p SecureRandom.uuid

Slide 313

Slide 313 text

#82 Get Random Data Ruby can generate random data for you require "securerandom" p SecureRandom.random_number p SecureRandom.random_number(100) puts p SecureRandom.hex(20) p SecureRandom.base64(20) p SecureRandom.urlsafe_base64(20) p SecureRandom.random_bytes(20) puts p SecureRandom.uuid

Slide 314

Slide 314 text

#82 Get Random Data Ruby can generate random data for you require "securerandom" p SecureRandom.random_number p SecureRandom.random_number(100) puts p SecureRandom.hex(20) p SecureRandom.base64(20) p SecureRandom.urlsafe_base64(20) p SecureRandom.random_bytes(20) puts p SecureRandom.uuid

Slide 315

Slide 315 text

#82 Get Random Data Ruby can generate random data for you require "securerandom" p SecureRandom.random_number p SecureRandom.random_number(100) puts p SecureRandom.hex(20) p SecureRandom.base64(20) p SecureRandom.urlsafe_base64(20) p SecureRandom.random_bytes(20) puts p SecureRandom.uuid 0.8337575534446421 50 "5f1edc66708381c18f5527ccc49c0fad7822d5f3" "aoRvYu/V6/MeluooKG+tf4yjyyU=" "WfWpdByTVMdliaymKB_FmqTGgE8" "!d\x94\xE6viS\xCB\xA7\xDB\x84\xCF\x8EY\xCBI\x9F6\xE6\xFD" "5243d395-f317-4394-aa5d-762005d1fe7a"

Slide 316

Slide 316 text

#83 From Benjamin Fleischer Read From the Web This is the easiest way to pull something off the Web require "open-uri" open("http://rubyrogues.com/feed/") do |feed| puts feed.read.scan(%r{(\d+\s*RR\b[^<]*)}) end

Slide 317

Slide 317 text

#83 From Benjamin Fleischer Read From the Web This is the easiest way to pull something off the Web require "open-uri" open("http://rubyrogues.com/feed/") do |feed| puts feed.read.scan(%r{(\d+\s*RR\b[^<]*)}) end

Slide 318

Slide 318 text

#83 From Benjamin Fleischer Read From the Web This is the easiest way to pull something off the Web require "open-uri" open("http://rubyrogues.com/feed/") do |feed| puts feed.read.scan(%r{(\d+\s*RR\b[^<]*)}) end

Slide 319

Slide 319 text

#83 From Benjamin Fleischer Read From the Web This is the easiest way to pull something off the Web require "open-uri" open("http://rubyrogues.com/feed/") do |feed| puts feed.read.scan(%r{(\d+\s*RR\b[^<]*)}) end 073 RR APIs 072 RR Entrepreneurship with Amy Hoy 071 RR Zero Downtime Deploys 070 RR What is a Good Starter Project? 069 RR Therapeutic Refactoring with Katrina Owen 068 RR Book Club: Growing Object Oriented Software Guided by Tests 067 RR Gary Bernhardt’s Testing Style 066 RR Rails Bridge with Sarah Mei 065 RR Functional vs Object Oriented Programming with Michael Feathers 064 RR Presenting at Conferences …

Slide 320

Slide 320 text

#84 Escape For the Shell This is a great helper when using Ruby as a glue language require "shellwords" p Shellwords.shellwords('one "two" "a longer three"') p Shellwords.shellwords("one 'two' 'a longer three'") p Shellwords.shellwords('"escaped \"quote\" characters"') p Shellwords.shellwords('escaped\ spaces') p Shellwords.shellwords(%Q{'back to'" back quoting"}) p Shellwords.shellescape("two words") p Shellwords.shellescape('"quotes" included') p Shellwords.shelljoin(["two words", '"quotes" included'])

Slide 321

Slide 321 text

#84 Escape For the Shell This is a great helper when using Ruby as a glue language require "shellwords" p Shellwords.shellwords('one "two" "a longer three"') p Shellwords.shellwords("one 'two' 'a longer three'") p Shellwords.shellwords('"escaped \"quote\" characters"') p Shellwords.shellwords('escaped\ spaces') p Shellwords.shellwords(%Q{'back to'" back quoting"}) p Shellwords.shellescape("two words") p Shellwords.shellescape('"quotes" included') p Shellwords.shelljoin(["two words", '"quotes" included'])

Slide 322

Slide 322 text

#84 Escape For the Shell This is a great helper when using Ruby as a glue language require "shellwords" p Shellwords.shellwords('one "two" "a longer three"') p Shellwords.shellwords("one 'two' 'a longer three'") p Shellwords.shellwords('"escaped \"quote\" characters"') p Shellwords.shellwords('escaped\ spaces') p Shellwords.shellwords(%Q{'back to'" back quoting"}) p Shellwords.shellescape("two words") p Shellwords.shellescape('"quotes" included') p Shellwords.shelljoin(["two words", '"quotes" included'])

Slide 323

Slide 323 text

#84 Escape For the Shell This is a great helper when using Ruby as a glue language require "shellwords" p Shellwords.shellwords('one "two" "a longer three"') p Shellwords.shellwords("one 'two' 'a longer three'") p Shellwords.shellwords('"escaped \"quote\" characters"') p Shellwords.shellwords('escaped\ spaces') p Shellwords.shellwords(%Q{'back to'" back quoting"}) p Shellwords.shellescape("two words") p Shellwords.shellescape('"quotes" included') p Shellwords.shelljoin(["two words", '"quotes" included'])

Slide 324

Slide 324 text

#84 Escape For the Shell This is a great helper when using Ruby as a glue language require "shellwords" p Shellwords.shellwords('one "two" "a longer three"') p Shellwords.shellwords("one 'two' 'a longer three'") p Shellwords.shellwords('"escaped \"quote\" characters"') p Shellwords.shellwords('escaped\ spaces') p Shellwords.shellwords(%Q{'back to'" back quoting"}) p Shellwords.shellescape("two words") p Shellwords.shellescape('"quotes" included') p Shellwords.shelljoin(["two words", '"quotes" included']) ["one", "two", "a longer three"] ["one", "two", "a longer three"] ["escaped \"quote\" characters"] ["escaped spaces"] ["back to back quoting"] "two\\ words" "\\\"quotes\\\"\\ included" "two\\ words \\\"quotes\\\"\\ included"

Slide 325

Slide 325 text

#85 From Masatoshi Seki Template Methods Here’s a feature of ERb that I never see used require "erb" class Name def initialize(first, last) @first = first @last = last end attr_reader :first, :last extend ERB::DefMethod def_erb_method("full", "full_name.erb") def_erb_method("last_first", "last_name_first.erb") end james = Name.new("James", "Gray") puts james.full puts james.last_first

Slide 326

Slide 326 text

#85 From Masatoshi Seki Template Methods Here’s a feature of ERb that I never see used require "erb" class Name def initialize(first, last) @first = first @last = last end attr_reader :first, :last extend ERB::DefMethod def_erb_method("full", "full_name.erb") def_erb_method("last_first", "last_name_first.erb") end james = Name.new("James", "Gray") puts james.full puts james.last_first

Slide 327

Slide 327 text

#85 From Masatoshi Seki Template Methods Here’s a feature of ERb that I never see used require "erb" class Name def initialize(first, last) @first = first @last = last end attr_reader :first, :last extend ERB::DefMethod def_erb_method("full", "full_name.erb") def_erb_method("last_first", "last_name_first.erb") end james = Name.new("James", "Gray") puts james.full puts james.last_first

Slide 328

Slide 328 text

#85 From Masatoshi Seki Template Methods Here’s a feature of ERb that I never see used require "erb" class Name def initialize(first, last) @first = first @last = last end attr_reader :first, :last extend ERB::DefMethod def_erb_method("full", "full_name.erb") def_erb_method("last_first", "last_name_first.erb") end james = Name.new("James", "Gray") puts james.full puts james.last_first <%= first %> <%= last %> <%= last %>, <%= first %>

Slide 329

Slide 329 text

#85 From Masatoshi Seki Template Methods Here’s a feature of ERb that I never see used require "erb" class Name def initialize(first, last) @first = first @last = last end attr_reader :first, :last extend ERB::DefMethod def_erb_method("full", "full_name.erb") def_erb_method("last_first", "last_name_first.erb") end james = Name.new("James", "Gray") puts james.full puts james.last_first James Gray Gray, James <%= first %> <%= last %> <%= last %>, <%= first %>

Slide 330

Slide 330 text

#86 From Piotr Szotkowski Unix Like File Manipulation This library has an excellent interface require "fileutils" FileUtils.mkdir dir, options FileUtils.mkdir_p dir, options FileUtils.ln src, dest, options FileUtils.ln_s src, dest, options FileUtils.cp src, dest, options FileUtils.cp_r src, dest, options FileUtils.mv src, dest, options FileUtils.rm files, options FileUtils.rm_r files, options FileUtils.rm_rf files, options FileUtils.chmod mode, files, options FileUtils.chmod_R mode, files, options FileUtils.chown user, group, files, options FileUtils.chown_R user, group, files, options FileUtils.touch files, options

Slide 331

Slide 331 text

#86 From Piotr Szotkowski Unix Like File Manipulation This library has an excellent interface require "fileutils" FileUtils.mkdir dir, options FileUtils.mkdir_p dir, options FileUtils.ln src, dest, options FileUtils.ln_s src, dest, options FileUtils.cp src, dest, options FileUtils.cp_r src, dest, options FileUtils.mv src, dest, options FileUtils.rm files, options FileUtils.rm_r files, options FileUtils.rm_rf files, options FileUtils.chmod mode, files, options FileUtils.chmod_R mode, files, options FileUtils.chown user, group, files, options FileUtils.chown_R user, group, files, options FileUtils.touch files, options

Slide 332

Slide 332 text

#87 From Eric Hodel And You Can Tweak It Get verbose output or try a dry run before you commit to the real thing require "fileutils" module FileSystemWork extend FileUtils # or FileUtils::Verbose, FileUtils::DryRun, ... module_function def do_work touch "file.txt" # or whatever end end FileSystemWork.do_work

Slide 333

Slide 333 text

#87 From Eric Hodel And You Can Tweak It Get verbose output or try a dry run before you commit to the real thing require "fileutils" module FileSystemWork extend FileUtils # or FileUtils::Verbose, FileUtils::DryRun, ... module_function def do_work touch "file.txt" # or whatever end end FileSystemWork.do_work

Slide 334

Slide 334 text

#87 From Eric Hodel And You Can Tweak It Get verbose output or try a dry run before you commit to the real thing require "fileutils" module FileSystemWork extend FileUtils # or FileUtils::Verbose, FileUtils::DryRun, ... module_function def do_work touch "file.txt" # or whatever end end FileSystemWork.do_work

Slide 335

Slide 335 text

#87 From Eric Hodel And You Can Tweak It Get verbose output or try a dry run before you commit to the real thing require "fileutils" module FileSystemWork extend FileUtils # or FileUtils::Verbose, FileUtils::DryRun, ... module_function def do_work touch "file.txt" # or whatever end end FileSystemWork.do_work

Slide 336

Slide 336 text

#88 An OO File Interface Pathname combines File, Dir, File::Stat, and more in one pretty interface require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths # work with paths puts path.realpath puts path.relative_path_from(dir) puts # use paths to do work path.open do |io| 5.times do puts io.gets end end

Slide 337

Slide 337 text

#88 An OO File Interface Pathname combines File, Dir, File::Stat, and more in one pretty interface require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths # work with paths puts path.realpath puts path.relative_path_from(dir) puts # use paths to do work path.open do |io| 5.times do puts io.gets end end

Slide 338

Slide 338 text

#88 An OO File Interface Pathname combines File, Dir, File::Stat, and more in one pretty interface require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths # work with paths puts path.realpath puts path.relative_path_from(dir) puts # use paths to do work path.open do |io| 5.times do puts io.gets end end

Slide 339

Slide 339 text

#88 An OO File Interface Pathname combines File, Dir, File::Stat, and more in one pretty interface require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths # work with paths puts path.realpath puts path.relative_path_from(dir) puts # use paths to do work path.open do |io| 5.times do puts io.gets end end

Slide 340

Slide 340 text

#88 An OO File Interface Pathname combines File, Dir, File::Stat, and more in one pretty interface require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths # work with paths puts path.realpath puts path.relative_path_from(dir) puts # use paths to do work path.open do |io| 5.times do puts io.gets end end /Users/james/Desktop/the_standard_library/an_oo_file_interface.rb an_oo_file_interface.rb require "pathname" # build paths dir = Pathname.pwd # or Pathname.new(...) path = dir + __FILE__ # add paths

Slide 341

Slide 341 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 342

Slide 342 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 343

Slide 343 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 344

Slide 344 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 345

Slide 345 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 346

Slide 346 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 347

Slide 347 text

#89 From Piotr Szotkowski The World’s Easiest Database It’s multiprocessing safe too require "pstore" db = PStore.new("accounts.pstore") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end James: 100.0 Dana: 100.0

Slide 348

Slide 348 text

#90 From Piotr Szotkowski Or Use the Drop-in YAML Version Same thing, but human readable require "yaml/store" db = YAML::Store.new("accounts.yml") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 349

Slide 349 text

#90 From Piotr Szotkowski Or Use the Drop-in YAML Version Same thing, but human readable require "yaml/store" db = YAML::Store.new("accounts.yml") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 350

Slide 350 text

#90 From Piotr Szotkowski Or Use the Drop-in YAML Version Same thing, but human readable require "yaml/store" db = YAML::Store.new("accounts.yml") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end

Slide 351

Slide 351 text

#90 From Piotr Szotkowski Or Use the Drop-in YAML Version Same thing, but human readable require "yaml/store" db = YAML::Store.new("accounts.yml") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end James: 100.0 Dana: 100.0

Slide 352

Slide 352 text

#90 From Piotr Szotkowski Or Use the Drop-in YAML Version Same thing, but human readable require "yaml/store" db = YAML::Store.new("accounts.yml") db.transaction do db["james"] = 100.00 db["dana"] = 100.00 end db.transaction do db["dana"] += 200.00 db["james"] -= 200.00 db.abort if db["james"] < 0.0 end db.transaction(true) do puts "James: %p" % db["james"] puts "Dana: %p" % db["dana"] end James: 100.0 Dana: 100.0 --- james: 100.0 dana: 100.0

Slide 353

Slide 353 text

#91 From Piotr Szotkowski A More Complete Set Array is nice, but this is safer and more full-featured require "set" animals = Set.new animals << "cat" animals.add("bat") p animals.add?("rat") p animals.add?("rat") p animals p animals.member?("tiger") p animals.subset?(Set["bat", "cat"]) p animals.superset?(%w[lions tigers bears].to_set) ordered = SortedSet.new (1..10).to_a.shuffle.each do |n| ordered << n end p ordered

Slide 354

Slide 354 text

#91 From Piotr Szotkowski A More Complete Set Array is nice, but this is safer and more full-featured require "set" animals = Set.new animals << "cat" animals.add("bat") p animals.add?("rat") p animals.add?("rat") p animals p animals.member?("tiger") p animals.subset?(Set["bat", "cat"]) p animals.superset?(%w[lions tigers bears].to_set) ordered = SortedSet.new (1..10).to_a.shuffle.each do |n| ordered << n end p ordered

Slide 355

Slide 355 text

#91 From Piotr Szotkowski A More Complete Set Array is nice, but this is safer and more full-featured require "set" animals = Set.new animals << "cat" animals.add("bat") p animals.add?("rat") p animals.add?("rat") p animals p animals.member?("tiger") p animals.subset?(Set["bat", "cat"]) p animals.superset?(%w[lions tigers bears].to_set) ordered = SortedSet.new (1..10).to_a.shuffle.each do |n| ordered << n end p ordered

Slide 356

Slide 356 text

#91 From Piotr Szotkowski A More Complete Set Array is nice, but this is safer and more full-featured require "set" animals = Set.new animals << "cat" animals.add("bat") p animals.add?("rat") p animals.add?("rat") p animals p animals.member?("tiger") p animals.subset?(Set["bat", "cat"]) p animals.superset?(%w[lions tigers bears].to_set) ordered = SortedSet.new (1..10).to_a.shuffle.each do |n| ordered << n end p ordered

Slide 357

Slide 357 text

#91 From Piotr Szotkowski A More Complete Set Array is nice, but this is safer and more full-featured require "set" animals = Set.new animals << "cat" animals.add("bat") p animals.add?("rat") p animals.add?("rat") p animals p animals.member?("tiger") p animals.subset?(Set["bat", "cat"]) p animals.superset?(%w[lions tigers bears].to_set) ordered = SortedSet.new (1..10).to_a.shuffle.each do |n| ordered << n end p ordered # nil # false false false #

Slide 358

Slide 358 text

Tools

Slide 359

Slide 359 text

#92 From Benjamin Fleischer Mini Ruby Programs The command-line switches -n, -p, -i, and -e can save you a lot of work $ cat data.txt ones twos threes $ ruby -pi.bak -e 'sub(/s\Z/, "")' data.txt $ cat data.txt one two three $ cat data.txt.bak ones twos threes

Slide 360

Slide 360 text

#92 From Benjamin Fleischer Mini Ruby Programs The command-line switches -n, -p, -i, and -e can save you a lot of work $ cat data.txt ones twos threes $ ruby -pi.bak -e 'sub(/s\Z/, "")' data.txt $ cat data.txt one two three $ cat data.txt.bak ones twos threes ARGF.each_line do |$_| # -e code goes here print $_ # for -p, not -n end

Slide 361

Slide 361 text

#92 From Benjamin Fleischer Mini Ruby Programs The command-line switches -n, -p, -i, and -e can save you a lot of work $ cat data.txt ones twos threes $ ruby -pi.bak -e 'sub(/s\Z/, "")' data.txt $ cat data.txt one two three $ cat data.txt.bak ones twos threes

Slide 362

Slide 362 text

#93 The IO-like ARGF This object wraps Unix style file arguments in an IO interface ARGF.each_line do |line| puts "#{ARGF.lineno}: #{line}" end

Slide 363

Slide 363 text

#93 The IO-like ARGF This object wraps Unix style file arguments in an IO interface ARGF.each_line do |line| puts "#{ARGF.lineno}: #{line}" end

Slide 364

Slide 364 text

#93 The IO-like ARGF This object wraps Unix style file arguments in an IO interface ARGF.each_line do |line| puts "#{ARGF.lineno}: #{line}" end $ ruby the_iolike_argf.rb one.txt two.txt three.txt 1: one 2: two 3: three

Slide 365

Slide 365 text

#94 From Greg Brown ARGF Sans the Command-line You can still use the ARGF magic without Ruby setting it up concatenated_files = ARGF.class.new("one.txt", "two.txt", "three.txt") concatenated_files.each_line do |line| puts line end

Slide 366

Slide 366 text

#94 From Greg Brown ARGF Sans the Command-line You can still use the ARGF magic without Ruby setting it up concatenated_files = ARGF.class.new("one.txt", "two.txt", "three.txt") concatenated_files.each_line do |line| puts line end

Slide 367

Slide 367 text

#94 From Greg Brown ARGF Sans the Command-line You can still use the ARGF magic without Ruby setting it up concatenated_files = ARGF.class.new("one.txt", "two.txt", "three.txt") concatenated_files.each_line do |line| puts line end one two three

Slide 368

Slide 368 text

#95 From Benjamin Fleischer The “Flip-flop” Operator Obscure, but it can do a lot of work for you $ cat numbers.txt one two three four five six seven eight nine ten $ ruby -ne 'print if /\At/../e\Z/' numbers.txt two three ten

Slide 369

Slide 369 text

#95 From Benjamin Fleischer The “Flip-flop” Operator Obscure, but it can do a lot of work for you $ cat numbers.txt one two three four five six seven eight nine ten $ ruby -ne 'print if /\At/../e\Z/' numbers.txt two three ten

Slide 370

Slide 370 text

#96 From Nick Seiger Out of Order Code This Perlish syntax is handy in one-liners $ cat blenders.txt Ninja $99.99 Vitamix $378.95 Blendtec $399.99 $ ruby -ne 'BEGIN { total = 0 }; END { puts "$%.2f" % total }; total += $_[/\$(\d+(?:\.\d+)?)/, 1].to_f' blenders.txt $878.93

Slide 371

Slide 371 text

#96 From Nick Seiger Out of Order Code This Perlish syntax is handy in one-liners $ cat blenders.txt Ninja $99.99 Vitamix $378.95 Blendtec $399.99 $ ruby -ne 'BEGIN { total = 0 }; END { puts "$%.2f" % total }; total += $_[/\$(\d+(?:\.\d+)?)/, 1].to_f' blenders.txt $878.93

Slide 372

Slide 372 text

#96 From Nick Seiger Out of Order Code This Perlish syntax is handy in one-liners $ cat blenders.txt Ninja $99.99 Vitamix $378.95 Blendtec $399.99 $ ruby -ne 'BEGIN { total = 0 }; END { puts "$%.2f" % total }; total += $_[/\$(\d+(?:\.\d+)?)/, 1].to_f' blenders.txt $878.93

Slide 373

Slide 373 text

#97 From Michael Foley IRb’s Last Result This really pays off in a Rails console as well >> "semaj".reverse => "james" >> _.capitalize => "James" >> 40 + 2 => 42 >> n = _ => 42 >> n / 2 => 21

Slide 374

Slide 374 text

#97 From Michael Foley IRb’s Last Result This really pays off in a Rails console as well >> "semaj".reverse => "james" >> _.capitalize => "James" >> 40 + 2 => 42 >> n = _ => 42 >> n / 2 => 21

Slide 375

Slide 375 text

#98 IRb’s “Job” Managment Pry really wasn’t the first >> self => main >> irb "james" >> capitalize => "James" >> jobs => #0->irb on main (#: stop) #1->irb#1 on james (#: running) >> fg 0 => # >> self => main >> fg 1 => # >> self => "james" >> exit => # >> self => main >> exit

Slide 376

Slide 376 text

#98 IRb’s “Job” Managment Pry really wasn’t the first >> self => main >> irb "james" >> capitalize => "James" >> jobs => #0->irb on main (#: stop) #1->irb#1 on james (#: running) >> fg 0 => # >> self => main >> fg 1 => # >> self => "james" >> exit => # >> self => main >> exit

Slide 377

Slide 377 text

#98 IRb’s “Job” Managment Pry really wasn’t the first >> self => main >> irb "james" >> capitalize => "James" >> jobs => #0->irb on main (#: stop) #1->irb#1 on james (#: running) >> fg 0 => # >> self => main >> fg 1 => # >> self => "james" >> exit => # >> self => main >> exit

Slide 378

Slide 378 text

#98 IRb’s “Job” Managment Pry really wasn’t the first >> self => main >> irb "james" >> capitalize => "James" >> jobs => #0->irb on main (#: stop) #1->irb#1 on james (#: running) >> fg 0 => # >> self => main >> fg 1 => # >> self => "james" >> exit => # >> self => main >> exit

Slide 379

Slide 379 text

#98 IRb’s “Job” Managment Pry really wasn’t the first >> self => main >> irb "james" >> capitalize => "James" >> jobs => #0->irb on main (#: stop) #1->irb#1 on james (#: running) >> fg 0 => # >> self => main >> fg 1 => # >> self => "james" >> exit => # >> self => main >> exit

Slide 380

Slide 380 text

#99 Trigger IRb as Needed It’s often handy to be able to drop into an IRb session require "irb" def my_program_context @my_program_context ||= Struct.new(:value).new(40) end trap(:INT) do IRB.start trap(:INT, "EXIT") end loop do puts "Current value: #{my_program_context.value}" sleep 1 end

Slide 381

Slide 381 text

#99 Trigger IRb as Needed It’s often handy to be able to drop into an IRb session require "irb" def my_program_context @my_program_context ||= Struct.new(:value).new(40) end trap(:INT) do IRB.start trap(:INT, "EXIT") end loop do puts "Current value: #{my_program_context.value}" sleep 1 end

Slide 382

Slide 382 text

#99 Trigger IRb as Needed It’s often handy to be able to drop into an IRb session require "irb" def my_program_context @my_program_context ||= Struct.new(:value).new(40) end trap(:INT) do IRB.start trap(:INT, "EXIT") end loop do puts "Current value: #{my_program_context.value}" sleep 1 end

Slide 383

Slide 383 text

#99 Trigger IRb as Needed It’s often handy to be able to drop into an IRb session require "irb" def my_program_context @my_program_context ||= Struct.new(:value).new(40) end trap(:INT) do IRB.start trap(:INT, "EXIT") end loop do puts "Current value: #{my_program_context.value}" sleep 1 end $ ruby trigger_irb_as_needed.rb Current value: 40 Current value: 40 Current value: 40 ^C1.9.3-p194 :001 > my_program_context.value += 2 => 42 1.9.3-p194 :002 > exit Current value: 42 Current value: 42 Current value: 42

Slide 384

Slide 384 text

#99 Trigger IRb as Needed It’s often handy to be able to drop into an IRb session require "irb" def my_program_context @my_program_context ||= Struct.new(:value).new(40) end trap(:INT) do IRB.start trap(:INT, "EXIT") end loop do puts "Current value: #{my_program_context.value}" sleep 1 end $ ruby trigger_irb_as_needed.rb Current value: 40 Current value: 40 Current value: 40 ^C1.9.3-p194 :001 > my_program_context.value += 2 => 42 1.9.3-p194 :002 > exit Current value: 42 Current value: 42 Current value: 42

Slide 385

Slide 385 text

#100 From Peter Cooper Pretty JSON This tool ships with Ruby 1.9 or later $ curl http://twitter.com/statuses/user_timeline/JEG2.json [{"id_str":"253982951187034113","place":null,"retweeted":false,"in_reply_to_sta tus_id_str":null,"in_reply_to_status_id":null,"in_reply_to_screen_name":null,"i n_reply_to_user_id_str":null,"user":{"id":20941662,"profile_image_url":"http:\/ \/a0.twimg.com\/profile_images\/2311650093\/fkgorpzafxmsafxpf6wi_normal.png","p rofile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/th eme7\/bg.gif","location":"Edmond, OK","profile_use_background_image":true,"prof ile_text_color":"333333","follow_request_sent":false,"default_profile":false,…

Slide 386

Slide 386 text

#100 From Peter Cooper Pretty JSON This tool ships with Ruby 1.9 or later $ curl http://twitter.com/statuses/user_timeline/JEG2.json | prettify_json.rb [ { "id_str": "253982951187034113", "place": null, "retweeted": false, "in_reply_to_status_id_str": null, "in_reply_to_status_id": null, "in_reply_to_screen_name": null, "in_reply_to_user_id_str": null, "user": { "id": 20941662, "profile_image_url": "http://…/fkgorpzafxmsafxpf6wi_normal.png", "profile_background_image_url_https": "https://…/bg.gif", "location": "Edmond, OK", "profile_use_background_image": true, "profile_text_color": "333333", "follow_request_sent": false, …

Slide 387

Slide 387 text

#100 From Peter Cooper Pretty JSON This tool ships with Ruby 1.9 or later $ curl http://twitter.com/statuses/user_timeline/JEG2.json | prettify_json.rb [ { "id_str": "253982951187034113", "place": null, "retweeted": false, "in_reply_to_status_id_str": null, "in_reply_to_status_id": null, "in_reply_to_screen_name": null, "in_reply_to_user_id_str": null, "user": { "id": 20941662, "profile_image_url": "http://…/fkgorpzafxmsafxpf6wi_normal.png", "profile_background_image_url_https": "https://…/bg.gif", "location": "Edmond, OK", "profile_use_background_image": true, "profile_text_color": "333333", "follow_request_sent": false, …

Slide 388

Slide 388 text

Black Magic

Slide 389

Slide 389 text

#101 Code That Never Crashes! Ruby is so powerful that it can make code run forever #!/usr/bin/env ruby -w at_exit do if $! and not [SystemExit, Interrupt].include? $!.class exec $PROGRAM_NAME end end loop do left, right = Array.new(2) { rand(-10..10) } operator = %w[+ - * /].sample puts "#{left} #{operator} #{right} = #{left.send(operator, right)}" end

Slide 390

Slide 390 text

#101 Code That Never Crashes! Ruby is so powerful that it can make code run forever #!/usr/bin/env ruby -w at_exit do if $! and not [SystemExit, Interrupt].include? $!.class exec $PROGRAM_NAME end end loop do left, right = Array.new(2) { rand(-10..10) } operator = %w[+ - * /].sample puts "#{left} #{operator} #{right} = #{left.send(operator, right)}" end

Slide 391

Slide 391 text

#101 Code That Never Crashes! Ruby is so powerful that it can make code run forever #!/usr/bin/env ruby -w at_exit do if $! and not [SystemExit, Interrupt].include? $!.class exec $PROGRAM_NAME end end loop do left, right = Array.new(2) { rand(-10..10) } operator = %w[+ - * /].sample puts "#{left} #{operator} #{right} = #{left.send(operator, right)}" end

Slide 392

Slide 392 text

#101 Code That Never Crashes! Ruby is so powerful that it can make code run forever #!/usr/bin/env ruby -w at_exit do if $! and not [SystemExit, Interrupt].include? $!.class exec $PROGRAM_NAME end end loop do left, right = Array.new(2) { rand(-10..10) } operator = %w[+ - * /].sample puts "#{left} #{operator} #{right} = #{left.send(operator, right)}" end

Slide 393

Slide 393 text

#101 Code That Never Crashes! Ruby is so powerful that it can make code run forever #!/usr/bin/env ruby -w at_exit do if $! and not [SystemExit, Interrupt].include? $!.class exec $PROGRAM_NAME end end loop do left, right = Array.new(2) { rand(-10..10) } operator = %w[+ - * /].sample puts "#{left} #{operator} #{right} = #{left.send(operator, right)}" end 10 - 7 = 3 -1 / -6 = 0 0 + -6 = -6 -2 + -9 = -11 -8 * 1 = -8 -9 - -2 = -7 -2 + -6 = -8 -3 + 9 = 6 -3 * 9 = -27 2 / -7 = -1 …

Slide 394

Slide 394 text

These Slides Will Be Online: https://speakerdeck.com/u/jeg2

Slide 395

Slide 395 text

Thanks