Video transcript & code
In the previous episode I briefly mentioned a gem called Dotenv. Today I want to cover it in a little more detail, because it's really quite cool.
Very often we find a need to pass small bits of configuration data into an application. One of the most common examples of this is an API key for some external service. We don't want to hardcode this value, because individual developers should be using their own API keys. They shouldn't have access to the production API key.
The simplest way to configure these types of simple variable on a per-machine basis is to make them environment variables. The system environment gives us a basic key-value store without having to deal with managing and parsing config files. And for some application hosts such as Heroku, environment variables are the only supported way to set configuration values without committing them to the source code repository in some form.
api_key = ENV["YOYODYNE_API_KEY"]
This is fine for staging and production servers. But when we're running the code locally as developers, it's less than ideal. We have to remember to set the API keys before running the application, or we have to put these per-app keys in our global shell init files, or one of several other inconvenient schemes.
$ export YOYODYNE_API_KEY=SECRET123 $ rails s
This is where the Dotenv gem comes in. We start by adding it to our Gemfile. If we're working on a Rails app, we can make a dependency on the
dotenv-rails gem, and we're done setting it up.
gem "dotenv-rails", :groups => [:development, :test]
Note that it is typically only needed in the development and test environments, since it's assumed that in production the environment variables will already be set.
For other kinds of application, we depend on the base
Then we find a spot as early as possible in our app startup sequence to initialize Dotenv. For instance, the Rubytapas.com Sinatra app, I have a file called environment which is loaded before anything else.
In that file, I require
dotenv and then invoke
require 'dotenv' Dotenv.load(File.expand_path("../.env", __FILE__))
In this case I've specified an explicit filename for it to load, but we can also call it with no arguments and it will find a file called
.env in the current directory.
require 'dotenv' Dotenv.load
For demonstration purposes, let's jump over to an empty project. We'll require "dotenv" and tell it to load, just like before.
require 'dotenv' Dotenv.load
Now we'll create a
.env file. Inside, we can make simple key-value assignments. We'll set up an API key variable.
Back in the code, we'll access the the environment variable
require "dotenv" Dotenv.load api_key = ENV["YOYODYNE_API_KEY"] api_key # => "SECRET123"
Lo and behold, it has the value we set in the
.env file. This is what Dotenv does: it finds the variable file, and updates the applications global environment using the definitions it finds there.
Now let's try setting the environment variable before loading up Dotenv.
ENV["YOYODYNE_API_KEY"] = "PRODUCTION_KEY" require "dotenv" Dotenv.load api_key = ENV["YOYODYNE_API_KEY"] api_key # => "PRODUCTION_KEY"
This simulates the production environment where the environment variables are always configured before the application starts. We can see that this time, the pre-set value overrides the
.env value. Dotenv only takes effect when an environment variable is missing.
If the keys in the
.env file are specific to each developer, then we should keep this file out of revision control, for instance by adding it to our
.gitignore file. On the other hand, if there are some common API keys shared among developers, we can check it into our repo.
There's one other Dotenv feature worth mentioning. Very often, project Rakefiles also need environment variables to be configured. Dotenv has special support for Rake to make this easier. In our Rakefile we can just require
dotenv/tasks. Then we make any task that needs the environment variables loaded up depend on the special
require "dotenv/tasks" task :my_task => :dotenv do # ... end
Remembering the correct environment variables to set before starting up an application has always been an annoying speed-bump for me. Dotenv smoothes over that speed bump, and as a result it has become one of my most essential tools.