Add info and tests for has_many :through.

This commit is contained in:
Andy Stewart 2010-07-05 13:01:02 +01:00
parent 2e19ed3e55
commit f65cd0f722
3 changed files with 110 additions and 0 deletions

View File

@ -212,6 +212,46 @@ To find out who made a `version`'s object look that way, use `version.originator
>> last_version.terminator # 'Bob'
## Has-Many-Through Associations
PaperTrail can track most changes to the join table. Specifically it can track all additions but it can only track removals which fire the `after_destroy` callback on the join table. Here are some examples:
Given these models:
class Book < ActiveRecord::Base
has_many :authorships, :dependent => :destroy
has_many :authors, :through => :authorships, :source => :person
has_paper_trail
end
class Authorship < ActiveRecord::Base
belongs_to :book
belongs_to :person
has_paper_trail # NOTE
end
class Person < ActiveRecord::Base
has_many :authorships, :dependent => :destroy
has_many :books, :through => :authorships
has_paper_trail
end
Then each of the following will store authorship versions:
>> @book.authors << @dostoyevsky
>> @book.authors.create :name => 'Tolstoy'
>> @book.authorships.last.destroy
>> @book.authorships.clear
But none of these will:
>> @book.authors.delete @tolstoy
>> @book.author_ids = [@solzhenistyn.id, @dostoyevsky.id]
>> @book.authors = []
There may be a way to store authorship versions, probably using association callbacks, no matter how the collection is manipulated but I haven't found it yet. Let me know if you do.
## Storing metadata
You can store arbitrary model-level metadata alongside each version like this:

View File

@ -24,6 +24,24 @@ class Article < ActiveRecord::Base
:article_id => Proc.new { |article| article.id } }
end
class Book < ActiveRecord::Base
has_many :authorships, :dependent => :destroy
has_many :authors, :through => :authorships, :source => :person
has_paper_trail
end
class Authorship < ActiveRecord::Base
belongs_to :book
belongs_to :person
has_paper_trail
end
class Person < ActiveRecord::Base
has_many :authorships, :dependent => :destroy
has_many :books, :through => :authorships
has_paper_trail
end
class HasPaperTrailModelTest < Test::Unit::TestCase
load_schema
@ -586,5 +604,44 @@ class HasPaperTrailModelTest < Test::Unit::TestCase
end
end
context ":has_many :through" do
setup do
@book = Book.create :title => 'War and Peace'
@dostoyevsky = Person.create :name => 'Dostoyevsky'
@solzhenitsyn = Person.create :name => 'Solzhenitsyn'
end
should 'store version on source <<' do
count = Version.count
@book.authors << @dostoyevsky
assert_equal 1, Version.count - count
assert_equal Version.last, @book.authorships.first.versions.first
end
should 'store version on source create' do
count = Version.count
@book.authors.create :name => 'Tolstoy'
assert_equal 2, Version.count - count
assert_same_elements [Person.last, Authorship.last], [Version.all[-2].item, Version.last.item]
end
should 'store version on join destroy' do
@book.authors << @dostoyevsky
count = Version.count
@book.authorships(true).last.destroy
assert_equal 1, Version.count - count
assert_equal @book, Version.last.reify.book
assert_equal @dostoyevsky, Version.last.reify.person
end
should 'store version on join clear' do
@book.authors << @dostoyevsky
count = Version.count
@book.authorships(true).clear
assert_equal 1, Version.count - count
assert_equal @book, Version.last.reify.book
assert_equal @dostoyevsky, Version.last.reify.person
end
end
end

View File

@ -49,4 +49,17 @@ ActiveRecord::Schema.define(:version => 0) do
t.string :content
end
create_table :books, :force => true do |t|
t.string :title
end
create_table :authorships, :force => true do |t|
t.integer :book_id
t.integer :person_id
end
create_table :people, :force => true do |t|
t.string :name
end
end