RubyGreenBlue

Enumerable Constants for Active Record Models

Posted by keith about 1 year ago

A problem that had often bothered me in the past is this:

You're modelling a concept in a database like, say, property listings and a property has an attribute of "type". The possible values of "type" are not arbitrary. They are some fixed set of values like "rental", "for sale" and "auction", and the software needs to do stuff depending on the value of "type".

So this is like a :belongs_to where a property belongs to a suburb say. But there is an important difference. With a belongs_to :suburb the values for suburb are arbitrary. That is to say that the software doesn't care about the values, only that there is some value (which the user cares about, of course). But the program does care about the set of values available for the "type" attribute.

Setting up a has_many/belongs_to relationship always seemed overkill but it gave me a nice list of available attributes and I could enforce the relationship. The other problem was that the data in the table would mirror some set of constants in the code. If you changed one, you had to make sure you changed the other or things got out of whack. Not to mention problems of populating a new database and making sure all such "non-arbitrary" value set tables had the correct values (including correct ids to match the constants in code) in them.

Making the attribute "type" a string and stuffing values "rental", "for sale" and "auction" in it felt particularly bad.

Solution

I felt the correct way to do it was to use integer values in the field and represent these values as constants in the code (which is where the values have meaning).

But writing validation for these and coming up with a list of human readable descriptions (for drop-downs) was tedious. It felt like the values belonged in the database... but wait, no, the values have meaning in the code...

So I've written a Rails plugin to take away the tediousness of it all and make it a lot more DRY. With it I get

  • an easy way to define, in a nice DSL looking way, what the constants are
  • validation code generated dynamically
  • a nice way of getting the list of values and descriptions (useful for UI stuff)

Examples

Model definition:

class Property < ActiveRecord::Base
  enumerable_constant :type, :constants => [:rental, :for_sale, :auction]
end

Some view code:

<p><label for="property_type">Property Type</label><br/>
< %= collection_select(:property, :type, Property::Type.constants, :id, :title, {:prompt => 'Select a Property Type'})  % ><br />

Note the use of Property::Type.constants in place of where you would normally use a collection object like @suburbs

Some code that tests the value

property = Property.find :first
if property.type == Property::Type::FOR_SALE
  # do some stuff
end

Other things you can access

>> Property::Type.constants.first.name
=> "RENTAL"
>> Property::Type.constants.first.title
=> "Rental"
>> Property::Type.constants.first.value
=> 1

Download the Enumerable Constants project