$30 off During Our Annual Pro Sale. View Details »

Code to Joy

avdi
September 15, 2012

Code to Joy

A random walk through the Ruby language and standard library, highlighting some tools and idioms that make me happy.

avdi

September 15, 2012
Tweet

More Decks by avdi

Other Decks in Programming

Transcript

  1. Code to Joy Avdi Grimm ShipRise GoGaRuCo 2012 Avdi Grimm

    (ShipRise) Code to Joy GoGaRuCo 2012 1 / 50
  2. Code to Joy Introduction Introduction These are a few of

    my favorite things. Avdi Grimm (ShipRise) GoGaRuCo 2012 2 / 50
  3. Code to Joy Introduction My Job is Awesome • My

    job is awesome Avdi Grimm (ShipRise) GoGaRuCo 2012 3 / 50
  4. Code to Joy Introduction My Job is Awesome • My

    job is awesome • Remotely pairing with many different people Avdi Grimm (ShipRise) GoGaRuCo 2012 3 / 50
  5. Code to Joy Introduction My Job is Awesome • My

    job is awesome • Remotely pairing with many different people • Different levels of knowledge, different problems to solve Avdi Grimm (ShipRise) GoGaRuCo 2012 3 / 50
  6. Code to Joy Introduction My Job is Awesome • My

    job is awesome • Remotely pairing with many different people • Different levels of knowledge, different problems to solve • ”That’s so cool, I didn’t know you could do that” Avdi Grimm (ShipRise) GoGaRuCo 2012 3 / 50
  7. Code to Joy Introduction Ruby is Awesome Ruby is designed

    to make programmers happy. — Matz It still finds ways to make me smile after all these years. Avdi Grimm (ShipRise) GoGaRuCo 2012 4 / 50
  8. Code to Joy Introduction Postcards are Awesome • Work in

    progress: Confident Ruby Avdi Grimm (ShipRise) GoGaRuCo 2012 5 / 50
  9. Code to Joy Introduction Postcards are Awesome • Work in

    progress: Confident Ruby • ”Pay with a postcard” Avdi Grimm (ShipRise) GoGaRuCo 2012 5 / 50
  10. Code to Joy Introduction Postcards are Awesome • Work in

    progress: Confident Ruby • ”Pay with a postcard” • Postcards make me happy! Avdi Grimm (ShipRise) GoGaRuCo 2012 5 / 50
  11. Code to Joy Introduction The Plan • A random walk

    through the Ruby language and standard library. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 6 / 50
  12. Code to Joy Introduction The Plan • A random walk

    through the Ruby language and standard library. . . • . . . stopping in at some of my favorite idioms and tools. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 6 / 50
  13. Code to Joy Introduction The Plan • A random walk

    through the Ruby language and standard library. . . • . . . stopping in at some of my favorite idioms and tools. . . • . . . and around the world, in postcards! Avdi Grimm (ShipRise) GoGaRuCo 2012 6 / 50
  14. Code to Joy Splats Simple Destructuring spawn( ’true’ ) pid,

    status = *Process.wait2 pid # => 9159 status # => #<Process::Status: pid 9159 exit 0> Avdi Grimm (ShipRise) GoGaRuCo 2012 8 / 50
  15. Code to Joy Splats Implicit splat spawn( ’true’ ) pid,

    status = Process.wait2 pid # => 16804 status # => #<Process::Status: pid 16804 exit 0> Avdi Grimm (ShipRise) GoGaRuCo 2012 9 / 50
  16. Code to Joy Splats Multiple return values def send_request #

    ... code = 404 message = "Not found" # ??? end Avdi Grimm (ShipRise) GoGaRuCo 2012 10 / 50
  17. Code to Joy Splats One client wants individual values code,

    message = send_request logger.info " #{ code } : #{ message } " Avdi Grimm (ShipRise) GoGaRuCo 2012 11 / 50
  18. Code to Joy Splats Another client wants an object response

    = send_request case response.code when 400..599 then handle_error(response) else # ... end Avdi Grimm (ShipRise) GoGaRuCo 2012 12 / 50
  19. Code to Joy Splats cake.have && cake.eat Response = Struct.new(

    :code , :message ) r = Response.new(404, "Not found" ) # Attributes: r.code # => 404 r.message # => "Not found" # Splat: code, msg = *r code # => 404 msg # => "Not found" Avdi Grimm (ShipRise) GoGaRuCo 2012 13 / 50
  20. Code to Joy Splats One little problem Response = Struct.new(

    :code , :message ) r = Response.new(404, "Not found" ) code, msg = r # no splat code # => #<struct Response code=404, message="Not found"> msg # => nil Avdi Grimm (ShipRise) GoGaRuCo 2012 14 / 50
  21. Code to Joy Splats An implicitly splattable Struct Response =

    Struct.new( :code , :message ) do alias_method :to_ary , :to_a end Avdi Grimm (ShipRise) GoGaRuCo 2012 15 / 50
  22. Code to Joy Splats An implicitly splattable Struct Response =

    Struct.new( :code , :message ) do alias_method :to_ary , :to_a end r = Response.new(404, "Not found" ) code, msg = r # look ma, no splat! code # => 404 msg # => "Not found" Avdi Grimm (ShipRise) GoGaRuCo 2012 15 / 50
  23. Code to Joy Splats Returning a splattable Struct def send_request

    # ... Response.new(code, message) end Avdi Grimm (ShipRise) GoGaRuCo 2012 16 / 50
  24. Code to Joy Splats Splatting the Struct into component parts

    code, message = send_request logger.info " #{ code } : #{ message } " Avdi Grimm (ShipRise) GoGaRuCo 2012 17 / 50
  25. Code to Joy Splats Using the response Struct as-is response

    = send_request case response.code when 400..599 then handle_error(response) else # ... end Avdi Grimm (ShipRise) GoGaRuCo 2012 18 / 50
  26. Code to Joy Splats Splats & Structs • Return multiple

    values from a method. . . • . . . *Or* a single result object. . . • . . . from the same line of code. Magic! Avdi Grimm (ShipRise) GoGaRuCo 2012 19 / 50
  27. Code to Joy YAML::Store Persisting Posts require ’yaml/store’ repo =

    YAML::Store.new( ’blog.yml’ ) Post = Struct.new( :title , :body ) repo.transaction do posts = repo[ :posts ] = [] posts << Post.new( "F1rst Post!!!" , "FIRST" ) posts << Post.new( "Hiatus" , "This blog is on vacation" ) end Avdi Grimm (ShipRise) GoGaRuCo 2012 21 / 50
  28. Code to Joy YAML::Store The Persisted Data cat blog.yml ---

    :posts: - !ruby/struct:Post title: F1rst Post!!! body: FIRST - !ruby/struct:Post title: Hiatus body: This blog is on vacation Avdi Grimm (ShipRise) GoGaRuCo 2012 22 / 50
  29. Code to Joy YAML::Store Associations Post = Struct.new( :title ,

    :body , :category ) Category = Struct.new( :name ) food = Category.new( :food ) repo.transaction do posts = repo[ :posts ] = [] posts << Post.new( "Cookie!!!" , "C is for cookie!" , food) posts << Post.new( "Durian" , "Stinky!" , food) end Avdi Grimm (ShipRise) GoGaRuCo 2012 23 / 50
  30. Code to Joy YAML::Store Associations YAML --- :posts: - !ruby/struct:Post

    title: Cookie!!! body: C is for cookie! category: &8509480 !ruby/struct:Category name: :food - !ruby/struct:Post title: Durian body: Stinky! category: *8509480 Avdi Grimm (ShipRise) GoGaRuCo 2012 24 / 50
  31. Code to Joy YAML::Store PStore • Drop-in replacement require ’pstore’

    2 repo = PStore.new( ’blog.pstore’ ) repo.transaction do 4 # ... end Avdi Grimm (ShipRise) GoGaRuCo 2012 25 / 50
  32. Code to Joy YAML::Store PStore • Drop-in replacement require ’pstore’

    2 repo = PStore.new( ’blog.pstore’ ) repo.transaction do 4 # ... end • Faster Adding 1000 posts, with a transaction for each write: user system total real yaml 50.210000 0.210000 50.420000 ( 54.024277) pstore 3.290000 0.130000 3.420000 ( 10.887887) Avdi Grimm (ShipRise) GoGaRuCo 2012 25 / 50
  33. Code to Joy YAML::Store YAML#Store • Incredibly simple Hash-like persistence

    mechanism • Great for local command-line apps • Supports complex object trees • Oh yeah, one more thing. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 26 / 50
  34. Code to Joy YAML::Store YAML#Store • Incredibly simple Hash-like persistence

    mechanism • Great for local command-line apps • Supports complex object trees • Oh yeah, one more thing. . . • ZOMG NOSQL!!! Avdi Grimm (ShipRise) GoGaRuCo 2012 26 / 50
  35. Code to Joy Enumerators Kernel#to_enum Convert any old method into

    an Enumerator def names yield ’Ylva’ yield ’Brighid’ yield ’Shifra’ yield ’Yasmin’ end e = to_enum( :names ) # => #<Enumerator: main:names> e.to_a # => ["Ylva", "Brighid", "Shifra", "Yasmin"] Avdi Grimm (ShipRise) GoGaRuCo 2012 28 / 50
  36. Code to Joy Enumerators Searching for config files + project

    |-- .rutrc |-- file1 |-- file2 |-+ subproject <-- you are here |-- .rutrc |-- file3 \-- ... Avdi Grimm (ShipRise) GoGaRuCo 2012 29 / 50
  37. Code to Joy Enumerators Pathname#ascend require ’pathname’ Dir.chdir( ’/home/avdi/dev’ )

    Pathname.pwd.ascend do |dir| puts dir end /home/avdi/dev /home/avdi /home / Avdi Grimm (ShipRise) GoGaRuCo 2012 30 / 50
  38. Code to Joy Enumerators Searching upwards for config files require

    ’pathname’ curdir = Pathname.pwd configs = curdir.to_enum( :ascend ). each_with_object([]){ |dir, files| config = (dir + ’.rutrc’ ) files << config if config.file? } Avdi Grimm (ShipRise) GoGaRuCo 2012 31 / 50
  39. Code to Joy Enumerators Searching upwards for config files curdir

    = Pathname.pwd configs = curdir.to_enum(:ascend). each_with_object([]) { |dir, files| config = (dir + ’.rutrc’) files « config if config.file? } Avdi Grimm (ShipRise) GoGaRuCo 2012 31 / 50
  40. Code to Joy Enumerators Searching upwards for config files curdir

    = Pathname.pwd configs = curdir.to_enum(:ascend). each_with_object([]) { |dir, files| config = (dir + ’.rutrc’) files « config if config.file? } Avdi Grimm (ShipRise) GoGaRuCo 2012 31 / 50
  41. Code to Joy Enumerators Searching upwards for config files curdir

    = Pathname.pwd configs = curdir.to_enum(:ascend). each_with_object([]) { |dir, files| config = (dir + ’.rutrc’) files « config if config.file? } Avdi Grimm (ShipRise) GoGaRuCo 2012 31 / 50
  42. Code to Joy Enumerators Enumerator and #to_enum • Turn any

    method into an iterable series • Make all the power of Enumerator available to the series Avdi Grimm (ShipRise) GoGaRuCo 2012 32 / 50
  43. Code to Joy Break Give me a break Avdi Grimm

    (ShipRise) GoGaRuCo 2012 33 / 50
  44. Code to Joy Break break It breaks out of loops.

    Whoop-de-doo. jobs.each do |job| break if job == :terminate job.call end Avdi Grimm (ShipRise) GoGaRuCo 2012 34 / 50
  45. Code to Joy Break break beyond iteration def names yield

    ’Ylva’ yield ’Brighid’ yield ’Shifra’ yield ’Yasmin’ end Avdi Grimm (ShipRise) GoGaRuCo 2012 35 / 50
  46. Code to Joy Break break beyond iteration i = 0

    names do |name| puts name break if i >= 1 # that’s plenty! i += 1 end Ylva Brighid Avdi Grimm (ShipRise) GoGaRuCo 2012 35 / 50
  47. Code to Joy Break break beyond iteration def names yield

    ’Ylva’ yield ’Brighid’ # ---- We stopped here! ---- yield ’Shifra’ yield ’Yasmin’ end Without a return, throw, or raise. Avdi Grimm (ShipRise) GoGaRuCo 2012 35 / 50
  48. Code to Joy Break break respects ensure blocks def names_with_last

    yield ’Ylva’ yield ’Brighid’ yield ’Shifra’ yield ’Yasmin’ ensure puts "Grimm" end Avdi Grimm (ShipRise) GoGaRuCo 2012 36 / 50
  49. Code to Joy Break break respects ensure blocks i =

    0 names_with_last do |name| puts name break if i >= 1 # that’s plenty! i += 1 end Ylva Brighid Grimm Avdi Grimm (ShipRise) GoGaRuCo 2012 36 / 50
  50. Code to Joy Break break respects ensure blocks def names_with_last

    yield ’Ylva’ yield ’Brighid’ # ---- We stopped here... ---- yield ’Shifra’ yield ’Yasmin’ ensure # ...and jumped to here. puts "Grimm" end Avdi Grimm (ShipRise) GoGaRuCo 2012 36 / 50
  51. Code to Joy Break break with a value What if

    we want to capture a name? result = nil result = names do |name| if name =~ /^S/ result = name break end end result # => "Shifra" Avdi Grimm (ShipRise) GoGaRuCo 2012 37 / 50
  52. Code to Joy Break break with a value result =

    names do |name| break name if name =~ /^S/ end result # => "Shifra" This effectively overrides the method’s return value. Avdi Grimm (ShipRise) GoGaRuCo 2012 37 / 50
  53. Code to Joy Break Breaking from a #detect f =

    open( ’code-to-joy.org’ ) result = f.lines.detect do |line| break "<Line Not Found>" if f.lineno >= 100 line =~ /banana/ end result # => "<Line Not Found>" Avdi Grimm (ShipRise) GoGaRuCo 2012 38 / 50
  54. Code to Joy Subclassing Module A simple RPG Race =

    Struct.new( :name , :strength ) CharacterClass = Struct.new( :name , :charisma ) Character = Struct.new( :name , :race , :char_class ) human = Race.new( "Human" , 9) wizard = CharacterClass.new( "Tourist" , 3) player = Character.new( "Rhincewind" , human, wizard) Avdi Grimm (ShipRise) GoGaRuCo 2012 40 / 50
  55. Code to Joy Subclassing Module What we’d like Ideally, attributes

    would be delegated to either race or char_class. player.name # => "Rhincewind" player.strength # => 9 player.charisma # => 3 Avdi Grimm (ShipRise) GoGaRuCo 2012 41 / 50
  56. Code to Joy Subclassing Module Using Forwardable require ’forwardable’ Character

    = Struct.new( :name , :race , :char_class ) do extend Forwardable def_delegators :race , :strength def_delegators :char_class , :charisma end Avdi Grimm (ShipRise) GoGaRuCo 2012 42 / 50
  57. Code to Joy Subclassing Module Evolving with Forwardable Race =

    Struct.new( :name , :strength , :constitution ) human = Race.new( "Human" , 9, 5) # ... Character = Struct.new( :name , :race , :char_class ) do extend Forwardable def_delegators :race , :strength , :constitution # ... end Every change has to be made twice. Avdi Grimm (ShipRise) GoGaRuCo 2012 43 / 50
  58. Code to Joy Subclassing Module Using SimpleDelegator require ’delegate’ class

    Character < SimpleDelegator def initialize(name, race, char_class) @name = name super(race) end # ... end Only one object supported. Avdi Grimm (ShipRise) GoGaRuCo 2012 44 / 50
  59. Code to Joy Subclassing Module Subclassing Module class Delegate <

    Module def initialize(attribute) @attribute = attribute super() do # see next slide... end end end Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  60. Code to Joy Subclassing Module Subclassing Module # ... define_method(

    :method_missing ) do |message, *args, &block| target = send(attribute) if target.respond_to?(message) target.public_send(message, *args, &block) else super(message, *args, &block) end end # ... Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  61. Code to Joy Subclassing Module Subclassing Module # ... define_method(:method_missing)

    do |message, *args, &block| target = send(attribute) if target.respond_to?(message) target.public_send(message, *args, &block) else super(message, *args, &block) end end # ... Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  62. Code to Joy Subclassing Module Subclassing Module # ... define_method(:method_missing)

    do |message, *args, &block| target = send(attribute) if target.respond_to?(message) target.public_send(message, *args, &block) else super(message, *args, &block) end end # ... Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  63. Code to Joy Subclassing Module Subclassing Module # ... define_method(:method_missing)

    do |message, *args, &block| target = send(attribute) if target.respond_to?(message) target.public_send(message, *args, &block) else super(message, *args, &block) end end # ... Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  64. Code to Joy Subclassing Module Subclassing Module # ... define_method(:method_missing)

    do |message, *args, &block| target = send(attribute) if target.respond_to?(message) target.public_send(message, *args, &block) else super(message, *args, &block) end end # ... Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  65. Code to Joy Subclassing Module Subclassing Module class Delegate <

    Module # ... def to_s "Delegate( #{ @attribute.inspect } )" end end Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  66. Code to Joy Subclassing Module Subclassing Module Character = Struct.new(

    :name , :race , :char_class ) do include Delegate.new( :race ) include Delegate.new( :char_class ) end Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  67. Code to Joy Subclassing Module Subclassing Module player.class.ancestors # =>

    [Character, Delegate( :char_class ), Delegate( :race ), Struct, ...] Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  68. Code to Joy Subclassing Module Subclassing Module Race = Struct.new(

    :name , :strength ) CharacterClass = Struct.new( :name , :charisma ) # ... player = Character.new( "Rhincewind" , human, wizard) player.name # => "Rhincewind" player.strength # => 9 player.charisma # => 3 A chain of responsibility: 1 self 2 self.char_class 3 self.race 4 Other ancestors. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 45 / 50
  69. Code to Joy Subclassing Module Subclassing Module • Creates a

    new “kind” of module • Enables us to add state to the modules a class includes • Enabled us to implement a new type of message delegation in Ruby Avdi Grimm (ShipRise) GoGaRuCo 2012 46 / 50
  70. Code to Joy MINASWAN My Favorite Thing of All MINASWAN

    Avdi Grimm (ShipRise) GoGaRuCo 2012 47 / 50
  71. Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  72. Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? • Community drama bumming you out? Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  73. Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? • Community drama bumming you out? • If you’re blue, don’t know where to go to. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  74. Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? • Community drama bumming you out? • If you’re blue, don’t know where to go to. . . • . . . practice joyful coding Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  75. Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? • Community drama bumming you out? • If you’re blue, don’t know where to go to. . . • . . . practice joyful coding • How? Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  76. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  77. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code • Shared code is joyful code Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  78. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code • Shared code is joyful code • If you want to feel good about your craft. . . Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  79. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code • Shared code is joyful code • If you want to feel good about your craft. . . • . . . give someone a “wow” moment. Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  80. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code • Shared code is joyful code • If you want to feel good about your craft. . . • . . . give someone a “wow” moment. • Show one person something awesome. Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  81. Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code • Shared code is joyful code • If you want to feel good about your craft. . . • . . . give someone a “wow” moment. • Show one person something awesome. • I promise you’ll feel better! Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  82. Code to Joy Conclusion Thank You • Rate! http://spkr8.com/t/16041 •

    Contact! [email protected] • Tweet! @avdi • Read! http://confidentruby.com • 20% off: GOGARUCO2012 • . . . or send me a postcard! Avdi Grimm (ShipRise) GoGaRuCo 2012 50 / 50