On one of my current ruby projects, I'm reading in a YML file and using the generated data structure as a hackish set of global configuation settings:
firm_1:
departments:
sales: 419
executive: 999
IT: 232
locations:
NY: 19
WV: 27
CA: 102
firm_2:
...
Because these should be treated as constants, they should not be overwritten (accidentally, of course). I wanted to go ahead and freeze them:
global_conf = YAML.load_file("...")
global_conf.freeze
global_conf['firm_1'] = {'foo' => 'bar'}
=> TypeError: can't modify frozen hash
But, as you probably know, Ruby's freeze
doesn't affect the objects in a container.
global_conf['firm_1']['departments'] = {'foo' => 'bar'}
=> {"foo"=>"bar"}
That's bad.
So I hacked up a quick monkeypatch (or whatever the duck punchers call it these days) to recursively freeze containers:
#
# allow us to freeze deep data structures by recursively freezeing each nested object
#
class Hash
def deep_freeze # har, har ,har
each { |k,v| v.deep_freeze if v.respond_to? :deep_freeze }
freeze
end
end
class Array
def deep_freeze
each { |j| j.deep_freeze if j.respond_to? :deep_freeze }
freeze
end
end
After loading these patches, calling deep_freeze
does what we want:
global_conf = YAML.load_file("...")
global_conf.deep_freeze
global_conf['firm_1']['departments'] = {'foo' => 'bar'}
=> TypeError: can't modify frozen hash
Nice!
3 comments:
Hi, excellent code :), I have extended it to freezing child objects and to preventing simple access to hash and array single elements as well.
http://www.semintelligent.com/blog/articles/29/freezing-ruby-data-structures-recursively
I give you full credit for your code at the top of the article. Feel free to hack at my code, re-publish on your blog, extend etc as you wish :).
Thanks again!
Thanks for the tip. I added this to Enumerable instead of to Hash and Array. Works like a charm.
Enumerable is not a good choice because on a Hash, you'd then only iterate over values. In a deep freeze, you'll want your keys deep-frozen, too. It matters when using complex structures such as arrays containing arrays as keys; the Hash itself only freezes the first level.
Post a Comment