> node :node1 do > ip 192.whatever > title "Node 1" > end > > node :node2 do > ip 192.whatever > title "Node 2" > end <snip> > class ClusterManager > > def load_config_file config_file > instance_eval File.read(config_file) > end > > def node node_id, &block > # What goes here? > end > > end I'd think similarly, but a bit different in that ClusterManager::Builder should be the one doing the instance eval: class ClusterManager attr_accessor :nodes def initialize @nodes = [] end def describe @nodes.collect{ |node| node.describe }.join("\n") end class Node attr_accessor :id, :ip, :title def initialize( id ) @id = id end def describe "#@id -> #@ip \"#@title\"" end class Builder def process( node, dsl ) @node = node instance_eval &dsl end def ip( value ) @node.ip = value end def title( value ) @node.title = value end end end class Builder def process( manager, dsl ) @manager = manager @node_builder = Node::Builder.new instance_eval &dsl end def node( node_id, &block ) node = Node.new( node_id ) @node_builder.process( node, block ) @manager.nodes << node end end end dsl = lambda{ node :node1 do ip '192.whatever' title 'Node 1' end node :node2 do ip '192.whatever' title 'Node 2' end } manager = ClusterManager.new builder = ClusterManager::Builder.new builder.process( manager, dsl ) puts manager.describe ############### A few caveats about the above: 1) You'll notice I changed the DSL a little (quoted the 192.whatever values). That was simply for brevity in this illustration. 2) My Builders require the argument to process be lambdas (Procs) not strings. This was because I didn't want to complicate things with switches on whether to use the prefix & or not. But it would be as simple as an if/else to hide the lambda/string distinction from the "user" (which will be yourself, not the person writing in the DSL) inside the Builder. Alternatively, you can take a string and build it into a lambda for the Builder via eval("lambda{ #{dsl} }"). I wouldn't necessarily recommend that though (with all the evils of eval). Jacob Fugal