From: Joe Van Dyk Date: 2005-09-08T01:48:44+09:00 Subject: Re: domain-specific language On 8/18/05, Jacob Fugal wrote: > > node :node1 do > > ip 192.whatever > > title "Node 1" > > end > > > > node :node2 do > > ip 192.whatever > > title "Node 2" > > end > > > > > 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). Thank you for your ideas. I've adapted it somewhat for parts of my application, but I'm running into problems. I've got the following configuration file: option :display do display :text, :size => 20, :title => "DISPLAY" value :DISPLAY, :default => ENV['DISPLAY'] || 'localhost:0' end argument :xterm_title do display :text, :size => 20, :title => "Xterm Title" value "-T", :default => "You are on a xterm!" end argument :xterm_text_color do display :text, :size => 20, :title => "Xterm Font Color" value "-fg", :default => "red" end application :xterm do executable "/usr/X11R6/bin/xterm" title :Xterm node :fatire options :display arguments :xterm_text_color, :xterm_title end application :xeyes do executable "/usr/X11R6/bin/xeyes" title :Xeyes node :fatire options :display end Should be self-explanatory. I have some applications that take env options and command-line arguments, and I want to have a very readable and configurable file for defining those applications, options, and arguments (and some other things). The 'node' option for the application is the machine that the application should be started on. The 'display' option for the options/arguments state how the GUI should display the option/argument. My problem is trying to adapt your solution to fit something like this. There would be a lot of duplication if I had Builders for applications, nodes, options, and arguments. And I'm also having some difficulties getting each Application object to know what options and arguments it should have. I considered using YAML for the configuration file format, but I'm still leaning towards having a pure Ruby file. Thoughts are greatly appreciated! This is my first time trying to do this type of programming, so it's a little weird.