| Last updated on Sun Jul 18 12:00:00 CEST 2004 |
| Defining methods with closures |
|
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… |