Advanced Rails configuration - rails_config

Post date: 23-Jul-2013 07:29:25

Brand New Media's web department works primarily in Ruby on Rails, and Rails apps (like all apps) always have a certain amount of configuration. Host names, API keys - there's always some settings you want easy, central access to, or need to vary for different environments.

Rails' default behaviour is to use a central code file (application.rb) and a series of per-environment code files (development.rb, production.rb) for configuration, and that will serve the needs of a lot of Rails apps. Once you outgrow that, however, there's a lot of options - I personally like railsjedi's rails_config, which employs a structure of YAML files to provide a clear and expressive way to capture large amounts of configuration data. One feature I particularly like its concept of 'local' configuration files - files that capture install-specific (particularly, developer-specific) configuration, keeping it out of the code repository where it might clash with other developers set-ups.

The next level beyond this, though, is instancing - deploying the same application multiple times, with configuration tailored to each instance. At this point another of rails_config's features comes to the fore - customisable configuration paths. The pattern we decided to pursue was to set an environment variable for each instance, and use that value in in the configuration stack to find and load an instance-specific configuration file. This needs to happen after rails_config has been initialised, though, so a naive approach would be to add this logic into our application.rb, in an after_initialize block.

But this quickly leads to a chicken-and-egg problem - in the configuration stack we're both setting up rails_config, and attempting to use information from it (for example, configuring the excellent Devise authentication gem for Facebook authentication requires the credentials of our instance-specific Facebook application.) We obviously need a more fine-grained approach to our initialisation process - one that will allow us to knit our instance configuration into the initialisation process at the right point (most naturally right after rails_config initialises itself.)

Luckily, Rails gives us this fine-grained access in the form of initializers.

So here's an example of an initializer that solves the problem I've described.

module RailsApp

class Application < Rails::Application

# Normal Rails app configuration

initializer :add_instance_config_to_rails_config, :after => :load_rails_config_settings, :before => :load_environment_config, :group => :all do

Settings.add_source!("#{Rails.root}/app/instances/#{INSTANCE_KEY}/settings.yml")

Settings.reload!

end

end

end

Initializers allow us to pick a place in the initialisation stack and insert ourselves there, based on the named steps described here. In this case, we're creating a new step (add_instance_config_to_rails_config) and inserting it before the default Rails environment load event, but after rails_config's own internal custom step.

And that's all there is to it! Try it yourself, and let me know how it goes. :)