Redundant Bidirectional Relationships in Rails Suck
April 10th, 2008
Update 06-JUN-2008: This plugin now includes acts_as_union, and we moved the repository to GitHub.
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 install git://github.com/sjlombardo/acts_as_network.git % 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.
9 Responses to “Redundant Bidirectional Relationships in Rails Suck”
Sorry, comments are closed for this article.


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
July 18th, 2008 at 06:02 PM Looks like a very useful plugin. I'm getting two failed tests right from the start. Anyone else experiencing this? Thanks. 1) Failure: test_assigments_conditions(ActsAsNetworkTest) [test/network_test.rb:252]: <[#<person>]> expected but was <[]>. 2) Failure: test_hmt_assignments(ActsAsNetworkTest) [test/network_test.rb:224]: <false> is not true. 23 tests, 103 assertions, 2 failures, 0 errors acts_as_network version: 0.2 rails version: 2.0.2 ruby version: 1.8.6 [i386-mswin32]
July 19th, 2008 at 12:14 PM
@Kim,
Thanks so much for finding this and working out the fix. We'll definitely work it in to the plugin asap. And yes, we need to textile these comments soon so you don't have to format them!
@Jason Galvin,
We just ran tests in Rails 2.0.2 and Rails 2.1 to confirm and everything's running just fine. Are you still using the old subversion plugin on rubyforge? We've moved to GitHub and I've updated the article above to reflect that.
Head over here to get the newest goods:
http://github.com/sjlombardo/acts_as_network/tree/master
You can use this command to get the freshest stuff:
July 20th, 2008 at 01:38 PM
@Kim, Stephen committed a fix for the issue you reported on GitHub yesterday:
http://github.com/sjlombardo/acts_as_network/commit/c7fcb8aacb8b41e37cbd5b7e54fe8816c67af2faJuly 22nd, 2008 at 05:12 PM OK, I figured out why my plugin tests we failing (details on my previous post, and sorry for the lack of formatting there). I have a model named invite.rb in my app, and I think the tests were somehow reading off this model rather than the Invite model for the plugin. Anyhow, as soon as I deleted the invite.rb model in my app, the plugin tests all pass. HTH
August 8th, 2008 at 05:08 PM I don't have time to hack it myself, but it would be great if you were able to support eager loading. Right now I get the error "NoMethodError: undefined method `loaded' for #<array:0xb6c5ecb4>", if I try to include my network table. Thanks for your work.