Code to Joy

4dea430d31b993abaf41cd9b54f8128d?s=47 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.

4dea430d31b993abaf41cd9b54f8128d?s=128

avdi

September 15, 2012
Tweet

Transcript

  1. 1.

    Code to Joy Avdi Grimm ShipRise GoGaRuCo 2012 Avdi Grimm

    (ShipRise) Code to Joy GoGaRuCo 2012 1 / 50
  2. 2.

    Code to Joy Introduction Introduction These are a few of

    my favorite things. Avdi Grimm (ShipRise) GoGaRuCo 2012 2 / 50
  3. 3.

    Code to Joy Introduction My Job is Awesome • My

    job is awesome Avdi Grimm (ShipRise) GoGaRuCo 2012 3 / 50
  4. 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. 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. 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. 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. 8.

    Code to Joy Introduction Postcards are Awesome • Work in

    progress: Confident Ruby Avdi Grimm (ShipRise) GoGaRuCo 2012 5 / 50
  9. 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. 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. 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. 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. 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. 15.

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

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

    Code to Joy Splats Multiple return values def send_request #

    ... code = 404 message = "Not found" # ??? end Avdi Grimm (ShipRise) GoGaRuCo 2012 10 / 50
  17. 18.

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

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

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

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

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

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

    Code to Joy Splats Returning a splattable Struct def send_request

    # ... Response.new(code, message) end Avdi Grimm (ShipRise) GoGaRuCo 2012 16 / 50
  24. 25.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Code to Joy Break Give me a break Avdi Grimm

    (ShipRise) GoGaRuCo 2012 33 / 50
  44. 47.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Code to Joy MINASWAN My Favorite Thing of All MINASWAN

    Avdi Grimm (ShipRise) GoGaRuCo 2012 47 / 50
  71. 75.

    Code to Joy Conclusion Sometimes we get discouraged • Technical

    debt got you down? Avdi Grimm (ShipRise) GoGaRuCo 2012 48 / 50
  72. 76.

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

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

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

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

    Code to Joy Conclusion Defend your joy • There’s no

    metric for joyful code Avdi Grimm (ShipRise) GoGaRuCo 2012 49 / 50
  77. 81.

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

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

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

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

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

    Code to Joy Conclusion Thank You • Rate! http://spkr8.com/t/16041 •

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