(In reference to
http://groups.google.com/group/ruby-talk-google/browse_thread/thread/1c1fa0bfca68084f/
)
I eventually settled on the below for my config file purposes. I
wanted configuration to be convenient and flexible. Using XML or YAML
robs me of the occasional #map, for example. Using a hash for
configuration is too error-prone; I should assume those doing the
configuring have no knowledge of ruby. That is,
foo = “bar”
size = 44
is a better config file than
{
:foo => “bar”,
:size => 44,
}
So here is what I use:
module ConfigReader_m
module Util
def self.file_contents(filename)
File.open(filename) { |f|
f.read
}
end
def self.no_verbose
previous_verbose = $VERBOSE
begin
$VERBOSE = nil
yield
ensure
$VERBOSE = previous_verbose
end
end
end
def config_each_pair(config_code)
previous_locals = local_variables
config_locals = eval(config_code + “\n” + “local_variables”)
(config_locals - previous_locals).each { |name|
yield(name, eval(name))
}
end
def config_to_hash(config_code)
hash = Hash.new
config_each_pair(config_code) { |name, value|
hash[name] = value
}
hash
end
def config_to_open_struct(config_code)
require ‘ostruct’
OpenStruct.new(config_to_hash(config_code))
end
def config_to_instance_variables(config_code)
config_each_pair(config_code) { |name, value|
ivar = “@” + name
existing_value = Util.no_verbose {
instance_variable_get(ivar)
}
if existing_value
raise “instance variable already set: #{name}”
end
instance_variable_set(ivar, value)
}
end
def config_file_each_pair(config_file)
config_each_pair(Util.file_contents(config_file))
end
def config_file_to_hash(config_file)
config_to_hash(Util.file_contents(config_file))
end
def config_file_to_open_struct(config_file)
config_to_open_struct(Util.file_contents(config_file))
end
def config_file_to_instance_variables(config_file)
config_to_instance_variables(Util.file_contents(config_file))
end
end
class ConfigReader
include ConfigReader_m
end
class Foo
include ConfigReader_m
def initialize(config)
config_to_instance_variables(config)
end
def test_config
print "@version: "
p @version
print "@format_spec: "
p @format_spec
print "@source_files: "
p @source_files
end
end
config = %q{
version = 3
format_spec = “format.xml”
source_files = %w(a b c).map { |base|
base + “.cxx”
}
}
def sep(header)
puts “-”*10 + header
end
sep(“config_each_pair”)
ConfigReader.new.config_each_pair(config) { |key, value|
print "#{key}: "
p value
}
sep(“config_to_hash”)
p ConfigReader.new.config_to_hash(config)
sep(“config_to_open_struct”)
p ConfigReader.new.config_to_open_struct(config)
sep(“config_to_instance_variables”)
Foo.new(config).test_config
output:
----------config_each_pair
version: 3
format_spec: “format.xml”
source_files: [“a.cxx”, “b.cxx”, “c.cxx”]
----------config_to_hash
{“source_files”=>[“a.cxx”, “b.cxx”, “c.cxx”], “version”=>3,
“format_spec”=>“format.xml”}
----------config_to_open_struct
#<OpenStruct version=3, format_spec=“format.xml”,
source_files=[“a.cxx”, “b.cxx”, “c.cxx”]>
----------config_to_instance_variables
@version: 3
@format_spec: “format.xml”
@source_files: [“a.cxx”, “b.cxx”, “c.cxx”]
Since OpenStruct is often the nicest choice, and since ostruct is
underused by newcomers (both my opinion only), I provided it for
convenience.
If you wish to provide a restricted list of local variables which will
become configuration parameters (only), you can do this:
config = %q{
version = 3
format_spec = “format.xml”
source_files = %w(a b c).map { |base|
base + “.cxx”
}
local_variables = [“version”, “format_spec”]
}