The Case of the Missing Method — A Ruby Mystery Story

The Case of the Missing Method — A Ruby Mystery Story

Business is slow for Ruby Private Investigator, Deirdre Bug. She’s on the verge of switching industry when she gets a call from an anxious young man. "Some class methods have gone missing," he tells her breathlessly. "I need your help."

Deirdre takes the case and begins exploring Ruby objects behind the scenes. Though she thinks she's on familiar ground — Ruby's object model, method lookup — she's about to discover that she really has no clue.

5bace0ba91ff3182c0e522ab4d67cd56?s=128

Nadia Odunayo

February 07, 2019
Tweet

Transcript

  1. THE CASE OF THE MISSING METHOD A Ruby Mystery Story

    Nadia Odunayo, @nodunayo
  2. Side hustle?

  3. None
  4. "

  5. None
  6. None
  7. Nadia Odunayo

  8. Nadia Odunayo

  9. Deirdre Bug

  10. Deirdre Bug

  11. Dee Bug

  12. CASE FILES #017 Missing Method

  13. CHAPTER ONE

  14. "

  15. $

  16. Mike • 24 years old • Junior Ruby developer •

    TDD-enthusiast %
  17. Apprenticeship Ruby Institute of Professionals (RIP)

  18. Jenny • 27 years old • Junior Ruby developer •

    Rails worshipper & • Mike’s best friend
  19. ' & “May the best person win!”

  20. Method lookup &

  21. $ Something doesn’t add up in Jenny’s notes!

  22. &

  23. $ You can help me find the answer!

  24. None
  25. None
  26. class

  27. instance object class

  28. instance object class class object

  29. instance object class class object class methods method_one method_two method_three

  30. & Any class you define is an instance of a

    class object called ‘Class'
  31. class Cake is an instance of class Class

  32. class Cake … def tasty? @flavour == “carrot” end def

    self.edible? true end end
  33. carrot = Cake.new

  34. carrot class Cake class methods tasty? def tasty? @flavour ==

    “carrot” end Class methods edible? def edible? true end
  35. & A method definition always comes from an object's class!

  36. carrot class Cake class methods tasty? def tasty? @flavour ==

    “carrot” end Class methods edible? def edible? true end
  37. class Cake … def tasty? @flavour == “carrot” end def

    self.edible? true end end
  38. None
  39. +

  40. "

  41. None
  42. ,

  43. CHAPTER TWO

  44. None
  45. None
  46. " The ‘edible?’ method must be somewhere in the ancestry

    tree!
  47. None
  48. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  49. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  50. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  51. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  52. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  53. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  54. None
  55. +

  56. "

  57. /01 234 567

  58. $ ObjectSpace

  59. 8 You can interact with all the ‘live’ objects within

    a Ruby session!
  60. ObjectSpace.count_objects[:T_CLASS]

  61. None
  62. +

  63. Where are you?!

  64. :

  65. CHAPTER THREE

  66. A Little Chat about Smalltalk A Lecture by Adele Alan

    ;
  67. Smalltalk Born 1970 Adele Alan ;

  68. Smalltalk The birth of object-oriented programming Adele Alan ;

  69. "

  70. " One class, yet two objects?!

  71. …psst! Pay attention!!

  72. What is the ‘class of a class’ in Smalltalk? ;

  73. Adele Alan ;

  74. Adele Alan ;

  75. The ‘class of a class’ is called a metaclass. ;

  76. Smalltalk Adele Alan Objective-C ;

  77. Smalltalk Adele Alan Objective-C Java ;

  78. Smalltalk Adele Alan Objective-C Java Python ;

  79. Smalltalk Adele Alan Objective-C Java Ruby Python ;

  80. =

  81. None
  82. def where?(object, method) object.class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  83. def where?(object, method) object.singleton_class.ancestors.detect { | klass | klass.instance_methods(false).include?(method) }

    end
  84. None
  85. CHAPTER FOUR

  86. None
  87. "Can I come round?! ' Of course!!

  88. "

  89. Singleton classes?

  90. None
  91. A Ellen • 43 years old • Freelance developer •

    Ruby committer
  92. "

  93. " Singleton classes — what are they?

  94. A Internal classes to hold methods defined for one particular

    object.
  95. " When does knowing about them become useful?

  96. Budgeting Inc. Personal finance for small business owners

  97. Budgeting Inc. A The codebase was a mess! •Lots of

    duplicate code •Inefficient development •Bugs and oversights
  98. / 0 1 ☹

  99. D ☹

  100. A I decided to create a DSL — a Domain

    Specific Language.
  101. +

  102. A Let me show you a basic version of a

    DSL…
  103. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  104. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  105. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  106. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  107. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  108. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  109. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  110. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  111. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  112. class CityInstance def self.construct(&block) city = new city.instance_eval(&block)
 city end

    
 attr_reader :taxes 
 
 def initialize
 @taxes = []
 end
 
 def tax(name)
 @taxes.push(name)
 end end

  113. None
  114. A The DSL allowed for quick and easy scaffolding of

    each new City subclass and any related classes.
  115. None
  116. Budgeting Inc. A • Effortless scaffolding • Better maintainability •

    Easier customisation All common code was consolidated in one place!
  117. / 0 1

  118. D

  119. None
  120. A Grasping singleton classes can save you from a lot

    of headache.
  121. A But, beware…!

  122. To DSL? •Complex repeated business rules? •Need to customise behaviour

    in some specific instances? ,
  123. A Using Rails? You see DSLs every single day.

  124. None
  125. None
  126. A Rails magic?

  127. A Rails magic? A collection of well-written DSLs.

  128. None
  129. A Complex Rails or Ruby? Funny bug? Singleton classes, perhaps…!

  130. F

  131. G

  132. None
  133. carrot class Cake class methods tasty? def tasty? @flavour ==

    “carrot” end Cake singleton methods edible? def edible? true end
  134. G Jenny knew about singleton classes all along…

  135. &

  136. "

  137. G My best friend tried to sabotage me!

  138. " You can’t give up now!!

  139. " Have you heard of DSLs…?

  140. G

  141. EPILOGUE 2 months later…

  142. Hack Night

  143. Oooh..! She’s Ruby famous…!

  144. A

  145. " Singleton classes — do I have to care about

    them?
  146. A Not really…

  147. Simplicity

  148. Consistency

  149. A Every method in Ruby is an instance method.

  150. Invisible Yet Everywhere

  151. "

  152. " Ruby Private Investigator Specialising in Ruby internals…? Deirdre Bug

  153. EPILOGUE Again

  154. Due to being distraught at Jenny’s betrayal, Mike performed badly

    at the RIP interview… ジェニーの裏切りに動揺した結果、 マイクはRIPの⾯面接に失敗してしま う。
  155. Jenny is 3 months into an apprenticeship at the RIP.

    ジェニーはRIP社にて3ヶ⽉月⽬目の⾒見見習い 中。
  156. Mike and Jenny no longer speak. マイクとジェニーはもう⼝口もきかない関係となっ た。

  157. Mike is still looking for work. マイクはいまだ求職中である。

  158. The shady mastermind behind Ruby is still at large… Rubyを陰で操る黒幕は今だ不不明である。

  159. THE END? 終 ?

  160. SOURCES FRIENDS OF DEE Ruby Under A Microscope Book by

    Pat Shaughnessy
 Ruby, Smalltalk and Class Variables Blog post by Pat Shaughnessy
 Ruby Tapas — Episodes 453, 454, and 456 Screencasts by Avdi Grimm Benefits of Writinng a DSL in Ruby Blog post by Gusto
 DslQandA Blog post by Martin Fowler Eigen What Now? Talk by Eliza de Jager Aaron Patterson Adam Cuppy Aly Blenkin Andy Croll Ana Carlos Avdi Grimm Ebi Jonathan Tsang Pat Shaughnessy Saron Yitbarek Tammer Saleh Nadia Odunayo, @nodunayo