Wolox

The Wolox engineering blog.

Extracting Configuration in Ruby Applications

Hi! Today I’m going to present you a little gem that I have extracted after solving the same problem over and over again in Rails. But it can also be used in any Ruby application.

The gem is called app_configuration. I have created this gem because in several Rails application I had the problem of configuring third party services and I could not set sensitive information in the environment files. If you are doing this you shouldn’t. The mayor issue with setting sensitive information in the environment files is that (unless you don’t use any kind of VCS) you are versioning it.

For example if I a need to implement Facebook connect, what I would normally do is create a Facebook app with my personal account only for development. Then in my development.rb file I would do something like this:

1
2
3
4
# Facebook Credentials
config.facebook = OpenStruct.new
config.facebook.api_key = ENV['FACEBOOK_API_KEY']
config.facebook.api_secret = ENV['FACEBOOK_API_SECRET']

and then if I have to access the Facebook credentials I would do something like this:

1
2
api_key = Rails.application.config.facebook.api_key
api_secret = Rails.appllication.config.facebook.api_secret

I use environmental variables instead of loading credentials from a file because most of the time I use Heroku for staging or production environments and the fastest way to config this kind of thing in Heroku is with environmental variables.

The hazard of this approach is that I have to remember to export the environmental variables each time I run the rails server. I could export all the variables in my .bashrc but I don’t like that because I could have name collisions. For example if I have application A that uses Facebook connect and I have application B that also uses Facebook connect I would have to do something like:

1
2
3
4
export A_FACEBOOK_API_KEY='1387492671756796476128476'
export A_FACEBOOK_API_SECRET='8848899039902917615613891347681356'
export B_FACEBOOK_API_KEY='5636372728717836136171717'
export B_FACEBOOK_API_SECRET='3334189741979478198741983798146175'

The other problem of this approach is that the configuraiton is not self-contained per project. So what I would end up doing is to create .env file in the root of the project and add this file to the .gitignore. The .env file would have all the corresponding exports. Now if you want to run the rails server you need to source the .env file like this:

1
source .env && bundle exec rails server

You could add an alias to reduce typing in your .bashrc file

1
alias rails="source .env && bundle exec rails"

or you could use the foreman gem that automatically reads all the variables defined in this file.

Sometimes we use Heroku for stagging and Amazon EC2 for production. Generally when we use Amazon to deploy our application we use Chef to setup the VMs. In this cases I prefered to generate a config file using Chef templates and reading sensitive data from encrypted data bags.

So this is why I ended up creating app_configuration. Before creating the gem I did some research in RubyGems and found at least 15 configuration gems but neither of them do what I really want.

What I wanted is the flexibilty to have a local self-contained configuration file and the posibillity to export the configuration with environmental variables. All the app_configuration features are deeply explained in the project’s documentation but here I will do a quick review.

The previous Facebook connect example could be refactored using app_configuration like this. Instead of defining your configuration in each of the enviroment files you will define it in the application.rb:

1
AppConfiguration.for(:facebook)

then if you want to read the Facebook API key and secret you can do it like this

1
2
key    = AppConfiguration[:facebook].api_key
secret = AppConfiguration[:facebook].api_secret

This assumes that there is a .facebook.yml file in the current working directory with the following content:

1
2
api_key: 1387492671756796476128476
api_secret: 8848899039902917615613891347681356

Environmental variables have more precedence than configuration files. So if FACEBOOK_API_KEY is exported, app_configuration will use that value. Note that the attribute name is prefixed with the name of the configuration (in this case FACEBOOK_). This is done to avoid name collisions. Also remember to add you configuration files to the .gitignore.

So well that is pretty much it. Hope you like it and are willing to give it try. Pull requests are welcome. You can fork app_configuration on Github.

Comments