Get more articles like this about using Rails Like A Pro

Keep Your Secrets Secret

Rails is well known for its convention over configuration approach to development and it is one that makes a developer’s life a little easier. There are times though, when you need some configuration in your app and Rails (until the official release of 4.1) doesn’t offer you any guidance as to how to do this.

Like a lot of things in Ruby, there are many ways to tackle this problem. A quick search on GitHub reveals lots of gems offering solutions to this. It’s worth checking this out so you can get a feel for how others have solved this and so form your own opinion about what is best for your situation.

Keep your secrets secret

I’d like to show you the method that I prefer, one that is well suited to a 12 factor approach favoured by platforms like Heroku. In particular, check out the ‘Store config in the environment’ concept.

At it’s simplest, we want to avoid hard-coding configuration values our app relies on so that we can deploy it to different environments easily.

The added benefit of this is that we keep our secrets, like our API keys, and database passwords, out of source control and safe from the prying eyes of malicious users.

Keep it simple with Ruby

If you looked through the list of gems on GitHub that can help you with your configuration then you will have noticed that a few of them favour storing values in a .yml file, similar to how Rails configures the database connection.

This can work fine and can be made to fit in with a 12 factor approach however, for me it adds some extra moving parts that we don’t need, eg. parsing a .yml file into an object that Ruby can use.

Or in the words of a popular configuration library, Configatron:

YAML is terrible and should be driven from the face of the Earth. Because of this Configatron V3 does not support it. Sorry.

Wherever I can, I prefer to keep things as uncomplicated as possible and go with a plain Ruby approach.

Hashr

We’re going to use a gem called Hashr, which gives us a nice integration with environment variables, meaning it will be very easy to configure on Heroku when we deploy it there.

So let’s get writing some code.

lib/config.rb

require 'hashr’

module YourApplication
  def self.config
    Thread.current[:config] ||= Config.new
  end

  class Config < Hashr
    extend Hashr::EnvDefaults

    define(
      ga: {
        ua: ’UA-1234’
      }
    )
  end
end

We have to nest the Config class within your application namespace so that it doesn’t collide with the Ruby core class of the same name.

We require the Hashr gem because at the point this file is loaded (see below) we can’t guarantee that this dependency has been loaded by bundler.

Next we set up a convenience class method in the app namespace for easy access to this config object. This is an optional step, as it just means you have a little less to type when reading config values elsewhere in your app.

Thread.current[:config] gives us access to a global variable for the currently running thread. We then store an instance of the Config object in there to avoid having to reinitialise it every time we need to read a value.

Our Config class inherits from the Hashr gem, which adds some extra methods to the Ruby standard library Hash class.

The only extra method you need to worry about right now is the define method, which you can see is used inside this class.

define allows us to setup a Hash that stores our configuration values in. You may be wondering why not use a Hash directly? Well, the main two benefits for me are the environment variable magic (see below) and access to the hash via methods, eg. Config.new.ga.ua.

Environment variables

extend Hashr::EnvDefaults adds the environment variable magic we need. This magic isn’t really that magic though, and consists of allowing an environment variable that matches a Hash key to overwrite it, eg. Config.new.ga.ua can be overridden by ENV['GA_UA'].

In the example above, I set a default value for my Google Analytics UA variable. This is just a placeholder value for use in the development environment as we don’t want to keep any sensitive configuration values in our source control.

For us to make use of this configuration object, we need to tell Rails about it. config/application.rb will look something like this:

require File.expand_path('../boot', __FILE__)
...

require File.expand_path('../../lib/config', __FILE__)

module YourApplication
  class Application < Rails::Application
    config.settings = YourApplication.config
    ...

Files in lib aren’t automatically required so we need to require the config file we just created in order for it to be available to us throughout the app.

With that done, we can now access our ga hash from the config object by calling YourApplication.config.ga.ua. Most likely this will be in a partial somewhere, app/views/shared/_google_analytics_js.html.erb:

<script type='text/javascript'>
  var ua = '<%= YourApplication.config.ga.ua %>';
  ... ga code here ...
</script>

Setting environment variables on Heroku

Now that we have a configuration object setup and we’re using the values in the app, it’s time to deploy things and get it working on production.

We have one final step before the code is deployed and that is to set our config values using environment variables on Heroku. One command should do it…

heroku config:set GA_UA=UA-12345-12 -a your-app-name-on-heroku

Once that’s done, you’re safe to deploy the new code to Heroku.

Get more articles like this about using Rails Like A Pro