Redundant Bidirectional Relationships in Rails Suck
April 10th, 2008
A better-late-than-never announcement: we released a Rails plugin a while ago that implements a better, DRYer way to roll network relationships using ActiveRecord. It's called, acts_as_network and it now updated to support Rails 2.0.
So why is this such a problem? It may not be immediately apparent, but the short answer is that these types of relationships usually require 2 redundant rows of storage in your database. Take a social network relationship: one record might say that Jack is Jill's friend, but a separate row must be present to say Jill is Jack's friend.
acts_as_network does away with this nonsense, and lets you say implicitly that If Jack is Jill's friend then Jill is Jack's friend. Or, in Ruby
# Jane invites Jack to be friends invite = Invite.create(:person => jane, :person_target => jack, :message => "let's be friends!") jane.friends.include?(jack) => false # Jack is not yet Jane's friend jack.friends.include?(jane) => false # Jane is not yet Jack's friend either invite.is_accepted = true # Now Jack accepts the invite invite.save and jane.reload and jack.reload jane.friends.include?(jack) => true # Jack is Janes friend now jack.friends.include?(jane) => true # Jane is also Jacks friend
The syntax is clean, and it stores only one row in your HABTM table. Online Documentation available or install/upgrade the plugin:
% script/plugin source http://actsasnetwork.rubyforge.org/svn/plugins % script/plugin install acts_as_network % rake doc:plugins
Much thanks to Maurycy for submitting patches to AAN!
Note: for a more in depth look at the acts_as_network syntax and usage please check out the original release page.


May 10th, 2008 at 01:32 AM Hey Stephen, this is great, it's exactly what I was looking for. Do you mind if I ask you a question? I'm having a problem.
Here's my setup (Rails 2.0.2): I'm getting the following error (in IRB): Am I missing something? I thought the friends method was supposed to have been added (along with 4 others).
(just to clarify, there are no connections records yet, I'm expecting back empty arrays)
Thanks.
May 27th, 2008 at 09:24 AM @tobin - The virtual relationship accessor is named based on the first parameter to the acts_as_network call. So, in the example code you posted the Machine class should respond like this: If you'd like the relationship to be called
friendsthen you'll need to define as such: Hope that helps clarify!June 9th, 2008 at 11:22 PM I have run into problems when using Models with two words in their name. For example: class BigFoo < ActiveRecord::Base acts_as_network :friends, :through => :invites, :conditions => "is_accepted = 't'" end It fails due to your use of 'name.downcase' when creating the has_many relationships 'in' and 'out'. (Line 235-247 in network.rb) It tries to define the source relationship as 'bigfoo_target' and 'bigfoo' when it should be 'big_foo_target' and 'big_foo'. I have remedied this on my copy by changing the name.downcase to name.tableize.singularize This should be safer. I haven't run the unit tests so this needs further investigation. But it would be good if a fix to this did make it into your repo. Other than that, this is a great addition to active record. Thanks!! Kim
June 9th, 2008 at 11:31 PM Sorry about the lack of formatting in that previous comment. There are 3 lines of code near the top which you can hopefully extract. I'm not up to speed with blogging and the likes. Kim