Thanks for this review of many to many associations and much more! It's funny because I discovered a lot of these features my self when implemented a group based permission system last week. Groups had memberships to different sections and so one (almost the same thing). The problem I discovered though was that it's hard to sort (in my case) the groups belonging to a certain section because you looping over the membership. If I wanted to sort by name it wouldn't work because the name field is not in the membership table. It's probably easy to fix this, I just need to read the API some more:)
I could have done with this 18 months ago; I spent yonks working through the complexities of a 'I follow you, but you might not follow me' association type with the people on the RailsSpace google group.
The app ended up with a Subcriber/Subscribee naming convention (it sums up how people are interacting with one another on social networks nowadays), and, therefore, used a subscribers_controller and subscribee_controller, giving /users/1/subscribers and /users/1/subscribees routes and API. Is that something you looked at for this Friendships example, Ryan?
Great screencast as always! I wasn't aware of the inverse parameter yet either.
I am missing one thing though; I want someone who was "friended by" to be able to cancel the friendship and not just leave it up to the one who initiated the friendship.
Also I wonder won't this method leave you open for duplicate friendships? After Ryan befriends Fred, Fred can still befriend Ryan as well. Is there a way to validate against this without custom logic?
*Adam Hill
I don't think theres a special inverse parameter. Ryan just creates another association with some special conditions and because the name will interfer with the first "friends" association he add the namespace "inverse" to it. Or am I wrong?:)
Very nice !
Anyway, you don't get all friends at once like this. I've exposed the problem here : http://stackoverflow.com/questions/788582/cant-define-joins-conditions-in-hasmany-relationship
For performances reasons, friendships must be retreived both ways in one request, especially if your friendship table contains 1.000.000+ lines like in http://woopets.com !
It's apparently not doable with rails 2.3.
@Ryan: great screencast, this comes just right in time. Could anyone advise on how to adapt this with STI user models, e.g.
class User < ARB
class Contact < User
class Reseller < Contact
class Employee < Reseller
Both, resellers and employees should have many contacts through supervisions (:user_id => references, :contact_id => integer). If i use:
class Reseller < Contact
has_many :contacts, :through => supervisions, :source => :user
end
ActiveRecord throws an InvalidStatement error, because there is no reseller_id column in the supervisions table. Any hints?
Hi Ryan,
Thanks for screencast.
One thing I noticed that in any social networking site you can't add any person directly!!!
You must need permission of that person.
Did you miss it or you didn't include it because of lack of time?
I further recommend this article about how to get through this jungle of redundant ? Friendship, Membership, XYZOwnership & Co. assocation tables to put/abstract them into 1 table (acts_as_double_polymorphic_join) with the yet up to date plugin () has_many_polymorphs:
http://m.onkey.org/2007/8/14/excuse-me-wtf-is-polymorphs
Correct me for a niftier solution :-)
This is exactly what I needed!
Thank you, Ryan.
Ryan, have you tried the acts_as_network plugin?
you must have been hungry.. you typed "dessert" instead of delete.. :-D
@Fredd - yes you're right. That's what I get for commenting before I've finished the s'cast. I'm used to the auto-recipricate type self-referential many-to-many as per the excellent Rails Recipes book and based on the notes thought it might perform those functions automatically.
Hello Ryan,
The last two episodes have been especially useful. I am still waiting for your nested forms and more jquery episodes either here or as screencasts at PragProg. The ActiveRecord and Forms screencasts have been extremely useful too.
Thanks.
Bharat
Thanks again Ryan. Keep up the good work. I would love to see a Globalize 2 and Rails 2.2 tutorial.
@Jason
You can specify the foreign_key to use:
class Reseller < Contact
has_many :contacts, :through => supervisions, :source => :user, :foreign_key => 'user_id'
end
For anyone struggling, like me, to get a good implementation of this without having to resort to calling two different associations in your views/controllers, there's an awesome plugin called "has_many_friends" on github that handles all the annoying details for you.
http://github.com/swemoney/has_many_friends/tree/master
@zzg: Nifty scaffold is a part of Ryan's Nifty Generators:
http://github.com/ryanb/nifty-generators/
Wow Ryan...sometimes I feel like I learned more here than in 4 years of computer science classes.
Your comment at the beginning about where to put controller code was especially valuable. My users controller is littered with tons of methods like that.
I don´t know how you keep making these with little or no compensation...we need to find a business model for you so that you can continue producing these...you have a gift!
Brian
I really enjoyed this episode, and it's falling right in line with a project I'm working on.
One of the questions I have about this set up though is this: how can we set it up so that if one member of the friendship destroys the friendship, the whole thing goes and not just half?
“Your comment at the beginning about where to put controller code was especially valuable.”
— 24. Brian Armstrong
Yeap Ryan Bates is my hero
Thanks. Great episode :-)
Two quick questions:
a) Maybe I'm missing something: Where was the 'current_user' variable set ?
b) Didn't the addition of he "remove" option the the users/show view introduced the "n+1" problem? It seems that a separate query has to be issued for each friendship in order to get the friend's name. Am I right ? Should there be an 'include' clause somewhere in order to fix it ?
Thanks so much for your casts !!
Nadav, the 'current_user' variable is part of the authentication plugin Ryan is using, which looked like either restful_authentication or his own nifty_authentication...
I get the following when I try to add a friendship:
ArgumentError in FriendshipsController#create
Unknown key(s): Friendship
RAILS_ROOT: /Users/Jay/Ruby/bull
Application Trace | Framework Trace | Full Trace
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.2/lib/active_support/core_ext/hash/keys.rb:47:in `assert_valid_keys'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations.rb:1506:in `create_belongs_to_reflection'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations.rb:994:in `belongs_to'
(eval):3:in `belongs_to'
Nice exhibit, Ryan. My question is how you handle the delete of the respective reciprocal friendship? I've found that to be a case where the :after_remove option on the has_many has failed me, and I had to resort to some kludges. I wonder if there is a clean(er) way? You don't cover that part.
Thank you Ryan, for another concise and very accurate one. I'm glad you did this the 'right' way through a join model. Self-referential made me think of this: http://pastie.org/546374, ugh !
This article was a better example of design than it was of 'railsy' stuff. Very hip and MDD ;)
Nice way to redeem yourself for the one where a rails controller spawns of a background process by appending an ampersand to a process name. You kick of processes per request, and you may as well be doing CGI. I know you may have a legitimate purpose and that you do not do this randomly. But also because it strikes me as a poorly documented dependency, one which even at runtime exists only ephemerally.
Anyway, nice one this time. Also, do you know of Dr. Nic - for some more design-level stuff you could show http://drnicwilliams.com/2006/10/04/i-love-map-by-pluralisation/ or metamagic models. I think his stuff is cool..
Ciao
Also - how you manage to do this with only Saturday afternoons and Sundays not even that late into the night.. http://github.com/ryanb/railscasts-episodes/graphs/punch_card Maybe you could post an episode on how to balance the demands of life and code, cause you seem superorganized. http://github.com/chicagogrooves/chess_on_rails/graphs/punch_card bears so little resemblance to yours its hilarious :)
Trying to implement this sort of functionality into my site, but having some small problems.
1. I'm using the restful authentication plugin
2. Each "user" has_one "Profile".
Here is my question & problem:
1. Should I associate the friendship with the Profile_id or the User_id? Is one more efficient than the other?
2. How can I get the Profile_id into the create method of the friendships_controller? (if that's what I end up using)
PS - Love your screencasts consistently blown away...
What if you don't use the plugin? What will the value of current_user be?
I also don't understand why we had to define user here: (:friend_id => user)
Oh, what if a user is already a friend, and I dont want to display the "Add Friend" link next to their name? How can I check if they are already friends, and only display "Add Friend" if they are not?
THANKS!
great rails cast. is delete automatically understood as the destroy action? and the post method will also be understood as create action? how is that possible? super noob here or should i just trust that that works?
How do you prevent a user from befriending himself? For example, RyanB can friend RyanB in your video.
Never mind, worked out how to do this with a named_scope that takes the current record as a parameter to a lambda expression. This is then passed into the conditions expression to exclude the specified record.
Thanks for a great screencast!
All kinds of Ball Bearing, Ball Bearings, Deep Groove Ball Bearing, Housing Bearing,Miniature bearings, Automobile Bearing, Electric Tool Bearings, Textile Machine Bearing, Computer Series Bearing and so on."
we provide our buyers with an efficient and manageable procurement process covering every phase of the international supply chain and
streamlining trade channels. Also welcome wholesaling, feedback now!
Here we have new style Coach Handbags.All the Coach purses are good quality and lower price.A fashion Coach Outlet is dreamed by the fashion females.Welcome to our store discountbagshop.com.I am sure you will find one for yourself.
Thanks for writing this. I really feel as though I know so much more about this than I did before. Your blog really brought some things to light that I never would have thought about before reading it. You should continue this, Im sure most people would agree youve got a gift.
Deep Groove Ball Bearing, Housing Bearing,Miniature bearings, Automobile Bearing,Here we have new style Coach Handbags.All the Coach purses are good quality and lower price.A fashion Coach Outlet is dreamed by the fashion females.
You kick of processes per request, and you may as well be doing CGI. I know you may have a legitimate purpose and that you do not do this randomly.
The blog article very surprised to me! Your writing is good. In this I learned a lot! Thank you!
Discount Wholesale Electronics, Wholesale Cell Phones, Electronic Gadgets and More from the Best Dropship Wholesaler
very cool article ,like my cool stuff .very useful.thanks for sharing the article!
Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets
How can I check if they are already friends, and only display "Add Friend" if they are not?
Intimately, the post is actually the best on this laudable topic. I harmonize with your conclusions and will eagerly look forward to your future updates. Saying thanks will not just be adequate, for the fantastic lucidity in your writing.






