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

Metaprogramming Ruby for Greater Good

jarkko
September 26, 2014

Metaprogramming Ruby for Greater Good

My talk at Reaktor Dev Day 2014

jarkko

September 26, 2014
Tweet

More Decks by jarkko

Other Decks in Programming

Transcript

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

    speaker.talk_length=(length) @talk_length = length end
  2. 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
  3. class Conference puts "Hello world (open)" def venue end #

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

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

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

    end > c = Conference.new > c.class => Conference > c.class.superclass => ChunkyBacon
  7. 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
  8. No.

  9. 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
  10. So, what are class methods? Singleton methods for the class

    object itself Residing in the singleton class of the class object
  11. Creating code dynamically in Ruby » eval » instance_eval &

    class_eval » define_method & Class.new » method_missing
  12. eval » Just executes a line of code » Super

    powerful… » …and dangerous in wrong hands » Code injection hazard
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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.
  18. 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!"
  19. class Animal attr_accessor :legs, :diet end cat = Animal.new cat.legs

    = 4 cat.legs # => 4 cat.diet = "Small birds" cat.diet # => "Small birds"
  20. 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
  21. 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"
  22. # 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
  23. 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
  24. 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
  25. Dave Thomas's screencasts at Prag Prog A bit dated (1.8)

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

    to have a bunch of metaprogramming sprinkled in.
  27. ?