<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>PaulBarry.com - Using ActiveRecord Composed Of</title>
  <subtitle type="html">My thoughts, ideas, questions and concerns on technology, sports, music and life</subtitle>
  <id>tag:paulbarry.com,2007:Paulbarry.com</id>
  <generator uri="http://www.paulbarry.com" version="3.0">PaulBarry.com</generator>
  <link href="http://paulbarry.com/xml/atom/article/4890/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="http://paulbarry.com/articles/2008/04/19/using-activerecord-composed-of" rel="alternate" type="text/html"/>

  <updated>2008-11-06T18:07:11-05:00</updated>
  <entry>
    <author>
      <name>Paul Barry</name>
      <email>mail@paulbarry.com</email>
    </author>
    <id>urn:uuid:acc0d44e-580d-4fb4-819c-341e2b02064a</id>

    <published>2008-04-19T13:02:13-04:00</published>
    <updated>2008-04-19T13:02:13-04:00</updated>
    <title type="html">Using ActiveRecord Composed Of</title>
    <link href="http://paulbarry.com/articles/2008/04/19/using-activerecord-composed-of" rel="alternate" type="text/html"/>

    <category term="technology" scheme="http://paulbarry.com/articles/category/technology" label="Technology"/>
        <category term="Rails" scheme="http://paulbarry.com/articles/tag/rails"/>
    <category term="ComposedOf" scheme="http://paulbarry.com/articles/tag/composedof"/>
    <category term="Ruby" scheme="http://paulbarry.com/articles/tag/ruby"/>
    <category term="ValueObject" scheme="http://paulbarry.com/articles/tag/valueobject"/>
        <summary type="html">&lt;p&gt;ActiveRecord has a feature called &lt;code&gt;composed_of&lt;/code&gt; that allows you to take multiple fields of a model and treat them as one object.  Let&apos;s say you have a requirement for your application to track the phone number of each user.  Also, you need to be able to query for users by parts of the phone number like area code, prefix, line number and extenstion.  So the phone number (212) 123-4567 x55 needs to be stored in separate fields as &lt;code&gt;:area_code =&amp;gt; 212&lt;/code&gt;, &lt;code&gt;:prefix =&amp;gt; 123&lt;/code&gt;, &lt;code&gt;:line_number =&amp;gt; 4567&lt;/code&gt; and &lt;code&gt;:extenstion =&amp;gt; 55&lt;/code&gt;.  So first we create a rails app with a user record with those fields:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails myapp
$ cd myapp
$ script/generate model user name:string \
  phone_number_area_code:integer \
  phone_number_prefix:integer \
  phone_number_line_number:integer \
  phone_number_extension:integer
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What we would like to do is treat all of those &lt;code&gt;phone_number_*&lt;/code&gt; columns as one object.  So first let&apos;s create a base object that has some of the plumbing functionality.  Put this in &lt;code&gt;app/models/value_object.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class ValueObject

  class &amp;lt;&amp;lt; self
    def has_fields(*args)
      (class &amp;lt;&amp;lt; self; self; end).send(:define_method, :fields) do
        args.map(&amp;amp;:to_s)
      end
      fields.each{|f| attr_reader f}
    end
    def mapping
      fields.map{|e| [&quot;#{name.underscore}_#{e}&quot;, e]}      
    end
    def mapper
      lambda do |params|
        PhoneNumber.new *PhoneNumber.fields.map{|e| params[e].to_i}
      end
    end
  end

  def initialize(*args)
    args.each_with_index do |a, i|
      instance_variable_set &quot;@#{self.class.fields[i]}&quot;, args[i]
    end
  end

  def to_s
    &quot;(#{area_code}) #{prefix}-#{line_number} x#{extension}&quot;
  end

  def ==(o)
    o &amp;amp;&amp;amp; self.class.fields.all?{|f| self.send(f) == o.send(f)}
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here&apos;s our subclass of that specific for the phone number.  Add the PhoneNumber class at &lt;code&gt;app/models/phone_number.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class PhoneNumber &amp;lt; ValueObject

  has_fields :area_code, :prefix, :line_number, :extension

  def to_s
    &quot;(#{area_code}) #{prefix}-#{line_number} x#{extension}&quot;
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rather than explain what this does, I&apos;ll just give you the specs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require File.dirname(__FILE__) + &apos;/../spec_helper&apos;

describe PhoneNumber do

  before(:each) do
    @phone_number = PhoneNumber.new(212, 123, 4567, 55)
  end  

  it &quot;should have an area_code getter&quot; do
    @phone_number.area_code.should == 212
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.prefix.should == 123
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.line_number.should == 4567
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.extension.should == 55
  end

  describe &quot;#==&quot; do
    it &quot;should be equal if both number match&quot; do
      @phone_number.==(PhoneNumber.new(212, 123, 4567, 55)).should be_true
    end
    it &quot;should not be equal if both numbers do not match&quot; do
      @phone_number.==(PhoneNumber.new(212, 123, 4567, 54)).should be_false
    end
  end

  describe &quot;#to_s&quot; do
    it &quot;should format the phone number as (212) 123-4567 x55&quot; do
      @phone_number.to_s.should == &quot;(212) 123-4567 x55&quot;
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After you&apos;ve had a chance to read through that, it should be pretty clear what the &lt;code&gt;PhoneNumber&lt;/code&gt; class does.  Next we modify the User model to use this class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base
  composed_of :phone_number, :mapping =&amp;gt; PhoneNumber.mapping, &amp;amp;PhoneNumber.mapper
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;ValueObject&lt;/code&gt; base class provides us with the methods &lt;code&gt;mapping&lt;/code&gt; and &lt;code&gt;mapper&lt;/code&gt;, which are needed to get our User model&apos;s phone_number method to do what we want.  Here&apos;s what we want it to do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;it &quot;should be able to set phone number from hash&quot; do
  @user.phone_number = {&quot;area_code&quot; =&amp;gt; &quot;212&quot;, &quot;prefix&quot; =&amp;gt; &quot;123&quot;, 
    &quot;line_number&quot; =&amp;gt; &quot;4567&quot;, &quot;extension&quot; =&amp;gt; &quot;55&quot;}
  @user.phone_number.should == PhoneNumber.new(212, 123, 4567, 55)
end

it &quot;should be able to find a user by area code&quot; do
  @user.phone_number = PhoneNumber.new(212, 123, 4567, 55)
  @user.save
  @user.should == User.find_by_phone_number_area_code(212)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can treat our phone number as a single object, but collect and store the values as separate fields.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;ActiveRecord has a feature called &lt;code&gt;composed_of&lt;/code&gt; that allows you to take multiple fields of a model and treat them as one object.  Let&apos;s say you have a requirement for your application to track the phone number of each user.  Also, you need to be able to query for users by parts of the phone number like area code, prefix, line number and extenstion.  So the phone number (212) 123-4567 x55 needs to be stored in separate fields as &lt;code&gt;:area_code =&amp;gt; 212&lt;/code&gt;, &lt;code&gt;:prefix =&amp;gt; 123&lt;/code&gt;, &lt;code&gt;:line_number =&amp;gt; 4567&lt;/code&gt; and &lt;code&gt;:extenstion =&amp;gt; 55&lt;/code&gt;.  So first we create a rails app with a user record with those fields:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails myapp
$ cd myapp
$ script/generate model user name:string \
  phone_number_area_code:integer \
  phone_number_prefix:integer \
  phone_number_line_number:integer \
  phone_number_extension:integer
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What we would like to do is treat all of those &lt;code&gt;phone_number_*&lt;/code&gt; columns as one object.  So first let&apos;s create a base object that has some of the plumbing functionality.  Put this in &lt;code&gt;app/models/value_object.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class ValueObject

  class &amp;lt;&amp;lt; self
    def has_fields(*args)
      (class &amp;lt;&amp;lt; self; self; end).send(:define_method, :fields) do
        args.map(&amp;amp;:to_s)
      end
      fields.each{|f| attr_reader f}
    end
    def mapping
      fields.map{|e| [&quot;#{name.underscore}_#{e}&quot;, e]}      
    end
    def mapper
      lambda do |params|
        PhoneNumber.new *PhoneNumber.fields.map{|e| params[e].to_i}
      end
    end
  end

  def initialize(*args)
    args.each_with_index do |a, i|
      instance_variable_set &quot;@#{self.class.fields[i]}&quot;, args[i]
    end
  end

  def to_s
    &quot;(#{area_code}) #{prefix}-#{line_number} x#{extension}&quot;
  end

  def ==(o)
    o &amp;amp;&amp;amp; self.class.fields.all?{|f| self.send(f) == o.send(f)}
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here&apos;s our subclass of that specific for the phone number.  Add the PhoneNumber class at &lt;code&gt;app/models/phone_number.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class PhoneNumber &amp;lt; ValueObject

  has_fields :area_code, :prefix, :line_number, :extension

  def to_s
    &quot;(#{area_code}) #{prefix}-#{line_number} x#{extension}&quot;
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rather than explain what this does, I&apos;ll just give you the specs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require File.dirname(__FILE__) + &apos;/../spec_helper&apos;

describe PhoneNumber do

  before(:each) do
    @phone_number = PhoneNumber.new(212, 123, 4567, 55)
  end  

  it &quot;should have an area_code getter&quot; do
    @phone_number.area_code.should == 212
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.prefix.should == 123
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.line_number.should == 4567
  end

  it &quot;should have an area_code getter&quot; do
    @phone_number.extension.should == 55
  end

  describe &quot;#==&quot; do
    it &quot;should be equal if both number match&quot; do
      @phone_number.==(PhoneNumber.new(212, 123, 4567, 55)).should be_true
    end
    it &quot;should not be equal if both numbers do not match&quot; do
      @phone_number.==(PhoneNumber.new(212, 123, 4567, 54)).should be_false
    end
  end

  describe &quot;#to_s&quot; do
    it &quot;should format the phone number as (212) 123-4567 x55&quot; do
      @phone_number.to_s.should == &quot;(212) 123-4567 x55&quot;
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After you&apos;ve had a chance to read through that, it should be pretty clear what the &lt;code&gt;PhoneNumber&lt;/code&gt; class does.  Next we modify the User model to use this class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class User &amp;lt; ActiveRecord::Base
  composed_of :phone_number, :mapping =&amp;gt; PhoneNumber.mapping, &amp;amp;PhoneNumber.mapper
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;ValueObject&lt;/code&gt; base class provides us with the methods &lt;code&gt;mapping&lt;/code&gt; and &lt;code&gt;mapper&lt;/code&gt;, which are needed to get our User model&apos;s phone_number method to do what we want.  Here&apos;s what we want it to do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;it &quot;should be able to set phone number from hash&quot; do
  @user.phone_number = {&quot;area_code&quot; =&amp;gt; &quot;212&quot;, &quot;prefix&quot; =&amp;gt; &quot;123&quot;, 
    &quot;line_number&quot; =&amp;gt; &quot;4567&quot;, &quot;extension&quot; =&amp;gt; &quot;55&quot;}
  @user.phone_number.should == PhoneNumber.new(212, 123, 4567, 55)
end

it &quot;should be able to find a user by area code&quot; do
  @user.phone_number = PhoneNumber.new(212, 123, 4567, 55)
  @user.save
  @user.should == User.find_by_phone_number_area_code(212)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can treat our phone number as a single object, but collect and store the values as separate fields.&lt;/p&gt;
</content>
  </entry>
  </feed>