Reputation:
I have a configuration class in Ruby that used to have keys like "core.username" and "core.servers", which was stored in a YAML file just like that.
Now I'm trying to change it to be nested, but without having to change all the places that refer to keys in the old way. I've managed it with the reader-method:
def [](key)
namespace, *rest = key.split(".")
target = @config[namespace]
rest.each do |k|
return nil unless target[k]
target = target[k]
end
target
end
But when I tried the same with the writer-class, that works, but isn't set in the @config
-hash. @config
is set with just a call to YAML.load_file
I managed to get it working with eval
, but that is not something I would like to keep for long.
def []=(key, value)
namespace, *rest = key.split(".")
target = "@config[\"#{namespace}\"]"
rest.each do |key|
target += "[\"#{key}\"]"
end
eval "#{target} = value"
self[key]
end
Is there any decent way to achieve this, preferably without changing plugins and code throughout?
Upvotes: 2
Views: 2916
Reputation: 23989
I used recursion:
def change(hash)
if hash.is_an? Hash
hash.inject({}) do |acc, kv|
hash[change(kv.first)] = change(kv.last)
hash
end
else
hash.to_s.split('.').trim # Do your fancy stuff here
end
end
Upvotes: 0
Reputation: 370357
def []=(key, value)
subkeys = key.split(".")
lastkey = subkeys.pop
subhash = subkeys.inject(@config) do |hash, k|
hash[k]
end
subhash[lastkey] = value
end
Edit: Fixed the split. PS: You can also replace the inject with an each-loop like in the [] method if you prefer. The important thing is that you do not call [] with the last key, but instead []= to set the value.
Upvotes: 1