Last updated on Sun Jul 18 15:07:48 CEST 2004
Back to Ruby stuff

Basis TOP

We will use Module#define_method. Keep in mind that this method is private so

  SomeModuleOrClass.define_method(:foo){ }

wouldn’t work. This is why the relatively obscure class << self; … idiom (see singleton methods) is needed.

Defining normal instance methods TOP

This adds a bit of dinamicity to plain reopening of the class/module plus redefinition, cause the method is defined using a closure, and we can take the name from a string/symbol objects without having to interpolate it into a string passed to eval.

 class A; end
 a = 1
 A.send(:define_method, :foo){ puts "a" }
 A.send(:public, :foo)
 A.new.foo

Note that we had to make the method public explicitly; however, the following works fine:

 class A
    def foo; end
    define_method(:foo){ puts "foo" }  # redefine
    define_method(:bar){ puts "bar" }
 end
 A.new.foo
 A.new.bar

Limitations TOP

In 1.8.x, it is still impossible to define methods that accept blocks using define_method, so we still need cheap tricks like string interpolation + eval.

However, in 1.9 you can do something like

 module A
   define_method(:foo){|*a,&b| p a; b.call }
 end
 extend A
 foo { puts "12345" }

At that point, all you can do with eval+string interpolation is possible with define_method, and then some more.

Defining singleton methods TOP

define_method is especially attractive cause it works with a closure, but class << obj opens a new scope, so a naive attempt to define a method dynamically can be limiting:

 a = 1
 foo = SomeClass.new
 class << foo
    define_method(:bar){ a += 1 } # doesn't work, cause a is in another scope
 end

In order to stay in the same scope, so that the closure captures the environment we are interested in, we need something like

 a = 1
 foo = SomeClass.new
 class << foo; self end.send(:define_method, :bar){ a += 1}

Let’s analyze the class << self; self end.send(:defile_method; :x){ } idiom step by step:

class << someobject:opens the singleton class of someobject. Note in that in the class body, self is the singleton class.
class << someobject; self end:returns the singleton class of someobject, that is
 singleton_class = class << someobj; self end
send :define_method:define_method is a private method, so we need to call it using Object#send which bypasses access restrictions. The argument to define_method is given as the 2nd arg. to send. Finally, the body of the method we want to define is passed as a block. Since the block is specified in the "external scope" (not inside the singleton class scope), the closure captures the environment of interest.
 

Copyright © MJFP
batsman dot geo at yahoo dot com