domain-specific language

J

Joe Van Dyk

I'm writing an application that controls a group/cluster of linux
computers/nodes. I need to have a configuration file that lists the
nodes in the cluster.

It would be neat if the configuration file was in Ruby. If I had
nodes named node1 and node3, the configuration file could look a
little like:

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end

So, is there some standard Ruby idiom for how to read a file and
execute the code? Just load the file and instance_eval it?
 
J

Jamis Buck

I'm writing an application that controls a group/cluster of linux
computers/nodes. I need to have a configuration file that lists the
nodes in the cluster.

It would be neat if the configuration file was in Ruby. If I had
nodes named node1 and node3, the configuration file could look a
little like:

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end

So, is there some standard Ruby idiom for how to read a file and
execute the code? Just load the file and instance_eval it?

That's what SwitchTower does. I'm sure there's many other ways to do
it, but it works well enough.

- Jamis
 
J

Joe Van Dyk

On Aug 18, 2005, at 3:40 PM, Joe Van Dyk wrote:
=20
=20
That's what SwitchTower does. I'm sure there's many other ways to do
it, but it works well enough.

Thanks! I'll check it out.

So, say I have the following class

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

Or should I set it up differently?
 
A

Ara.T.Howard

I'm writing an application that controls a group/cluster of linux
computers/nodes. I need to have a configuration file that lists the
nodes in the cluster.

It would be neat if the configuration file was in Ruby. If I had
nodes named node1 and node3, the configuration file could look a
little like:

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end

So, is there some standard Ruby idiom for how to read a file and
execute the code? Just load the file and instance_eval it?


harp:~ > cat a.rb
config = <<-config
nodes :
1 :
ip : 192.whatever
title : node 1
2 :
ip : 192.whatever
title : node 2
config

require 'yaml'

config = YAML::load config

config['nodes'].each do |nid, node|
puts "node <#{ nid }> => <#{ node.inspect }>"
end


harp:~ > ruby a.rb
node <1> => <{"title"=>"node 1", "ip"=>"192.whatever"}>
node <2> => <{"title"=>"node 2", "ip"=>"192.whatever"}>

so all you have to do is 'YAML::load(IO::read(configfile))'.

what kind of clustering are you working with?

you may, or may not, find this useful:

http://raa.ruby-lang.org/project/rq/
http://www.linuxjournal.com/article/7922

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
J

Jacob Fugal

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end

class ClusterManager
=20
def load_config_file config_file
instance_eval File.read(config_file)
end
=20
def node node_id, &block
# What goes here?
end
=20
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 =3D []
end

def describe
@nodes.collect{ |node| node.describe }.join("\n")
end

class Node
attr_accessor :id, :ip, :title

def initialize( id )
@id =3D id
end

def describe
"#@id -> #@ip \"#@title\""
end

class Builder
def process( node, dsl )
@node =3D node
instance_eval &dsl
end

def ip( value )
@node.ip =3D value
end

def title( value )
@node.title =3D value
end
end
end

class Builder
def process( manager, dsl )
@manager =3D manager
@node_builder =3D Node::Builder.new
instance_eval &dsl
end

def node( node_id, &block )
node =3D Node.new( node_id )
@node_builder.process( node, block )
@manager.nodes << node
end
end
end

dsl =3D lambda{
node :node1 do
ip '192.whatever'
title 'Node 1'
end

node :node2 do
ip '192.whatever'
title 'Node 2'
end
}

manager =3D ClusterManager.new
builder =3D 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
 
R

Robert Klemme

Joe said:
Thanks! I'll check it out.

So, say I have the following class

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

Or should I set it up differently?

Here's how I'd do it;

class ClusterManager

def self.load_config_file config_file
cm = new
cm.instance_eval File.read(config_file)
cm
end

def initialize ...
# and other CM methods

def node node_id, &block
# What goes here?
end

class Node
...
end
end

Then you can do

cm = ClusterManager.load_config_file "cluster.conf"

Instead of class Node you could also use Hashes. Then you can have

node(
:name => "foo",
:size => 10,
...
)

Kind regards

robert
 
J

Joe Van Dyk

On Fri, 19 Aug 2005, Joe Van Dyk wrote:
=20
I'm writing an application that controls a group/cluster of linux
computers/nodes. I need to have a configuration file that lists the
nodes in the cluster.

It would be neat if the configuration file was in Ruby. If I had
nodes named node1 and node3, the configuration file could look a
little like:

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end

So, is there some standard Ruby idiom for how to read a file and
execute the code? Just load the file and instance_eval it?
=20
=20
harp:~ > cat a.rb
config =3D <<-config
nodes :
1 :
ip : 192.whatever
title : node 1
2 :
ip : 192.whatever
title : node 2
config
=20
require 'yaml'
=20
config =3D YAML::load config
=20
config['nodes'].each do |nid, node|
puts "node <#{ nid }> =3D> <#{ node.inspect }>"
end
=20
=20
harp:~ > ruby a.rb
node <1> =3D> <{"title"=3D>"node 1", "ip"=3D>"192.whatever"}>
node <2> =3D> <{"title"=3D>"node 2", "ip"=3D>"192.whatever"}>
=20
so all you have to do is 'YAML::load(IO::read(configfile))'.
=20
what kind of clustering are you working with?
=20
you may, or may not, find this useful:
=20
http://raa.ruby-lang.org/project/rq/
http://www.linuxjournal.com/article/7922

Thanks for the pointers.

Essentially, there's a bunch of applications that need to be started
on a few machines.

Imagine you have 20 applications. You need to start those
applications on three different machines (nodes in a cluster). Some
applications will run on all machines, some applications will only run
on some machines. All the applications take many (20 or so)
environment options and command-line arguments and a user needs to be
able to change those options/arguments.

The users need to be able to select what application will get run on
what computer. They need to be able to make sure that the
applications haven't died, and if they have, to restart the
application. They need to be able to view log files for the
applications. Since these are real-time applications, the "cluster
manager application" that takes requests from the user's GUI and
distributes them to the computers in the cluster will probably need to
enforce certain restrictions on the user (i.e. can't run too many
cpu-intensive applications on one computer).

And because the applications and their env options and command-line
args change frequently, depending on what the user's objectives are,
it needs to be heavily driven by configuration files (that hopefully
are easy to change). I figure Ruby's syntax would be a good fit for
the configuration files.

The current design:

The GUI. The GUI is populated primarily from configuration files that
detail the available applications, their environment options and
command-line arguments.

The Cluster Manager. This connects the Node Managers to the GUIs.
Sends start and kill application requests from the GUIs to the Node
Managers, and sends node status updates from the nodes to the GUIs (so
the GUIs can see what's going on with each node and their
applications). Uses XML-RPC for communication (non-ruby clients will
need to access this).

The Node Manager. Starts and Kills applications on a node and sends
status updates (what applications are running, the node load average,
stuff like that) to the Cluster Manager. Uses DRb for communication
with the Cluster Manager.
 
J

Joe Van Dyk

node :node1 do
ip 192.whatever
title "Node 1"
end

node :node2 do
ip 192.whatever
title "Node 2"
end
=20
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
=20
I'd think similarly, but a bit different in that
ClusterManager::Builder should be the one doing the instance eval:
=20
class ClusterManager
attr_accessor :nodes
=20
def initialize
@nodes =3D []
end
=20
def describe
@nodes.collect{ |node| node.describe }.join("\n")
end
=20
class Node
attr_accessor :id, :ip, :title
=20
def initialize( id )
@id =3D id
end
=20
def describe
"#@id -> #@ip \"#@title\""
end
=20
class Builder
def process( node, dsl )
@node =3D node
instance_eval &dsl
end
=20
def ip( value )
@node.ip =3D value
end
=20
def title( value )
@node.title =3D value
end
end
end
=20
class Builder
def process( manager, dsl )
@manager =3D manager
@node_builder =3D Node::Builder.new
instance_eval &dsl
end
=20
def node( node_id, &block )
node =3D Node.new( node_id )
@node_builder.process( node, block )
@manager.nodes << node
end
end
end
=20
dsl =3D lambda{
node :node1 do
ip '192.whatever'
title 'Node 1'
end
=20
node :node2 do
ip '192.whatever'
title 'Node 2'
end
}
=20
manager =3D ClusterManager.new
builder =3D ClusterManager::Builder.new
builder.process( manager, dsl )
=20
puts manager.describe
=20
###############
=20
A few caveats about the above:
=20
1) You'll notice I changed the DSL a little (quoted the 192.whatever
values). That was simply for brevity in this illustration.
=20
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 =3D> 20, :title =3D> "DISPLAY"
value :DISPLAY, :default =3D> ENV['DISPLAY'] || 'localhost:0'
end

argument :xterm_title do
display :text, :size =3D> 20, :title =3D> "Xterm Title"
value "-T", :default =3D> "You are on a xterm!"
end

argument :xterm_text_color do
display :text, :size =3D> 20, :title =3D> "Xterm Font Color"
value "-fg", :default =3D> "red"
end


application :xterm do=20
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.=20
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.
 
J

Joe Van Dyk

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 =3D []
end

def describe
@nodes.collect{ |node| node.describe }.join("\n")
end

class Node
attr_accessor :id, :ip, :title

def initialize( id )
@id =3D id
end

def describe
"#@id -> #@ip \"#@title\""
end

class Builder
def process( node, dsl )
@node =3D node
instance_eval &dsl
end

def ip( value )
@node.ip =3D value
end

def title( value )
@node.title =3D value
end
end
end

class Builder
def process( manager, dsl )
@manager =3D manager
@node_builder =3D Node::Builder.new
instance_eval &dsl
end

def node( node_id, &block )
node =3D Node.new( node_id )
@node_builder.process( node, block )
@manager.nodes << node
end
end
end

dsl =3D lambda{
node :node1 do
ip '192.whatever'
title 'Node 1'
end

node :node2 do
ip '192.whatever'
title 'Node 2'
end
}

manager =3D ClusterManager.new
builder =3D 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).
=20
Thank you for your ideas. I've adapted it somewhat for parts of my
application, but I'm running into problems.
=20
I've got the following configuration file:
=20
option :display do
display :text, :size =3D> 20, :title =3D> "DISPLAY"
value :DISPLAY, :default =3D> ENV['DISPLAY'] || 'localhost:0'
end
=20
argument :xterm_title do
display :text, :size =3D> 20, :title =3D> "Xterm Title"
value "-T", :default =3D> "You are on a xterm!"
end
=20
argument :xterm_text_color do
display :text, :size =3D> 20, :title =3D> "Xterm Font Color"
value "-fg", :default =3D> "red"
end
=20
=20
application :xterm do
executable "/usr/X11R6/bin/xterm"
title :Xterm
node :fatire
options :display
arguments :xterm_text_color, :xterm_title
end
=20
application :xeyes do
executable "/usr/X11R6/bin/xeyes"
title :Xeyes
node :fatire
options :display
end
=20
=20
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.
=20
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.
=20
I considered using YAML for the configuration file format, but I'm
still leaning towards having a pure Ruby file.
=20
Thoughts are greatly appreciated! This is my first time trying to do
this type of programming, so it's a little weird.
=20


I would also like to be able to 'group' things together, like

group :xterm_options_arguments do
arguments :xterm_title, :xterm_text_color
options :display
end

application :xterm do
group :xterm_options_arguments
title :Xterm
...=20
end

So that applications with common options and arguments can specify a groupi=
ng.

(btw, an 'argument' is a command-line argument, like 'ls -F'... the -F
is an argument. An option is something like "DISPLAY=3Dmy_machine:1
xeyes".. the DISPLAY is an option).
 
J

Joe Van Dyk

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 =3D []
end

def describe
@nodes.collect{ |node| node.describe }.join("\n")
end

class Node
attr_accessor :id, :ip, :title

def initialize( id )
@id =3D id
end

def describe
"#@id -> #@ip \"#@title\""
end

class Builder
def process( node, dsl )
@node =3D node
instance_eval &dsl
end

def ip( value )
@node.ip =3D value
end

def title( value )
@node.title =3D value
end
end
end

class Builder
def process( manager, dsl )
@manager =3D manager
@node_builder =3D Node::Builder.new
instance_eval &dsl
end

def node( node_id, &block )
node =3D Node.new( node_id )
@node_builder.process( node, block )
@manager.nodes << node
end
end
end

dsl =3D lambda{
node :node1 do
ip '192.whatever'
title 'Node 1'
end

node :node2 do
ip '192.whatever'
title 'Node 2'
end
}

manager =3D ClusterManager.new
builder =3D 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 =3D> 20, :title =3D> "DISPLAY"
value :DISPLAY, :default =3D> ENV['DISPLAY'] || 'localhost:0'
end

argument :xterm_title do
display :text, :size =3D> 20, :title =3D> "Xterm Title"
value "-T", :default =3D> "You are on a xterm!"
end

argument :xterm_text_color do
display :text, :size =3D> 20, :title =3D> "Xterm Font Color"
value "-fg", :default =3D> "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.
=20
=20
I would also like to be able to 'group' things together, like
=20
group :xterm_options_arguments do
arguments :xterm_title, :xterm_text_color
options :display
end
=20
application :xterm do
group :xterm_options_arguments
title :Xterm
...
end
=20
So that applications with common options and arguments can specify a grou= ping.
=20
(btw, an 'argument' is a command-line argument, like 'ls -F'... the -F
is an argument. An option is something like "DISPLAY=3Dmy_machine:1
xeyes".. the DISPLAY is an option).

And grouping of applications would also be nice:

application_group :all_x_apps
applications :xterm, :xeyes
display :checkbox, :title =3D> "Start all X applications"
end

So, in the GUI, there would be a checkbox titled "Start all
applications" that would start the xterm and xeyes applications.

In other words, I'd like to be able to do grouping of various things
in this configuration file.
 
J

Joe Van Dyk

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 =3D> 20, :title =3D> "DISPLAY"
value :DISPLAY, :default =3D> ENV['DISPLAY'] || 'localhost:0'
end

argument :xterm_title do
display :text, :size =3D> 20, :title =3D> "Xterm Title"
value "-T", :default =3D> "You are on a xterm!"
end

argument :xterm_text_color do
display :text, :size =3D> 20, :title =3D> "Xterm Font Color"
value "-fg", :default =3D> "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 readabl= e
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 som= e
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.


I would also like to be able to 'group' things together, like

group :xterm_options_arguments do
arguments :xterm_title, :xterm_text_color
options :display
end

application :xterm do
group :xterm_options_arguments
title :Xterm
...
end

So that applications with common options and arguments can specify a gr= ouping.

(btw, an 'argument' is a command-line argument, like 'ls -F'... the -F
is an argument. An option is something like "DISPLAY=3Dmy_machine:1
xeyes".. the DISPLAY is an option).
=20
And grouping of applications would also be nice:
=20
application_group :all_x_apps
applications :xterm, :xeyes
display :checkbox, :title =3D> "Start all X applications"
end
=20
So, in the GUI, there would be a checkbox titled "Start all
applications" that would start the xterm and xeyes applications.
=20
In other words, I'd like to be able to do grouping of various things
in this configuration file.


Here's the start of what I have so far. How's it look?

require 'test/unit'

class ConfigLoader
attr_accessor :configuration, :applications, :eek:ptions

def initialize
@applications =3D Hash.new
@options =3D Hash.new
end

def process configuration
instance_eval configuration
end

def get_application_options application_id
@options.values.find_all do |option|=20
if option.option_type =3D=3D :env_option
@applications[application_id].options.include? option.option_id
end
end
end

def get_application_arguments application_id
@options.values.find_all do |option|=20
if option.option_type =3D=3D :command_line_argument
@applications[application_id].options.include? option.option_id
end
end
end

def application application_id, &block
application =3D Application.new application_id
@applications[application_id] =3D application=20
application.instance_eval &block
end

def option option_id, &block
option =3D Option.new option_id, :env_option
@options[option_id] =3D option
option.instance_eval &block
end

def argument option_id, &block
option =3D Option.new option_id, :command_line_argument
@options[option_id] =3D option
option.instance_eval &block
end
class Option
attr_accessor :name, :value, :default, :eek:ption_id, :eek:ption_type
def initialize option_id, option_type
@option_id =3D option_id
@option_type =3D option_type
@name =3D nil
@value =3D nil
end

def set name, args
@name =3D name
@value =3D @default =3D args[:default]
end
end

class Application
attr_accessor :executable, :application_id, :title, :node, :eek:ptions
def initialize application_id
@application_id =3D application_id
@executable =3D nil
@title =3D nil
@node =3D nil
@options =3D []
end

def executable value=3Dnil
@executable =3D value if value
@executable
end

def title value=3Dnil
@title =3D value if value
@title
end

def node value=3Dnil
@node =3D value if value
@node
end

def options *args
@options =3D args if args.size > 0
@options
end

end
end

class TestConfigLoader < Test::Unit::TestCase

def test_process
a =3D ConfigLoader.new
a.process sample_configuration

# Test to see that the application stuff was set ok.
assert_equal "/usr/X11R6/bin/xeyes", a.applications[:xeyes].executable
assert_equal [:display], a.applications[:xeyes].options
assert_equal :fatire, a.applications[:xeyes].node
assert_equal :Xeyes, a.applications[:xeyes].title

# Test to see that the options were set ok.
assert_equal :DISPLAY, a.options[:display].name
# Not sure how to properly test these... they depend on your env=20
# and if your DISPLAY is not set, then it should be something else=20
# ('localhost:0'). So I'd be duplicating logic in the tests.
#assert_equal "vandyk-j:0", a.options[:display].default
#assert_equal "vandyk-j:0", a.options[:display].value

# Test to see that the env options are ok.
assert_equal [a.options[:display]], a.get_application_options:)xeyes)
assert_equal [a.options[:display]], a.get_application_options:)xterm)

# Test to see that the command line arguments are ok.
assert_equal [a.options[:xterm_font_color], a.options[:xterm_title]],=
=20
a.get_application_arguments:)xterm)
end


# A sample configuration file
def sample_configuration
<<-EOF
option :display do
#display :text, :size =3D> 20, :title =3D> "DISPLAY"
set :DISPLAY, :default =3D> ENV['DISPLAY'] || 'localhost:0'
end

argument :xterm_title do
#display :text, :size =3D> 20, :title =3D> "Xterm Title"
set "-T", :default =3D> "You are on a xterm!"
end

argument :xterm_font_color do
#display :text, :size =3D> 20, :title =3D> "Xterm Font Color"
set "-fg", :default =3D> "red"
end

application :xterm do=20
executable "/usr/X11R6/bin/xterm"
title :Xterm
node :fatire
options :display, :xterm_font_color, :xterm_title
end

application :xeyes do
executable "/usr/X11R6/bin/xeyes"
title :Xeyes
node :fatire
options :display
end
EOF
end
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top