Serialized Attributes in Rails

Over the last couple days I've been playing around with the Marshal library in Ruby for the serialization of objects. The idea came to me when I was snooping around Mint's database structure. It stores application settings and certain statistics in serialized format. I experimented for a while in pure Ruby, and after having reasonably success, moved to Rails.

Now for some great advice, always search first! I ended up implementing my Ruby Marshal technique in Rails, only to later find out about Rails's serialize() method. While it uses YAML in place of Marshal to serialize the data, it was a perfect solution.

# serialize(attr_name, class_name = Object)

If you have an attribute that needs to be saved to the database as an object,
and retrieved as the same object, then specify the name of that attribute
using this method and it will be handled automatically. The serialization is
done through YAML. If class_name is specified, the serialized object must be
of that class on retrieval or SerializationTypeMismatch will be raised.


# Parameters

attr_name - The field name that should be serialized.
class_name - Optional, class name that the object type should be equal to.


# Example

class User
  serialize :preferences
end

This worked great, except for one part. It was difficult to write forms to create/update the serialized attributes, since the application had no idea what fields I wanted to store. So after a bit of playing, I came up with some getter/setter methods.

class Article < Content
  serialize :data

  def title
    data[:title]
  end

  def title=(value)
    self.data[:title] = value
  end
end

That solved the form.text_field :title problem, but it still didn't work because when I created a new Article, there was no such thing as data[:title]. The solution was to initialize the :data attribute when creating the object. Here's an example:

class ArticlesController < ApplicationController::Base
  def new
    @article = Article.new(:data => { :title => nil })
  end

  def create
    @article = Article.new(:data => { :title => params[:article][:title] })
    # Code to save record...
  end
end

That's it. It's not the cleanest solution I think, but it did the job just fine for the application I was writing.

Filed in Programming.