My first little gem has_attributes_from for merging attributes from one ActiveRecord Class to another individual STI subclass.
So why, do we want to do that, you might ask? Well, I was working on a rails project where clients take care of the administration (planning and billing) for child daycare centres. In this project I have all kinds of people objects, like a child, a contactperson, a father, mother, caretaker etc. etc. So, the ideal casus for a Single Table Inheritance implementation. So I implemented a ‘classic’ Person Class as follows:
create_table :people, :force => true do |t|
t.string :firstname
t.string :lastname
t.string :initials
t.string :type
t.string :social_security_number
t.string :gender
t.datetime :date_of_birth
end
However, I like to add certain extra attributes to a Child, like for example its nickname and information about its allergies. So I introduce another class which I call ChildDetail. Of course I can add these attributes to the people table as well, but in this project I had several more fields to add and some other attributes for a father., which would lead to a lot of columns for only two subclasses (of the five in total).
create_table :child_details, :force => true do |t|
t.string :nickname
t.string :vaccination
t.string :allergy
t.integer :child_id #belongs_to relationship with Child
end
Ok, now I can access the extra attributes
class Person < ActiveRecord::Base
end
class Child < Person
has_one :child_detail
end
class ChildDetail < ActiveRecord::Base
belongs_to :child
end
child = Child.first
=>#<Child id: 2, firstname: "William", lastname: "Oxener", type: "Child", social_security_number: "123456789", gender: "m", date_of_birth: "2005-12-02 00:00:00">
puts child.child_detail.nickname
=>"Bill"
This is not really the way I want it, I like to ask directly for the nickname of a child without going through a child_detail. So to solve this ‘problem’ I wrote the has_attributes_from gem. Add the following line to your environment.rb file
config.gem 'dovadi-has_attributes_from', :lib => 'has_attributes_from', :version => '>=0.1.1', :source => 'http://gems.github.com'
Install and unpack this gem to your vendor directory or install as a plugin
./script/plugin install git://github.com/dovadi/has_attributes_from.git
Now we can do the following:
class Person < ActiveRecord::Base
end
class Child < Person
has_attributes_from :child_detail
validates_presence_of :nickname
end
class ChildDetail < ActiveRecord::Base
end
With has_attributes_from the attributes from ChidDetail are merged with Child. A child object acts as one single object and I can even do validation on nickname directly (or the other attributes from ChildDetail).
child = Child.first
=>#<Child id: 2, firstname: "William", lastname: "Oxener", type: "Child", social_security_number: "123456789", gender: "m", date_of_birth: "2005-12-02 00:00:00">
puts child.nickname
=>"Bill"
child.update_attributes(:nickname=>nil)
=>false
child.errors.full_messages
=> ["Nickname must be present"]
child.nickname="Daam"
=>"Daam"
child.save
=>true
I think this is much nicer, besides it was fun to make and a good exercise to put some Ruby meta programming into practice.

