AppConfigLoader
A customizable YAML configuration library for Rails and Ruby, featuring wildcards, nesting, namespacing and local override.
Getting Started
Add the following line to your Gemfile:
gem 'app_config_loader'
Run the bundle command to install it.
Create a YAML file in the default directory config/app_configs
to put your app config. For example:
# config/app_configs/some_service.yml
'*.some_service':
'host': dev.someservice.com
'production.some_service':
'host': prod.someservice.com
From this point onward, you can access your app config values through APP_CONFIG
.
# when AppConfigLoader's env is not "production"
APP_CONFIG['some_service.host'] # => 'dev.someservice.com'
# when AppConfigLoader's env is set to "production"
APP_CONFIG['some_service.host'] # => 'prod.someservice.com'
Non-Rails Environment
When using AppConfigLoader
outside of Rails, getting started is a little different:
- the default app config file directory is
<pwd>/app_configs
- you need to manually initialize
AppConfigLoader
by including the following early on in your code:AppConfigLoader.init
Configuration
You can configure where and how AppConfigLoader load your app config file(s).
In Rails, the following code should be placed
before Rails is initialized but after your Rails application is defined (e.g. config/application.rb
). Outside Rails, this should be placed before the AppConfigLoader.init
call.
AppConfigLoader.configure do |config|
config.use_domain = true
config.domain = 'us'
config.config_paths << '/path/to/additional_config.yml'
end
Below is a list of all options available:
-
config.const_name
- (String) the name of the constant to which the config object is assigned. Default:APP_CONFIG
-
config.env
- (String) app config environment. This determines which set of app config values to load. Default: Rails -Rails.env
, non-Rails -ENV['RACK_ENV'] || ENV['RUBY_ENV']
-
config.use_domain
- (Boolean) whether the app config key uses a domain. Default:false
-
config.domain
- (String) app config domain. This determines which set of app config values to load. It is only applicable ifuse_domain
is set to true. Default:nil
-
config.config_paths
- (Array<String>) a list of paths from where app config YAML files should be loaded. Path can either be a file or a directory. With a directory path, all YAML files within that directory will be loaded. Default: Rails -['<rails root>/config/app_configs']
, non-Rails -['<pwd>/app_configs']
-
config.local_overrides
- (String) path to a local overrides app config file. Entries in this file take precedence over all values from other files. Default: Rails -<rails root>/config/app_configs/local_overrides.yml
, non-Rails -<pwd>/app_configs/local_overrides.yml
Defining your App Configs
All app configs are defined as key-value entries in YAML format. A app config value can be any valid YAML value, except for Hash. The app config key must follow either of the format below depending on whether use_domain
is enabled:
-
<env>.<key...>
- whenuse_domain
is disabled (e.g.production.some_service.host
) -
<env>.<domain>.<key...>
- whenuse_domain
is enabled (e.g.production.us.some_service.host
)
Basic Example
# app config in YAML file
'test.timeout': 3000
'test.some_service.host': 'dev.someservice.com'
'production.timeout': 500
'production.some_service.host': 'prod.someservice.com'
'production.some_service.port': 8000
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'test'
end
# after AppConfigLoader is initialized
APP_CONFIG['timeout'] # => 3000
APP_CONFIG['some_service.host'] # => dev.someservice.com
APP_CONFIG['some_service.port'] # => nil
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'production'
end
# after AppConfigLoader is initialized
APP_CONFIG['timeout'] # => 500
APP_CONFIG['some_service.host'] # => prod.someservice.com
APP_CONFIG['some_service.port'] # => 8000
Nested Definition
'production.some_service':
'host': 'prod.someservice.com'
'port': 8000
APP_CONFIG['some_service.host'] # => prod.someservice.com
APP_CONFIG['some_service.port'] # => 8000
Namespacing with Domain
You may use domain
to group your configuration. For example, you can provide different configuration when running in different regions.
# app config in YAML file
'production.us.some_service.host': 'prod.someservice.com'
'production.hk.some_service.host': 'prod.someservice.com.hk'
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'production'
config.use_domain = true
config.domain = 'us'
end
# after AppConfigLoader is initialized
APP_CONFIG['some_service.host'] # => prod.someservice.com
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'production'
config.use_domain = true
config.domain = 'hk'
end
# after AppConfigLoader is initialized
APP_CONFIG['some_service.host'] # => prod.someservice.com.hk
Using Wildcard Env and Domain
You may use the wildcard *
in place of the env and the domain. This allows you to specify app config entry that is applicable in any env or domain. An app config entry with a specific env or domain always takes precedence over ones with wildcard of the same key.
# app config in YAML file
'*.some_service.host': 'dev.someservice.com'
'production.some_service.host': 'prod.someservice.com'
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'test'
end
# after AppConfigLoader is initialized
APP_CONFIG['some_service.host'] # => dev.someservice.com
# Rails initialization
AppConfigLoader.configure do |config|
config.env = 'production'
end
# after AppConfigLoader is initialized
APP_CONFIG['some_service.host'] # => prod.someservice.com
Specificity
When there a multiple app config entries for the same key, the final value is resolved based upon the specificity of the entry. The entry with the highest specificity take precedence over all other entries. In the case when more than one entries have the same specificity, the entry that is last read will take precedence.
'production.us.some_service.host': 'prod.someservice.com' # highest specificity
'*.us.some_service.host': 'prod.someservice.com'
'production.*.some_service.host': 'prod.someservice.com'
'*.*.some_service.host': 'prod.someservice.com' # lowest specificity
Key Conflict
In most cases, entries belonging to or falling under the same key can be resolved by specificity. However, there are cases when the entries' values
conflict with each other and a ConfigKeyConflict
error will be raises.
-
Assigning value to key with children
*.key_with_children.child_1: 'child1' *.key_with_children.child_2: 'child2' production.key_with_children: 'some value'
The config above will raise
ConfigKeyConflict
error, regardless of the different in specificity. -
Assigning children to key with value
production.key_with_value: 'some value' *.key_with_value.child_1: 'child1'
The config above will raise
ConfigKeyConflict
error, regardless of the different in specificity.
Local Overrides File
The local overrides file provides a quick way to override any entries defined in regular app config files. Entries within this file are resolved according to the specificity rule. Once resolved, the resolved entries will override any existing entries regardless of specificity. This is meant for development or testing purposes. Typically, you would not want to check this file into your source repository.
Manual Load
Apart from initializing the module using AppConfigLoader.init
, you may also manually parse and load app config.
config = AppConfigLoader::Config.new
config.use_domain = true
config.env = 'development'
config.config_paths << '/path/to/app_config.yml'
app_config = AppConfigLoader::load(config)
app_config['some_config.key'] #=> config value for the 'some_config.key' key
Contributors
Bug reports and pull requests are welcome on GitHub at https://github.com/lscspirit/app_config_loader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Copyright
Copyright (c) 2016 Derrick Yeung, released under the MIT license. See MIT License for details.