I had forgotten that there are still Rubyists who don’t know the
class << self; self end.send(:define_method, :foo){ } idiom.
I wrote a short
explanation and some hints on how to use define_method.
Module#define_method is my method of choice (har har) to define methods
dynamically, since when I am meta-programming I usually need to define them
as a closure anyway: this is especially useful when you create a new
object, add some singleton methods to it that modify some data in your
environment and then perform an instance_eval on a Proc; this is a
way to define a restricted language to be used inside that block.
Let’s see a stupid synthetic example:
require "pp"
class Foo
def initialize(state = "initial")
@state = [state]
end
def update(&block)
magic_obj = Object.new # throw-away obj, only to capture method calls
state = @state
class << magic_obj; self end.send(:define_method, :do_magic) do |a|
puts "This object allows do_magic operations only..."
puts "i.e. we have restricted the allowable operations inside the block"
state << a
end
magic_obj.instance_eval(&block)
end
end
f = Foo.new
f.update do
# this block uses a different "vocabulary", i.e. it can behave as a new,
# restricted language. This is more general than the <tt>yield self</tt>
# technique used normally.
do_magic "foo"
end
pp f
I think I’ll probably write a bit more on this (and meta-programming
in general) in my Ruby
section.
BTW, I use this technique (with a couple additional meta-programming layers
though) in rpa-base, which
allows me to define a IMHO very good language for the rpafied
install.rb files: they are as descriptive and short as it gets; I
designed the language first and then implemented it with heavy
meta-programming in Ruby…