The question is this: how are you supposed to do it?
I’ve seen a lot of blog posts that say that HM:T has beaten HABTM and that we can now all easily have data on the join model and go on to show how to enter that data into the join model but don’t show a recommended way of modifying it later.
I’ve checked against a pile of books, too, as well as the AR api and Google’s exhaustive collection of blogs. Neither The Rails Way nor Agile Web Development show an example that I can find.
Obviously this is something really simple that I have missed so if someone could please post a link to the place where it is actually demonstrated that would be wonderful.
Taking the example from http://blog.hasmanythrough.com/2006/8/19/magic-join-model-creation if you’re unsure what I mean:
That automagically creates the join record with the role attribute set to “author”. Pretty nifty, eh? But we can do better.
class Contribution < ActiveRecord::Base belongs_to :book belongs_to :contributor belongs_to :author, :class_name => "Contributor" belongs_to :editor, :class_name => "Contributor" end class Book < ActiveRecord::Base has_many :contributions, :dependent => :destroy has_many :contributors, :through => :contributions, :uniq => true has_many :authors, :through => :contributions, :source => :author, :conditions => "contributions.role = 'author'" do def <<(author) Contribution.with_scope(:create => {:role => "author"}) { self.concat author } end end has_many :editors, :through => :contributions, :source => :editor, :conditions => "contributions.role = 'editor'" do def <<(editor) Contribution.with_scope(:create => {:role => "editor"}) { self.concat editor } end end end
Then give this a shot…
dave = Contributor.create(:name => "Dave") chad = Contributor.create(:name => "Chad") awdr = Book.create(:title => "Agile Web Development with Rails") awdr.authors << dave awdr.editors << chad
That’s brilliant (although I suspect it will break now because at some point with_scope got removed from public so I’m not sure what people suggest nowadays).
What if you added another field to contributions? Let’s say, for lack of a better example, time_spent. This is how you are supposed to find it, as far as I can tell:
dave = Contributor.find_by_name “Dave”
book = Book.find_by_title “Agile Web Development with Rails”
contribution = Contribution.find_by_book_id_and_author_id book.id, dave.id
And then you can now go
contribution.time_spent = 9.months
But surely there is a way to get that contribution object out of the collection proxy? Something like:
contribution = dave.books.proxy_object(dave.books.first) or
contribution = dave.books.proxy_objects.first
(Yes, I realise this would be essentially the same thing, but the method I showed first strikes me as being very non-rails).
So, is there some wonderful resource where this has been demonstrated that I’ve just missed?
Cheers, Morgan.