Simplifying ActiveRecord Connection Strings

Jul 24, 2011

Over the years I've gone to great lengths to avoid committing production

passwords into version control. With Rails this has inevitably meant a

number of different ways to ensure the database.yml was where it needed to

be on the production servers, with the right credentials. Custom Capistrano

recipes to create a new config when setting up a server, moving it into

place after a deploy, it worked well enough but it's always felt a little

heavy.

There's got to be a better way

There are a whole range of configuration settings that are environment

dependent. Which database to connect to, what queues Resque workers should

listen to, the Rack/Application environment mode. What we need is a

standard way on each server to set these environment variables. Oh hang on,

environment variables!

It's easy enough to either export an environment variable in a shell, or

pass it in with the command like RACK_ENV=production rackup config.ru

but a more complicated configuration like ActiveRecord requires doesn't

quite work like that. It's expecting a Hash that defines the database

adapter to use, username, password, the database, a host, and possibly a

bunch of other options. We could probably serialise that back and forth

between a Hash and something like JSON but it feels like a bit of a hack.

What would be better is if we could come up with a simple and uniform string

format that could define how to locate any resource, including a database. I

propose we call such a string a Uniform Resource Locator, or URL for short ;)

Using URLs to connect to databases

This isn't a new technique, other ORMs like Sequel have been doing this for

years. It's just that ActiveRecord hasn't supported it, until now. I've

created a gem that adds support for URL based connections to ActiveRecord,

it's called activerecord_url_connections.

Add it to your Gemfile:

gem "activerecord_url_connections"

And now you can connect to your database by adding the following to an

initializer (e.g., config/initializers/activerecord.rb):

ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])

Now you can either export the DATABASE_URL environment variable in your

app environment or set it when you start your app like so:

DATABASE_URL=postgres://localhost/myapp_production

No more juggling database.yml on production servers, no risk of checking

sensitive credentials into version control.

Convention Over Configuration

Something still doesn't feel quite right about this approach though. Creating

an initializer just to have that one line seems a bit needless, especially

when it will be exactly the same in almost every app. So in much the same

way Rails assumes a default connection to use if you don't provide one, so

it will look to see if you've set DATABASE_URL and use it when available.

Stick with the conventions, and you'll not need to do anything beyond adding

the gem and setting DATABASE_URL. Sweet!

But wait, there's more!

There's some further good news too. Firstly, for anyone using Heroku for

hosting Rack apps that use ActiveRecord this means connection to your

database will "just work" (it's not needed for Rails as Heroku create a

database.yml file so the existing Rails behaviour will work). Secondly

this change has made it into ActiveRecord for the 3.2 release so you'll

only need this gem to backport the behaviour to previous releases.

Hi, I'm Glenn! 👋 I'm currently Director of Product @ HashiCorp, and we're hiring! If you'd like to come and work with me and help make Terraform Cloud even more amazing we have multiple positions opening in Product ManagementDesign, and Engineering & Engineering Management across a range of levels (i.e., junior through to senior). Please send in an application ASAP so we can get in touch.