A look at ActiveRecord association setters

If you’ve been working in Rails for more than a short while, you should be familiar with ActiveRecord class methods, and how they modify your model definitions with helpful magic methods.

For example, the has_many declaration gives you access to a host of accessors and setters that make it easy to manipulate associated child objects:

Class Family < ActiveRecord
  has_many :members
end

class Member < ActiveRecord
  belongs_to :family
end

# Create a new family and save it
family = Member.new(:name => “The Johnsons”)
family.save

# Add a family member through array insertion
family.members << Member.new(:name => “Sam”)

# Add a family member through association creation
family.members.create(:name => “Sally”)

In the example code above, we created our new family members through the “members” collection. By doing so, the family_id attribute/column is initialized for us. Otherwise, we’d have to set it manually like this:

sunny = Member.create(:name => “Sunny”, :family_id => family.id)

Okay, you might not find that impressive. I mean, we’re only saving a few dozen characters of code here, right?

Turns out there’s another convenient way of building associated objects that I feel doesn’t get enough press. It’s the collection=objects method, which takes an array of associated objects like so:

family.members = [ Member.new(:name => “Jean”), Member.new(:name => “Tom”) ]

You’ll find that for each of these objects the family_id attribute was set, just as they would have been above. As for Sam, Sally and Sunny? They’ve been usurped by Jean and Tom, the once-loyal babysitters. That’s right, in addition to creating new database records for Jean and Tom, the old records are deleted too. That’s a lot of good stuff going on for a single line of code.

Editor’s note—I need to come up with better examples.

It’s also worth mentioning this setter method also works when initializing ActiveRecord objects using a hash. The example code below will create a new family, add a couple family members to it, and save all three objects to the database.

Family.create :name => "The Johnsons", :members => 
  [ Member.new(:name => "Sam"), Member.new(:name => "Sally") ]

Okay, so this isn’t revolutionary, but it’s another solid tool to add to your Rails arsenal if you haven’t already.

Legacy Rails - beware of 'type' columns

Earlier this week I was writing an app that had models connected to a number of legacy databases. To do this, I followed PragDave’s example of subclassing from ‘gatekeeper’ models that establish separate database connections. Without going into too much detail, here’s an example:

class Finance < ActiveRecord::Base
  self.abstract_class = true
  establish_connection(
    ActiveRecord::Base.configurations["finance_#{ENV['RAILS_ENV']}"]
  )
end

class Receipt < Finance
end

class Income< Finance
end

Looks good, but I hit a snag – one of my legacy tables had a ‘type’ column defined. When Rails sees a column named ‘type’ for models that aren’t immediate children of ActiveRecord::Base, it assumes that column holds the class name associated with your model. This is how Rails implements single table inheritance, and if that’s not what you intended, you’ve got a little extra work ahead of you.