Thursday, March 22, 2007

Rails: ActiveRecord Serialize method

On my current project we have a model with a few attributes that are instances. Generally, this is handled with a relationship (e.g. belongs_to). However, these attributes are not ActiveRecord::Base subclass instances. ActiveRecord handles this situation by providing the ActiveRecord::Base.serialize class method.

As a contrived example, imagine a UserAccount class that has a AuthorizationConfirmation instance as an attribute.
class CreateModels < ActiveRecord::Migration
def self.up
create_table :user_accounts do |t|
t.column :authorization_confirmation, :string
end
end

def self.down
drop_table :user_accounts
end
end

class UserAccount < ActiveRecord::Base
serialize :authorization_confirmation, AuthorizationConfirmation
end

class AuthorizationConfirmation
attr_accessor :fingerprint, :key
end
As you can see in the above example, the UserAccount class has the attribute authorization_confirmation. The value stored in authorization_confirmation is expected to be an instance of the AuthorizationConfirmation class.

A simple test proves the expected behavior.
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")

class UserAccountTest < Test::Unit::TestCase
def test_authorization_confirmation_is_serialized_and_deserialized
account = UserAccount.new
account.authorization_confirmation = AuthorizationConfirmation.new
account.authorization_confirmation.fingerprint = "Xc1sseE"
account.authorization_confirmation.key = "gEteEQ"
account.save
retrieved = UserAccount.find account.id
assert_equal "Xc1sseE", retrieved.authorization_confirmation.fingerprint
assert_equal "gEteEQ", retrieved.authorization_confirmation.key
end
end
The test proves that the instance of AuthorizationConfirmation is saved and retrieved as expected.

A quick peek in the database shows the instance as yaml.
 id | authorization_confirmation                           
----+------+-------------------------------------------------------------------------------
6 | --- !ruby/object:AuthorizationConfirmation
fingerprint: Xc1sseE
key: gEteEQ
NOTE: At the time of this writing any attribute that is serialized cannot be nil. Ticket 7293 should resolve this problem.

3 comments:

  1. Michael Schuerig5:58 PM

    Wouldn't it be more useful to use an aggregate (composed_of) for your purpose? Serialized attributes can't be used in conditions, aggregates can.

    ReplyDelete
  2. Michael,
    Until this week I had never seen composed_of or serialize so I really didn't know which option would be better. The composed_of method is much better documented; however, the serialize method seemed better suited for my specific case based on it's simple implementation. Part of this decision was based on the fact that my aggregate objects are reference data provided by an external service and I never need to query based on the values.

    Thanks for the tip, I may switch to composed_of in the future just to see how it works out.

    ReplyDelete
  3. Anonymous6:15 PM

    YMMV but I find serialization a major slow-down. One of my biggest methods had a 50% slowdown just due to one serialization. YAML is probably the culprit: slow as sh*t.

    ReplyDelete

Note: Only a member of this blog may post a comment.