Plugin to Support composed_of Aggregations in ActiveRecord Finder Methods

Thu, 03 Jan 2008 15:11:00 -0800
rails, activerecord, composed_of, plugin

In Rails, hooking up an ActiveRecord model to use a value object to aggregate over a set of database fields is a piece of cake. With the accessor methods that are created for a composed_of association, you can now deal exclusively with the composed_of field on your model, instead of directly manipulating or querying the individual database fields that it aggregates. Or can you? As long as all you're doing with the aggregate field is getting and setting its value, your aggregated database fields remain encapsulated. However, if you want to retrieve instances of your model from the database through a call to a finder method, you must do so on the individual database fields.

Consider the following ActiveRecord model definition:

class Customer < ActiveRecord::Base
  composed_of :balance, :class_name => "Money", :mapping => %w(balance_amount amount)
end

Given such a model, we can do something like this with no problem:

customer = Customer.new
customer.balance = Money.new(512.08)
customer.balance                      # returns #<Money:abc @amount=512.08>
customer.save!

However, now that we've saved the record, we might want to get that record back from the database at some point with code that looks something like:

customer = Customer.find(:all, :conditions => { :balance => Money.new(512.08) })

or like:

customer = Customer.find_all_by_balance( Money.new(512) )

This would provide full encapsulation of the aggregated database fields for the purposes of both record creation and retrieval. The problem is, at the time of my posting this article, it doesn't work. Instead, you have to do this:

customer = Customer.find(:all, :conditions => { :balance_amount => 512.08 })

To deal with this problem, I've submitted a ticket, which is currently scheduled to be available in Rails 2.1.

If you need this functionality, but your project is using a pre-2.1 release of Rails, I've also created a plugin version of the changes I submitted in the aforementioned ticket. To install:

script/plugin install git://github.com/ryankinderman/find_conditions_with_aggregation.git

Addendum: The patch has been committed to changeset 8671. Yay!