Skip to content
← All posts
5 min read Dawid Skłodowski

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.