Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Metaprogramming Ruby for Greater Good

82476266af9d460415d8f1fc16bb54ed?s=47 jarkko
September 26, 2014

Metaprogramming Ruby for Greater Good

My talk at Reaktor Dev Day 2014

82476266af9d460415d8f1fc16bb54ed?s=128

jarkko

September 26, 2014
Tweet

More Decks by jarkko

Other Decks in Programming

Transcript

  1. Metaprogramming RUBY for Greater Good

  2. Who?

  3. @jarkko jlaine.net [en-us] notkea.fi [fi-fi]

  4. bearmetal.eu

  5. What?

  6. Code that writes code

  7. “Writing code that manipulates language constructs at runtime.” Paolo Perrotta

  8. Why?

  9. Magic

  10. Not clever, Flexible

  11. Why Ruby?

  12. In Ruby Metaprogramming is just Programming

  13. Advanced OO concepts in Ruby

  14. Prototypes vs Classes

  15. speaker = Object.new def speaker.talk_length @talk_length ||= 30 end def

    speaker.talk_length=(length) @talk_length = length end
  16. gary = speaker.clone gary.talk_length # => 30 gary.talk_length = 60

    gary.talk_length # => 60 scott = gary.clone scott.talk_length # => 60 scott.talk_length = 45 scott.talk_length # => 45
  17. None
  18. Everything is executable

  19. class Conference puts "Hello world (open)" def venue end #

    … end # Hello world (open) # => nil
  20. class Conference < Event end > c = Conference.new >

    c.class => Conference > c.class.superclass
  21. class Conference < Event end > c = Conference.new >

    c.class => Conference > c.class.superclass => ChunkyBacon
  22. class ChunkyBacon end Event = ChunkyBacon class Conference < Event

    end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon
  23. def gimme_a_class [Array, Hash, String][rand(3)] end class Conference < gimme_a_class()

    end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon
  24. Open classes class String alias_method :old_reverse, :reverse def reverse old_reverse.upcase

    end end > speaker = "Gary" > speaker.reverse => "YRAG"
  25. Everything is an object > String.instance_method(:reverse) => #<UnboundMethod: String#reverse>

  26. Even classes

  27. Wait, what?

  28. But, classes have class methods. They are special.

  29. No.

  30. class Conference def self.in_finland # return conferences in Finland end

    end
  31. Singleton methods def speaker.talk_length @talk_length ||= 30 end Defined for

    a single object
  32. Ruby method lookup Right, then up

  33. Where are singleton methods sitting?

  34. Singleton class aka ghost class, eigenclass, metaclass anonymous class below

    object's own class
  35. None
  36. Singleton class Just a regular class, except » Hidden from

    the generic class hierarchy » Cannot be inherited directly » Only ever has a single instance of itself
  37. So, what are class methods?

  38. So, what are class methods? Singleton methods for the class

    object itself
  39. So, what are class methods? Singleton methods for the class

    object itself Residing in the singleton class of the class object
  40. None
  41. Singleton classes are classes and classes are objects, so…

  42. …a singleton class must have its own singleton class

  43. Singleton classes all the way down

  44. Metaprogramming, anyone?

  45. Creating code dynamically in Ruby

  46. Creating code dynamically in Ruby » eval » instance_eval &

    class_eval » define_method & Class.new » method_missing
  47. eval meth = "my_method" eval <<-END def #{meth} "foo" end

    END
  48. eval » Just executes a line of code » Super

    powerful… » …and dangerous in wrong hands » Code injection hazard
  49. instance_eval » Can also take a string, but also a

    code block… » …which it then executes in the scope of the receiver. » Methods will become class methods (because self == the class) obj.instance_eval do self # => obj @v # => obj's instance var end
  50. instance_eval class Turtle def move(&block) instance_eval(&block) end def right(n); end;

    # … end t = Turtle.new t.move do right(3) up left(2) down end
  51. class_eval » Can only be called for classes and modules

    » creates instance methods » can be used to do stuff inside the class definition [String, Array, Hash].each do |cls| cls.class_eval { include HelloWorld } end
  52. define_method » Create a method dynamically » Can be put

    inside other methods class Cat < Animal [:leg, :head, :tail].each do |part| define_method(part) do # … end end end
  53. method_missing The catch-all method » Called whenever a method is

    not found in the inheritance chain. » By default throws a NoMethodError. » Can be modified to catch any method call, thus “creating” a ghost method » The name of which you don't have to even know.
  54. method_missing class Speaker def method_missing(met, *args) if met.to_s == "speak"

    "I might as well say something: #{args[0]}" else super end end end gary = Speaker.new gary.talk("Destroy it") # => NoMethodError gary.speak("Just destroy it!") # => "I might as well say something: Just destroy it!"
  55. Example Rewrite attr_accessor in Ruby

  56. class Animal attr_accessor :legs, :diet end cat = Animal.new cat.legs

    = 4 cat.legs # => 4 cat.diet = "Small birds" cat.diet # => "Small birds"
  57. What is attr_accessor there?

  58. A class method errrrr... A singleton method of the class

    object
  59. class Animal def self.nattr_accessor(*meths) meths.each do |meth| # getter define_method(meth)

    do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end
  60. class Cat < Animal nattr_accessor :legs, :diet end c =

    Cat.new c.legs = 4 c.diet = "Small birds" c.legs # => 4 c.diet # => "Small birds"
  61. # Making it more reusable class Object def self.nattr_accessor(*meths) meths.each

    do |meth| # getter define_method(meth) do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end
  62. module Nattr def nattr_accessor(*meths) meths.each do |meth| # getter define_method(meth)

    do instance_variable_get("@#{meth}") end # setter define_method("#{meth}=") do |wut| instance_variable_set("@#{meth}", wut) end end end end
  63. class Animal include Nattr end Won't work (creates instance methods)

  64. class Animal extend Nattr end class Cat < Animal nattr_accessor

    :legs, :diet end c = Cat.new c.legs = 4 c.diet = "Mice" c.legs c.diet
  65. Problems with metaprogramming

  66. Abstraction and Indirection Code can become harder to » Read

    » Debug » Search for
  67. Powertool Apply with care Use when you need it, not

    when you can
  68. Where now? Only scratched the surface

  69. Dave Thomas's screencasts at Prag Prog A bit dated (1.8)

    but lay a solid foundation Paolo Perrotta's Metaprogramming Ruby
  70. Read the source, Luke Any non-trivial Ruby application is bound

    to have a bunch of metaprogramming sprinkled in.
  71. In Ruby Metaprogramming is just programming

  72. Thank you

  73. ?