An example of how to do a Ruby DSL with nested blocks and no block params required (updated)

Posted: June 2, 2012 by Steve in Uncategorized

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}"

Comments are closed.