Cascading YAML config system?

The apps I’ve been writing lately have their own config in YAML files, which is sort of a natural evolution of the old global constants I had been using before. And just yesterday I was thinking about how I want to better organize the text in the files and I came up with a way to cascade my configurations. That is, a naming convention plus a deep merge utility for Hash objects.

The Problem

The problem is that I started to have bunches of YAML config files that started looking like:

awesome_app.production_host.customer_site.yml
awesome_app.production_host.business_site.yml
awesome_app.production_host.admin_site.yml
awesome_app.test_host.customer_site.yml
awesome_app.test_host.business_site.yml
awesome_app.test_host.admin_site.yml
awesome_app.development_host.customer_site.yml
awesome_app.development_host.business_site.yml
awesome_app.development_host.admin_site.yml

Let’s say I’m on my development host: I have 3 Rails apps that run as a suite, hence the 3 development_host files. Each of those files contains a configuration like:

application:
  name:               Awesome App Customer Site
email:
  contact_addresses:
    ads_contact:      advertising@awesome_app.com
    support_contact:  support@awesome_app.com
logs:
  base_directory:     /home/awesome_user/logs
  rotation_frequency: daily

What differs from app to app in this case is the application name. The business site might have instead:

application:
  name:               AwesomeNet: The Awesome Business Portal
...

and the admin console might have:

application:
  name:               Awesome App Administration
...

Thinking Ahead

Right now the configurations are relatively small so keeping 3 files up to date isn’t too bad. But I’m starting to see that cutting-and-pasting is not going to work long term. So I need a way to simplify the configurations but still allow for flexibility.

So what if we factored out the common bits into one YAML file and then just applied the deltas? Then I’d have 4 configuration files:

awesome_app.development_host.common_bits.yml
awesome_app.development_host.customer_site.yml
awesome_app.development_host.business_site.yml
awesome_app.development_host.admin_site.yml

Thus the common_bits file will have:

email:
  contact_addresses:
    ads_contact:      advertising@awesome_app.com
    support_contact:  support@awesome_app.com
logs:
  base_directory:     /home/awesome_user/logs
  rotation_frequency: daily

And the other three files will have their names of their applications:

application:
  name:               Awesome App Customer Site
application:
  name:               AwesomeNet: The Awesome Business Portal
application:
  name:               Awesome App Administration

Then using a utility like Steve Midgley’s Deep Merge gem (Download) I can use common_bits as the base configuration and deep merge the individual site configurations on top of that. e.g.

app_name = 'awesome_app'
host_name = 'development_host'
site_name = 'business_site'

all_sites_config_file = app_name + '.' + host_name + '.common_bits.yml'
site_config_file = app_name + '.' + host_name + '.' + site_name + '.yml'

# Use all_sites_config_file to start, then apply site_config_file
$GLOBAL_CONFIG = YAML.load_file(all_sites_config_file)
$SITE_CONFIG = YAML.load_file(site_config_file)
$GLOBAL_CONFIG.deep_merge!($SITE_CONFIG)

I was pretty happy with that, then I made an observation…

Naming Convention Implies Hierarchy

What if we used a hierarchy of dots? That is:

awesome_app.yml                                 # Master config
awesome_app.development_host.yml                # Development host config
awesome_app.development_host.customer_site.yml  # Dev customers
awesome_app.development_host.business_site.yml  # Biz network
awesome_app.development_host.admin_site.yml     # Admin

Huh. So using periods as a kind of separator I can create a kind of cascading configuration.

Leave a Comment

You must be logged in to post a comment.