From: furtive.clown@... Date: 2008-01-09T17:10:01+09:00 Subject: using ruby for config files (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 # ----------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"] }