Configuration for Rails, the Right Way

I still see people promoting various gems and plugins to handle miscellaneous configuration elements for your application. One little known secret is that Rails 3 allows you to define your own configuration elements trivially.

In this case, I wanted to use the nifty wkhtmltopdf utility to create a PDF. I was able to call the binary just fine with Homebrew on OSX but found that I had to use a custom binary checked into git for our production environment on Heroku. So I created a configuration variable to store where wkhtmltopdf could be found in the current environment.

First, we define a default value for all environments in config/application.rb:

Then we override the default setting as necessary in config/environments/:

Configurator::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# Point Heroku explicitly to the binary we need to use
config.wkhtmltopdf = "#{Rails.root}/bin/wkhtmltopdf"
end

Yes, that’s it. Just use Rails’s environment support and config to store your own configuration elements. They’re trivial to set, trivial to access and require no third-party gems or custom text files.

Feedback

Comments: 23

jasoncrawford

One issue with this is that I find myself wanting to group my config elements together. E.g., say I’m writing an app that uses Urban Airship, and I have my own client object that encapsulates their API. I want to configure our app key and secret for dev and prod environments. Using this method I *could* do:

Anonymous

When I started a green-field Rails 3 project a year ago, everyone was explaining how Configuration was an enormous and scary problem to be solved. I was getting pitched everything from Gems, to NoSQL DB’s, to RDBMS’s, etc. In looking at what they had in place, I mentally pieced together the chronology of their former solutions. In its first incarnation, years back, it was simply a SQL table of key-value pairs. (I was pleased to see key-value approach rather than eternally growing columns.) The problem was there was no RAM persistence. This was getting hit every time a Config value was referenced in code. Some time elapses, and someone realized how crippling that was to their app, and decided to solve the problem with MemCacheD! [MemCacheD itself was not the problem. It was how it was incorrectly applied.] They loaded all the config values from SQL into MemCacheD upon app startup. They implement yet-another, entirely separate persistence mechanism to manage and maintain. This was horribly confusing to every single dev that ever worked on the project. Naturally, new devs would find themselves changing values in the Config DB, but seeing no change after they restarted the application. I can’t count the # of hours wasted on that alone. That system is still in place, and I witnessed the same thing happen to 2 new devs this year.

Anyway, to all these various “solutions” people were pitching me, I replied, “The most reliable, leanest, and fastest data-storage mechanism already exists in every programming language…. [dramatic pause] RAM, and an object.”

All I could think was, “Are developers aware that one prime function of an object is to hold data?” They’ve done that since objects were invented in the 80’s. Even without objects, RAM itself has been the easiest thing to access since the Eniac.

I wanted things in YAML so it was a little more human readable, and could be edited with the ease of editing a file. I wanted to make it polymorphic based on enviro. And I wanted to have dynamic portions [Rails.root, etc], so I pass it through the ERB parser. Here’s config/config.yml:

Qweqwewq

Reed G. Law

I’m using @iq9’s solution and it works great. For testing, you can change/delete a setting like so: Settings.instance_variable_set(:@setting_name, false). You could also add a class method to reload the settings.

Khoa Nguyen

Anonymous

This doesn’t feel clean to me for a couple of reasons:
* Everything else you configure in application.rb is kinda standard Rails configuration, so I wouldn’t want to include my application-specific stuff in there.
* I want to be able to specify passwords and other sensitive information and not check those in to my version control system.
* I prefer defining my configs in YAML rather than code, so even non-technical people could edit them.
* I don’t want my configs to be split up in several files.

I wrote my own app config (like Configatron) a while ago and have been improving and reusing it ever since. It sits in lib/app_config.rb and loads config/app_config.yml.

craig wickesser

I like using Settings Logic (https://github.com/binarylogic/settingslogic). I put all config into a YAML file (which is setup with dev, test and prod blocks) and then load it up. Then I can just call “Settings.facebook_app_id”, or whatever.

I like this b/c all of my config is in one file that I can choose to keep out of version control if I need to. I can even load more than one file if I need to.

And, adding a single gem as a dependency isn’t an issue…my normal rails apps use lots of gems anyways.

However, I do appreciate Mike pointing out the ability to use the built-in Rails config, never thought about that approach

Ssws

Peder Linder

What would you do with a configuration string that contains non-ascii characters, e.g. the name of the CEO? I guess you could set “encoding: utf-8″ for the application.rb file but I don’t like the idea of having files with different encoding specifications in my project.

Chris

Hi Mike,
Good call. I’ve tried this approach and liked it, except for dealing with accidental code checkins of secret api keys etc. Like others, I too have written my own gem, which has worked well for my team. https://github.com/kindkid/constantinople

gerhardlazu

I feel having app-specific configs available through the user environment under which the app runs makes a lot more sense. Heroku got this right. If you’re using Chef or Puppet to prep servers for app deployment (you are, right?), that’s the only place where you want the actual values stored. An app is no longer this single Rails instance which contains all its configs, but a myriad of small services that work independently and require to know only so much. With this approach, if you change a value and re-cook the servers, they make the new values available instantly, not even a USR2 required.

Robert Reiz

I used the example from iq9. That worked pretty good. But the settings.rb in the “config/initializers” directory is called AFTER application.rb, environment.rb, development.rb, test.rb and production.rb. If I want to use the Settings.xxx in one of this files, it didn’t worked. I just put the Settings class directly into the “environment.rb” before “MyApp::Application.initialize!”. In that way I can use the Settings.xxx in all environments files, too.