rails: yml merging
Say I have a yml file for my rails configuration...
settings.yml
defaults: &defaults
interceptor_email: robot@wearemanalive.com
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
and I want to have another yml file that is NOT included in version control that each developer maintains locally...
user_settings.yml
development:
interceptor_email: userfoo@domain.com
How can I merge these keys? I am processing my yml files with esb, so that is also an option. Just having trouble figuring out how to do it. I have it setup so keys fa开发者_如何学Cllback to the defaults if a key is missing for my environments.
Can't you read the two yml files separately?
settings = YAML.load(path_to_settings)[RAILS_ENV].symbolize_keys
user_settings = YAML.load(path_to_user_settings)[RAILS_ENV].symbolize_keys
settings.merge!(user_settings)
Now you should have the hash value of the settings, then you can merge the hash if you want. If the second hash has the same key as the first hash, the first one will be overwritten.
This is how I do it (disclaimer, I just wrote it, so it doesn't have unit tests yet etc...I'll update this as I improve on it):
require 'yaml'
# read config files (currently only yaml supported), merge user config files over
# defaults and make the parsed data available to the rest of your application.
#
module YourNamespace class Config
attr_reader :files, :get
# Accepts a string filename or an array of string filenames to parse.
# If an array is supplied, values from later files will override values
# of earlier files with the same name.
# Will choke if YAML.load_file returns false (invalid or empty file)
#
def initialize( files )
@files = files.respond_to?( 'map' ) ? files : [ files ]
@get = @files \
\
.map { | file | YAML.load_file file } \
.reduce( {}, :merge! )
;
end
end end
You can call it like this:
config = YourNamespace::Config.new 'config.yml'
# or have the second one override the first
#
config = YourNamespace::Config.new [ 'config-defaults.yml', 'config.yml' ]
And if you want to go fancy, there's a lot of room for improvement here. Ideally make ´Config´ an interface that does not deal with files, and implement in YamlConfig
, IniConfig
, CliConfig
, DbConfig
, CookieConfig
. That way if you decide one day the that that new config format super seeding yaml is so cool, you can easily change it without breaking anything. And you can have the command line configuration options easily override the ones coming from the configuration files. And you can reuse the config module for any ruby project regardless of where the config values come from. Or maybe just stop inventing hot water. A quick browse makes me think there's some pretty hot water over there...
Next write some documentation, unit tests, input validation, error handling and create some fancy read/write accessors for the config values. Maybe you'd like to be able to ask for a config value like this instead of writing arrays and hashes all the time:
config.get 'app.component.section.setting'
# or this if you want to keep them separate:
#
config.get( 'app', 'component', 'section', 'setting' )
精彩评论