# Using ruby for config files

**URL:** <https://www.ruby-forum.com/t/using-ruby-for-config-files/125458>\
**Category:** Ruby\
**Created:** [January 9, 2008, 9:10am UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458 "2008-01-09T09:10:52Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 9, 2008, 9:10am UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/1 "2008-01-09T09:10:52Z")

</div>

(In reference to  
[http://groups.google.com/group/ruby-talk-google/browse\_thread/thread/1c1fa0bfca68084f/](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”]  
}

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 9, 2008, 10:00am UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/2 "2008-01-09T10:00:10Z")

</div>

Hmm the config style looks nice, but the code looks a bit complex?

I use yaml right now even though it has a few disadvantages (for me in  
this regard) because of indent (where a user may get parse errors).  
And from this yaml info I generate or build system info, on top of it.

I think what would be cool, would be a minimal “config” interpreter with  
only  
a very few (but still quite) readable lines of ruby code. (Anyway,  
that’s just my opinion)

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 9, 2008, 3:45pm UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/3 "2008-01-09T15:45:53Z")

</div>

On Jan 9, 3:09 am, removed\_email\_address@domain.invalid wrote:

> 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

If you just want to have configuration files that are little more than  
key/value pairs, why not use the Windows INI file format? There are  
already ruby gems that provide read/write access to INI files:

$ gem list ini -r -d

\*\*\* REMOTE GEMS \*\*\*

ini (0.1.1)  
INI file reader and writer

inifile (0.1.0)  
INI file reader and writer

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 9, 2008, 6:36pm UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/4 "2008-01-09T18:36:55Z")

</div>

On Jan 9, 9:44 am, Karl von Laudermann [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> If you just want to have configuration files that are little more than  
> key/value pairs, why not use the Windows INI file format? There are  
> already ruby gems that provide read/write access to INI files:

Because using XML, YAML, or INI files robs me of the occasional #map,  
for instance, as shown in my example. The whole point is that I  
_don’t_ want configuration files that are little more than key/value  
pairs. Even if that was the case at the beginning of the project,  
eventually it becomes too restrictive as the project grows.

–FC

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 9, 2008, 7:26pm UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/5 "2008-01-09T19:26:25Z")

</div>

On Jan 9, 2008, at 11:35 AM, removed\_email\_address@domain.invalid wrote:

> _don’t_ want configuration files that are little more than key/value  
> pairs. Even if that was the case at the beginning of the project,  
> eventually it becomes too restrictive as the project grows.

I sometimes use the stupid simple:

#!/usr/bin/env ruby -wKU

require “ostruct”

module Config  
module\_function

def load\_config\_file(path)  
eval \<\<-END\_CONFIG  
config = OpenStruct.new  
#{File.read(path)}  
config  
END\_CONFIG  
end  
end

**END**

Here are the tests:

#!/usr/bin/env ruby -wKU

require “test/unit”  
require “tempfile”

require “config”

class TestConfig \< Test::Unit::TestCase  
def test\_config\_returns\_a\_customized\_ostruct  
assert\_instance\_of(OpenStruct, config)  
end

def  
test\_config\_object\_is\_passed\_into\_the\_file\_and\_used\_to\_set\_options  
c = config(\<\<-END\_SETTINGS)  
config.string\_setting = “just a String”  
config.integer\_setting = 41  
END\_SETTINGS  
assert\_equal(“just a String”, c.string\_setting)  
assert\_equal(41, c.integer\_setting)  
end

def test\_exceptions\_bubble\_up\_to\_the\_caller  
assert\_raise(RuntimeError) do  
config(\<\<-END\_ERROR)  
raise “Oops!”  
END\_ERROR  
end  
end

private

def config(content = String.new)  
cf = Tempfile.new(“ender\_config\_test”)  
cf \<\< content  
cf.flush  
Config.load\_config\_file(cf.path)  
end  
end

**END**

James Edward G. II

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 10, 2008, 9:53pm UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/6 "2008-01-10T21:53:46Z")

</div>

I used to love YAML but the whitespace / indent issue has bitten me  
too many times.

I needed a config file today, and eventually went for a  
lib/config.rb like:

CONFIG = {  
:servera =\> {  
:hostname =\> ‘[a.com](http://a.com)’,  
:remote\_user =\> ‘usera’  
},  
:serverb =\> {  
:hostname =\> ‘[b.org](http://b.org)’,  
:remote\_user =\> ‘userb’  
}  
}

I just load it in with ‘require config’.

I know it’s a bit cryptic to non-rubyists (compared to e.g. yaml)  
but it has the HUGE advantage of being easy to validate (ruby -wc  
lib/config.rb).

Be interested to know how others do it - an include/mixin maybe?

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [December 30, 2008, 10:00am UTC](https://www.ruby-forum.com/t/using-ruby-for-config-files/125458/7 "2008-12-30T10:00:18Z")

</div>

hi,

I am new to Ruby and this thread was one of the solution I was looking  
for.

May be my question must be very basic ☹

Based on the Dick D. comment I would like to use the config as  
described.  
But can someone let me know how to use the variables defined in this  
config file.

viz., I want to call the value of :hostname from another ruby file, how  
shall I do this?

Thanks
