Defining Class Methods Dynamically in Ruby
metaprogramming, ruby
Solution
`klass.class` is the same in both cases (Class): all classes are instances of `Class`. As a result you're defining scream and then redefining it.
What are often thought of as class methods in ruby are actually singleton methods (there's lots of stuff to read about eigenclasses etc if you are interested).
The
def some_object.foo
end
Construct creates singleton methods. Very often this will be inside a class definition, using self but you can do it on anything, for example if you do
x = 'dog'
def x.bark
"Woof"
end
Then x.bark will return woof, but bark won't be defined on any other string.
Here your method needs to reference your `noise` variable, so you'll need to use `define_singleton_method` to define your method.
If you're still in ruby 1.8 you can't use define_singleton_method - you need to use the fact that singleton methods are methods on the eigenclass.
klass = Class.new
eigenclass = class << klass; self; end
eigenclass.send(:define_method, :scream){noise}
Is equivalent to using define_singleton_method
Problem
In Ruby 1.9.3 I need to create a few class instances which each have similar instance- and class-methods but which vary only by a few fixed parameters. The distinction of their class type is also important so I cannot simply use separate instances of the same class. A simplified example looks like this. ``` module Animal private def self.make_animal(name, legs, noise) klass = Class.new klass.const_set(:NUM_LEGS, legs) klass.class.send(:define_method, :scream) { noise.upcase + '!' } Animal.const_set(name, klass) end make_animal :Tiger, 4, 'roar' make_animal :Human, 2, 'derp' end ``` This seems to work fine except that the variables used in the block which dynamically defines the "scream" method are bound at runtime of the "scream" method instead of runtime of the "make_animal" method. ``` Animal::Human::NUM_LEGS # => 2 -- ok Animal::Tiger::NUM_LEGS # => 4 -- ok Animal::Human.scream # => "DERP!" -- ok Animal::Tiger.scream # => "DERP!" -- fail! ``` How can I modify the above code so that the Tiger screams `"ROAR!"`? [Note] I really do need to maintain the goofy OO structure in the example for reasons that are too involved to describe here. I'm interested only in learning how to programmatically define class methods on dynamically defined classes with parameterized method implementations.