Adaptive struct
Sometimes it's convenient to write configuration files in ruby as structure with attributes defined on demands (PHP programmers really like it).
Let's take a look at how this adaptive structure can be defined.
Hash with dynamic methods
Config is nested hash. So it's natural to write method_mising method,
with will treat missing method name as key.
class Hash
def method_missing(method, *args)
method = method.to_s
if method.gsub!(/=$/, '')
self[method.to_sym] = args.first
else
self[method.to_sym] ||= {}
end
end
end
a = {}
a.bb.cc.dd = 1
a.bb.ff.ee = 2
a.xxx = 'yyy'
require 'yaml'
puts a.to_yaml
It prints
---
:bb:
:cc:
:dd: 1
:ff:
:ee: 2
Cool!
But we need more fun.
Nested configs
class Hash
def method_missing(method, *args, &block)
method = method.to_s
if method.gsub!(/=$/,'') || !args.empty?
self[method.to_sym] = args.first
else
res = self[method.to_sym] ||= {}
res.instance_eval(&block) if block
res
end
end
end
a = {}
a.bb.cc {
dd.ee 3;
ff 4
}
a.xxx {
yyy 'y'
zzz 'z'
}
require 'yaml'
puts a.to_yaml
Output:
---
:xxx:
:yyy: y
:zzz: z
:bb:
:cc:
:dd:
:ee: 3
:ff: 4
We are so good!
But here we stop using equal sign. Why?
Code
a.xxx {
yyy = 'y'
zzz = 'z'
}
does not work, because
yyy
and
zzz
are treated as local variables.
My be we can use binding to fix it? Try it!
At least we can easily get this code working:
a.xxx.ttt.www {|cfg|
cfg.yyy = 'y'
cfg.zzz = 'z'
}
--
ArtemVoroztsov - 15 Apr 2008