[![Build Status](https://travis-ci.org/timgaleckas/hierarchical_config.svg?branch=master)](https://travis-ci.org/timgaleckas/hierarchical_config) ## What is it HierarchicalConfig is a library that implements a strategy for configuring an application in a static, declarative, robust, and intuitive way ## Principles 1. You should not be able to change your config once it's loaded. That's not configuration, that's globals. 2. You should be able to check in to source control a config file that holds defaults and defines requirements. 3. You should be able to define defaults accross multiple environments without repeating yourself. 4. You should be able to change configuration per box based on deploy that does not affect the defaults or requirements that are checked in to source control. This can be accomplished with a deploy time file or environment variables that are also highly configurable and validated. ## Usage 1. require 'hierarchical_config' 2. MY_APP_CONFIG = HierarchicalConfig.load_config( 'config_name', 'config_directory', 'environment_name' ) ## How does it work HierarchicalConfig loads a yaml file from the config directory. Each top level name in the yaml file is either a default stanza (for one or more environments,) or a specific environment. It applies least specific rules first and proceeds to most specific. It then reads an optional overrides file and does the same. If any REQUIRED values have not been overriden by actual values, it raises an exception. The object that it returns is a deeply nested tree of configuration that can't be modified and raises exceptions if you ask for values that it doesn't know about. * No more having environments load without the mail server address configured. * No more silent failures and returning nil for something you thought was configured. * No more copy and pasting configuration accross environments. * No more stupid YAML tricks to dry up your configuration. * No more tricky config that changes based on runtime side effects. * No more config that is hidden from developers and not in source control (unless you specifically need to.) ## Example ### config/app.yml defaults: root: child_a: 1 child_b: 2 child_c: grandchild_a: 3 grandchild_b: 4 super_secret_password: !REQUIRED check: true defaults[development,test]: super_secret_password: not_that_secret development: root: child_b: 8 env_vars: super_secret_password: SUPER_SECRET env_vars[production]: super_secret_password: SUPER_DUPER_SECRET ### config/app-overrides.yml production: super_secret_password: cant_trust_dev_with_this_we_symlink_this_file ## Results ### development :root: :child_a: 1 :child_b: 8 :child_c: :grandchild_a: 3 :grandchild_b: 4 :super_secret_password: <%= ENV['SUPER_SECRET'] || 'not_that_secret' %> :check: true ### test :root: :child_a: 1 :child_b: 2 :child_c: :grandchild_a: 3 :grandchild_b: 4 :super_secret_password: <%= ENV['SUPER_SECRET'] || 'not_that_secret' %> :check: true ### production :root: :child_a: 1 :child_b: 2 :child_c: :grandchild_a: 3 :grandchild_b: 4 :super_secret_password: <%= ENV['SUPER_DUPER_SECRET'] || 'cant_trust_dev_with_this_we_symlink_this_file' %> :check: true ### staging RuntimeError: ["app.super_secret_password is REQUIRED for staging"] ### Code You can access children nodes with a method call or an index operator, in any combination. Add a `?` to the name of a node to get a boolean value back. # in development environment AppConfig = HierarchicalConfig.load_config('app', Rails.root.join('config'), Rails.env) AppConfig.root.child_a # => 1 AppConfig.root["child_c"].grandchild_a # => 3 AppConfig[:super_secret_password] # => 'not_that_secret' AppConfig.check # => true AppConfig.check? # => true AppConfig.super_secret_password? # => true