aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/associations
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/associations')
-rw-r--r--activerecord/test/cases/associations/association_scope_test.rb16
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb57
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb2
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb6
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb14
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb29
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb63
-rw-r--r--activerecord/test/cases/associations/extension_test.rb13
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb50
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb217
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb208
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb50
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb22
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb104
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb26
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb45
-rw-r--r--activerecord/test/cases/associations/required_test.rb2
21 files changed, 702 insertions, 244 deletions
diff --git a/activerecord/test/cases/associations/association_scope_test.rb b/activerecord/test/cases/associations/association_scope_test.rb
deleted file mode 100644
index c322333f6d..0000000000
--- a/activerecord/test/cases/associations/association_scope_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require "cases/helper"
-require "models/post"
-require "models/author"
-
-module ActiveRecord
- module Associations
- class AssociationScopeTest < ActiveRecord::TestCase
- test "does not duplicate conditions" do
- scope = AssociationScope.scope(Author.new.association(:welcome_posts),
- Author.connection)
- binds = scope.where_clause.binds.map(&:value)
- assert_equal binds.uniq, binds
- end
- end
- end
-end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 5875a1871f..0f7a249bf3 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/project"
@@ -116,6 +118,44 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
+ def test_default
+ david = developers(:david)
+ jamis = developers(:jamis)
+
+ model = Class.new(ActiveRecord::Base) do
+ self.table_name = "ships"
+ def self.name; "Temp"; end
+ belongs_to :developer, default: -> { david }
+ end
+
+ ship = model.create!
+ assert_equal david, ship.developer
+
+ ship = model.create!(developer: jamis)
+ assert_equal jamis, ship.developer
+
+ ship.update!(developer: nil)
+ assert_equal david, ship.developer
+ end
+
+ def test_default_with_lambda
+ model = Class.new(ActiveRecord::Base) do
+ self.table_name = "ships"
+ def self.name; "Temp"; end
+ belongs_to :developer, default: -> { default_developer }
+
+ def default_developer
+ Developer.first
+ end
+ end
+
+ ship = model.create!
+ assert_equal developers(:david), ship.developer
+
+ ship = model.create!(developer: developers(:jamis))
+ assert_equal developers(:jamis), ship.developer
+ end
+
def test_default_scope_on_relations_is_not_cached
counter = 0
@@ -177,6 +217,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
remove_column :admin_users, :region_id if column_exists?(:admin_users, :region_id)
drop_table :admin_regions, if_exists: true
end
+
+ Admin::User.reset_column_information
end
def test_natural_assignment
@@ -342,7 +384,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable = Member.new name: "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
- assert_equal "members", sponsor.association(:sponsorable).aliased_table_name
end
def test_with_polymorphic_and_condition
@@ -610,8 +651,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_new_record_with_foreign_key_but_no_object
client = Client.new("firm_id" => 1)
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- assert_equal Firm.all.merge!(order: "id").first, client.firm_with_basic_id
+ assert_equal Firm.first, client.firm_with_basic_id
end
def test_setting_foreign_key_after_nil_target_loaded
@@ -1131,6 +1171,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Column.create! record: record
assert_equal 1, Column.count
end
+
+ def test_multiple_counter_cache_with_after_create_update
+ post = posts(:welcome)
+ parent = comments(:greetings)
+
+ assert_difference "parent.reload.children_count", +1 do
+ assert_difference "post.reload.comments_count", +1 do
+ CommentWithAfterCreateUpdate.create(body: "foo", post: post, parent: parent)
+ end
+ end
+ end
end
class BelongsToWithForeignKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
index 8a0e041864..88221b012e 100644
--- a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/content"
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index 5fd2411f6f..e096cd4a0b 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/author"
@@ -7,7 +9,7 @@ require "models/computer"
require "models/company"
class AssociationCallbacksTest < ActiveRecord::TestCase
- fixtures :posts, :authors, :projects, :developers
+ fixtures :posts, :authors, :author_addresses, :projects, :developers
def setup
@david = authors(:david)
@@ -128,7 +130,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
assert ar.developers_log.empty?
alice = Developer.new(name: "alice")
ar.developers_with_callbacks << alice
- assert_equal"after_adding#{alice.id}", ar.developers_log.last
+ assert_equal "after_adding#{alice.id}", ar.developers_log.last
bob = ar.developers_with_callbacks.create(name: "bob")
assert_equal "after_adding#{bob.id}", ar.developers_log.last
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index ddb5c7a4aa..e717621928 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -12,7 +14,7 @@ require "models/vertex"
require "models/edge"
class CascadedEagerLoadingTest < ActiveRecord::TestCase
- fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments,
+ fixtures :authors, :author_addresses, :mixins, :companies, :posts, :topics, :accounts, :comments,
:categorizations, :people, :categories, :edges, :vertices
def test_eager_association_loading_with_cascaded_two_levels
@@ -34,18 +36,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
- assert_nothing_raised do
- Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a
- end
authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a
- assert_equal 1, assert_no_queries { authors.size }
+ assert_equal 3, assert_no_queries { authors.size }
assert_equal 10, assert_no_queries { authors[0].comments.size }
end
def test_eager_association_loading_grafts_stashed_associations_to_correct_parent
- assert_nothing_raised do
- Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").to_a
- end
assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").first
end
@@ -140,7 +136,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_multiple_stis_and_order
- author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
+ author = Author.all.merge!(includes: { posts: [ :special_comments, :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
assert_equal authors(:david), author
assert_no_queries do
author.posts.first.special_comments
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index 4f0fe3236e..8754889143 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tagging"
@@ -11,25 +13,32 @@ end
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
def setup
- generate_test_objects
- end
-
- def generate_test_objects
post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
- Tagging.create(taggable: post)
+ @tagging = Tagging.create(taggable: post)
+ @old = ActiveRecord::Base.store_full_sti_class
end
- def test_class_names
- old = ActiveRecord::Base.store_full_sti_class
+ def teardown
+ ActiveRecord::Base.store_full_sti_class = @old
+ end
+ def test_class_names_with_includes
ActiveRecord::Base.store_full_sti_class = false
post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
assert_nil post.tagging
ActiveRecord::Base.store_full_sti_class = true
post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
- assert_instance_of Tagging, post.tagging
- ensure
- ActiveRecord::Base.store_full_sti_class = old
+ assert_equal @tagging, post.tagging
+ end
+
+ def test_class_names_with_eager_load
+ ActiveRecord::Base.store_full_sti_class = false
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_nil post.tagging
+
+ ActiveRecord::Base.store_full_sti_class = true
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
end
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index e9f551b6b2..c5b2b77bd4 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tag"
diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb
index 16eff15026..420a5a805b 100644
--- a/activerecord/test/cases/associations/eager_singularization_test.rb
+++ b/activerecord/test/cases/associations/eager_singularization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class EagerSingularizationTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 11f4aae5b3..9830917bc3 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tagging"
@@ -38,6 +40,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_nil member.favourite_club
end
+ def test_should_work_inverse_of_with_eager_load
+ author = authors(:david)
+ assert_same author, author.posts.first.author
+ assert_same author, author.posts.eager_load(:comments).first.author
+ end
+
def test_loading_with_one_association
posts = Post.all.merge!(includes: :comments).to_a
post = posts.find { |p| p.id == 1 }
@@ -68,6 +76,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
"expected to find only david's posts"
end
+ def test_loading_with_scope_including_joins
+ assert_equal clubs(:boring_club), Member.preload(:general_club).find(1).general_club
+ assert_equal clubs(:boring_club), Member.eager_load(:general_club).find(1).general_club
+ end
+
def test_with_ordering
list = Post.all.merge!(includes: :comments, order: "posts.id DESC").to_a
[:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other,
@@ -271,9 +284,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_from_an_association_that_has_a_hash_of_conditions
- assert_nothing_raised do
- Author.all.merge!(includes: :hello_posts_with_hash_conditions).to_a
- end
assert !Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
end
@@ -417,7 +427,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
- Comment.includes(:post).references(:posts).order(quoted_posts_id)
+ Comment.includes(:post).references(:posts).order(Arel.sql(quoted_posts_id))
end
end
@@ -520,6 +530,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
end
+ def test_preloading_has_many_through_with_implicit_source
+ authors = Author.includes(:very_special_comments).to_a
+ assert_no_queries do
+ special_comment_authors = authors.map { |author| [author.name, author.very_special_comments.size] }
+ assert_equal [["David", 1], ["Mary", 0], ["Bob", 0]], special_comment_authors
+ end
+ end
+
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first
assert_equal [], author.special_nonexistent_post_comments
@@ -769,7 +787,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
Tagging.create!(taggable_type: "Post", taggable_id: post2.id, tag: tag)
tag_with_includes = OrderedTag.includes(:tagged_posts).find(tag.id)
- assert_equal(tag_with_includes.taggings.map(&:taggable).map(&:title), tag_with_includes.tagged_posts.map(&:title))
+ assert_equal tag_with_includes.ordered_taggings.map(&:taggable).map(&:title), tag_with_includes.tagged_posts.map(&:title)
end
def test_eager_has_many_through_multiple_with_order
@@ -851,23 +869,19 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
- def find_all_ordered(className, include = nil)
- className.all.merge!(order: "#{className.table_name}.#{className.primary_key}", includes: include).to_a
- end
-
def test_limited_eager_with_order
assert_equal(
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: "UPPER(posts.title)", limit: 2, offset: 1
+ order: Arel.sql("UPPER(posts.title)"), limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: "UPPER(posts.title) DESC", limit: 2, offset: 1
+ order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1
).to_a
)
end
@@ -877,14 +891,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1
+ order: [Arel.sql("UPPER(posts.title)"), "posts.id"], limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1
+ order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -1059,7 +1073,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_with_sti_sharing_association
- assert_queries(2) do #should not do 1 query per subclass
+ assert_queries(2) do # should not do 1 query per subclass
Comment.includes(:post).to_a
end
end
@@ -1094,12 +1108,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal authors(:david), assert_no_queries { posts[0].author }
posts = assert_queries(2) do
- Post.all.merge!(select: "distinct posts.*", includes: :author, joins: [:comments], where: "comments.body like 'Thank you%'", order: "posts.id").to_a
- end
- assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author }
-
- posts = assert_queries(2) do
Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: "posts.id").to_a
end
assert_equal posts(:welcome, :thinking), posts
@@ -1295,6 +1303,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal projects.last.mentor.developers.first.contracts, projects.last.developers.last.contracts
end
+ def test_preloading_has_many_through_with_custom_scope
+ project = Project.includes(:developers_named_david_with_hash_conditions).find(projects(:active_record).id)
+ assert_equal [developers(:david)], project.developers_named_david_with_hash_conditions
+ end
+
test "scoping with a circular preload" do
assert_equal Comment.find(1), Comment.preload(post: :comments).scoping { Comment.find(1) }
end
@@ -1363,6 +1376,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_nothing_raised do
authors(:david).essays.includes(:writer).any?
authors(:david).essays.includes(:writer).exists?
+ authors(:david).essays.includes(:owner).where("name IS NOT NULL").exists?
end
end
@@ -1487,9 +1501,18 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal posts(:welcome), post
end
+ test "eager-loading with a polymorphic association and using the existential predicate" do
+ assert_equal true, authors(:david).essays.eager_load(:writer).exists?
+ end
+
# CollectionProxy#reader is expensive, so the preloader avoids calling it.
test "preloading has_many_through association avoids calling association.reader" do
ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never
Author.preload(:readonly_comments).first!
end
+
+ private
+ def find_all_ordered(klass, include = nil)
+ klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
+ end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 974a3080d4..5eacb5a3d8 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -36,6 +38,11 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
assert_equal comments(:greetings), posts(:welcome).comments.not_again.find_most_recent
end
+ def test_extension_with_dirty_target
+ comment = posts(:welcome).comments.build(body: "New comment")
+ assert_equal comment, posts(:welcome).comments.with_content("New comment")
+ end
+
def test_marshalling_extensions
david = developers(:david)
assert_equal projects(:action_controller), david.projects.find_most_recent
@@ -73,6 +80,12 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
assert_equal post.association(:comments), post.comments.where("1=1").the_association
end
+ def test_association_with_default_scope
+ assert_raises OopsError do
+ posts(:welcome).comments.destroy_all
+ end
+ end
+
private
def extend!(model)
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index d6b595d7e7..c817d7267b 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -86,12 +88,6 @@ class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
-ActiveSupport::Deprecation.silence do
- class DeveloperWithConstantClassName < Developer
- has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys
- end
-end
-
class DeveloperWithExtendOption < Developer
module NamedExtension
def category
@@ -111,6 +107,21 @@ class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base
association_foreign_key: "developer_id"
end
+class Kitchen < ActiveRecord::Base
+ has_one :sink
+end
+
+class Sink < ActiveRecord::Base
+ has_and_belongs_to_many :sources, join_table: :edges
+ belongs_to :kitchen
+ accepts_nested_attributes_for :kitchen
+end
+
+class Source < ActiveRecord::Base
+ self.table_name = "men"
+ has_and_belongs_to_many :sinks, join_table: :edges
+end
+
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers
@@ -352,19 +363,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
- def test_create_by_new_record
- devel = Developer.new(name: "Marcel", salary: 75000)
- devel.projects.build(name: "Make bed")
- proj2 = devel.projects.build(name: "Lie in it")
- assert_equal devel.projects.last, proj2
- assert !proj2.persisted?
- devel.save
- assert devel.persisted?
- assert proj2.persisted?
- assert_equal devel.projects.last, proj2
- assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
- end
-
def test_creation_respects_hash_condition
# in Oracle '' is saved as null therefore need to save ' ' in not null column
post = categories(:general).post_with_conditions.build(body: " ")
@@ -939,7 +937,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_not_nil Developer._reflections["shared_computers"]
# Checking the fixture for named association is important here, because it's the only way
# we've been able to reproduce this bug
- assert_not_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers")
+ assert_not_nil File.read(File.expand_path("../../fixtures/developers.yml", __dir__)).index("shared_computers")
assert_equal developers(:david).shared_computers.first, computers(:laptop)
end
@@ -950,13 +948,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
end
- def test_with_constant_class_name
- assert_nothing_raised do
- developer = DeveloperWithConstantClassName.new
- developer.projects
- end
- end
-
def test_alternate_database
professor = Professor.create(name: "Plum")
course = Course.create(name: "Forensics")
@@ -1021,4 +1012,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
ActiveRecord::Base.partial_writes = original_partial_writes
end
end
+
+ def test_has_and_belongs_to_many_with_belongs_to
+ sink = Sink.create! kitchen: Kitchen.new, sources: [Source.new]
+ assert_equal 1, sink.sources.count
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ede3a44090..18548f8516 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -40,7 +42,7 @@ require "models/zine"
require "models/interest"
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
- fixtures :authors, :posts, :comments
+ fixtures :authors, :author_addresses, :posts, :comments
def test_should_generate_valid_sql
author = authors(:david)
@@ -51,7 +53,7 @@ class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCa
end
class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
- fixtures :authors, :essays, :subscribers, :subscriptions, :people
+ fixtures :authors, :author_addresses, :essays, :subscribers, :subscriptions, :people
def test_custom_primary_key_on_new_record_should_fetch_with_query
subscriber = Subscriber.new(nick: "webster132")
@@ -61,7 +63,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
assert_equal 2, subscriber.subscriptions.size
end
- assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: "webster132")
+ assert_equal Subscription.where(subscriber_id: "webster132"), subscriber.subscriptions
end
def test_association_primary_key_on_new_record_should_fetch_with_query
@@ -72,12 +74,23 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
assert_equal 1, author.essays.size
end
- assert_equal author.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), author.essays
end
def test_has_many_custom_primary_key
david = authors(:david)
- assert_equal david.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), david.essays
+ end
+
+ def test_ids_on_unloaded_association_with_custom_primary_key
+ david = people(:david)
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
+ end
+
+ def test_ids_on_loaded_association_with_custom_primary_key
+ david = people(:david)
+ david.essays.load
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
end
def test_has_many_assignment_with_custom_primary_key
@@ -100,7 +113,7 @@ end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
- :developers_projects, :topics, :authors, :comments,
+ :developers_projects, :topics, :authors, :author_addresses, :comments,
:posts, :readers, :taggings, :cars, :jobs, :tags,
:categorizations, :zines, :interests
@@ -241,6 +254,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "defaulty", bulb.name
end
+ def test_build_from_association_sets_inverse_instance
+ car = Car.new(name: "honda")
+
+ bulb = car.bulbs.build
+ assert_equal car, bulb.car
+ end
+
def test_do_not_call_callbacks_for_delete_all
car = Car.create(name: "honda")
car.funky_bulbs.create!
@@ -456,7 +476,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_resets_cached_counters
+ Reader.delete_all
+
person = Person.create!(first_name: "tenderlove")
+
post = Post.first
assert_equal [], person.readers
@@ -490,21 +513,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_predicate person.references, :exists?
end
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
- assert_equal 3, Firm.all.merge!(order: "id").first.clients.count
+ assert_equal 3, Firm.first.clients.count
end
def test_counting
- assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count
+ assert_equal 3, Firm.first.plain_clients.count
end
def test_counting_with_single_hash
- assert_equal 1, Firm.all.merge!(order: "id").first.plain_clients.where(name: "Microsoft").count
+ assert_equal 1, Firm.first.plain_clients.where(name: "Microsoft").count
end
def test_counting_with_column_name_and_hash
- assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count(:name)
+ assert_equal 3, Firm.first.plain_clients.count(:name)
end
def test_counting_with_association_limit
@@ -514,7 +536,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding
- assert_equal 3, Firm.all.merge!(order: "id").first.clients.length
+ assert_equal 3, Firm.first.clients.length
end
def test_finding_array_compatibility
@@ -554,8 +576,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_taking_with_a_number
+ klass = Class.new(Author) do
+ has_many :posts, -> { order(:id) }
+
+ def self.name
+ "Author"
+ end
+ end
+
# taking from unloaded Relation
- bob = Author.find(authors(:bob).id)
+ bob = klass.find(authors(:bob).id)
new_post = bob.posts.build
assert_not bob.posts.loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
@@ -586,27 +616,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding_default_orders
- assert_equal "Summit", Firm.all.merge!(order: "id").first.clients.first.name
+ assert_equal "Summit", Firm.first.clients.first.name
end
def test_finding_with_different_class_name_and_order
- assert_equal "Apex", Firm.all.merge!(order: "id").first.clients_sorted_desc.first.name
+ assert_equal "Apex", Firm.first.clients_sorted_desc.first.name
end
def test_finding_with_foreign_key
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_of_firm.first.name
+ assert_equal "Microsoft", Firm.first.clients_of_firm.first.name
end
def test_finding_with_condition
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms.first.name
+ assert_equal "Microsoft", Firm.first.clients_like_ms.first.name
end
def test_finding_with_condition_hash
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms_with_hash_conditions.first.name
+ assert_equal "Microsoft", Firm.first.clients_like_ms_with_hash_conditions.first.name
end
def test_finding_using_primary_key
- assert_equal "Summit", Firm.all.merge!(order: "id").first.clients_using_primary_key.first.name
+ assert_equal "Summit", Firm.first.clients_using_primary_key.first.name
end
def test_update_all_on_association_accessed_before_save
@@ -629,7 +659,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_ids
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
@@ -649,7 +679,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_one_message_on_primary_key
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
e = assert_raises(ActiveRecord::RecordNotFound) do
firm.clients.find(0)
@@ -675,7 +705,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_equal 3, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
end
@@ -720,25 +750,59 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all_sanitized
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
summit = firm.clients.where("name = 'Summit'").to_a
assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
assert_equal summit, firm.clients.where("name = :name", name: "Summit").to_a
end
def test_find_first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.order("id").first
assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
end
def test_find_first_sanitized
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client2 = Client.find(2)
- assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = ?", "Client"], order: "id").first
- assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = :type", { type: "Client" }], order: "id").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = ?", "Client").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = :type", type: "Client").first
+ end
+
+ def test_find_first_after_reset_scope
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, firm.clients.first, "Expected #first to return a new object"
+ end
+
+ def test_find_first_after_reset
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reset
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reset to return a new object"
+ end
+
+ def test_find_first_after_reload
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reload
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reload to return a new object"
end
def test_find_all_with_include_and_conditions
@@ -767,7 +831,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_scoped_grouped_having
- assert_equal 1, authors(:david).popular_grouped_posts.length
+ assert_equal 2, authors(:david).popular_grouped_posts.length
assert_equal 0, authors(:mary).popular_grouped_posts.length
end
@@ -783,6 +847,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id)
end
+ def test_select_with_block_and_dirty_target
+ assert_equal 2, posts(:welcome).comments.select { true }.size
+ posts(:welcome).comments.build
+ assert_equal 3, posts(:welcome).comments.select { true }.size
+ end
+
def test_select_without_foreign_key
assert_equal companies(:first_firm).accounts.first.credit_limit, companies(:first_firm).accounts.select(:credit_limit).first.credit_limit
end
@@ -827,7 +897,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.plain_clients.create!
end
end
@@ -1349,7 +1419,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
Client.create(client_of: firm.id, name: "SmallTime Inc.")
# only one of two clients is included in the association due to the :conditions key
assert_equal 2, Client.where(client_of: firm.id).size
- assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
+ assert_equal 1, firm.dependent_hash_conditional_clients_of_firm.size
firm.destroy
# only the correctly associated client should have been deleted
assert_equal 1, Client.where(client_of: firm.id).size
@@ -1509,8 +1579,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_destroy_dependent_when_deleted_from_association
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_equal 3, firm.clients.size
client = firm.clients.first
@@ -1620,7 +1689,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_less
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
@@ -1634,7 +1703,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_new
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
@@ -1897,7 +1966,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_many_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert firm.clients.many? }
end
@@ -1936,7 +2005,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_none_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert ! firm.clients.none? }
end
@@ -1971,7 +2040,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_one_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert ! firm.clients.one? }
end
@@ -2031,14 +2100,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal client_association.new.attributes, client_association.send(:new).attributes
end
- def test_respond_to_private_class_methods
- client_association = companies(:first_firm).clients
- assert !client_association.respond_to?(:private_method)
- assert client_association.respond_to?(:private_method, true)
- end
-
def test_creating_using_primary_key
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client = firm.clients_using_primary_key.create!(name: "test")
assert_equal firm.name, client.firm_name
end
@@ -2140,6 +2203,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "Post", tagging.taggable_type
end
+ def test_build_from_polymorphic_association_sets_inverse_instance
+ post = Post.new
+ tagging = post.taggings.build
+
+ assert_equal post, tagging.taggable
+ end
+
def test_dont_call_save_callbacks_twice_on_has_many
firm = companies(:first_firm)
contract = firm.contracts.create!
@@ -2251,7 +2321,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "association with extend option with multiple extensions" do
post = posts(:welcome)
assert_equal "lifo", post.comments_with_extend_2.author
- assert_equal "hello", post.comments_with_extend_2.greeting
+ assert_equal "hullo", post.comments_with_extend_2.greeting
+ end
+
+ test "extend option affects per association" do
+ post = posts(:welcome)
+ assert_equal "lifo", post.comments_with_extend.author
+ assert_equal "lifo", post.comments_with_extend_2.author
+ assert_equal "hello", post.comments_with_extend.greeting
+ assert_equal "hullo", post.comments_with_extend_2.greeting
end
test "delete record with complex joins" do
@@ -2272,7 +2350,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "does not duplicate associations when used with natural primary keys" do
speedometer = Speedometer.create!(id: "4")
- speedometer.minivans.create!(minivan_id: "a-van-red" , name: "a van", color: "red")
+ speedometer.minivans.create!(minivan_id: "a-van-red", name: "a van", color: "red")
assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
assert_equal 1, speedometer.reload.minivans.to_a.size
@@ -2311,8 +2389,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
car = Car.create!
bulb = Bulb.create! name: "other", car: car
- assert_equal bulb, Car.find(car.id).all_bulbs.first
- assert_equal bulb, Car.includes(:all_bulbs).find(car.id).all_bulbs.first
+ assert_equal [bulb], Car.find(car.id).all_bulbs
+ assert_equal [bulb], Car.includes(:all_bulbs).find(car.id).all_bulbs
+ assert_equal [bulb], Car.eager_load(:all_bulbs).find(car.id).all_bulbs
end
test "raises RecordNotDestroyed when replaced child can't be destroyed" do
@@ -2430,6 +2509,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_same car, new_bulb.car
end
+ test "reattach to new objects replaces inverse association and foreign key" do
+ bulb = Bulb.create!(car: Car.create!)
+ assert bulb.car_id
+ car = Car.new
+ car.bulbs << bulb
+ assert_equal car, bulb.car
+ assert_nil bulb.car_id
+ end
+
test "in memory replacement maintains order" do
first_bulb = Bulb.create!
second_bulb = Bulb.create!
@@ -2441,15 +2529,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
- test "double insertion of new object to association when same association used in the after create callback of a new object" do
+ test "association size calculation works with default scoped selects when not previously fetched" do
+ firm = Firm.create!(name: "Firm")
+ 5.times { firm.developers_with_select << Developer.create!(name: "Developer") }
+
+ same_firm = Firm.find(firm.id)
+ assert_equal 5, same_firm.developers_with_select.size
+ end
+
+ test "prevent double insertion of new object when the parent association loaded in the after save callback" do
reset_callbacks(:save, Bulb) do
Bulb.after_save { |record| record.car.bulbs.load }
+
car = Car.create!
car.bulbs << Bulb.new
+
assert_equal 1, car.bulbs.size
end
end
+ test "prevent double firing the before save callback of new object when the parent association saved in the callback" do
+ reset_callbacks(:save, Bulb) do
+ count = 0
+ Bulb.before_save { |record| record.car.save && count += 1 }
+
+ car = Car.create!
+ car.bulbs.create!
+
+ assert_equal 1, count
+ end
+ end
+
class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
self.table_name = "authors"
has_many :posts_with_error_destroying,
@@ -2486,11 +2596,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb.id], car.bulb_ids
assert_no_queries { car.bulb_ids }
+
+ bulb2 = car.bulbs.create!
+
+ assert_equal [bulb.id, bulb2.id], car.bulb_ids
+ assert_no_queries { car.bulb_ids }
end
def test_loading_association_in_validate_callback_doesnt_affect_persistence
reset_callbacks(:validation, Bulb) do
- Bulb.after_validation { |m| m.car.bulbs.load }
+ Bulb.after_validation { |record| record.car.bulbs.load }
car = Car.create!(name: "Car")
bulb = car.bulbs.create!
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index ea52fb5a67..56a4b7c4d1 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/person"
@@ -64,10 +66,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
club1.members.sort_by(&:id)
end
- def make_model(name)
- Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
- end
-
def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
def self.name; "Person"; end
@@ -152,20 +150,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert after_destroy_called, "after destroy should be called"
end
- def make_no_pk_hm_t
- lesson = make_model "Lesson"
- student = make_model "Student"
-
- lesson_student = make_model "LessonStudent"
- lesson_student.table_name = "lessons_students"
-
- lesson_student.belongs_to :lesson, anonymous_class: lesson
- lesson_student.belongs_to :student, anonymous_class: student
- lesson.has_many :lesson_students, anonymous_class: lesson_student
- lesson.has_many :students, through: :lesson_students, anonymous_class: student
- [lesson, lesson_student, student]
- end
-
def test_pk_is_not_required_for_join
post = Post.includes(:scategories).first
post2 = Post.includes(:categories).first
@@ -337,6 +321,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_includes post.single_people, person
end
+ def test_build_then_remove_then_save
+ post = posts(:thinking)
+ post.people.build(first_name: "Bob")
+ ted = post.people.build(first_name: "Ted")
+ post.people.delete(ted)
+ post.save!
+ post.reload
+
+ assert_equal ["Bob"], post.people.collect(&:first_name)
+ end
+
def test_both_parent_ids_set_when_saving_new
post = Post.new(title: "Hello", body: "world")
person = Person.new(first_name: "Sean")
@@ -822,7 +817,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
sarah = Person.create!(first_name: "Sarah", primary_contact_id: people(:susan).id, gender: "F", number1_fan_id: 1)
john = Person.create!(first_name: "John", primary_contact_id: sarah.id, gender: "M", number1_fan_id: 1)
assert_equal sarah.agents, [john]
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
+ assert_equal people(:susan).agents.flat_map(&:agents).sort, people(:susan).agents_of_agents.sort
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
@@ -874,6 +869,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [dev], company.developers
end
+ def test_collection_singular_ids_setter_with_required_type_cast
+ company = companies(:rails_core)
+ dev = Developer.first
+
+ company.developer_ids = [dev.id.to_s]
+ assert_equal [dev], company.developers
+ end
+
def test_collection_singular_ids_setter_with_string_primary_keys
assert_nothing_raised do
book = books(:awdr)
@@ -885,32 +888,20 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_collection_singular_ids_setter_with_changed_primary_key
- company = companies(:first_firm)
- client = companies(:first_client)
- company.clients_using_primary_key_ids = [client.name]
- assert_equal [client], company.clients_using_primary_key
- end
-
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
ids = [Developer.first.id, -9999]
e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
- assert_match(/Couldn't find all Developers with 'id'/, e.message)
- end
-
- def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
- company = companies(:first_firm)
- ids = [Client.first.name, "unknown client"]
- e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids }
- assert_match(/Couldn't find all Clients with 'name'/, e.message)
+ msg = "Couldn't find all Developers with 'id': (1, -9999) (found 1 results, but was looking for 2). Couldn't find Developer with id -9999."
+ assert_equal(msg, e.message)
end
def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
author = authors(:david)
ids = [categories(:general).name, "Unknown"]
e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
- assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
+ msg = "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2). Couldn't find Category with name Unknown."
+ assert_equal msg, e.message
end
def test_build_a_model_from_hm_through_association_with_where_clause
@@ -953,6 +944,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_has_many_through_polymorphic_with_rewhere
+ post = TaggedPost.create!(title: "Tagged", body: "Post")
+ tag = post.tags.create!(name: "Tag")
+ assert_equal [tag], TaggedPost.preload(:tags).last.tags
+ assert_equal [tag], TaggedPost.eager_load(:tags).last.tags
+ end
+
def test_has_many_through_polymorphic_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories
@@ -1135,6 +1133,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
end
+ def test_has_many_through_associations_sum_on_columns
+ post1 = Post.create(title: "active", body: "sample")
+ post2 = Post.create(title: "inactive", body: "sample")
+
+ person1 = Person.create(first_name: "aaron", followers_count: 1)
+ person2 = Person.create(first_name: "schmit", followers_count: 2)
+ person3 = Person.create(first_name: "bill", followers_count: 3)
+ person4 = Person.create(first_name: "cal", followers_count: 4)
+
+ Reader.create(post_id: post1.id, person_id: person1.id)
+ Reader.create(post_id: post1.id, person_id: person2.id)
+ Reader.create(post_id: post1.id, person_id: person3.id)
+ Reader.create(post_id: post1.id, person_id: person4.id)
+
+ Reader.create(post_id: post2.id, person_id: person1.id)
+ Reader.create(post_id: post2.id, person_id: person2.id)
+ Reader.create(post_id: post2.id, person_id: person3.id)
+ Reader.create(post_id: post2.id, person_id: person4.id)
+
+ active_persons = Person.joins(:readers).joins(:posts).distinct(true).where("posts.title" => "active")
+
+ assert_equal active_persons.map(&:followers_count).reduce(:+), 10
+ assert_equal active_persons.sum(:followers_count), 10
+ assert_equal active_persons.sum(:followers_count), active_persons.map(&:followers_count).reduce(:+)
+ end
+
def test_has_many_through_associations_on_new_records_use_null_relations
person = Person.new
@@ -1234,6 +1258,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
TenantMembership.current_member = nil
end
+ def test_has_many_through_with_scope_that_has_joined_same_table_with_parent_relation
+ assert_equal authors(:david), Author.joins(:comments_for_first_author).take
+ end
+
+ def test_has_many_through_with_unscope_should_affect_to_through_scope
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments
+ end
+
+ def test_has_many_through_with_scope_should_accept_string_and_hash_join
+ assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take
+ end
+
def test_has_many_through_with_scope_should_respect_table_alias
family = Family.create!
users = 3.times.map { User.create! }
@@ -1245,6 +1281,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 0, users[2].family_members.to_a.size
end
+ def test_through_scope_is_affected_by_unscoping
+ author = authors(:david)
+
+ expected = author.comments.to_a
+ FirstPost.unscoped do
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
+ def test_through_scope_isnt_affected_by_scoping
+ author = authors(:david)
+
+ expected = author.comments_on_first_posts.to_a
+ FirstPost.where(id: 2).scoping do
+ author.comments_on_first_posts.reset
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
def test_incorrectly_ordered_through_associations
assert_raises(ActiveRecord::HasManyThroughOrderError) do
DeveloperWithIncorrectlyOrderedHasManyThrough.create(
@@ -1252,4 +1307,87 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
)
end
end
+
+ def test_has_many_through_update_ids_with_conditions
+ author = Author.create!(name: "Bill")
+ category = categories(:general)
+
+ author.update(
+ special_categories_with_condition_ids: [category.id],
+ nonspecial_categories_with_condition_ids: [category.id]
+ )
+
+ assert_equal [category.id], author.special_categories_with_condition_ids
+ assert_equal [category.id], author.nonspecial_categories_with_condition_ids
+
+ author.update(nonspecial_categories_with_condition_ids: [])
+ author.reload
+
+ assert_equal [category.id], author.special_categories_with_condition_ids
+ assert_equal [], author.nonspecial_categories_with_condition_ids
+ 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 = authors(:mary)
+ book1 = Book.create!(name: "essays on single has many through associations 1")
+ post.author.books << book1
+ subscription1 = Subscription.first
+ book1.subscriptions << subscription1
+ assert_equal [subscription1], post.subscriptions.to_a
+
+ post.author = authors(:bob)
+ book2 = Book.create!(name: "essays on single has many through associations 2")
+ post.author.books << book2
+ subscription2 = Subscription.second
+ book2.subscriptions << subscription2
+ assert_equal [subscription2], 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 = authors(:mary)
+ book1 = Book.create!(name: "essays on nested has many through associations 1")
+ post.author.books << book1
+ subscription1 = Subscription.first
+ book1.subscriptions << subscription1
+ assert_equal [subscription1], post.subscriptions.to_a
+
+ post.author = authors(:bob)
+ book2 = Book.create!(name: "essays on nested has many through associations 2")
+ post.author.books << book2
+ subscription2 = Subscription.second
+ book2.subscriptions << subscription2
+ assert_equal [subscription2], post.subscriptions.to_a
+ end
+
+ private
+ def make_model(name)
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
+ end
+
+ def make_no_pk_hm_t
+ lesson = make_model "Lesson"
+ student = make_model "Student"
+
+ lesson_student = make_model "LessonStudent"
+ lesson_student.table_name = "lessons_students"
+
+ lesson_student.belongs_to :lesson, anonymous_class: lesson
+ lesson_student.belongs_to :student, anonymous_class: student
+ lesson.has_many :lesson_students, anonymous_class: lesson_student
+ lesson.has_many :students, through: :lesson_students, anonymous_class: student
+ [lesson, lesson_student, student]
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 7c11d2e7fc..ec5d95080b 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -13,7 +15,7 @@ require "models/post"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates, :authors, :author_addresses
def setup
Account.destroyed_account_ids.clear
@@ -28,7 +30,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
companies(:first_firm).account
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query"
+ log_all = ActiveRecord::SQLCounter.log_all
+ assert log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{log_all}"
end
def test_has_one_cache_nils
@@ -307,6 +310,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_create_when_parent_is_new_raises
+ firm = Firm.new
+ error = assert_raise(ActiveRecord::RecordNotSaved) do
+ firm.create_account
+ end
+
+ assert_equal "You cannot call create unless the parent is saved", error.message
+ end
+
def test_reload_association
odegy = companies(:odegy)
@@ -679,4 +691,38 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
SpecialAuthor.joins(book: :subscription).where.not(where_clause)
end
end
+
+ class DestroyByParentBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "DestroyByParentAuthor"
+ before_destroy :dont, unless: :destroyed_by_association
+
+ def dont
+ throw(:abort)
+ end
+ end
+
+ class DestroyByParentAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_one :book, class_name: "DestroyByParentBook", foreign_key: "author_id", dependent: :destroy
+ end
+
+ test "destroyed_by_association set in child destroy callback on parent destroy" do
+ author = DestroyByParentAuthor.create!(name: "Test")
+ book = DestroyByParentBook.create!(author: author)
+
+ author.destroy
+
+ assert_not DestroyByParentBook.exists?(book.id)
+ end
+
+ test "destroyed_by_association set in child destroy callback on replace" do
+ author = DestroyByParentAuthor.create!(name: "Test")
+ book = DestroyByParentBook.create!(author: author)
+
+ author.book = DestroyByParentBook.create!
+ author.save!
+
+ assert_not DestroyByParentBook.exists?(book.id)
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 38a729d2d4..1d37457464 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/club"
require "models/member_type"
@@ -23,7 +25,7 @@ require "models/customer_carrier"
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans,
- :dashboards, :speedometers, :authors, :posts, :comments, :categories, :essays, :owners
+ :dashboards, :speedometers, :authors, :author_addresses, :posts, :comments, :categories, :essays, :owners
def setup
@member = members(:groucho)
@@ -98,7 +100,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_through_eager_loading
- members = assert_queries(3) do #base table, through table, clubs table
+ members = assert_queries(3) do # base table, through table, clubs table
Member.all.merge!(includes: :club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
@@ -106,7 +108,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_through_eager_loading_through_polymorphic
- members = assert_queries(3) do #base table, through table, clubs table
+ members = assert_queries(3) do # base table, through table, clubs table
Member.all.merge!(includes: :sponsor_club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
@@ -137,7 +139,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
- Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
+ Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].club }
@@ -145,7 +147,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
- Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
@@ -154,7 +156,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save!
members = assert_queries(1) do
- Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index 7414869c8f..7be875fec6 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -10,7 +12,7 @@ require "models/tagging"
require "models/tag"
class InnerJoinAssociationTest < ActiveRecord::TestCase
- fixtures :authors, :essays, :posts, :comments, :categories, :categories_posts, :categorizations,
+ fixtures :authors, :author_addresses, :essays, :posts, :comments, :categories, :categories_posts, :categorizations,
:taggings, :tags
def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
@@ -25,6 +27,24 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
end
+ def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations_with_left_outer_joins
+ sql = Person.joins(agents: :agents).left_outer_joins(agents: :agents).to_sql
+ assert_match(/agents_people_4/i, sql)
+ end
+
+ def test_construct_finder_sql_does_not_table_name_collide_with_string_joins
+ sql = Person.joins(:agents).joins("JOIN people agents_people ON agents_people.primary_contact_id = people.id").to_sql
+ assert_match(/agents_people_2/i, sql)
+ end
+
+ def test_construct_finder_sql_does_not_table_name_collide_with_aliased_joins
+ people = Person.arel_table
+ agents = people.alias("agents_people")
+ constraint = agents[:primary_contact_id].eq(people[:id])
+ sql = Person.joins(:agents).joins(agents.create_join(agents, agents.create_on(constraint))).to_sql
+ assert_match(/agents_people_2/i, sql)
+ end
+
def test_construct_finder_sql_ignores_empty_joins_hash
sql = Author.joins({}).to_sql
assert_no_match(/JOIN/i, sql)
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 287b3e9ebc..c0d328ca8a 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/man"
require "models/face"
@@ -16,6 +18,8 @@ require "models/admin/user"
require "models/developer"
require "models/company"
require "models/project"
+require "models/author"
+require "models/post"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -24,11 +28,9 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
monkey_reflection = MixedCaseMonkey.reflect_on_association(:man)
man_reflection = Man.reflect_on_association(:mixed_case_monkey)
- assert_respond_to monkey_reflection, :has_inverse?
assert monkey_reflection.has_inverse?, "The monkey reflection should have an inverse"
assert_equal man_reflection, monkey_reflection.inverse_of, "The monkey reflection's inverse should be the man reflection"
- assert_respond_to man_reflection, :has_inverse?
assert man_reflection.has_inverse?, "The man reflection should have an inverse"
assert_equal monkey_reflection, man_reflection.inverse_of, "The man reflection's inverse should be the monkey reflection"
end
@@ -37,7 +39,6 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
account_reflection = Admin::Account.reflect_on_association(:users)
user_reflection = Admin::User.reflect_on_association(:account)
- assert_respond_to account_reflection, :has_inverse?
assert account_reflection.has_inverse?, "The Admin::Account reflection should have an inverse"
assert_equal user_reflection, account_reflection.inverse_of, "The Admin::Account reflection's inverse should be the Admin::User reflection"
end
@@ -46,11 +47,9 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
car_reflection = Car.reflect_on_association(:bulb)
bulb_reflection = Bulb.reflect_on_association(:car)
- assert_respond_to car_reflection, :has_inverse?
assert car_reflection.has_inverse?, "The Car reflection should have an inverse"
assert_equal bulb_reflection, car_reflection.inverse_of, "The Car reflection's inverse should be the Bulb reflection"
- assert_respond_to bulb_reflection, :has_inverse?
assert bulb_reflection.has_inverse?, "The Bulb reflection should have an inverse"
assert_equal car_reflection, bulb_reflection.inverse_of, "The Bulb reflection's inverse should be the Car reflection"
end
@@ -59,11 +58,24 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
comment_reflection = Comment.reflect_on_association(:ratings)
rating_reflection = Rating.reflect_on_association(:comment)
- assert_respond_to comment_reflection, :has_inverse?
assert comment_reflection.has_inverse?, "The Comment reflection should have an inverse"
assert_equal rating_reflection, comment_reflection.inverse_of, "The Comment reflection's inverse should be the Rating reflection"
end
+ def test_has_many_and_belongs_to_should_find_inverse_automatically_for_sti
+ author_reflection = Author.reflect_on_association(:posts)
+ author_child_reflection = Author.reflect_on_association(:special_posts)
+ post_reflection = Post.reflect_on_association(:author)
+
+ assert_respond_to author_reflection, :has_inverse?
+ assert author_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+
+ assert_respond_to author_child_reflection, :has_inverse?
+ assert author_child_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_child_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+ end
+
def test_has_one_and_belongs_to_automatic_inverse_shares_objects
car = Car.first
bulb = Bulb.create!(car: car)
@@ -107,24 +119,17 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
def test_polymorphic_and_has_many_through_relationships_should_not_have_inverses
sponsor_reflection = Sponsor.reflect_on_association(:sponsorable)
- assert_respond_to sponsor_reflection, :has_inverse?
assert !sponsor_reflection.has_inverse?, "A polymorphic association should not find an inverse automatically"
club_reflection = Club.reflect_on_association(:members)
- assert_respond_to club_reflection, :has_inverse?
assert !club_reflection.has_inverse?, "A has_many_through association should not find an inverse automatically"
end
- def test_polymorphic_relationships_should_still_not_have_inverses_when_non_polymorphic_relationship_has_the_same_name
+ def test_polymorphic_has_one_should_find_inverse_automatically
man_reflection = Man.reflect_on_association(:polymorphic_face_without_inverse)
- face_reflection = Face.reflect_on_association(:man)
- assert_respond_to face_reflection, :has_inverse?
- assert face_reflection.has_inverse?, "For this test, the non-polymorphic association must have an inverse"
-
- assert_respond_to man_reflection, :has_inverse?
- assert !man_reflection.has_inverse?, "The target of a polymorphic association should not find an inverse automatically"
+ assert man_reflection.has_inverse?
end
end
@@ -145,41 +150,24 @@ class InverseAssociationTests < ActiveRecord::TestCase
def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
has_one_with_inverse_ref = Man.reflect_on_association(:face)
- assert_respond_to has_one_with_inverse_ref, :has_inverse?
assert has_one_with_inverse_ref.has_inverse?
has_many_with_inverse_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_with_inverse_ref, :has_inverse?
assert has_many_with_inverse_ref.has_inverse?
belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_with_inverse_ref, :has_inverse?
assert belongs_to_with_inverse_ref.has_inverse?
has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
- assert_respond_to has_one_without_inverse_ref, :has_inverse?
assert !has_one_without_inverse_ref.has_inverse?
has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
- assert_respond_to has_many_without_inverse_ref, :has_inverse?
assert !has_many_without_inverse_ref.has_inverse?
belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
- assert_respond_to belongs_to_without_inverse_ref, :has_inverse?
assert !belongs_to_without_inverse_ref.has_inverse?
end
- def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
- has_one_ref = Man.reflect_on_association(:face)
- assert_respond_to has_one_ref, :inverse_of
-
- has_many_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_ref, :inverse_of
-
- belongs_to_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_ref, :inverse_of
- end
-
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
has_one_ref = Man.reflect_on_association(:face)
assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of
@@ -295,7 +283,7 @@ class InverseHasOneTests < ActiveRecord::TestCase
end
class InverseHasManyTests < ActiveRecord::TestCase
- fixtures :men, :interests
+ fixtures :men, :interests, :posts, :authors, :author_addresses
def test_parent_instance_should_be_shared_with_every_child_on_find
m = men(:gordon)
@@ -309,6 +297,27 @@ class InverseHasManyTests < ActiveRecord::TestCase
end
end
+ def test_parent_instance_should_be_shared_with_every_child_on_find_for_sti
+ a = authors(:david)
+ ps = a.posts
+ ps.each do |p|
+ assert_equal a.name, p.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to parent instance"
+ p.author.name = "Mungo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ sps = a.special_posts
+ sps.each do |sp|
+ assert_equal a.name, sp.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to parent instance"
+ sp.author.name = "Mungo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+ end
+
def test_parent_instance_should_be_shared_with_eager_loaded_children
m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests).first
is = m.interests
@@ -475,7 +484,10 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_raise_record_not_found_error_when_no_ids_are_passed
man = Man.create!
- assert_raise(ActiveRecord::RecordNotFound) { man.interests.find() }
+ exception = assert_raise(ActiveRecord::RecordNotFound) { man.interests.load.find() }
+
+ assert_equal exception.model, "Interest"
+ assert_equal exception.primary_key, "id"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
@@ -651,20 +663,6 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
- def test_child_instance_should_be_shared_with_replaced_via_method_parent
- face = faces(:confused)
- new_man = Man.new
-
- assert_not_nil face.polymorphic_man
- face.polymorphic_man = new_man
-
- assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
- face.description = "Bongo"
- assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
- new_man.polymorphic_face.description = "Mungo"
- assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
- end
-
def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed
new_man = Man.new
face = Face.new
@@ -677,6 +675,16 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
assert_equal old_inversed_man.object_id, new_inversed_man.object_id
end
+ def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed_with_validation
+ face = Face.new man: Man.new
+
+ old_inversed_man = face.man
+ face.save!
+ new_inversed_man = face.man
+
+ assert_equal old_inversed_man.object_id, new_inversed_man.object_id
+ end
+
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
i = interests(:llama_wrangling)
m = i.polymorphic_man
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index a4345f3857..5d83c9435b 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/tag"
require "models/tagging"
@@ -19,7 +21,7 @@ require "models/car"
class AssociationsJoinModelTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books,
+ fixtures :posts, :authors, :author_addresses, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books,
# Reload edges table from fixtures as otherwise repeated test was failing
:edges
@@ -97,11 +99,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
- post = SubStiPost.create title: "SubStiPost", body: "SubStiPost body"
- assert_instance_of SubStiPost, post
+ post = SubAbstractStiPost.create title: "SubAbstractStiPost", body: "SubAbstractStiPost body"
+ assert_instance_of SubAbstractStiPost, post
tagging = tags(:misc).taggings.create(taggable: post)
- assert_equal "SubStiPost", tagging.taggable_type
+ assert_equal "SubAbstractStiPost", tagging.taggable_type
end
def test_polymorphic_has_many_going_through_join_model_with_inheritance
@@ -402,7 +404,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2
+ assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2.sort_by(&:id)
end
def test_has_many_through_polymorphic_has_many
@@ -465,7 +467,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
new_tag = Tag.new(name: "new")
saved_post.tags << new_tag
- assert new_tag.persisted? #consistent with habtm!
+ assert new_tag.persisted? # consistent with habtm!
assert saved_post.persisted?
assert_includes saved_post.tags, new_tag
@@ -494,25 +496,25 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
post_thinking = posts(:thinking)
assert_nothing_raised { post_thinking.tags << push }
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 1, post_thinking.reload.tags.size)
assert_equal(count + 1, post_thinking.tags.reload.size)
assert_kind_of Tag, post_thinking.tags.create!(name: "foo")
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 2, post_thinking.reload.tags.size)
assert_equal(count + 2, post_thinking.tags.reload.size)
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(name: "abc"), Tag.create!(name: "def")) }
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 4, post_thinking.reload.tags.size)
assert_equal(count + 4, post_thinking.tags.reload.size)
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
index 42dbbad1c8..c95d0425cd 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -7,7 +9,7 @@ require "models/categorization"
require "models/person"
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
- fixtures :authors, :essays, :posts, :comments, :categorizations, :people
+ fixtures :authors, :author_addresses, :essays, :posts, :comments, :categorizations, :people
def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
result = Author.left_outer_joins(:thinking_posts, :welcome_posts).to_a
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index dc26f6a383..65d30d011b 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/post"
@@ -22,9 +24,14 @@ require "models/category"
require "models/categorization"
require "models/membership"
require "models/essay"
+require "models/hotel"
+require "models/department"
+require "models/chef"
+require "models/cake_designer"
+require "models/drink_designer"
class NestedThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :authors, :books, :posts, :subscriptions, :subscribers, :tags, :taggings,
+ fixtures :authors, :author_addresses, :books, :posts, :subscriptions, :subscribers, :tags, :taggings,
:people, :readers, :references, :jobs, :ratings, :comments, :members, :member_details,
:member_types, :sponsors, :clubs, :organizations, :categories, :categories_posts,
:categorizations, :memberships, :essays
@@ -423,6 +430,11 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert authors.empty?
end
+ def test_nested_has_many_through_with_scope_on_polymorphic_reflection
+ authors = Author.joins(:ordered_posts).where("posts.id" => posts(:misc_by_bob).id)
+ assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id)
+ end
+
def test_has_many_through_with_foreign_key_option_on_through_reflection
assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id")
assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors
@@ -567,6 +579,37 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert !c.post_taggings.empty?
end
+ def test_polymorphic_has_many_through_when_through_association_has_not_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_when_through_association_has_already_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_joined_different_table_twice
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ hotel = Hotel.create!(departments: [department])
+
+ assert_equal hotel, Hotel.joins(:cake_designers, :drink_designers).take
+ end
+
private
def assert_includes_and_joins_equal(query, expected, association)
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 45e1803858..65a3bb5efe 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class RequiredAssociationsTest < ActiveRecord::TestCase