Ruby 1.9 puts an extra hurdle in DSL development. Consider this simple DSL example which runs fine in 1.8:
class ProxyObject
def initialize
@o = Array.new
end
def method_missing(m, *a, &b)
@o.send m, *a, &b
end
end
def dsl(&b)
ProxyObject.new.instance_eval &b
end
module A
class B; end
dsl do
p Module.nesting # => [A]
push B
p pop # => A::B
end
end
This DSL simply proxies to an Array object. In ruby 1.8 everything will work, in specific, resolving the constant B, which resolves to the class A::B. This constant resolution works because we are in the lexical scope of class A, as we can see by the module nesting. (For more info on module nesting see this post)
Now run the code in ruby 1.9:
module A
class B; end
dsl do
p Module.nesting # => [#<Class:#<ProxyObject:0x97beab8>>]
push B # => uninitialized constant ProxyObject::B
p pop
end
end
Now things break. Our module nesting no longer contains A because instance_eval has changed the nesting to the metaclass of our ProxyObject instance, which doesn’t help us at all. But wait… my last blog post to the rescue! In ruby 1.9 we can dynamically add modules to our lexical scope. So all we need to do is find the original module nesting of the block, then add those modules to our nesting, and then eval the block as we normally would:
def dsl(&b)
modules = b.binding.eval "Module.nesting"
Kernel.with_module(*modules) do
ProxyObject.new.instance_eval &b
end
end
module A
class B; end
dsl do
p Module.nesting # => [#<Class:#<ProxyObject:0x8d6d190>>, #<Class:A>, A, Kernel]
push B
p pop # => A::B
end
end
Please let me know if you see any issues with this approach or have other ideas.
Credit goes to Lourens for mentioning this problem with 1.9 to me.

I guess the funniest part is this coming up just a few days after you posted the with_const post :)
Comment by mtoledo — June 3, 2009 @ 12:52 am
Thanks for this post. I had just posted to ruby-talk because I noticed the same thing.
Comment by gilgamesh — June 27, 2009 @ 6:30 am