Load configuration files from multiple directories and merge them together. Supports YAML, JSON, and other formats.
- 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 
HashWithIndifferentAccessfor easy key access 
Add to your Gemfile:
gem 'config_files'Or install it:
gem install config_filesrequire '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)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 
productionoverridestaginganddefaults - Values from 
stagingoverridedefaults - Returns the merged result
 
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.
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.
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.
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: falseThe 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
  }
}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", ...]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)Run the tests:
bundle exec rake testTest with multiple Ruby versions:
./scripts/test_multiple_rubies.shThe gem is tested on Ruby 2.7+ with ActiveSupport 6.1+. See TESTING.md for details.
Define search directories:
config_directories etc: ['dir1', 'dir2']Load files once and cache them:
static_config_files :database, :app_settingsReload files on each access:
dynamic_config_files :feature_flags- 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
 
MIT License. See LICENCE.txt for details.
See CHANGELOG.md for version history and changes.