Overriding ActiveRecord's attribute accessors

I sometimes create database columns with the intent of having comma-separated values as their contents; a common occurrence is for email recipients. Having a comma-separated list of recipients in my interface, however, isn't really the way I'd like to display the column's contents. When I'm working with multiple values in my views, I'd much rather have an array. By overriding ActiveRecord's default accessor for the column, we can convert the string into something more flexible on the interface side.

This tip is mentioned on ActiveRecord's documentation page, but then again, there's a lot of stuff in there that rarely gets dug out. Take the following SQL, for example:

+----+----------+--------------------------------------------+
| id | subject  | recipients                                 |
+----+----------+--------------------------------------------+
| 1  | Hello!   | yourname@hotmail.com,yourfriend@gmail.com  |
| 2  | Hi there | me@yahoo.com,you@msn.com,him@somewhere.com |
+----+----------+--------------------------------------------+

Our recipients column looks good, but we'd really rather have it in a more Ruby-like form when we access its value. We're sort of serializiing (or marshalling) the data to the database, and we'd like to deserialize (unmarshal) the data when we access it in the view or otherwise.

class Configuration < ActiveRecord::Base
  def recipients
    read_attribute(:recipients).split(/\,\s*/)
    # OR …
    self[:recipients].split(/,\s*/)
  end
end

Our recipients column will now return an array of recipients that we can alter as we'd like in our view without having to go through the trouble of splitting the string ourselves.

If we would like to take an array of items from our view and convert it back into its comma-separated form when writing back to the database, we can use the following code:

class Configuration < ActiveRecord::Base
  def recipients=(value)
    write_attribute(:recipients, value.join(','))
    # OR …
    self[:recipients] = value.join(',')
  end
end

Leave a Reply