Rails apps extra configuration nightmare – solved!
I believe that many of you (ruby devs) have seen a file in one of your projects with with a lot of constants and full of if Rails.env.production?. It’s a nightmare. Even worse, it’s an example file that’s supposed to have a local gitignored copy. And because their content is usually not being verified, getting an uninitialized constant error with no clue of what happened and becoming annoyed is just a matter of time.
Another evil thing is the fact that all types of configuration items (access keys, mailer settings, etc.) are often split across various files and classes. Instead of having their dedicated place and being easily accessible, they mess up the code and their maintenance is a nightmare.
Unfortunately, Rails doesn’t offer any way to manage and verify custom configurations. Rails 4.1 introduces config/secrets.yml, which partially solves the problem, but it’s not enough to keep all the configuration maintainable.
I came up with my own solution called a9n (a numeronym for application) to keep my ruby and rails apps configuration easily maintainable, verifiable and clean.
Sources and gem
How it works?
a9n expects configuration.yml.example and/or configuration.yml file in the app’s config directory. You can have both – configuration.yml.example tracked by git and local configuration.yml ignored by git) – or just a single configuration.yml tracked by git.
If both files exist, content of configuration.yml is validated. It means that all the keys from the example file must exist in the local file, otherwise A9n::MissingConfigurationVariables is raised with information about missing keys.
All configuration keys are accessible by calling a method on an A9n. Let’s say you have:
defaults:
email_from: 'no-reply@knapo.net'
production:
app_host: 'knapo.net'
development:
app_host: 'localhost:3000'
So you can access the config by:
A9n.app_host # => `knapo.net` in production and `localhost:3000` in development
A9n.email_from # => `no-reply@knapo.net` in each environment
Custom and multiple configuration files
If you want to split a configuration, you can use multiple files. All files from config/a9n are loaded by default, but you may pass custom file paths as an argument to A9n.load, e.g. A9n.load(‘config/facebook.yml’, ‘config/mongoid.yml’). In such cases config items are accessible through the scope consistent with the file name. E.g. Having config/a9n/mandrill.yml:
defaults:
username: "knapo"
api_key: "1a2b3c4d"
production:
api_key: "5e6f7g8h"
You can access it by:
A9n.mandrill.username # => knapo
A9n.mandrill.api_key # => 1a2b3c4d
in production and 5e6f7g8h
in other envs
Capistrano
If you are using use capistrano and you feel safe enough to keep all your instance (staging, production) configuration in the repository, you may find it useful to use capistrano extensions.
Add an instance configuration file e.g. configuration.yml.staging, configuration.yml.production (NOTE: file extension must be consistent with the capistrano stage) and add
require 'a9n/capistrano'
to your deploy.rb file. This way configuration.yml.[stage] overrides configuration.yml on each deploy. Otherwise you’d need to store configuration.yml in shared directory and link it in a regular way.
More details and the setup instructions are available on github.