In a previous post, I explained how to do a DSL with nested block but with no block params required. I have a need for this capability now, but when I went back to it to use what I had learned before, I am not sure what I wrote. It at least doesn’t work on 1.9.3 for sure. I am not sure what ruby version I got it to run in before. So I now have a working version for 1.9.3.
The basics are to take an anonymous block and define a method on the contained class to allow that block to be called in the context of an instance of that class. There has to be a Thread.exclusive block around the method assignment for the anonymous block to make sure that there aren’t any threading issues with two of these DSL’s running in different threads.
class Node def initialize(name=nil) @name = name @children = [] end def node(name, &block) child = Node.new(name) @children.push(child) child.instance_exec(&block) if block end def instance_exec(*args, &block) method_name = nil Thread.exclusive do n = 0 n += 1 while respond_to?(method_name = "__instance_exec#{n}") self.class.instance_eval { define_method(method_name, &block) } end begin send(method_name, *args) ensure self.class.instance_eval { remove_method(method_name) } rescue nil end end end def tree(name, &block) @tree = Node.new(name) @tree.instance_exec(&block) @tree end tree("Simpsons family tree") do node("gramps") do node("homer+marge") do node("bart") node("lisa") node("maggie") end end end puts "tree = #{@tree.inspect}" |