ConfigFiles
Load configuration files from multiple directories and merge them together. Supports YAML, JSON, and other formats.
Features
- Load configs from multiple directories
- Merge YAML, JSON, and other file formats
- Directory precedence (first directory wins)
- Static (cached) or dynamic (reloaded) config files
- Returns
HashWithIndifferentAccess
for easy key access
Installation
Add to your Gemfile:
gem 'config_files'
Or install it:
gem install config_files
Usage
require 'config_files'
class MyApp
include ConfigFiles
# Search these directories in order (first wins)
config_directories etc: [
'config/production',
'config/staging',
'config/defaults'
]
# Load these config files
static_config_files :database, :app_settings
dynamic_config_files :feature_flags
end
# Use the configs
MyApp.database[:host] # => "prod-db.example.com"
MyApp.app_settings[:debug] # => false
MyApp.feature_flags[:new_ui] # => true (reloaded each time)
How it works
The gem searches for config files in each directory you specify. If you have:
config/production/database.yml
config/staging/database.yml
config/defaults/database.yml
And you configure directories as ['config/production', 'config/staging', 'config/defaults']
, then:
- It loads all three files
- Merges them together
- Values from
production
overridestaging
anddefaults
- Values from
staging
overridedefaults
- Returns the merged result
Multiple file formats
You can mix YAML and JSON files:
config/
├── app.yml # YAML format
├── app.json # JSON format
└── app.conf # Other formats
All files with the same base name get merged together. Files are processed alphabetically, so app.json
loads before app.yml
.
Static vs Dynamic
class AppConfig
include ConfigFiles
config_directories etc: ['config']
# Static: loaded once and cached
static_config_files :database
# Dynamic: reloaded from disk each time
dynamic_config_files :feature_flags
end
AppConfig.database # Fast (cached)
AppConfig.feature_flags # Slower (reads from disk)
Use static for configs that don't change. Use dynamic for configs that might change while your app is running.
Directory precedence
Earlier directories in the list win:
config_directories etc: [
'config/production', # Highest priority
'config/staging', # Medium priority
'config/defaults' # Lowest priority
]
So if production/app.yml
has debug: false
and defaults/app.yml
has debug: true
, the result will have debug: false
.
Configuration merging
Configs are deep-merged. Given these files:
# config/defaults/app.yml
database:
host: localhost
port: 5432
pool_size: 5
app:
debug: true
name: MyApp
# config/production/app.yml
database:
host: prod-db.example.com
pool_size: 20
app:
debug: false
The result is:
{
database: {
host: "prod-db.example.com", # from production
port: 5432, # from defaults
pool_size: 20 # from production
},
app: {
debug: false, # from production
name: "MyApp" # from defaults
}
}
Custom directory keys
You can use different directory sets:
class AppConfig
include ConfigFiles
config_directories(
etc: ['config/app', '/etc/myapp'],
secrets: ['secrets/production', 'secrets/shared']
)
static_config_files :database # searches in 'etc' directories
end
# Access the directory paths
AppConfig.etc_dir # => ["/full/path/to/config/app", "/etc/myapp"]
AppConfig.secrets_dir # => ["/full/path/to/secrets/production", ...]
Error handling
Missing files and directories are ignored:
class AppConfig
include ConfigFiles
config_directories etc: [
'config/nonexistent', # ignored
'config/existing' # used
]
static_config_files :missing_file
end
AppConfig.missing_file # => {} (empty hash)
Testing
Run the tests:
bundle exec rake test
Test with multiple Ruby versions:
./scripts/test_multiple_rubies.sh
The gem is tested on Ruby 2.7+ with ActiveSupport 6.1+. See TESTING.md for details.
API
config_directories(hash)
Define search directories:
config_directories etc: ['dir1', 'dir2']
static_config_files(*files)
Load files once and cache them:
static_config_files :database, :app_settings
dynamic_config_files(*files)
Reload files on each access:
dynamic_config_files :feature_flags
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-feature
) - Make your changes
- Run the tests (
bundle exec rake test
) - Commit your changes (
git commit -am 'Add feature'
) - Push to the branch (
git push origin my-feature
) - Create a Pull Request
License
MIT License. See LICENCE.txt for details.
Changelog
See CHANGELOG.md for version history and changes.