Gmane
Picon
From: David Heinemeier Hansson <david.heinemeier@...>
Subject: Re: HABTM and extra attributes
Newsgroups: gmane.comp.lang.ruby.rails
Date: 2005-12-02 21:27:20 GMT (2 years, 23 weeks, 4 days, 1 hour and 4 minutes ago)
> With the current version of Rails, Chris' approach works just fine in
> this scenario, and the code required in the view is fairly
> straightforward to write and maintain.  Peter's right in that it just
> seems like HABTM isn't "meant to" support this level of complexity;
> I'm concerned that a later Rails update could break it.

HABTM is indeed not well suited for complexity. It's pushing back
because it wants you to discover the implicit model object that's
missing from the equation. In the author/book example that model is
Authorship. So you would have:

class Author
  has_many :authorships
end

class Book
  has_many :authorships
end

class Authorship
  belongs_to :author
  belongs_to :book
end

Now the domain is explicit, but we're still in a bit of trouble. Since
this pretty domain will force us to write SQL by hand to get
performant access. So you'd probably have:

class Author
  has_many :authorships

  def books
    find_by_sql(
      "SELECT books.* " +
      "FROM books, authors, authorships " +
      "WHERE authorships.book_id = books.id AND + "
      "authorships.author_id = #{id}"
    )
  end
end

And you would create new relationships by doing

b = Book.create :title => "Agile Web Development with Rails"
david = Author.create :name => "David Heinemeier Hansson"
dave  = Author.create :name => "Dave Thomas"

Authorship.create([
  { :book => b, :author => david },
  { :book => b, :author => dave }
])

Now this is actually a lot less painful than one could imagine. But
its still not painless enough. So we're currently working on allowing:

class Author
  has_many :authorships
  has_many :books, :through => :authorships
end

This would expose both the join model (authorship) and allow
convenient access to the model on the other side of that join model.
One could even imagine:

class Author
  has_many :authorships
  has_many :books, :through => :authorships
  has_many :agents, :through => :authorships
end

class Authorship
  belongs_to :agent
  belongs_to :author
  belongs_to :book
end

So if I was having pushback from HABTM today, I would try to discover
which implicit domain model I hadn't revealed yet. Then I'd spend the
few minutes making the manual accessors for that join model. And I
would then look forward to the day where I could remove my manual
access as :through materializes.
--
David Heinemeier Hansson
http://www.loudthinking.com -- Broadcasting Brain
http://www.basecamphq.com   -- Online project management
http://www.backpackit.com   -- Personal information manager
http://www.rubyonrails.com  -- Web-application framework