Quacking like an ActiveRecord in Rails 2.3.4

Mike Burns

You know everything about pretending to be an ActiveRecord::Base object, and especially the bit about ActiveRecord::Errors. That part’s really cool.

What we are doing here is making an object that quacks like an ActiveRecord::Base object but has no database table backing it. As examples: a search object, a credit card object, or a remote user object. To do this, you need id, new_record?, and the attributes must work in a specific fashion. You can also have errors produce an ActiveRecord::Errors instance so user errors will show in forms and whatnot.

However you may not have noticed that Rails 2.3.4 broke API compatibility for ActiveRecord::Errors. If your ActiveRecord::Base-like class provides an #errors instance method, it must now provide these class methods:

  • self_and_descendants_from_active_record, which produces an array of classes.
  • human_name, which takes an optional hash and produces a string.
  • human_attribute_name, which takes a string and optional hash and produces a string.

The last two of these methods are useful in your day-to-day Rails knowledge (the first is an internal, undocumented method used by Rails to produce the class itself and its parents, up to and excluding ActiveRecord::Base).

human_name is used to produce a humanized string representing the class/table name. If your class is named AuthenticationRecord, .human_name will produce "Authenticationrecord". Any options you pass to this are normally sent to I18n.translate.

human_attribute_name is used to map an attribute to a human-readable string. For example, the attribute created_at is mapped to "Created at". Any options passed to this are normally sent to I18n.translate.

Here are some tests that need to pass now:

class SearchTest < ActiveSupport::TestCase
  should "conform to the ActiveRecord::Errors interface" do
    assert_respond_to Search, :self_and_descendants_from_active_record
    assert_respond_to Search, :human_name
    assert_respond_to Search, :human_attribute_name

    assert_equal [Search], Search.self_and_descendants_from_active_record
    assert_equal "Search", Search.human_name
    assert_equal "Published at", Search.human_attribute_name('published_at')
  end
end
author image
Mike Burns

Pair with one of our expert developers to level up your skills with Coaching by thoughtbot. Save time learning best practices and techniques for reducing technical debt in Ember, Ruby, Haskell, and Go in 1-on-1 sessions tailored to your goals.