Master Sword
<%= 'Cause it's late and your mama don't know %>
Sword

Customize Named Model Attributes with ActiveModel::Serializers

If you’ve used ActiveModel::Serializers to serialize your Ruby objects into JSON (presumably in a Rails application), you’re familiar with the tools that Yehuda Katz’ brainchild affords us. When using Rails to build JSON APIs for front-end JavaScript applications (or general consumption), we can rely on ActiveModel::Serializers for serializing not only database-backed model attributes, but also custom attributes and associated records.

class GiraffeSerializer < ActiveModel::Serializer
  attributes :name, :age, :name_and_age, :height, :children

  def name_and_age
    "#{name} - #{age} year(s) old"
  end
end

In most applications, these features will suffice. However, when working on an AngularJS front-end recently, we sought to serialize a boolean model attribute as a string. The “why” is somewhat irrelevant, but it had something to do with a quirk in binding model attributes to form inputs in a directive. In any case, the task seemed simple. Perhaps something like the following would work.

class UserSerializer < ActiveModel::Serializer
  attributes :boolean_attribute

  def boolean_attribute
    boolean_attribute.to_s
  end
end

As you can probably see already, this isn’t a solution. The nasty feeling we permit when writing this code is accompanied by an ugly stack trace error. Onto the next one.

Luckily, “the next one” just so happens to be an ideal solution. If you seek to modify a model attribute (while retaining its name in serialization), you can do so by overriding the attributes method. (This is, in fact, documented.)

class UserSerializer < ActiveModel::Serializer
  attributes :email, :first_name, :last_name

  def attributes
    hash = super
    hash["boolean_attribute"] = object.boolean_attribute.to_s
    hash
  end
end

Notice that we leave boolean_attribute from the inital attributes call. This is because we’ve specified it explicitly in our custom attributes method. And with this, we’ve serialized our boolean attribute as a string without altering its name. (Note that object refers to the object being serialized.)

If you haven’t had the chance to dig into ActiveModel::Serializers yet, I recommend that you give it a go. This whole “JSON API in Rails” thing is pretty fun.