A Guide to Using ActiveRecord's Enumerated Type

I’ve used ActiveRecord’s built in enumerated type a lot since it was introduced in Rails 4.1. Although it’s getting a facelift in Rails 5, there are still plenty of gotchas to look out for.

Up first is update_all, a low level method that builds a single SQL UPDATE statement and sends it directly to the database.

class Post < ActiveRecord::Base
  enum status: { draft: 0, published: 1 }
end

Post.update_all(status: :published)

It shouldn’t come as a surprise that using a symbol with update_all does not work. It’s designed to work with primitive types and doesn’t typecast values. It doesn’t run callbacks or validations either. For update_all to work, any values it’s passed must go through ActiveRecord’s typecasting behaviour.

In other words, :published has to be translated to its raw value, the integer part of the element. This isn’t hard to do.

Post.update_all(status: Post.statuses[:published])

Next up are where query methods. Arguments passed to where do get typecast by ActiveRecord, but not in a way you might expect.

Post.where(status: 'draft')

The above code fails silently. Under the hood, Rails calls to_i on ‘draft’, and its return value, 0, gets used in the generated query. This happens when where typecasts the value for integer-type columns. If I use a symbol, we would see nil instead of 01.

When using where, ActiveRecord does not know that status is defined as an enum, and treats it according to its schema definition, an integer.

SELECT "posts".* FROM "posts" WHERE "posts"."status" = $1  [["status", 0]]

In this case, however, one can and should use the generated scope Post.draft, even when querying through an association.

class User < ActiveRecord::Base
  has_many :published_posts, -> {
    Post.published
  }, class_name: 'Post'
end

However, there are times when using where is necessary. For example, if I have a table of posts I want to filter by status by passing its value as a parameter.

= link_to 'Published', params.merge(status: :published)

We can use the same approach we used with update_all, but it’s better to create a scope that handles the type conversion for us.

class Post < ActiveRecord::Base
  enum status: { draft: 0, published: 1 }

  scope :status, ->(status) {
    where(status: statuses[status])
  }
end

I should mention that this behaviour has been made consistent in Rails 5. Going forward where will recognise status as an enum, and will do the conversion for us.

I frequently see enum columns defined with string type. There are many ways to implement enums, and you can use types other than integer, but ActiveRecord only supports integer. If you use strings you’ll get silent failures and nasty surprises.

 add_column :posts, :string, :status, default: 0
 p = Post.create!
 p.draft?
 #=> true
 p.status
 #=> 0
 p.published!
 p.published?
 #=> true
 p.status
 #=> 0 # BOOM!

Enums are misused frequently in controllers and views. I recently answered a question on Stack Overflow that had this code:

<%= link_to "Waiting", property_path(property, {:status => 'Waiting for Response'}), method: :patch) %>
<%= link_to "Registered", property_path(property, {:status => 'Registered'}), method: :patch) %>
# Two more of these...

Not only is this needlessly verbose, but you must remember to change the view code each time you add a new status. It’s best to generate these links automatically.

<% Property.statuses.each_key do |status| %>
  <%= link_to status, property_path(property, { status: status }), method: :patch %>
<% end %>

The controller was in worse shape.

def approve
  if params[:status]== 'Registered'
     @property.update_attributes(:status => 1)
     redirect_to :back, flash: {notice: "Property Registered."}
  elsif params[:status]== 'Waiting for Response'
     @property.update_attributes(:status => 3)
     redirect_to :back, flash: {notice: "Waiting for Response"}
  elsif
    # and more...
  end
end

Given the new code, it could be condensed into this.

def approve
  @property.update!(status: params[:status])
  redirect_to :back, notice: t(".#{params[:status]}")
end

Which brings me to my next point. Don’t validate enums; you don’t have to. Rails does it automatically for you.

post.update(status: :rubbish)
#=> ArgumentError: 'rubbish' is not a valid status

And use the handy generated methods when you can: post.published! instead of post.update(status: :published).

Finally, use a database index. You will likely need to filter results by an enum value.

add_index :posts, :status

I hope you found this post useful.

  1. Although Symbol does not define to_i, ActiveModel rescues the error.

Mohamad's ‘World Famous’ Hummus

If you say the same thing often enough, at worst people will believe you, and at best it might become true. But it’s more likely neither happens, and people find you rather funny. Perhaps my grand opinion of my own hummus is not warranted by the facts, but like anything, if you make hummus often enough, you do get good at it. And so was born Mohamad’s ‘World Famous’ Hummus.

Jokes aside, hummus (حُمُّص) is the Arabic word for chickpeas. In Lebanon, we eat hummus in a variety of ways: hummus mtabal, fatteh, hummus bi tahini, and many more. Outside of the Middle East, it is the latter of these dishes, hummus bi tahini (حُمُّص بطحينة‎), that is best known and referred to by its mononym hummus. It means “chickpeas with tahini,” and it’s very easy to make. Tahini (طحينة‎), by the way, is toasted sesame paste. It is what makes hummus creamy, and gives it its distinct taste.

The Ingredients

I’m not much of a recipe person. I usually eye-things-out, and work better with sprinkles, dollops, and smidgens. But I make hummus often and I have a mental record of the quantities. Although taste may vary from time to time, the differences are nuanced, and it makes for a nice surprise once in a while.

To make hummus, you need the following ingredients.

  1. 150 grams of dried chickpeas.
  2. A medium-sized, peeled clove of garic.
  3. The juice of one-half of a lemon.
  4. A table spoon of tahini.
  5. Salt to taste, usually one-half of a teaspoon.

It’s important that the chickpeas are dry, and not precooked, canned, or soaked in liquid. Precooked or canned chickpeas usually impart a strange and undesirable flavour.

The following ingredients are optional. But your hummus can’t be “world famous” without proper garnishing. For me, the following ingredients are not only important for the aesthetics of my hummus, but also for its flavor.

  1. A few shoots of parsley, finely chopped.
  2. One-half of a tomatoe, finely chopped.
  3. Some olive oil.
  4. A sprinkle or two of red chili powder (optional).
  5. A shoot of fresh mint.

You also need some tools.

  1. A pressure cooker or bicorbonate of soda.
  2. A blender or very strong hands.

The Process

If you have a pressure cooker, cook the chickpeas until they are soft and easy to peel with your fingers. Usually, a cooking time of around 30 minutes will do although your mileage may vary. If you don’t have a pressure cooker, soak the chickpeas in water overnight with a spoon or two of bicarbonate of soda, then cook them until they’re soft. According to my grandmother, undercooked chickpeas give you belly ache.

Once the chickpeas are cooked, drain them in a colander, but not before you save a few table-spoons of their broth. I use the broth to fortify the flavour. Pour the chickpeas into a bowl filled with water. This loosens the peels and raises them to the surface. Gently massage the chickpeas with your finger tips until their skins come off. Remove and discard the skins. You don’t have to peel all the chickpeas; I peel around a half to two-thirds, and that’s enough. Removing the peels makes your hummus creamier, and taste better.

Put the chickpeas into the blender with the garlic, the lemon juice, and the salt. Add the broth you saved from cooking the chickpeas and another 50 mililitres of fresh water. Blend the mixture until it becomes creamy. Add the tahini. Blend again for another minute. You might have to stir the ingredients if there’s not enough liquid to blend the mixture properly.

Now take a moment to consider the consistency of your hummus. Reward yourself with a nibble. If changes are necessary, now is a good time to make them.

At this stage, your hummus might be perfect, too thick, or too runny. If it’s too thick, add water incrementally and continue to blend the mixture; if it’s too runny, add more tahini; if its flavour is bland, perhaps dash of lemon juice or a scrap of garlic can fix it. Maybe it’s the salt.

The Presentation

This is my favourite part–besides eating it, of course.

Scoop the hummus out of the blender and onto a shallow plate and spread it around evenly. You should create a circular depression between the center and the edge of the plate. I usually apply the base of my utensil in a circular motion. This depression will host the garnish. If you’re the dextrous type, a peak should form in the middle of the plate.

Add a pinch of parsley on opposite sides of the peak. Add some chopped tomatoes to the other two sides. Add a sprinkle of chili powder between each pinch of parsley and the adjacent tomato. Pour a healthy serving of olive oil all around. Finally, the crowning moment should have you add a shoot of mint to the peak in the center of the plate.

Of course, you can present your hummus in anyway that pleases you. Your hummus is your canvas, so express yourself. All that’s left is to serve it with some pita bread and a big smile.

Four Alternatives to Using ActiveRecord Callbacks and Observers

Callbacks suck, and many experienced Rails programmers will tell you so. It took me a couple of years to finally understand why, but I did eventually, and I have rarely used them since.

If we should avoid callbacks, what should we use instead?

First, let’s look at a naive scenario. You have probably seen a similar example before.

class User < ActiveRecord::Base
  after_create :send_welcome_email

private

  def send_welcome_email
    UserMailer.welcome_email(self).deliver_later
  end
end

A user registers and he or she is sent a welcome e-mail. We know that in most cases this is a bad1 idea. So how can we make this better?

1. Send the e-mail from the controller

The first and most obvious way is to send the e-mail from the controller.

class UsersController < ApplicatinController::Base
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.welcome_email(@user).deliver_later
      redirect_to @user
    else
      render :new
    end
  end
end

This is better because our code is explicit and has no unexpected side effects. Calling @user.save saves the user instance, and that’s it. Instead, the e-mail is sent explicitly.

In many cases this is fine, but there is a downside, and that is our code is not reusable. We might have to send an e-mail after user registration in other places (for example, from an admin panel or through an API call), and hardcoding this logic in the controller means we have to repeat ourselves.

2. Use a controller concern

Concerns are just Ruby modules we can use to share common functionality between different models and controllers. We can extract the bit of code we want to reuse and encapsulate it in a method in our module.

module UserRegistration
  extend ActiveSupport::Concern

  def save_user_and_send_weclome_email(user, mailer: UserMailer)
    if user.save?
      mailer.welcome_email(user).deliver_later
      user
    else
      false
    end
  end
end

It’s not necessary to pass the user as an argument; methods in concerns have access to the state of the objects they’re mixed into. But I prefer to be more explicit than rely on state. Passing the user as an argument defines an explicit contract between the caller and the callee.

Here’s how we might use it.

class UsersController < ApplicatinController::Base
  include UserRegistration

  def create
    @user = User.new(user_params)
    if save_user_and_deliver_email(@user)
      redirect_to @user
    else
      render :new
    end
  end
end

Concerns give us code reuse, but at the cost of increased abstraction. For example, you can’t immediately see what save_user_and_deliver_email does, and to find out you must open another file. It is also not immediately obvious where else this method is being used.

3. Use a service object

Service objects encapsulate bits of reusable functionality in their own class. Because they’re not modules, they don’t bloat other classes or suffer from the same issues that concerns do. They are easy to reason about, and a joy to test.

# app/services/create_user.rb
class CreateUser
  attr_reader :user, :mailer

  def initialize(user, mailer: UserMailer)
    @user = user
    @mailer = mailer
  end

  def call
    if user.save
      mailer.welcome_email(user).deliver_later
      user
    else
      false
    end
  end
end

We can reuse this service object from anywhere–the console, a rake task, or another controller–free from side effects.

class UsersController < ApplicatinController::Base
  def create
    @user = User.new(user_params)
    service = CreateUser.new(@user)
    if service.call
      redirect_to @user
    else
      render :new
    end
  end
end

I find service objects are easy to extend. Let’s say that, besides sending an e-mail, we need to create an activity item for a feed. This is an easy change to make.

class CreateUser
  # ...
  def call
    if user.save
      mailer.welcome_email(user).deliver_later
      UserActivityJob.perform_later(user)
    end
  end
end

4. Use a model method

If you don’t find the benefits of using concerns and service objects compelling, you can replace callbacks with vanilla methods in your model.

class User < ActiveRecord::Base
  def save_and_deliver_email(mailer: UserMailer)
    if save
      mailer.welcome_email(self).deliver_later
      self
    else
      false
    end
  end
end
class UsersController < ApplicatinController::Base
  def create
    @user = User.new(user_params)
    if @user.save_and_send_welcome_email
      redirect_to @user
    # ...
  end
end

I’m not a fan of this approach, even if it’s an improvement on callbacks. In my opinion, an ActiveRecord model should be responsible for its persistence and internal business logic only. It should not concern itself with sending e-mails.

When do I use callbacks?

I use callbacks when dealing with the internal state of the object.

class Invitation < ActiveRecord::Base
  before_create :set_token, :downcase_email

private

  def downcase_email
    email.downcase!
  end

  def set_token
    # A database uniqueness constraint prevents a clash
    self.token = SecureRandom.urlsafe_base64
  end
 end

But I look for ways to avoid them. For example, it’s better to use a setter method to downcase the email2.

class User < ActiveRecord::Base
  before_create :set_token

  def email=(value)
    super(value.downcase)
  end

  # ...
end

Callbacks are not intrinsically bad, and they have their uses. But they give you a lot of rope to tie yourself with. They are attractive because they make certain tasks look and feel deceptively easy. It’s always worth asking yourself if there’s a better way.

  1. Because it violates SRP. It tightly couples user creation with sending emails. It obfuscates the intention of your code. It leads to undesirable side effects. It makes your code deterministic. Etc…

  2. We can check for the presence of value, but we shouldn’t; value will be an empty string when a form is submitted with a blank email. If we get errors because of value being nil, it’s probably a bug in the application, and we want it to fail.

Project Euler Problem 6: Sum of Square Difference

Let’s solve problem six in Project Euler in a programming language of your choice. Yes, that’s correct, you can choose!

Spoiler alert

If you have an academic interest in Project Euler, and you have not solved problem six, I suggest you do so before reading this post lest you deprive yourself of an opportunity to learn.

The problem

This is one of the easiest Project Euler problems.

Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.

Up to it is trivial, but we can use math to solve it up to massive numbers, like a thousand quinquagintaquadringentillion (that’s , or 1 followed by 2703 zeros), that could otherwise take years to solve even for a computer.

Linear solution

Sometimes the most obvious solution isn’t necessarily the best one.

This was my first try in Ruby.

(1..100).reduce(:+)**2 - (1..100).map { |n| n**2 }.reduce(:+)

For small numbers, this works well. But what if, instead of 100, you had to solve for a thousand quinquagintaquadringentillion? No one wants to create arrays with that many elements or loops with that many iterations.

A better solution

With the help of elementary math–specifically, mathematical induction–we can solve this problem up to any number.

This is the square of the sum up to .

And the sum of squares up to .

And the solution up to .

Using these formulas, we can solve this problem up to nonsensical numbers in microseconds. Here’s the solution in Ruby; it’s not terribly interesting.

(n * (n + 1) / 2)**2 - (n * (n + 1)) * ((n * 2) + 1) / 6

I was curious, so I created benchmarks to illustrate the efficiency of four different techniques: Using map and reduce, enumerators, while loop, and induction. I tested the earlier three against , and the latter against a thousand quinquagintaquadringentillion, or .

def map_reduce(n)
  (1..n).reduce(:+)**2 - (1..n).map { |n| n**2 }.reduce(:+)
end

def enum(n)
  sum_of_squares = square_of_sum = 0
  n.downto 1 do |i|
    square_of_sum += i; sum_of_squares += i * i
  end
  square_of_sum**2 - sum_of_squares
end

def while_loop(n)
  sum_of_squares = square_of_sum = 0
  while n > 0
    square_of_sum += n; sum_of_squares += n * n; n -= 1
  end
  square_of_sum**2 - sum_of_squares
end
Benchmarking with 10000000 iterations
       user     system      total        real
MapReduce  2.890000   0.030000   2.920000 (  2.928501)
Enum       1.610000   0.010000   1.620000 (  1.611081)
Loop       1.200000   0.000000   1.200000 (  1.201676)

Benchmarking with 10^2703 iterations
Induction  0.000000   0.000000   0.000000 (  0.000010)

It’s hardly surprising, but map/reduce, enumerator, and while loop took 2.9, 1.6, and 1.2 seconds respectively. I imagine while is faster because it deals only with primitive types and no data structures.

Using our equation took 10 microseconds for a ridiculously larger number, which is 10 millionth of a second. I ran these on my first gen MacBook Pro Retina.

Playing around with Swift and JavaScript

Again, no surprises here, but I was curious.

function diff(n) {
  var sumOfSquares = squareOfSum = 0;

  while(n) {
    sumOfSquares += n * n, squareOfSum += n, n--;
  }

  squareOfSum *= squareOfSum;
  return squareOfSum - sumOfSquares;
}

On my computer, using map and reduce fills up the stack and throws an exception at , while using a loop solves up to in circa 3.5 seconds.

Swift didn’t fair much better.

func diff(n: Int) -> Int {
    let array = [Int](1...n)

    let sumOfSquares = array.map({ $0 * $0 }).reduce(0, +)
    let sum = array.reduce(0, +)
    let squareOfSum = sum * sum

    return squareOfSum - sumOfSquares
}

You can find the sample code including benchmarks on Github.

Adding a Dynamic Maintenance Mode to Your Rails App

In an ideal world, you could update your application with zero downtime, and your users would be rewarded with snazzy new features without noticing how they got there.

Of course, this is not always the case. There are times when downtime is inevitable, and having a proper and scalable maintenance strategy to deal with downtime is necessary, especially when making changes to the database.

The following post describes a strategy we recently used. We had two requirements.

  1. Allow some users to access the application while it’s undergoing maintenance.
  2. Serve a custom, internationalized template.

There are several ways to implement a maintenance strategy, but they typically work by bypassing requests to the application server and serving a static HTML page. Heroku has a built-in maintenance feature that works in a similar way. But it meets none of our requirements because it bypasses the app entirely.

In our case, requests must hit the application so that it can determine whether to allow access and which language to serve to the user.

We implemented this easily using ENV variables, a controller action, and Rails’ built in I18n support. Here’s how we did it.

Remembering the user’s location

For better usability, we should remember the user’s intended location before redirecting her away. This way, when the app exits maintenance, we can redirect users back to the page they originally wanted.

This is sometimes referred to as friendly forwarding. Friendly forwarding is a feature we can reuse in other places, so it’s a good idea to implement it in a concern1 and mix it into application_controller.rb.

# app/controllers/concerns/friendly_forwarding.rb
def redirect_back_or(default, options = {})
  location = session.delete(:forwarding_url) || default
  redirect_to location, options
end

def store_location
  session[:forwarding_url] = request.url if request.get?
end

When called, store_location saves the request URL in the session, but only if it’s a GET request 2. The redirect_back_or method takes a default route to use if no forwarding URL is present in the session, and an options hash. This way we can forward the same options that redirect_to accepts, including flash messages.

Maintenance mode

To implement maintenance mode, we use a concern that we mix into application_controller.rb.

module MaintenanceMode
  extend ActiveSupport::Concern

  included do
    before_action :handle_maintenance
  end

private

  def handle_maintenance
    if maintenance_mode_enabled?
      unless remote_address_whitelisted?
        store_location
        redirect_to maintenance_path
      end
    end
  end

  def maintenance_mode_enabled?
    ENV['MAINTENANCE_MODE'].present?
  end

  def maintenance_mode_disabled?
    !maintenance_mode_enabled?
  end

  def remote_address_whitelisted?
    maintainer_ips.split(',').include?(request.remote_ip)
  end

  def maintainer_ips
    ENV['MAINTAINER_IPS'] || String.new
  end
end

There’s not much that requires explanation here. When the mode is enabled, the MaintenanceMode concern redirects to the maintenance page unless the current IP address is whitelisted.

class ApplicationController < ActionController::Base
  include FriendlyForwarding
  include MaintenanceMode
end

All that remains is to add a controller that renders the maintenance page, a route, and some tests.

# config/routes.rb
get :maintenance, to: 'maintenance#show'
class MaintenanceController < ApplicationController
  skip_before_action :handle_maintenance

  def show
    render :show, status: 503
  end
end

The skip_before_action ensures that we don’t check for maintenance mode when we are viewing the maintenance page itself. This stops the application from going into an infinite loop.

Using render explicitly allows us to set the HTTP status code to 503, or :service_unavailable. The default status code in Rails is 200, or :success.

Redirecting the user back to their intended location

We can stop now, but we should make one more usability improvement. We should redirect away from the maintenance page and back to the app if the mode is disabled.

This is a small change, but it’s important–especially if your maintenance page has no navigation–because many users will refresh the page hoping the app will appear again. If we don’t redirect the user back, she could be stuck until frustrated enough to type the app’s root URL in the address bar. Plus, we made the effort to add friendly forwarding, and unless we make this change we will not have a chance to use it here.

When visiting maintenance_controller#show, we should redirect back to the application under two conditions:

  1. If maintenance mode is disabled.
  2. If the IP address is whitelisted.

We can use a before_action to add this extra functionality.

class MaintenanceController < ApplicationController
  # ...
  before_action :redirect_if_maintenance_disabled
  # ...

private

  def redirect_if_maintenance_disabled
    if maintenance_mode_disabled? || remote_address_whitelisted?
      redirect_back_or root_path
    end
  end
end

Using it

To enter maintenance mode, we set an ENV variable on Heroku.

heroku config:set MAINTENANCE_MODE=enabled

From the app’s perspective, it doesn’t really matter what the value of MAINTENANCE_MODE is (or its name, for that matter), so enabled serves for clarity. Our logic checks for the presence of the variable, not its value.

To allow access to an IP address, we set another ENV variable. Its value should be a comma-delimited list of IP addresses for whom we want to enable access.

heroku config:set MAINTAINER_IPS=1.2.3.4,9.8.7.6

And finally, to exit maintenance mode, we unset the variables.

heroku config:unset MAINTENANCE_MODE

Testing it

We want to test a number of conditions:

  1. The app redirects to the maintenance page when the mode is enabled.
  2. The app does not redirect to the maintenance page when the mode is disabled, or if the current IP is whitelisted.
  3. The app redirects away from the maintenance page if the mode is disabled, or if the current IP is whitelisted.
  4. The app redirects back to the user’s intended location once maintenance mode is disabled.
require 'test_helper'

class MaintenanceModeTest < ActionDispatch::IntegrationTest
  teardown do
    ENV.delete('MAINTENANCE_MODE')
    ENV.delete('MAINTAINER_IPS')
  end

  test "does not redirect to maintenance page if mode is disabled" do
    get login_path
    assert_response :success
  end

  test "redirects to maintenance page if mode is enabled" do
    ENV['MAINTENANCE_MODE'] = 'enabled'
    get login_path
    assert_redirected_to maintenance_path
    follow_redirect!
    assert_response :service_unavailable
  end

  test "does not redirect to maintenance page if mode is enabled and IP is whitelisted" do
    ENV['MAINTENANCE_MODE'] = 'enabled'
    ENV['MAINTAINER_IPS'] = '1.2.3.4'
    get login_path, {}, { 'REMOTE_ADDR' => '1.2.3.4' }
    assert_response :success
  end

  test "redirects away from maintenance page when mode is disabled" do
    get maintenance_path
    assert_redirected_to root_path
    follow_redirect!
    assert_response :success
  end

  test "redirects away from maintenance page when mode is enabled and IP is whitelisted" do
    ENV['MAINTENANCE_MODE'] = 'enabled'
    ENV['MAINTAINER_IPS'] = '1.2.3.4'
    get maintenance_path, {}, { 'REMOTE_ADDR' => '1.2.3.4' }
    assert_redirected_to root_path
  end

  test "redirects back to requested page when mode is disabled" do
    ENV['MAINTENANCE_MODE'] = 'enabled'
    get login_path
    assert_redirected_to maintenance_path
    ENV['MAINTENANCE_MODE'] = nil
    get maintenance_path
    assert_redirected_to login_path
  end
end

And there it is, a flexible and dynamic maintenance mode for your Rails application.

Caveats

This feature is useful when you need to restrict access but keep your app running. Otherwise, you might want to implement a maintenance page at the DNS or web server level, or use Heroku’s built in solution.

Footnotes

  1. For example, when redirecting to the login page after a user requests a protected resource.

  2. There actually is an HTTP specification for redirecting POST requests. But it appears that most frameworks don’t handle this requirement adequately, so we generally don’t want to redirect back if the user was posting a form. For more on this topic, see this article on Programmers.