Fix nested has many :through associations on unpersisted instances

Fixes: #16313
This commit is contained in:
Zoltan Kiss 2014-10-10 15:13:53 -05:00
parent 8b451e3a31
commit 1813350f09
4 changed files with 70 additions and 1 deletions

@ -1,3 +1,39 @@
* Fix nested `has many :through` associations on unpersisted parent instances.
For example, if you have
class Post < ActiveRecord::Base
has_many :books, through: :author
has_many :subscriptions, through: :books
end
class Author < ActiveRecord::Base
has_one :post
has_many :books
has_many :subscriptions, through: :books
end
class Book < ActiveRecord::Base
belongs_to :author
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :book
end
Before:
If `post` is not persisted, e.g `post = Post.new`, then `post.subscriptions`
will be empty no matter what.
After:
If `post` is not persisted, then `post.subscriptions` can be set and used
just like it would if `post` were persisted.
Fixes #16313.
*Zoltan Kiss*
* Add `assert_enqueued_emails` and `assert_no_enqueued_emails`.
Example:

@ -77,7 +77,7 @@ def stale_state
end
def foreign_key_present?
through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
through_reflection.belongs_to_or_through? && !owner[through_reflection.foreign_key].nil?
end
def ensure_mutable

@ -139,6 +139,10 @@ def primary_key_type
klass.type_for_attribute(klass.primary_key)
end
def belongs_to_or_through?
belongs_to? || (through_reflection && through_reflection.belongs_to_or_through?)
end
# Returns the class name for the macro.
#
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>

@ -1166,4 +1166,33 @@ def test_build_for_has_many_through_association
post_through = organization.posts.build
assert_equal post_direct.author_id, post_through.author_id
end
def test_single_has_many_through_association_with_unpersisted_parent_instance
post_with_single_has_many_through = Class.new(Post) do
def self.name; 'PostWithSingleHasManyThrough'; end
has_many :subscriptions, through: :author
end
post = post_with_single_has_many_through.new
post.author = Author.create!(name: 'Federico Morissette')
book = Book.create!(name: 'essays on single has many through associations')
post.author.books << book
subscription = Subscription.first
book.subscriptions << subscription
assert_equal [subscription], post.subscriptions.to_a
end
def test_nested_has_many_through_association_with_unpersisted_parent_instance
post_with_nested_has_many_through = Class.new(Post) do
def self.name; 'PostWithNestedHasManyThrough'; end
has_many :books, through: :author
has_many :subscriptions, through: :books
end
post = post_with_nested_has_many_through.new
post.author = Author.create!(name: 'Obie Weissnat')
book = Book.create!(name: 'essays on nested single has many through associations')
post.author.books << book
subscription = Subscription.first
book.subscriptions << subscription
assert_equal [subscription], post.subscriptions.to_a
end
end