Making #composed_of more useful

November 16, 2006 code 3 min read

Update: my patch finally got added to edge rails.

Active Record allows you to abstract fields into an aggregate object by using the composed\_of declaration. This is handy, but the current implementation can be a real pain.

The first and somewhat trivial issue is the composed_of declaration builds a string and evals it to define the attribute accessors. It’s not a big deal, but it’s dirty. The second issue is that aggregate objects are not easy to manipulate in Rails, especially in forms.

Fixing it

So, I’ve written a plugin that overrides the Active Record implementation of composed\_of, which allows you to specify a block to convert incoming parameters to the correct type.

Personally, this has been most helpful when using the Money gem:

class Account < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(cents cents) do |amount|

If \#balance= receives anything besides a Money object, it will call the block to try to convert the parameter to a Money object.

>> account = :balance => 100
>> account.balance
=> #&lt;Money:0x2612770 @cents=10000, @currency="USD"&gt;

And now it can transparently be used in forms:

  <%= text_field :account, :balance %>

This can even be used for more advanced aggregations:

class User < ActiveRecord::Base
  composed_of :address, :class_name => "Address"
		:mapping => [%w(street street), %w(city city), %w(state state), %w(zip zip)] do |addr|[:street], addr[:city], addr[:state], addr[:zip])

A user can now be created from a hash: => {:street => "123 A Street", :city => "Somewhere", :state => "NO", :zip => 12345})

I didn’t quite realize all the implications of this extension until I was writing up the docs for this plugin. Active Record magically does type casting for a limited set of types, namely dates and numbers. But this essentially allows you to have a form of type casting for any attribute. Interesting…


This has also been submitted as a patch to the Rails trac but hasn’t been accepted yet.

  script/plugin install
This content is open source. Suggest Improvements.


avatar of Brandon Keepers I am Brandon Keepers, and I work at GitHub on making Open Source more approachable, effective, and ubiquitous. I tend to think like an engineer, work like an artist, dream like an astronaut, love like a human, and sleep like a baby.