tag:kinderman.net,2005:/tag/activerecordkinderman.net : Tag activerecord, everything about activerecordcode code code2010-07-22T14:05:30-07:00Typotag:kinderman.net,2005:Article/1072008-01-03T15:11:17-08:002010-07-22T14:05:30-07:00ryanPlugin to Support composed_of Aggregations in ActiveRecord Finder Methods<p>In Rails, hooking up an ActiveRecord model to use a value object to aggregate over a set of database fields is a <a href="http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html">piece of cake</a>. With the accessor methods that are created for a <code>composed_of</code> association, you can now deal exclusively with the <code>composed_of</code> 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.</p>
<p>Consider the following ActiveRecord model definition:</p>
<pre><code>class Customer < ActiveRecord::Base
composed_of :balance, :class_name => "Money", :mapping => %w(balance_amount amount)
end
</code></pre>
<p>Given such a model, we can do something like this with no problem:</p>
<pre><code>customer = Customer.new
customer.balance = Money.new(512.08)
customer.balance # returns #<Money:abc @amount=512.08>
customer.save!
</code></pre>
<p>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:</p>
<pre><code>customer = Customer.find(:all, :conditions => { :balance => Money.new(512.08) })
</code></pre>
<p>or like:</p>
<pre><code>customer = Customer.find_all_by_balance( Money.new(512) )
</code></pre>
<p>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:</p>
<pre><code>customer = Customer.find(:all, :conditions => { :balance_amount => 512.08 })
</code></pre>
<p>To deal with this problem, I've submitted <a href="http://dev.rubyonrails.org/ticket/10572">a ticket</a>, which is currently scheduled to be available in Rails 2.1.<br/>
</p>
<p>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:</p>
<p><code>script/plugin install git://github.com/ryankinderman/find_conditions_with_aggregation.git</code></p>
<p><em>Addendum</em>: The patch has been committed to changeset <a href="http://dev.rubyonrails.org/changeset/8671">8671</a>. Yay!</p>