Rails 4.1: secrets.yml, mailer previews, enums, and Spring
Rails 4.1 is a quietly excellent point release. A tour of the features we adopted immediately — config/secrets.yml, ActionMailer previews, ActiveRecord enums, the Spring preloader, and request variants — and the small print on each.
Rails 4.1 is the kind of release that does not make headlines and yet improves your day-to-day life more than the ones that do. There is no single tentpole feature; instead there is a handful of well-judged additions that each remove a recurring annoyance. We have moved several apps to it and adopted most of these within the first week. Here is the tour, with the small print.
config/secrets.yml: one home for secrets
For years, configuration secrets in Rails were a scattered mess — values hard-coded
in initializers, smuggled through ENV, or buried in secret_token.rb. 4.1
introduces config/secrets.yml, a single, environment-aware home for them:
# config/secrets.yml
development:
secret_key_base: a_long_dev_key
stripe_api_key: sk_test_xxx
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
stripe_api_key: <%= ENV["STRIPE_API_KEY"] %>
Rails.application.secrets.stripe_api_key # => the value for the current env
The pattern that has emerged, and the one we follow, is the best of both worlds:
real values for development and test live in the file, while production reads from
ENV via the ERB interpolation. That keeps the names of your secrets in one
documented, version-controlled place (so a new developer can see what the app
needs) while keeping the production values out of the repository. Note that
secret_key_base — the key that signs your session cookies — now lives here too,
replacing the old secret_token.rb.
The one caveat: anything with a real value in secrets.yml is committed, so keep
production credentials in ENV, not in the file. The file documents the shape;
the environment supplies the secrets.
ActionMailer previews: see your emails in the browser
This one is a pure quality-of-life win and our favourite in the release. Testing email used to mean triggering the real flow, then squinting at a letter_opener tab or a test mailbox. Previews let you render a mailer in the browser, with whatever data you set up, just by visiting a URL:
# test/mailers/previews/user_mailer_preview.rb
class UserMailerPreview < ActionMailer::Preview
def welcome
UserMailer.welcome(User.first)
end
def password_reset
UserMailer.password_reset(User.first)
end
end
Visit /rails/mailers/user_mailer/welcome and you see the rendered email, both
HTML and text parts, refreshed on every reload as you tweak the template. It turns
email design from a slow, blind round-trip into a tight feedback loop exactly like
working on a normal view. If you build anything email-heavy, this alone justifies
the upgrade.
ActiveRecord enums: named states without the lookup table
A status column is almost always an integer pretending to be a set of named
states, with a STATUSES = { ... } constant and a pile of helper methods bolted on
by hand. 4.1’s enum macro formalises exactly that:
class Order < ActiveRecord::Base
enum status: [:pending, :paid, :shipped, :cancelled]
end
order.paid? # => false
order.paid! # sets status to :paid and saves
order.status # => "pending"
Order.shipped # scope: all shipped orders
You store a compact integer in the database and work with readable names in Ruby, and you get predicate methods, bang setters, and scopes for free. Two sharp edges to respect, both rooted in the same fact — the mapping is positional. The integer stored is the index in the array, so you must only ever append new states; reorder or remove one and you silently remap existing rows to the wrong meaning. For anything where that risk is unacceptable, use the explicit hash form that pins names to numbers:
enum status: { pending: 0, paid: 10, shipped: 20, cancelled: 99 }
The explicit hash is what we use in practice — it makes the database values stable and self-documenting, and it leaves gaps so you can insert states later.
Spring: the application preloader
Spring keeps your application running in the background between commands, so
rails console, rake, and your test runner do not pay the multi-second Rails
boot every single time. On a large app, where boot can take ten or fifteen
seconds, this is the difference between a console that opens instantly and one you
dread. It is on by default in development in 4.1.
The trade-off is real, though, and worth knowing up front: because the app stays
loaded, Spring sometimes serves you stale code — most often after you change an
initializer, add a gem, or edit something loaded at boot. The fix is a reflex:
spring stop (or bin/spring stop) and let it reload. Knowing that one command
saves you from the occasional baffling “but I changed that!” moment.
Action Pack variants: one action, many formats
Variants let a single controller action render a different template based on the request — most usefully the device type — without branching logic everywhere:
# in a before_action
request.variant = :phone if browser_is_mobile?
# app/views/posts/show.html+phone.erb -> served to phones
# app/views/posts/show.html.erb -> the default
Rails picks show.html+phone.erb when the variant is :phone and falls back to
the plain template otherwise. It is a clean way to serve tailored markup to
different clients from one action, and it composes with the rest of the rendering
pipeline instead of fighting it.
The smaller additions
A few more worth a mention: Module#concerning gives you an inline way to group
related methods inside a class without a separate concern file;
ActiveSupport::MessageVerifier and the secrets work make signing/verifying data
straightforward; and the mailer/secrets changes tidy up a lot of boilerplate
generators used to scatter around.
Verdict
4.1 is an easy, low-risk upgrade with an unusually good ratio of small features to
daily payoff. Mailer previews and Spring change how it feels to work on the app;
secrets.yml and enum clean up patterns every codebase had reinvented by hand.
Adopt the explicit-hash form of enum, keep production values in ENV, and learn
the spring stop reflex, and there is essentially no downside. This is Rails doing
what it does best — noticing the repetitive things everyone writes and folding the
good version into the framework.