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.rb560
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb6
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb75
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb103
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb90
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb66
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb42
-rw-r--r--activerecord/test/cases/associations/eager_test.rb763
-rw-r--r--activerecord/test/cases/associations/extension_test.rb33
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb454
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb1440
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb793
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb380
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb196
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb92
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb370
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb295
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb70
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb208
-rw-r--r--activerecord/test/cases/associations/required_test.rb44
21 files changed, 3928 insertions, 2168 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 472e270f8c..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 a3046d526e..3525fa2ab8 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,28 +1,30 @@
-require 'cases/helper'
-require 'models/developer'
-require 'models/project'
-require 'models/company'
-require 'models/topic'
-require 'models/reply'
-require 'models/computer'
-require 'models/post'
-require 'models/author'
-require 'models/tag'
-require 'models/tagging'
-require 'models/comment'
-require 'models/sponsor'
-require 'models/member'
-require 'models/essay'
-require 'models/toy'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/column'
-require 'models/record'
-require 'models/admin'
-require 'models/admin/user'
-require 'models/ship'
-require 'models/treasure'
-require 'models/parrot'
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/developer"
+require "models/project"
+require "models/company"
+require "models/topic"
+require "models/reply"
+require "models/computer"
+require "models/post"
+require "models/author"
+require "models/tag"
+require "models/tagging"
+require "models/comment"
+require "models/sponsor"
+require "models/member"
+require "models/essay"
+require "models/toy"
+require "models/invoice"
+require "models/line_item"
+require "models/column"
+require "models/record"
+require "models/admin"
+require "models/admin/user"
+require "models/ship"
+require "models/treasure"
+require "models/parrot"
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -30,9 +32,27 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
:posts, :tags, :taggings, :comments, :sponsors, :members
def test_belongs_to
- firm = Client.find(3).firm
- assert_not_nil firm
- assert_equal companies(:first_firm).name, firm.name
+ client = Client.find(3)
+ first_firm = companies(:first_firm)
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) do
+ assert_equal first_firm, client.firm
+ assert_equal first_firm.name, client.firm.name
+ end
+ end
+
+ def test_assigning_belongs_to_on_destroyed_object
+ client = Client.create!(name: "Client")
+ client.destroy!
+ assert_raise(FrozenError) { client.firm = nil }
+ assert_raise(FrozenError) { client.firm = Firm.new(name: "Firm") }
+ end
+
+ def test_eager_loading_wont_mutate_owner_record
+ client = Client.eager_load(:firm_with_basic_id).first
+ assert_not_predicate client, :firm_id_came_from_user?
+
+ client = Client.preload(:firm_with_basic_id).first
+ assert_not_predicate client, :firm_id_came_from_user?
end
def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute
@@ -43,11 +63,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
Client.find(3).firm
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, 'ORDER BY was used in the query'
+ sql_log = ActiveRecord::SQLCounter.log
+ assert sql_log.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{sql_log}"
end
def test_belongs_to_with_primary_key
- client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
+ client = Client.create(name: "Primary key client", firm_name: companies(:first_firm).name)
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
end
@@ -77,7 +98,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert account.valid?
+ assert_predicate account, :valid?
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -93,8 +114,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert_not account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_not_predicate account, :valid?
+ assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -110,31 +131,69 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert_not account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_not_predicate account, :valid?
+ assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
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
comments = Class.new(ActiveRecord::Base) {
- self.table_name = 'comments'
- self.inheritance_column = 'not_there'
+ self.table_name = "comments"
+ self.inheritance_column = "not_there"
posts = Class.new(ActiveRecord::Base) {
- self.table_name = 'posts'
- self.inheritance_column = 'not_there'
+ self.table_name = "posts"
+ self.inheritance_column = "not_there"
default_scope -> {
counter += 1
- where("id = :inc", :inc => counter)
+ where("id = :inc", inc: counter)
}
- has_many :comments, :anonymous_class => comments
+ has_many :comments, anonymous_class: comments
}
- belongs_to :post, :anonymous_class => posts, :inverse_of => false
+ belongs_to :post, anonymous_class: posts, inverse_of: false
}
assert_equal 0, counter
@@ -166,9 +225,9 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Admin.const_set "Region", Class.new(ActiveRecord::Base)
e = assert_raise(ActiveRecord::AssociationTypeMismatch) {
- Admin::RegionalUser.new(region: 'wrong value')
+ Admin::RegionalUser.new(region: "wrong value")
}
- assert_match(/^Region\([^)]+\) expected, got String\([^)]+\)$/, e.message)
+ assert_match(/^Region\([^)]+\) expected, got "wrong value" which is an instance of String\([^)]+\)$/, e.message)
ensure
Admin.send :remove_const, "Region" if Admin.const_defined?("Region")
Admin.send :remove_const, "RegionalUser" if Admin.const_defined?("RegionalUser")
@@ -177,6 +236,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
@@ -203,15 +264,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_eager_loading_with_primary_key
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key).first
- assert citibank_result.association(:firm_with_primary_key).loaded?
+ citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key).first
+ assert_predicate citibank_result.association(:firm_with_primary_key), :loaded?
end
def test_eager_loading_with_primary_key_as_symbol
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
- citibank_result = Client.all.merge!(:where => {:name => "Citibank"}, :includes => :firm_with_primary_key_symbols).first
- assert citibank_result.association(:firm_with_primary_key_symbols).loaded?
+ citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key_symbols).first
+ assert_predicate citibank_result.association(:firm_with_primary_key_symbols), :loaded?
end
def test_creating_the_belonging_object
@@ -223,8 +284,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal apple, citibank.firm
end
+ def test_creating_the_belonging_object_from_new_record
+ citibank = Account.new("credit_limit" => 10)
+ apple = citibank.create_firm("name" => "Apple")
+ assert_equal apple, citibank.firm
+ citibank.save
+ citibank.reload
+ assert_equal apple, citibank.firm
+ end
+
def test_creating_the_belonging_object_with_primary_key
- client = Client.create(:name => "Primary key client")
+ client = Client.create(name: "Primary key client")
apple = client.create_firm_with_primary_key("name" => "Apple")
assert_equal apple, client.firm_with_primary_key
client.save
@@ -247,48 +317,82 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_building_the_belonging_object_with_explicit_sti_base_class
account = Account.new
- company = account.build_firm(:type => "Company")
+ company = account.build_firm(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_belonging_object_with_sti_subclass
account = Account.new
- company = account.build_firm(:type => "Firm")
+ company = account.build_firm(type: "Firm")
assert_kind_of Firm, company, "Expected #{company.class} to be a Firm"
end
def test_building_the_belonging_object_with_an_invalid_type
account = Account.new
- assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(:type => "InvalidType") }
+ assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(type: "InvalidType") }
end
def test_building_the_belonging_object_with_an_unrelated_type
account = Account.new
- assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { account.build_firm(type: "Account") }
end
def test_building_the_belonging_object_with_primary_key
- client = Client.create(:name => "Primary key client")
+ client = Client.create(name: "Primary key client")
apple = client.build_firm_with_primary_key("name" => "Apple")
client.save
assert_equal apple.name, client.firm_name
end
def test_create!
- client = Client.create!(:name => "Jimmy")
- account = client.create_account!(:credit_limit => 10)
+ client = Client.create!(name: "Jimmy")
+ account = client.create_account!(credit_limit: 10)
assert_equal account, client.account
- assert account.persisted?
+ assert_predicate account, :persisted?
client.save
client.reload
assert_equal account, client.account
end
def test_failing_create!
- client = Client.create!(:name => "Jimmy")
+ client = Client.create!(name: "Jimmy")
assert_raise(ActiveRecord::RecordInvalid) { client.create_account! }
assert_not_nil client.account
- assert client.account.new_record?
+ assert_predicate client.account, :new_record?
+ end
+
+ def test_reloading_the_belonging_object
+ odegy_account = accounts(:odegy_account)
+
+ assert_equal "Odegy", odegy_account.firm.name
+ Company.where(id: odegy_account.firm_id).update_all(name: "ODEGY")
+ assert_equal "Odegy", odegy_account.firm.name
+
+ assert_equal "ODEGY", odegy_account.reload_firm.name
+ end
+
+ def test_reload_the_belonging_object_with_query_cache
+ odegy_account_id = accounts(:odegy_account).id
+
+ connection = ActiveRecord::Base.connection
+ connection.enable_query_cache!
+ connection.clear_query_cache
+
+ # Populate the cache with a query
+ odegy_account = Account.find(odegy_account_id)
+
+ # Populate the cache with a second query
+ odegy_account.firm
+
+ assert_equal 2, connection.query_cache.size
+
+ # Clear the cache and fetch the firm again, populating the cache with a query
+ assert_queries(1) { odegy_account.reload_firm }
+
+ # This query is not cached anymore, so it should make a real SQL query
+ assert_queries(1) { Account.find(odegy_account_id) }
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
end
def test_natural_assignment_to_nil
@@ -301,7 +405,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_natural_assignment_to_nil_with_primary_key
- client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
+ client = Client.create(name: "Primary key client", firm_name: companies(:first_firm).name)
client.firm_with_primary_key = nil
client.save
client.association(:firm_with_primary_key).reload
@@ -325,19 +429,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.association(:sponsorable).reload
assert_nil sponsor.sponsorable
- sponsor.sponsorable_type = '' # the column doesn't have to be declared NOT NULL
+ sponsor.sponsorable_type = "" # the column doesn't have to be declared NOT NULL
assert_nil sponsor.association(:sponsorable).send(:klass)
sponsor.association(:sponsorable).reload
assert_nil sponsor.sponsorable
- sponsor.sponsorable = Member.new :name => "Bert"
+ 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
sponsor = Sponsor.create
- member = Member.create :name => "Bert"
+ member = Member.create name: "Bert"
sponsor.sponsorable = member
assert_equal member, sponsor.sponsorable
@@ -345,17 +448,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_with_select
- assert_equal 1, Company.find(2).firm_with_select.attributes.size
- assert_equal 1, Company.all.merge!(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size
+ assert_equal 1, Post.find(2).author_with_select.attributes.size
+ assert_equal 1, Post.includes(:author_with_select).find(2).author_with_select.attributes.size
+ end
+
+ def test_custom_attribute_with_select
+ assert_equal 2, Company.find(2).firm_with_select.attributes.size
+ assert_equal 2, Company.includes(:firm_with_select).find(2).firm_with_select.attributes.size
end
def test_belongs_to_without_counter_cache_option
# Ship has a conventionally named `treasures_count` column, but the counter_cache
# option is not given on the association.
- ship = Ship.create(name: 'Countless')
+ ship = Ship.create(name: "Countless")
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed unless counter_cache is given on the relation" do
- treasure = Treasure.new(name: 'Gold', ship: ship)
+ treasure = Treasure.new(name: "Gold", ship: ship)
treasure.save
end
@@ -377,31 +485,70 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_with_assigning_nil
- post = Post.find(1)
- comment = Comment.find(1)
+ topic = Topic.create!(title: "debate")
+ reply = Reply.create!(title: "blah!", content: "world around!", topic: topic)
+
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies.size
+
+ reply.topic = nil
+ reply.reload
- assert_equal post.id, comment.post_id
- assert_equal 2, Post.find(post.id).comments.size
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies.size
- comment.post = nil
+ reply.topic = nil
+ reply.save!
- assert_equal 1, Post.find(post.id).comments.size
+ assert_equal 0, topic.reload.replies.size
+ end
+
+ def test_belongs_to_counter_with_assigning_new_object
+ topic = Topic.create!(title: "debate")
+ reply = Reply.create!(title: "blah!", content: "world around!", topic: topic)
+
+ assert_equal topic.id, reply.parent_id
+ assert_equal 1, topic.reload.replies_count
+
+ topic2 = reply.build_topic(title: "debate2")
+ reply.save!
+
+ assert_not_equal topic.id, reply.parent_id
+ assert_equal topic2.id, reply.parent_id
+
+ assert_equal 0, topic.reload.replies_count
+ assert_equal 1, topic2.reload.replies_count
end
def test_belongs_to_with_primary_key_counter
debate = Topic.create("title" => "debate")
debate2 = Topic.create("title" => "debate2")
- reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate")
+ reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate2")
+
+ assert_equal 0, debate.reload.replies_count
+ assert_equal 1, debate2.reload.replies_count
+
+ reply.parent_title = "debate"
+ reply.save!
+
+ assert_equal 1, debate.reload.replies_count
+ assert_equal 0, debate2.reload.replies_count
+
+ assert_no_queries do
+ reply.topic_with_primary_key = debate
+ end
assert_equal 1, debate.reload.replies_count
assert_equal 0, debate2.reload.replies_count
reply.topic_with_primary_key = debate2
+ reply.save!
assert_equal 0, debate.reload.replies_count
assert_equal 1, debate2.reload.replies_count
reply.topic_with_primary_key = nil
+ reply.save!
assert_equal 0, debate.reload.replies_count
assert_equal 0, debate2.reload.replies_count
@@ -428,11 +575,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Topic.find(topic2.id).replies.size
reply1.topic = nil
+ reply1.save!
assert_equal 0, Topic.find(topic1.id).replies.size
assert_equal 0, Topic.find(topic2.id).replies.size
reply1.topic = topic1
+ reply1.save!
assert_equal 1, Topic.find(topic1.id).replies.size
assert_equal 0, Topic.find(topic2.id).replies.size
@@ -461,14 +610,65 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_after_save
- topic = Topic.create!(:title => "monday night")
- topic.replies.create!(:title => "re: monday night", :content => "football")
+ topic = Topic.create!(title: "monday night")
+
+ assert_queries(2) do
+ topic.replies.create!(title: "re: monday night", content: "football")
+ end
+
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.save!
assert_equal 1, Topic.find(topic.id)[:replies_count]
end
+ def test_belongs_to_counter_after_touch
+ topic = Topic.create!(title: "topic")
+
+ assert_equal 0, topic.replies_count
+ assert_equal 0, topic.after_touch_called
+
+ reply = Reply.create!(title: "blah!", content: "world around!", topic_with_primary_key: topic)
+
+ assert_equal 1, topic.replies_count
+ assert_equal 1, topic.after_touch_called
+
+ reply.destroy!
+
+ assert_equal 0, topic.replies_count
+ assert_equal 2, topic.after_touch_called
+ end
+
+ def test_belongs_to_touch_with_reassigning
+ debate = Topic.create!(title: "debate")
+ debate2 = Topic.create!(title: "debate2")
+ reply = Reply.create!(title: "blah!", content: "world around!", parent_title: "debate2")
+
+ time = 1.day.ago
+
+ debate.touch(time: time)
+ debate2.touch(time: time)
+
+ assert_queries(3) do
+ reply.parent_title = "debate"
+ reply.save!
+ end
+
+ assert_operator debate.reload.updated_at, :>, time
+ assert_operator debate2.reload.updated_at, :>, time
+
+ debate.touch(time: time)
+ debate2.touch(time: time)
+
+ assert_queries(3) do
+ reply.topic_with_primary_key = debate2
+ reply.save!
+ end
+
+ assert_operator debate.reload.updated_at, :>, time
+ assert_operator debate2.reload.updated_at, :>, time
+ end
+
def test_belongs_to_with_touch_option_on_touch
line_item = LineItem.create!
Invoice.create!(line_items: [line_item])
@@ -527,7 +727,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
line_item = LineItem.create!
Invoice.create!(line_items: [line_item])
- assert_queries(0) { line_item.save }
+ assert_no_queries { line_item.save }
end
def test_belongs_to_with_touch_option_on_destroy
@@ -564,8 +764,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_counter_when_update_columns
- topic = Topic.create!(:title => "37s")
- topic.replies.create!(:title => "re: 37s", :content => "rails")
+ topic = Topic.create!(title: "37s")
+ topic.replies.create!(title: "re: 37s", content: "rails")
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.update_columns(content: "rails is wonderful")
@@ -576,10 +776,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm = firm
- assert !final_cut.persisted?
+ assert_not_predicate final_cut, :persisted?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert_predicate final_cut, :persisted?
+ assert_predicate firm, :persisted?
assert_equal firm, final_cut.firm
final_cut.association(:firm).reload
assert_equal firm, final_cut.firm
@@ -589,10 +789,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm_with_primary_key = firm
- assert !final_cut.persisted?
+ assert_not_predicate final_cut, :persisted?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert_predicate final_cut, :persisted?
+ assert_predicate firm, :persisted?
assert_equal firm, final_cut.firm_with_primary_key
final_cut.association(:firm_with_primary_key).reload
assert_equal firm, final_cut.firm_with_primary_key
@@ -600,8 +800,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
@@ -623,7 +822,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_dont_find_target_when_foreign_key_is_null
tagging = taggings(:thinking_general)
- assert_queries(0) { tagging.super_tag }
+ assert_no_queries { tagging.super_tag }
+ end
+
+ def test_dont_find_target_when_saving_foreign_key_after_stale_association_loaded
+ client = Client.create!(name: "Test client", firm_with_basic_id: Firm.find(1))
+ client.firm_id = Firm.create!(name: "Test firm").id
+ assert_queries(1) { client.save! }
end
def test_field_name_same_as_foreign_key
@@ -632,11 +837,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_counter_cache
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
assert_equal 0, topic[:replies_count]
- reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
reply.topic = topic
+ reply.save!
assert_equal 1, topic.reload[:replies_count]
assert_equal 1, topic.replies.size
@@ -646,10 +852,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_counter_cache_double_destroy
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
5.times do
- topic.replies.create(:title => "re: zoom", :content => "speedy quick!")
+ topic.replies.create(title: "re: zoom", content: "speedy quick!")
end
assert_equal 5, topic.reload[:replies_count]
@@ -666,10 +872,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_concurrent_counter_cache_double_destroy
- topic = Topic.create :title => "Zoom-zoom-zoom"
+ topic = Topic.create title: "Zoom-zoom-zoom"
5.times do
- topic.replies.create(:title => "re: zoom", :content => "speedy quick!")
+ topic.replies.create(title: "re: zoom", content: "speedy quick!")
end
assert_equal 5, topic.reload[:replies_count]
@@ -687,11 +893,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_custom_counter_cache
- reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
assert_equal 0, reply[:replies_count]
- silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
+ silly = SillyReply.create(title: "gaga", content: "boo-boo")
silly.reply = reply
+ silly.save!
assert_equal 1, reply.reload[:replies_count]
assert_equal 1, reply.replies.size
@@ -700,10 +907,21 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 17, reply.replies.size
end
+ def test_replace_counter_cache
+ topic = Topic.create(title: "Zoom-zoom-zoom")
+ reply = Reply.create(title: "re: zoom", content: "speedy quick!")
+
+ reply.topic = topic
+ reply.save
+ topic.reload
+
+ assert_equal 1, topic.replies_count
+ end
+
def test_association_assignment_sticks
post = Post.first
- author1, author2 = Author.all.merge!(:limit => 2).to_a
+ author1, author2 = Author.all.merge!(limit: 2).to_a
assert_not_nil author1
assert_not_nil author2
@@ -723,7 +941,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_cant_save_readonly_association
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! }
- assert companies(:first_client).readonly_firm.readonly?
+ assert_predicate companies(:first_client).readonly_firm, :readonly?
end
def test_polymorphic_assignment_foreign_key_type_string
@@ -756,7 +974,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating
# should update when assigning a saved record
essay = Essay.new
- writer = Author.create(:name => "David")
+ writer = Author.create(name: "David")
essay.writer = writer
assert_equal "Author", essay.writer_type
@@ -781,7 +999,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_assignment_updates_foreign_id_field_for_new_and_saved_records
client = Client.new
- saved_firm = Firm.create :name => "Saved"
+ saved_firm = Firm.create name: "Saved"
new_firm = Firm.new
client.firm = saved_firm
@@ -793,7 +1011,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records
essay = Essay.new
- saved_writer = Author.create(:name => "David")
+ saved_writer = Author.create(name: "David")
new_writer = Author.new
essay.writer = saved_writer
@@ -809,7 +1027,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_nil essay.writer_type
essay.writer_id = 1
- essay.writer_type = 'Author'
+ essay.writer_type = "Author"
essay.writer = nil
assert_nil essay.writer_id
@@ -831,14 +1049,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Account.find(@account.id).save!
- Account.all.merge!(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(includes: :firm).find(@account.id).save!
end
@account.firm.delete
assert_nothing_raised do
Account.find(@account.id).save!
- Account.all.merge!(:includes => :firm).find(@account.id).save!
+ Account.all.merge!(includes: :firm).find(@account.id).save!
end
end
@@ -859,18 +1077,42 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_invalid_dependent_option_raises_exception
error = assert_raise ArgumentError do
- Class.new(Author).belongs_to :special_author_address, :dependent => :nullify
+ Class.new(Author).belongs_to :special_author_address, dependent: :nullify
+ end
+ assert_equal error.message, "The :dependent option must be one of [:destroy, :delete], but is :nullify"
+ end
+
+ class DestroyableBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "UndestroyableAuthor", dependent: :destroy
+ end
+
+ class UndestroyableAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_one :book, class_name: "DestroyableBook", foreign_key: "author_id"
+ before_destroy :dont
+
+ def dont
+ throw(:abort)
+ end
+ end
+
+ def test_dependency_should_halt_parent_destruction
+ author = UndestroyableAuthor.create!(name: "Test")
+ book = DestroyableBook.create!(author: author)
+
+ assert_no_difference ["UndestroyableAuthor.count", "DestroyableBook.count"] do
+ assert_not book.destroy
end
- assert_equal error.message, 'The :dependent option must be one of [:destroy, :delete], but is :nullify'
end
def test_attributes_are_being_set_when_initialized_from_belongs_to_association_with_where_clause
- new_firm = accounts(:signals37).build_firm(:name => 'Apple')
+ new_firm = accounts(:signals37).build_firm(name: "Apple")
assert_equal new_firm.name, "Apple"
end
def test_attributes_are_set_without_error_when_initialized_from_belongs_to_association_with_array_in_where_clause
- new_account = Account.where(:credit_limit => [ 50, 60 ]).new
+ new_account = Account.where(credit_limit: [ 50, 60 ]).new
assert_nil new_account.credit_limit
end
@@ -882,15 +1124,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
firm_proxy = client.send(:association_instance_get, :firm)
firm_with_condition_proxy = client.send(:association_instance_get, :firm_with_condition)
- assert !firm_proxy.stale_target?
- assert !firm_with_condition_proxy.stale_target?
+ assert_not_predicate firm_proxy, :stale_target?
+ assert_not_predicate firm_with_condition_proxy, :stale_target?
assert_equal companies(:first_firm), client.firm
assert_equal companies(:first_firm), client.firm_with_condition
client.client_of = companies(:another_firm).id
- assert firm_proxy.stale_target?
- assert firm_with_condition_proxy.stale_target?
+ assert_predicate firm_proxy, :stale_target?
+ assert_predicate firm_with_condition_proxy, :stale_target?
assert_equal companies(:another_firm), client.firm
assert_equal companies(:another_firm), client.firm_with_condition
end
@@ -901,12 +1143,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable
proxy = sponsor.send(:association_instance_get, :sponsorable)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal members(:groucho), sponsor.sponsorable
sponsor.sponsorable_id = members(:some_other_guy).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal members(:some_other_guy), sponsor.sponsorable
end
@@ -916,12 +1158,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable
proxy = sponsor.send(:association_instance_get, :sponsorable)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal members(:groucho), sponsor.sponsorable
- sponsor.sponsorable_type = 'Firm'
+ sponsor.sponsorable_type = "Firm"
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal companies(:first_firm), sponsor.sponsorable
end
@@ -943,9 +1185,20 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
comment = comments(:greetings)
- assert_difference lambda { post.reload.tags_count }, -1 do
- assert_difference 'comment.reload.tags_count', +1 do
+ assert_equal post.id, comment.id
+
+ assert_difference "post.reload.tags_count", -1 do
+ assert_difference "comment.reload.tags_count", +1 do
tagging.taggable = comment
+ tagging.save!
+ end
+ end
+
+ assert_difference "comment.reload.tags_count", -1 do
+ assert_difference "post.reload.tags_count", +1 do
+ tagging.taggable_type = post.class.polymorphic_name
+ tagging.taggable_id = post.id
+ tagging.save!
end
end
end
@@ -991,46 +1244,46 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_build_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.build_firm{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.build_firm { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_create_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.create_firm{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.create_firm { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_create_bang_with_block
- client = Client.create(:name => 'Client Company')
+ client = Client.create(name: "Client Company")
- firm = client.create_firm!{ |f| f.name = 'Agency Company' }
- assert_equal 'Agency Company', firm.name
+ firm = client.create_firm! { |f| f.name = "Agency Company" }
+ assert_equal "Agency Company", firm.name
end
def test_should_set_foreign_key_on_create_association
- client = Client.create! :name => "fuu"
+ client = Client.create! name: "fuu"
- firm = client.create_firm :name => "baa"
+ firm = client.create_firm name: "baa"
assert_equal firm.id, client.client_of
end
def test_should_set_foreign_key_on_create_association!
- client = Client.create! :name => "fuu"
+ client = Client.create! name: "fuu"
- firm = client.create_firm! :name => "baa"
+ firm = client.create_firm! name: "baa"
assert_equal firm.id, client.client_of
end
def test_self_referential_belongs_to_with_counter_cache_assigning_nil
- comment = Comment.create! :post => posts(:thinking), :body => "fuu"
+ comment = Comment.create! post: posts(:thinking), body: "fuu"
comment.parent = nil
comment.save!
- assert_equal nil, comment.reload.parent
+ assert_nil comment.reload.parent
assert_equal 0, comments(:greetings).reload.children_count
end
@@ -1045,9 +1298,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, parent.reload.children_count
end
+ def test_belongs_to_with_out_of_range_value_assigning
+ model = Class.new(Author) do
+ def self.name; "Temp"; end
+ validates :author_address, presence: true
+ end
+
+ author = model.new
+ author.author_address_id = 9223372036854775808 # out of range in the bigint
+
+ assert_nil author.author_address
+ assert_not_predicate author, :valid?
+ assert_equal [{ error: :blank }], author.errors.details[:author_address]
+ end
+
def test_polymorphic_with_custom_primary_key
toy = Toy.create!
- sponsor = Sponsor.create!(:sponsorable => toy)
+ sponsor = Sponsor.create!(sponsorable: toy)
assert_equal toy, sponsor.reload.sponsorable
end
@@ -1061,12 +1328,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
citibank.firm_id = apple.id.to_s
- assert !citibank.association(:firm).stale_target?
+ assert_not_predicate citibank.association(:firm), :stale_target?
end
def test_reflect_the_most_recent_change
author1, author2 = Author.limit(2)
- post = Post.new(:title => "foo", :body=> "bar")
+ post = Post.new(title: "foo", body: "bar")
post.author = author1
post.author_id = author2.id
@@ -1075,8 +1342,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal post.author_id, author2.id
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
belongs_to name
@@ -1085,16 +1352,21 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
end
- test 'belongs_to works with model called Record' do
+ test "belongs_to works with model called Record" do
record = Record.create!
Column.create! record: record
assert_equal 1, Column.count
end
- def test_association_force_reload_with_only_true_is_deprecated
- client = Client.find(3)
+ def test_multiple_counter_cache_with_after_create_update
+ post = posts(:welcome)
+ parent = comments(:greetings)
- assert_deprecated { client.firm(true) }
+ 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
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
index 2b867965ba..88221b012e 100644
--- a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -1,5 +1,7 @@
-require 'cases/helper'
-require 'models/content'
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/content"
class BidirectionalDestroyDependenciesTest < ActiveRecord::TestCase
fixtures :content, :content_positions
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index a4298a25a6..25d55dc4c9 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -1,19 +1,21 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/author'
-require 'models/project'
-require 'models/developer'
-require 'models/computer'
-require 'models/company'
+require "models/post"
+require "models/author"
+require "models/project"
+require "models/developer"
+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)
@thinking = posts(:thinking)
@authorless = posts(:authorless)
- assert @david.post_log.empty?
+ assert_empty @david.post_log
end
def test_adding_macro_callbacks
@@ -61,20 +63,20 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_has_many_callbacks_with_create
- morten = Author.create :name => "Morten"
- post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?"
+ morten = Author.create name: "Morten"
+ post = morten.posts_with_proc_callbacks.create! title: "Hello", body: "How are you doing?"
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
end
def test_has_many_callbacks_with_create!
- morten = Author.create! :name => "Morten"
- post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?"
+ morten = Author.create! name: "Morten"
+ post = morten.posts_with_proc_callbacks.create title: "Hello", body: "How are you doing?"
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
end
def test_has_many_callbacks_for_save_on_parent
- jack = Author.new :name => "Jack"
- jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
+ jack = Author.new name: "Jack"
+ jack.posts_with_callbacks.build title: "Call me back!", body: "Before you wake up and after you sleep"
callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"]
assert_equal callback_log, jack.post_log
@@ -84,8 +86,8 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_has_many_callbacks_for_destroy_on_parent
- firm = Firm.create! :name => "Firm"
- client = firm.clients.create! :name => "Client"
+ firm = Firm.create! name: "Firm"
+ client = firm.clients.create! name: "Client"
firm.destroy
assert_equal ["before_remove#{client.id}", "after_remove#{client.id}"], firm.log
@@ -94,7 +96,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_add_callback
david = developers(:david)
ar = projects(:active_record)
- assert ar.developers_log.empty?
+ assert_empty ar.developers_log
ar.developers_with_callbacks << david
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
ar.developers_with_callbacks << david
@@ -108,41 +110,40 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
klass = Class.new(Project) do
def self.name; Project.name; end
has_and_belongs_to_many :developers_with_callbacks,
- :class_name => "Developer",
- :before_add => lambda { |o,r|
+ class_name: "Developer",
+ before_add: lambda { |o, r|
dev = r
new_dev = r.new_record?
}
end
rec = klass.create!
- alice = Developer.new(:name => 'alice')
+ alice = Developer.new(name: "alice")
rec.developers_with_callbacks << alice
assert_equal alice, dev
assert_not_nil new_dev
assert new_dev, "record should not have been saved"
- assert_not alice.new_record?
+ assert_not_predicate alice, :new_record?
end
def test_has_and_belongs_to_many_after_add_called_after_save
ar = projects(:active_record)
- assert ar.developers_log.empty?
- alice = Developer.new(:name => 'alice')
+ assert_empty ar.developers_log
+ 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')
+ bob = ar.developers_with_callbacks.create(name: "bob")
assert_equal "after_adding#{bob.id}", ar.developers_log.last
- ar.developers_with_callbacks.build(:name => 'charlie')
+ ar.developers_with_callbacks.build(name: "charlie")
assert_equal "after_adding<new>", ar.developers_log.last
end
-
def test_has_and_belongs_to_many_remove_callback
david = developers(:david)
jamis = developers(:jamis)
activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
+ assert_empty activerecord.developers_log
activerecord.developers_with_callbacks.delete(david)
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
@@ -153,21 +154,21 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_does_not_fire_callbacks_on_clear
activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
+ assert_empty activerecord.developers_log
if activerecord.developers_with_callbacks.size == 0
activerecord.developers << developers(:david)
activerecord.developers << developers(:jamis)
activerecord.reload
assert activerecord.developers_with_callbacks.size == 2
end
- activerecord.developers_with_callbacks.flat_map {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.sort
+ activerecord.developers_with_callbacks.flat_map { |d| ["before_removing#{d.id}", "after_removing#{d.id}"] }.sort
assert activerecord.developers_with_callbacks.clear
- assert_predicate activerecord.developers_log, :empty?
+ assert_empty activerecord.developers_log
end
def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent
- project = Project.new :name => "Callbacks"
- project.developers_with_callbacks.build :name => "Jack", :salary => 95000
+ project = Project.new name: "Callbacks"
+ project.developers_with_callbacks.build name: "Jack", salary: 95000
callback_log = ["before_adding<new>", "after_adding<new>"]
assert_equal callback_log, project.developers_log
@@ -177,14 +178,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_dont_add_if_before_callback_raises_exception
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
begin
@david.unchangeable_posts << @authorless
rescue Exception
end
- assert @david.post_log.empty?
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_empty @david.post_log
+ assert_not_includes @david.unchangeable_posts, @authorless
@david.reload
- assert !@david.unchangeable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 51d8e0523e..49f754be63 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -1,56 +1,52 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/categorization'
-require 'models/category'
-require 'models/company'
-require 'models/topic'
-require 'models/reply'
-require 'models/person'
-require 'models/vertex'
-require 'models/edge'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/categorization"
+require "models/category"
+require "models/company"
+require "models/topic"
+require "models/reply"
+require "models/person"
+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
- authors = Author.all.merge!(:includes=>{:posts=>:comments}, :order=>"authors.id").to_a
+ authors = Author.includes(posts: :comments).order(:id).to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
- authors = Author.all.merge!(:includes=>[{:posts=>:comments}, :categorizations], :order=>"authors.id").to_a
+ authors = Author.includes({ posts: :comments }, :categorizations).order(:id).to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
assert_equal 1, authors[0].categorizations.size
assert_equal 2, authors[1].categorizations.size
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 }
+ authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).order(:id).to_a
+ 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
+ assert_equal people(:michael), Person.eager_load(primary_contact: :primary_contact).where("primary_contacts_people_2.first_name = ?", "Susan").order("people.id").first
end
def test_cascaded_eager_association_loading_with_join_for_count
- categories = Category.joins(:categorizations).includes([{:posts=>:comments}, :authors])
+ categories = Category.joins(:categorizations).includes([{ posts: :comments }, :authors])
assert_equal 4, categories.count
assert_equal 4, categories.to_a.count
@@ -59,7 +55,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_duplicated_includes
- categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null").references(:categorizations)
+ categories = Category.includes(:categorizations).includes(categorizations: :author).where("categorizations.id is not null").references(:categorizations)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.to_a.size
@@ -67,7 +63,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_twice_includes_edge_cases
- categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null").references(:posts)
+ categories = Category.includes(categorizations: :author).includes(categorizations: :post).where("posts.id is not null").references(:posts)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.to_a.size
@@ -82,29 +78,29 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
- authors = Author.all.merge!(:includes=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: [:comments, :categorizations] }, order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 10, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
+ assert_equal 10, authors[0].posts.collect { |post| post.comments.size }.inject(0) { |sum, i| sum + i }
end
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
- authors = Author.all.merge!(:includes=>{:posts=>[:comments, :author]}, :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: [:comments, :author] }, order: "authors.id").to_a
assert_equal 3, authors.size
assert_equal 5, authors[0].posts.size
assert_equal authors(:david).name, authors[0].name
- assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
+ assert_equal [authors(:david).name], authors[0].posts.collect { |post| post.author.name }.uniq
end
def test_eager_association_loading_with_cascaded_two_levels_with_condition
- authors = Author.all.merge!(:includes=>{:posts=>:comments}, :where=>"authors.id=1", :order=>"authors.id").to_a
+ authors = Author.all.merge!(includes: { posts: :comments }, where: "authors.id=1", order: "authors.id").to_a
assert_equal 1, authors.size
assert_equal 5, authors[0].posts.size
end
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
- firms = Firm.all.merge!(:includes=>{:account=>{:firm=>:account}}, :order=>"companies.id").to_a
+ firms = Firm.all.merge!(includes: { account: { firm: :account } }, order: "companies.id").to_a
assert_equal 2, firms.size
assert_equal firms.first.account, firms.first.account.firm.account
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
@@ -112,7 +108,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_has_many_sti
- topics = Topic.all.merge!(:includes => :replies, :order => 'topics.id').to_a
+ topics = Topic.all.merge!(includes: :replies, order: "topics.id").to_a
first, second, = topics(:first).replies.size, topics(:second).replies.size
assert_no_queries do
assert_equal first, topics[0].replies.size
@@ -121,26 +117,25 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_has_many_sti_and_subclasses
- silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
- silly.parent_id = 1
- assert silly.save
+ reply = Reply.new(title: "gaga", content: "boo-boo", parent_id: 1)
+ assert reply.save
- topics = Topic.all.merge!(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).to_a
- assert_no_queries do
+ topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a
+ assert_queries(0) do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
end
end
def test_eager_association_loading_with_belongs_to_sti
- replies = Reply.all.merge!(:includes => :topic, :order => 'topics.id').to_a
- assert replies.include?(topics(:second))
- assert !replies.include?(topics(:first))
+ replies = Reply.all.merge!(includes: :topic, order: "topics.id").to_a
+ assert_includes replies, topics(:second)
+ assert_not_includes replies, topics(:first)
assert_equal topics(:first), assert_no_queries { replies.first.topic }
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
@@ -149,7 +144,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_of_stis_with_multiple_references
- authors = Author.all.merge!(:includes => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :where => 'posts.id = 4').to_a
+ authors = Author.all.merge!(includes: { posts: { special_comments: { post: [ :special_comments, :very_special_comment ] } } }, order: "comments.body, very_special_comments_posts.body", where: "posts.id = 4").to_a
assert_equal [authors(:david)], authors
assert_no_queries do
authors.first.posts.first.special_comments.first.post.special_comments
@@ -158,20 +153,30 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_where_first_level_returns_nil
- authors = Author.all.merge!(:includes => {:post_about_thinking => :comments}, :order => 'authors.id DESC').to_a
+ authors = Author.all.merge!(includes: { post_about_thinking: :comments }, order: "authors.id DESC").to_a
assert_equal [authors(:bob), authors(:mary), authors(:david)], authors
assert_no_queries do
authors[2].post_about_thinking.comments.first
end
end
+ def test_preload_through_missing_records
+ post = Post.where.not(author_id: Author.select(:id)).preload(author: { comments: :post }).first!
+ assert_no_queries { assert_nil post.author }
+ end
+
+ def test_eager_association_loading_with_missing_first_record
+ posts = Post.where(id: 3).preload(author: { comments: :post }).to_a
+ assert_equal posts.size, 1
+ end
+
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
- source = Vertex.all.merge!(:includes=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id').first
+ source = Vertex.all.merge!(includes: { sinks: { sinks: { sinks: :sinks } } }, order: "vertices.id").first
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
end
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
- sink = Vertex.all.merge!(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first
+ sink = Vertex.all.merge!(includes: { sources: { sources: { sources: :sources } } }, order: "vertices.id DESC").first
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
end
@@ -183,6 +188,6 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
assert_equal 1, authors[1].comments.size
assert_equal 5, authors[0].posts.size
assert_equal 3, authors[1].posts.size
- assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum+i }
+ assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum + i }
end
end
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 75a6295350..673d5f1dcf 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,36 +1,88 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/tagging'
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+require "models/tagging"
module Namespaced
class Post < ActiveRecord::Base
- self.table_name = 'posts'
- has_one :tagging, :as => :taggable, :class_name => 'Tagging'
+ self.table_name = "posts"
+ has_one :tagging, as: :taggable, class_name: "Tagging"
+
+ def self.polymorphic_name
+ sti_name
+ end
end
end
-class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
-
+module PolymorphicFullStiClassNamesSharedTest
def setup
- generate_test_objects
+ @old_store_full_sti_class = ActiveRecord::Base.store_full_sti_class
+ ActiveRecord::Base.store_full_sti_class = store_full_sti_class
+
+ post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
+ @tagging = post.create_tagging!
end
- def generate_test_objects
- post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
- Tagging.create( :taggable => post )
+ def teardown
+ ActiveRecord::Base.store_full_sti_class = @old_store_full_sti_class
end
def test_class_names
- old = ActiveRecord::Base.store_full_sti_class
+ ActiveRecord::Base.store_full_sti_class = !store_full_sti_class
+ post = Namespaced::Post.find_by_title("Great stuff")
+ assert_nil post.tagging
+
+ ActiveRecord::Base.store_full_sti_class = store_full_sti_class
+ post = Namespaced::Post.find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
+ end
+
+ def test_class_names_with_includes
+ ActiveRecord::Base.store_full_sti_class = !store_full_sti_class
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
+ assert_nil post.tagging
+
+ ActiveRecord::Base.store_full_sti_class = store_full_sti_class
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
+ end
- ActiveRecord::Base.store_full_sti_class = false
- post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
+ def test_class_names_with_eager_load
+ ActiveRecord::Base.store_full_sti_class = !store_full_sti_class
+ 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.includes(:tagging).find_by_title('Great stuff')
- assert_instance_of Tagging, post.tagging
- ensure
- ActiveRecord::Base.store_full_sti_class = old
+ ActiveRecord::Base.store_full_sti_class = store_full_sti_class
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
end
+
+ def test_class_names_with_find_by
+ post = Namespaced::Post.find_by_title("Great stuff")
+
+ ActiveRecord::Base.store_full_sti_class = !store_full_sti_class
+ assert_nil Tagging.find_by(taggable: post)
+
+ ActiveRecord::Base.store_full_sti_class = store_full_sti_class
+ assert_equal @tagging, Tagging.find_by(taggable: post)
+ end
+end
+
+class PolymorphicFullStiClassNamesTest < ActiveRecord::TestCase
+ include PolymorphicFullStiClassNamesSharedTest
+
+ private
+ def store_full_sti_class
+ true
+ end
+end
+
+class PolymorphicNonFullStiClassNamesTest < ActiveRecord::TestCase
+ include PolymorphicFullStiClassNamesSharedTest
+
+ private
+ def store_full_sti_class
+ false
+ 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 f571198079..525ad3197a 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,18 +1,20 @@
-require 'cases/helper'
-require 'models/post'
-require 'models/tag'
-require 'models/author'
-require 'models/comment'
-require 'models/category'
-require 'models/categorization'
-require 'models/tagging'
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+require "models/tag"
+require "models/author"
+require "models/comment"
+require "models/category"
+require "models/categorization"
+require "models/tagging"
module Remembered
extend ActiveSupport::Concern
included do
after_create :remember
- protected
+ private
def remember; self.class.remembered << self; end
end
@@ -23,30 +25,30 @@ module Remembered
end
class ShapeExpression < ActiveRecord::Base
- belongs_to :shape, :polymorphic => true
- belongs_to :paint, :polymorphic => true
+ belongs_to :shape, polymorphic: true
+ belongs_to :paint, polymorphic: true
end
class Circle < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
class Square < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
class Triangle < ActiveRecord::Base
- has_many :shape_expressions, :as => :shape
+ has_many :shape_expressions, as: :shape
include Remembered
end
-class PaintColor < ActiveRecord::Base
- has_many :shape_expressions, :as => :paint
- belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
+class PaintColor < ActiveRecord::Base
+ has_many :shape_expressions, as: :paint
+ belongs_to :non_poly, foreign_key: "non_poly_one_id", class_name: "NonPolyOne"
include Remembered
end
class PaintTexture < ActiveRecord::Base
- has_many :shape_expressions, :as => :paint
- belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
+ has_many :shape_expressions, as: :paint
+ belongs_to :non_poly, foreign_key: "non_poly_two_id", class_name: "NonPolyTwo"
include Remembered
end
class NonPolyOne < ActiveRecord::Base
@@ -58,8 +60,6 @@ class NonPolyTwo < ActiveRecord::Base
include Remembered
end
-
-
class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
NUM_SIMPLE_OBJS = 50
NUM_SHAPE_EXPRESSIONS = 100
@@ -78,21 +78,21 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
end
1.upto(NUM_SIMPLE_OBJS) do
- PaintColor.create!(:non_poly_one_id => NonPolyOne.sample.id)
- PaintTexture.create!(:non_poly_two_id => NonPolyTwo.sample.id)
+ PaintColor.create!(non_poly_one_id: NonPolyOne.sample.id)
+ PaintTexture.create!(non_poly_two_id: NonPolyTwo.sample.id)
end
1.upto(NUM_SHAPE_EXPRESSIONS) do
shape_type = [Circle, Square, Triangle].sample
paint_type = [PaintColor, PaintTexture].sample
- ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.sample.id,
- :paint_type => paint_type.to_s, :paint_id => paint_type.sample.id)
+ ShapeExpression.create!(shape_type: shape_type.to_s, shape_id: shape_type.sample.id,
+ paint_type: paint_type.to_s, paint_id: paint_type.sample.id)
end
end
def test_include_query
- res = ShapeExpression.all.merge!(:includes => [ :shape, { :paint => :non_poly } ]).to_a
+ res = ShapeExpression.all.merge!(includes: [ :shape, { paint: :non_poly } ]).to_a
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
- assert_queries(0) do
+ assert_no_queries do
res.each do |se|
assert_not_nil se.paint.non_poly, "this is the association that was loading incorrectly before the change"
assert_not_nil se.shape, "just making sure other associations still work"
@@ -103,10 +103,10 @@ end
class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
def setup
- @davey_mcdave = Author.create(:name => 'Davey McDave')
- @first_post = @davey_mcdave.posts.create(:title => 'Davey Speaks', :body => 'Expressive wordage')
- @first_comment = @first_post.comments.create(:body => 'Inflamatory doublespeak')
- @first_categorization = @davey_mcdave.categorizations.create(:category => Category.first, :post => @first_post)
+ @davey_mcdave = Author.create(name: "Davey McDave")
+ @first_post = @davey_mcdave.posts.create(title: "Davey Speaks", body: "Expressive wordage")
+ @first_comment = @first_post.comments.create(body: "Inflamatory doublespeak")
+ @first_categorization = @davey_mcdave.categorizations.create(category: Category.first, post: @first_post)
end
teardown do
@@ -119,8 +119,8 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
def test_missing_data_in_a_nested_include_should_not_cause_errors_when_constructing_objects
assert_nothing_raised do
# @davey_mcdave doesn't have any author_favorites
- includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
- Author.all.merge!(:includes => includes, :where => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name').to_a
+ includes = { posts: :comments, categorizations: :category, author_favorites: :favorite_author }
+ Author.all.merge!(includes: includes, where: { authors: { name: @davey_mcdave.name } }, order: "categories.name").to_a
end
end
end
diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb
index a61a070331..420a5a805b 100644
--- a/activerecord/test/cases/associations/eager_singularization_test.rb
+++ b/activerecord/test/cases/associations/eager_singularization_test.rb
@@ -1,7 +1,7 @@
-require "cases/helper"
+# frozen_string_literal: true
+require "cases/helper"
-if ActiveRecord::Base.connection.supports_migrations?
class EagerSingularizationTest < ActiveRecord::TestCase
class Virus < ActiveRecord::Base
belongs_to :octopus
@@ -25,10 +25,10 @@ class EagerSingularizationTest < ActiveRecord::TestCase
class Crisis < ActiveRecord::Base
has_and_belongs_to_many :messes
- has_many :analyses, :dependent => :destroy
- has_many :successes, :through => :analyses
- has_many :dresses, :dependent => :destroy
- has_many :compresses, :through => :dresses
+ has_many :analyses, dependent: :destroy
+ has_many :successes, through: :analyses
+ has_many :dresses, dependent: :destroy
+ has_many :compresses, through: :dresses
end
class Analysis < ActiveRecord::Base
@@ -37,8 +37,8 @@ class EagerSingularizationTest < ActiveRecord::TestCase
end
class Success < ActiveRecord::Base
- has_many :analyses, :dependent => :destroy
- has_many :crises, :through => :analyses
+ has_many :analyses, dependent: :destroy
+ has_many :crises, through: :analyses
end
class Dress < ActiveRecord::Base
@@ -65,7 +65,7 @@ class EagerSingularizationTest < ActiveRecord::TestCase
connection.create_table :buses do |t|
t.column :name, :string
end
- connection.create_table :crises_messes, :id => false do |t|
+ connection.create_table :crises_messes, id: false do |t|
t.column :crisis_id, :integer
t.column :mess_id, :integer
end
@@ -104,45 +104,45 @@ class EagerSingularizationTest < ActiveRecord::TestCase
connection.drop_table :compresses
end
- def connection
- ActiveRecord::Base.connection
- end
-
def test_eager_no_extra_singularization_belongs_to
assert_nothing_raised do
- Virus.all.merge!(:includes => :octopus).to_a
+ Virus.all.merge!(includes: :octopus).to_a
end
end
def test_eager_no_extra_singularization_has_one
assert_nothing_raised do
- Octopus.all.merge!(:includes => :virus).to_a
+ Octopus.all.merge!(includes: :virus).to_a
end
end
def test_eager_no_extra_singularization_has_many
assert_nothing_raised do
- Bus.all.merge!(:includes => :passes).to_a
+ Bus.all.merge!(includes: :passes).to_a
end
end
def test_eager_no_extra_singularization_has_and_belongs_to_many
assert_nothing_raised do
- Crisis.all.merge!(:includes => :messes).to_a
- Mess.all.merge!(:includes => :crises).to_a
+ Crisis.all.merge!(includes: :messes).to_a
+ Mess.all.merge!(includes: :crises).to_a
end
end
def test_eager_no_extra_singularization_has_many_through_belongs_to
assert_nothing_raised do
- Crisis.all.merge!(:includes => :successes).to_a
+ Crisis.all.merge!(includes: :successes).to_a
end
end
def test_eager_no_extra_singularization_has_many_through_has_many
assert_nothing_raised do
- Crisis.all.merge!(:includes => :compresses).to_a
+ Crisis.all.merge!(includes: :compresses).to_a
end
end
-end
+
+ private
+ def connection
+ ActiveRecord::Base.connection
+ end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 7f2a2229ee..cd9c8a5285 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,75 +1,173 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/tagging'
-require 'models/tag'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/category'
-require 'models/company'
-require 'models/person'
-require 'models/reader'
-require 'models/owner'
-require 'models/pet'
-require 'models/reference'
-require 'models/job'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/book'
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/member'
-require 'models/membership'
-require 'models/club'
-require 'models/categorization'
-require 'models/sponsor'
-require 'models/mentor'
-require 'models/contract'
+require "models/post"
+require "models/tagging"
+require "models/tag"
+require "models/rating"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/category"
+require "models/company"
+require "models/person"
+require "models/reader"
+require "models/owner"
+require "models/pet"
+require "models/reference"
+require "models/job"
+require "models/subscriber"
+require "models/subscription"
+require "models/book"
+require "models/citation"
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/member"
+require "models/membership"
+require "models/club"
+require "models/categorization"
+require "models/sponsor"
+require "models/mentor"
+require "models/contract"
+
+class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
+ fixtures :citations
+
+ def test_preloading_too_many_ids
+ assert_equal Citation.count, Citation.preload(:reference_of).to_a.size
+ end
+
+ def test_eager_loading_too_may_ids
+ assert_equal Citation.count, Citation.eager_load(:citations).offset(0).size
+ end
+end
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :essays, :author_addresses, :categories, :categories_posts,
- :companies, :accounts, :tags, :taggings, :people, :readers, :categorizations,
+ :companies, :accounts, :tags, :taggings, :ratings, :people, :readers, :categorizations,
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
:developers, :projects, :developers_projects, :members, :memberships, :clubs, :sponsors
def test_eager_with_has_one_through_join_model_with_conditions_on_the_through
- member = Member.all.merge!(:includes => :favourite_club).find(members(:some_other_guy).id)
+ member = Member.all.merge!(includes: :favourite_club).find(members(:some_other_guy).id)
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
+ posts = Post.all.merge!(includes: :comments).to_a
post = posts.find { |p| p.id == 1 }
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
- post = Post.all.merge!(:includes => :comments, :where => "posts.title = 'Welcome to the weblog'").first
+ post = Post.all.merge!(includes: :comments, where: "posts.title = 'Welcome to the weblog'").first
assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
+ assert_includes post.comments, comments(:greetings)
- posts = Post.all.merge!(:includes => :last_comment).to_a
+ posts = Post.all.merge!(includes: :last_comment).to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_with_one_association_with_non_preload
- posts = Post.all.merge!(:includes => :last_comment, :order => 'comments.id DESC').to_a
+ posts = Post.all.merge!(includes: :last_comment, order: "comments.id DESC").to_a
post = posts.find { |p| p.id == 1 }
assert_equal Post.find(1).last_comment, post.last_comment
end
def test_loading_conditions_with_or
posts = authors(:david).posts.references(:comments).merge(
- :includes => :comments,
- :where => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
+ includes: :comments,
+ where: "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
).to_a
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
+ def test_loading_polymorphic_association_with_mixed_table_conditions
+ rating = Rating.first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_without_tag
+
+ rating = Rating.preload(:taggings_without_tag).first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_without_tag
+
+ rating = Rating.eager_load(:taggings_without_tag).first
+ assert_equal [taggings(:normal_comment_rating)], rating.taggings_without_tag
+ end
+
+ def test_loading_with_scope_including_joins
+ member = Member.first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+
+ member = Member.preload(:general_club).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+
+ member = Member.eager_load(:general_club).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+ end
+
+ def test_loading_association_with_same_table_joins
+ super_memberships = [memberships(:super_membership_of_boring_club)]
+
+ member = Member.joins(:favourite_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+
+ member = Member.joins(:favourite_memberships).preload(:super_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+
+ member = Member.joins(:favourite_memberships).eager_load(:super_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+ end
+
+ def test_loading_association_with_intersection_joins
+ member = Member.joins(:current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+
+ member = Member.joins(:current_membership).preload(:club, :current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+
+ member = Member.joins(:current_membership).eager_load(:club, :current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+ end
+
+ def test_loading_associations_dont_leak_instance_state
+ assertions = ->(firm) {
+ assert_equal companies(:first_firm), firm
+
+ assert_predicate firm.association(:readonly_account), :loaded?
+ assert_predicate firm.association(:accounts), :loaded?
+
+ assert_equal accounts(:signals37), firm.readonly_account
+ assert_equal [accounts(:signals37)], firm.accounts
+
+ assert_predicate firm.readonly_account, :readonly?
+ assert firm.accounts.none?(&:readonly?)
+ }
+
+ assertions.call(Firm.preload(:readonly_account, :accounts).first)
+ assertions.call(Firm.eager_load(:readonly_account, :accounts).first)
+ end
+
def test_with_ordering
- list = Post.all.merge!(:includes => :comments, :order => "posts.id DESC").to_a
+ 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,
:sti_habtm, :sti_post_and_comments, :sti_comments, :authorless, :thinking, :welcome
].each_with_index do |post, index|
@@ -90,49 +188,53 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_no_queries { authors.map(&:post) }
end
+ def test_calculate_with_string_in_from_and_eager_loading
+ assert_equal 10, Post.from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").count
+ end
+
def test_with_two_tables_in_from_without_getting_double_quoted
posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order("posts.id").to_a
assert_equal 2, posts.first.comments.size
end
def test_loading_with_multiple_associations
- posts = Post.all.merge!(:includes => [ :comments, :author, :categories ], :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :comments, :author, :categories ], order: "posts.id").to_a
assert_equal 2, posts.first.comments.size
assert_equal 2, posts.first.categories.size
- assert posts.first.comments.include?(comments(:greetings))
+ assert_includes posts.first.comments, comments(:greetings)
end
def test_duplicate_middle_objects
- comments = Comment.all.merge!(:where => 'post_id = 1', :includes => [:post => :author]).to_a
+ comments = Comment.all.merge!(where: "post_id = 1", includes: [post: :author]).to_a
assert_no_queries do
- comments.each {|comment| comment.post.author.name}
+ comments.each { |comment| comment.post.author.name }
end
end
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
assert_called(Comment.connection, :in_clause_length, returns: 5) do
- posts = Post.all.merge!(:includes=>:comments).to_a
+ posts = Post.all.merge!(includes: :comments).to_a
assert_equal 11, posts.size
end
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
assert_called(Comment.connection, :in_clause_length, returns: nil) do
- posts = Post.all.merge!(:includes=>:comments).to_a
+ posts = Post.all.merge!(includes: :comments).to_a
assert_equal 11, posts.size
end
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
assert_called(Comment.connection, :in_clause_length, times: 2, returns: 5) do
- posts = Post.all.merge!(:includes=>:categories).to_a
+ posts = Post.all.merge!(includes: :categories).to_a
assert_equal 11, posts.size
end
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
assert_called(Comment.connection, :in_clause_length, times: 2, returns: nil) do
- posts = Post.all.merge!(:includes=>:categories).to_a
+ posts = Post.all.merge!(includes: :categories).to_a
assert_equal 11, posts.size
end
end
@@ -141,7 +243,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: nil) do
post = posts(:welcome)
assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ Post.includes(:comments).where(id: post.id).to_a
end
end
end
@@ -150,7 +252,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: 1) do
post1, post2 = posts(:welcome), posts(:thinking)
assert_queries(3) do
- Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ Post.includes(:comments).where(id: [post1.id, post2.id]).to_a
end
end
end
@@ -159,50 +261,50 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_called(Comment.connection, :in_clause_length, returns: 3) do
post = posts(:welcome)
assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ Post.includes(:comments).where(id: post.id).to_a
end
end
end
def test_including_duplicate_objects_from_belongs_to
- popular_post = Post.create!(:title => 'foo', :body => "I like cars!")
- comment = popular_post.comments.create!(:body => "lol")
- popular_post.readers.create!(:person => people(:michael))
- popular_post.readers.create!(:person => people(:david))
+ popular_post = Post.create!(title: "foo", body: "I like cars!")
+ comment = popular_post.comments.create!(body: "lol")
+ popular_post.readers.create!(person: people(:michael))
+ popular_post.readers.create!(person: people(:david))
- readers = Reader.all.merge!(:where => ["post_id = ?", popular_post.id],
- :includes => {:post => :comments}).to_a
+ readers = Reader.all.merge!(where: ["post_id = ?", popular_post.id],
+ includes: { post: :comments }).to_a
readers.each do |reader|
assert_equal [comment], reader.post.comments
end
end
def test_including_duplicate_objects_from_has_many
- car_post = Post.create!(:title => 'foo', :body => "I like cars!")
+ car_post = Post.create!(title: "foo", body: "I like cars!")
car_post.categories << categories(:general)
car_post.categories << categories(:technology)
- comment = car_post.comments.create!(:body => "hmm")
- categories = Category.all.merge!(:where => { 'posts.id' => car_post.id },
- :includes => {:posts => :comments}).to_a
+ comment = car_post.comments.create!(body: "hmm")
+ categories = Category.all.merge!(where: { "posts.id" => car_post.id },
+ includes: { posts: :comments }).to_a
categories.each do |category|
assert_equal [comment], category.posts[0].comments
end
end
def test_associations_loaded_for_all_records
- post = Post.create!(:title => 'foo', :body => "I like cars!")
- SpecialComment.create!(:body => 'Come on!', :post => post)
- first_category = Category.create! :name => 'First!', :posts => [post]
- second_category = Category.create! :name => 'Second!', :posts => [post]
+ post = Post.create!(title: "foo", body: "I like cars!")
+ SpecialComment.create!(body: "Come on!", post: post)
+ first_category = Category.create! name: "First!", posts: [post]
+ second_category = Category.create! name: "Second!", posts: [post]
- categories = Category.where(:id => [first_category.id, second_category.id]).includes(:posts => :special_comments)
+ categories = Category.where(id: [first_category.id, second_category.id]).includes(posts: :special_comments)
assert_equal categories.map { |category| category.posts.first.special_comments.loaded? }, [true, true]
end
def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
author_id = authors(:david).id
- author = assert_queries(3) { Author.all.merge!(:includes => {:posts_with_comments => :comments}).find(author_id) } # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(includes: { posts_with_comments: :comments }).find(author_id) } # find the author, then find the posts, then find the comments
author.posts_with_comments.each do |post_with_comments|
assert_equal post_with_comments.comments.length, post_with_comments.comments.count
assert_nil post_with_comments.comments.to_a.uniq!
@@ -213,7 +315,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
author = authors(:david)
post = author.post_about_thinking_with_last_comment
last_comment = post.last_comment
- author = assert_queries(3) { Author.all.merge!(:includes => {:post_about_thinking_with_last_comment => :last_comment}).find(author.id)} # find the author, then find the posts, then find the comments
+ author = assert_queries(3) { Author.all.merge!(includes: { post_about_thinking_with_last_comment: :last_comment }).find(author.id) } # find the author, then find the posts, then find the comments
assert_no_queries do
assert_equal post, author.post_about_thinking_with_last_comment
assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
@@ -224,7 +326,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
post = posts(:welcome)
author = post.author
author_address = author.author_address
- post = assert_queries(3) { Post.all.merge!(:includes => {:author_with_address => :author_address}).find(post.id) } # find the post, then find the author, then find the address
+ post = assert_queries(3) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) } # find the post, then find the author, then find the address
assert_no_queries do
assert_equal author, post.author_with_address
assert_equal author_address, post.author_with_address.author_address
@@ -234,53 +336,50 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
post = posts(:welcome)
post.update!(author: nil)
- post = assert_queries(1) { Post.all.merge!(includes: {author_with_address: :author_address}).find(post.id) }
+ post = assert_queries(1) { Post.all.merge!(includes: { author_with_address: :author_address }).find(post.id) }
# find the post, then find the author which is null so no query for the author or address
assert_no_queries do
- assert_equal nil, post.author_with_address
+ assert_nil post.author_with_address
end
end
def test_finding_with_includes_on_null_belongs_to_polymorphic_association
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
sponsor.update!(sponsorable: nil)
- sponsor = assert_queries(1) { Sponsor.all.merge!(:includes => :sponsorable).find(sponsor.id) }
+ sponsor = assert_queries(1) { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
def test_finding_with_includes_on_empty_polymorphic_type_column
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
- sponsor.update!(sponsorable_type: '', sponsorable_id: nil) # sponsorable_type column might be declared NOT NULL
+ sponsor.update!(sponsorable_type: "", sponsorable_id: nil) # sponsorable_type column might be declared NOT NULL
sponsor = assert_queries(1) do
- assert_nothing_raised { Sponsor.all.merge!(:includes => :sponsorable).find(sponsor.id) }
+ assert_nothing_raised { Sponsor.all.merge!(includes: :sponsorable).find(sponsor.id) }
end
assert_no_queries do
- assert_equal nil, sponsor.sponsorable
+ assert_nil sponsor.sponsorable
end
end
def test_loading_from_an_association
- posts = authors(:david).posts.merge(:includes => :comments, :order => "posts.id").to_a
+ posts = authors(:david).posts.merge(includes: :comments, order: "posts.id").to_a
assert_equal 2, posts.first.comments.size
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?
+ assert_not_empty Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts
end
def test_loading_with_no_associations
- assert_nil Post.all.merge!(:includes => :author).find(posts(:authorless).id).author
+ assert_nil Post.all.merge!(includes: :author).find(posts(:authorless).id).author
end
# Regression test for 21c75e5
def test_nested_loading_does_not_raise_exception_when_association_does_not_exist
assert_nothing_raised do
- Post.all.merge!(:includes => {:author => :author_addresss}).find(posts(:authorless).id)
+ Post.all.merge!(includes: { author: :author_addresss }).find(posts(:authorless).id)
end
end
@@ -288,117 +387,117 @@ class EagerAssociationTest < ActiveRecord::TestCase
post_id = Comment.where(author_id: nil).where.not(post_id: nil).first.post_id
assert_nothing_raised do
- Post.preload(:comments => [{:author => :essays}]).find(post_id)
+ Post.preload(comments: [{ author: :essays }]).find(post_id)
end
end
def test_nested_loading_through_has_one_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}).find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }).find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'author_addresses.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "author_addresses.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'authors.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "authors.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_order_on_nested_association
- aa = AuthorAddress.all.merge!(:includes => {:author => :posts}, :order => 'posts.id').find(author_addresses(:david_address).id)
+ aa = AuthorAddress.all.merge!(includes: { author: :posts }, order: "posts.id").find(author_addresses(:david_address).id)
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions
aa = AuthorAddress.references(:author_addresses).merge(
- :includes => {:author => :posts},
- :where => "author_addresses.id > 0"
+ includes: { author: :posts },
+ where: "author_addresses.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions_on_association
aa = AuthorAddress.references(:authors).merge(
- :includes => {:author => :posts},
- :where => "authors.id > 0"
+ includes: { author: :posts },
+ where: "authors.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_nested_loading_through_has_one_association_with_conditions_on_nested_association
aa = AuthorAddress.references(:posts).merge(
- :includes => {:author => :posts},
- :where => "posts.id > 0"
+ includes: { author: :posts },
+ where: "posts.id > 0"
).find author_addresses(:david_address).id
assert_equal aa.author.posts.count, aa.author.posts.length
end
def test_eager_association_loading_with_belongs_to_and_foreign_keys
- pets = Pet.all.merge!(:includes => :owner).to_a
+ pets = Pet.all.merge!(includes: :owner).to_a
assert_equal 4, pets.length
end
def test_eager_association_loading_with_belongs_to
- comments = Comment.all.merge!(:includes => :post).to_a
+ comments = Comment.all.merge!(includes: :post).to_a
assert_equal 11, comments.length
titles = comments.map { |c| c.post.title }
- assert titles.include?(posts(:welcome).title)
- assert titles.include?(posts(:sti_post_and_comments).title)
+ assert_includes titles, posts(:welcome).title
+ assert_includes titles, posts(:sti_post_and_comments).title
end
def test_eager_association_loading_with_belongs_to_and_limit
- comments = Comment.all.merge!(:includes => :post, :limit => 5, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, limit: 5, order: "comments.id").to_a
assert_equal 5, comments.length
- assert_equal [1,2,3,5,6], comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
- comments = Comment.all.merge!(:includes => :post, :limit => 3, :offset => 2, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, limit: 3, offset: 2, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [3,5,6], comments.collect(&:id)
+ assert_equal [3, 5, 6], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 4", limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
- comments = Comment.all.merge!(:includes => :post, :where => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: ["post_id = ?", 4], limit: 3, offset: 1, order: "comments.id").to_a
assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect(&:id)
+ assert_equal [6, 7, 8], comments.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.includes(:post).references(:posts).where('posts.id = ?', 4)
+ Comment.includes(:post).references(:posts).where("posts.id = ?", 4)
end
end
def test_eager_association_loading_with_belongs_to_and_conditions_hash
comments = []
assert_nothing_raised do
- comments = Comment.all.merge!(:includes => :post, :where => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id').to_a
+ comments = Comment.all.merge!(includes: :post, where: { posts: { id: 4 } }, limit: 3, order: "comments.id").to_a
end
assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect(&:id)
+ assert_equal [5, 6, 7], comments.collect(&:id)
assert_no_queries do
comments.first.post
end
end
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
- quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
+ quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
Comment.includes(:post).references(:posts).where("#{quoted_posts_id} = ?", 4)
end
@@ -406,61 +505,61 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.all.merge!(:includes => :post, :order => 'posts.id').to_a
+ Comment.all.merge!(includes: :post, order: "posts.id").to_a
end
end
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')
+ 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
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
- posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :order => 'posts.id').to_a
+ posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, order: "posts.id").to_a
assert_equal 1, posts.length
assert_equal [1], posts.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
- posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id').to_a
+ posts = Post.all.merge!(includes: [:author, :very_special_comment], limit: 1, offset: 1, order: "posts.id").to_a
assert_equal 1, posts.length
assert_equal [2], posts.collect(&:id)
end
def test_eager_association_loading_with_belongs_to_inferred_foreign_key_from_association_name
- author_favorite = AuthorFavorite.all.merge!(:includes => :favorite_author).first
+ author_favorite = AuthorFavorite.all.merge!(includes: :favorite_author).first
assert_equal authors(:mary), assert_no_queries { author_favorite.favorite_author }
end
def test_eager_load_belongs_to_quotes_table_and_column_names
job = Job.includes(:ideal_reference).find jobs(:unicyclist).id
references(:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_unicyclist), job.ideal_reference}
+ assert_no_queries { assert_equal references(:michael_unicyclist), job.ideal_reference }
end
def test_eager_load_has_one_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :favourite_reference).find(people(:michael).id)
+ michael = Person.all.merge!(includes: :favourite_reference).find(people(:michael).id)
references(:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_unicyclist), michael.favourite_reference}
+ assert_no_queries { assert_equal references(:michael_unicyclist), michael.favourite_reference }
end
def test_eager_load_has_many_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :references).find(people(:michael).id)
- references(:michael_magician,:michael_unicyclist)
- assert_no_queries{ assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
+ michael = Person.all.merge!(includes: :references).find(people(:michael).id)
+ references(:michael_magician, :michael_unicyclist)
+ assert_no_queries { assert_equal references(:michael_magician, :michael_unicyclist), michael.references.sort_by(&:id) }
end
def test_eager_load_has_many_through_quotes_table_and_column_names
- michael = Person.all.merge!(:includes => :jobs).find(people(:michael).id)
+ michael = Person.all.merge!(includes: :jobs).find(people(:michael).id)
jobs(:magician, :unicyclist)
- assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
+ assert_no_queries { assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
end
def test_eager_load_has_many_with_string_keys
subscriptions = subscriptions(:webster_awdr, :webster_rfr)
- subscriber =Subscriber.all.merge!(:includes => :subscriptions).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(includes: :subscriptions).find(subscribers(:second).id)
assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
end
@@ -471,32 +570,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
b = Book.create!
- Subscription.create!(:subscriber_id => "PL", :book_id => b.id)
+ Subscription.create!(subscriber_id: "PL", book_id: b.id)
s.reload
s.book_ids = s.book_ids
end
def test_eager_load_has_many_through_with_string_keys
books = books(:awdr, :rfr)
- subscriber = Subscriber.all.merge!(:includes => :books).find(subscribers(:second).id)
+ subscriber = Subscriber.all.merge!(includes: :books).find(subscribers(:second).id)
assert_equal books, subscriber.books.sort_by(&:id)
end
def test_eager_load_belongs_to_with_string_keys
subscriber = subscribers(:second)
- subscription = Subscription.all.merge!(:includes => :subscriber).find(subscriptions(:webster_awdr).id)
+ subscription = Subscription.all.merge!(includes: :subscriber).find(subscriptions(:webster_awdr).id)
assert_equal subscriber, subscription.subscriber
end
def test_eager_association_loading_with_explicit_join
- posts = Post.all.merge!(:includes => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id').to_a
+ posts = Post.all.merge!(includes: :comments, joins: "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", limit: 1, order: "author_id").to_a
assert_equal 1, posts.length
end
def test_eager_with_has_many_through
- posts_with_comments = people(:michael).posts.merge(:includes => :comments, :order => 'posts.id').to_a
- posts_with_author = people(:michael).posts.merge(:includes => :author, :order => 'posts.id').to_a
- posts_with_comments_and_author = people(:michael).posts.merge(:includes => [ :comments, :author ], :order => 'posts.id').to_a
+ posts_with_comments = people(:michael).posts.merge(includes: :comments, order: "posts.id").to_a
+ posts_with_author = people(:michael).posts.merge(includes: :author, order: "posts.id").to_a
+ posts_with_comments_and_author = people(:michael).posts.merge(includes: [ :comments, :author ], order: "posts.id").to_a
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum + post.comments.size }
assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
@@ -504,35 +603,43 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_through_a_belongs_to_association
author = authors(:mary)
- Post.create!(:author => author, :title => "TITLE", :body => "BODY")
- author.author_favorites.create(:favorite_author_id => 1)
- author.author_favorites.create(:favorite_author_id => 2)
- posts_with_author_favorites = author.posts.merge(:includes => :author_favorites).to_a
+ Post.create!(author: author, title: "TITLE", body: "BODY")
+ author.author_favorites.create(favorite_author_id: 1)
+ author.author_favorites.create(favorite_author_id: 2)
+ posts_with_author_favorites = author.posts.merge(includes: :author_favorites).to_a
assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
end
def test_eager_with_has_many_through_an_sti_join_model
- author = Author.all.merge!(:includes => :special_post_comments, :order => 'authors.id').first
+ author = Author.all.merge!(includes: :special_post_comments, order: "authors.id").first
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
+ author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first
assert_equal [], author.special_nonexistent_post_comments
end
def test_eager_with_has_many_through_join_model_with_conditions
- assert_equal Author.all.merge!(:includes => :hello_post_comments,
- :order => 'authors.id').first.hello_post_comments.sort_by(&:id),
- Author.all.merge!(:order => 'authors.id').first.hello_post_comments.sort_by(&:id)
+ assert_equal Author.all.merge!(includes: :hello_post_comments,
+ order: "authors.id").first.hello_post_comments.sort_by(&:id),
+ Author.all.merge!(order: "authors.id").first.hello_post_comments.sort_by(&:id)
end
def test_eager_with_has_many_through_join_model_with_conditions_on_top_level
- assert_equal comments(:more_greetings), Author.all.merge!(:includes => :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
+ assert_equal comments(:more_greetings), Author.all.merge!(includes: :comments_with_order_and_conditions).find(authors(:david).id).comments_with_order_and_conditions.first
end
def test_eager_with_has_many_through_join_model_with_include
- author_comments = Author.all.merge!(:includes => :comments_with_include).find(authors(:david).id).comments_with_include.to_a
+ author_comments = Author.all.merge!(includes: :comments_with_include).find(authors(:david).id).comments_with_include.to_a
assert_no_queries do
author_comments.first.post.title
end
@@ -540,7 +647,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_many_through_with_conditions_join_model_with_include
post_tags = Post.find(posts(:welcome).id).misc_tags
- eager_post_tags = Post.all.merge!(:includes => :misc_tags).find(1).misc_tags
+ eager_post_tags = Post.all.merge!(includes: :misc_tags).find(1).misc_tags
assert_equal post_tags, eager_post_tags
end
@@ -551,67 +658,67 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit
- posts = Post.all.merge!(:order => 'posts.id asc', :includes => [ :author, :comments ], :limit => 2).to_a
+ posts = Post.all.merge!(order: "posts.id asc", includes: [ :author, :comments ], limit: 2).to_a
assert_equal 2, posts.size
assert_equal 3, posts.inject(0) { |sum, post| sum + post.comments.size }
end
def test_eager_with_has_many_and_limit_and_conditions
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.body = 'hello'", :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.body = 'hello'", order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "posts.body = ?", 'hello' ], :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: [ "posts.body = ?", "hello" ], order: "posts.id").to_a
assert_equal 2, posts.size
- assert_equal [4,5], posts.collect(&:id)
+ assert_equal [4, 5], posts.collect(&:id)
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
- posts = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David')
+ posts = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", "David")
assert_equal 2, posts.size
- count = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", 'David').count
+ count = Post.includes(:author, :comments).limit(2).references(:author).where("authors.name = ?", "David").count
assert_equal posts.size, count
end
def test_eager_with_has_many_and_limit_and_high_offset
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10, where: { "authors.name" => "David" }).to_a
assert_equal 0, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
assert_queries(1) do
posts = Post.references(:authors, :comments).
- merge(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ]).to_a
+ merge(includes: [ :author, :comments ], limit: 2, offset: 10,
+ where: [ "authors.name = ? and comments.body = ?", "David", "go crazy" ]).to_a
assert_equal 0, posts.size
end
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
assert_queries(1) do
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10,
- :where => { 'authors.name' => 'David', 'comments.body' => 'go crazy' }).to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10,
+ where: { "authors.name" => "David", "comments.body" => "go crazy" }).to_a
assert_equal 0, posts.size
end
end
def test_count_eager_with_has_many_and_limit_and_high_offset
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :offset => 10, :where => { 'authors.name' => 'David' }).count(:all)
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, offset: 10, where: { "authors.name" => "David" }).count(:all)
assert_equal 0, posts
end
def test_eager_with_has_many_and_limit_with_no_results
- posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.title = 'magic forest'").to_a
+ posts = Post.all.merge!(includes: [ :author, :comments ], limit: 2, where: "posts.title = 'magic forest'").to_a
assert_equal 0, posts.size
end
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
author = authors(:david)
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
- assert_equal author_posts_without_comments.size, author.posts.includes(:comments).where('comments.id is null').references(:comments).count
+ assert_equal author_posts_without_comments.size, author.posts.includes(:comments).where("comments.id is null").references(:comments).count
end
def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
@@ -621,13 +728,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_and_belongs_to_many_and_limit
- posts = Post.all.merge!(:includes => :categories, :order => "posts.id", :limit => 3).to_a
+ posts = Post.all.merge!(includes: :categories, order: "posts.id", limit: 3).to_a
assert_equal 3, posts.size
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
+ assert_includes posts[0].categories, categories(:technology)
+ assert_includes posts[1].categories, categories(:general)
end
# Since the preloader for habtm gets raw row hashes from the database and then
@@ -687,32 +794,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_habtm
- posts = Post.all.merge!(:includes => :categories, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: :categories, order: "posts.id").to_a
assert_equal 2, posts[0].categories.size
assert_equal 1, posts[1].categories.size
assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
+ assert_includes posts[0].categories, categories(:technology)
+ assert_includes posts[1].categories, categories(:general)
end
def test_eager_with_inheritance
- SpecialPost.all.merge!(:includes => [ :comments ]).to_a
+ SpecialPost.all.merge!(includes: [ :comments ]).to_a
end
def test_eager_has_one_with_association_inheritance
- post = Post.all.merge!(:includes => [ :very_special_comment ]).find(4)
+ post = Post.all.merge!(includes: [ :very_special_comment ]).find(4)
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
end
def test_eager_has_many_with_association_inheritance
- post = Post.all.merge!(:includes => [ :special_comments ]).find(4)
+ post = Post.all.merge!(includes: [ :special_comments ]).find(4)
post.special_comments.each do |special_comment|
assert special_comment.is_a?(SpecialComment)
end
end
def test_eager_habtm_with_association_inheritance
- post = Post.all.merge!(:includes => [ :special_categories ]).find(6)
+ post = Post.all.merge!(includes: [ :special_categories ]).find(6)
assert_equal 1, post.special_categories.size
post.special_categories.each do |special_category|
assert_equal "SpecialCategory", special_category.class.to_s
@@ -721,8 +828,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_has_one_dependent_does_not_destroy_dependent
assert_not_nil companies(:first_firm).account
- f = Firm.all.merge!(:includes => :account,
- :where => ["companies.name = ?", "37signals"]).first
+ f = Firm.all.merge!(includes: :account,
+ where: ["companies.name = ?", "37signals"]).first
assert_not_nil f.account
assert_equal companies(:first_firm, :reload).account, f.account
end
@@ -735,38 +842,45 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_invalid_association_reference
- assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=> :monkeys ).find(6)
+ e = assert_raise(ActiveRecord::AssociationNotFoundError) {
+ Post.all.merge!(includes: :monkeys).find(6)
}
- assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=>[ :monkeys ]).find(6)
+ assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message)
+
+ e = assert_raise(ActiveRecord::AssociationNotFoundError) {
+ Post.all.merge!(includes: [ :monkeys ]).find(6)
}
- assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- Post.all.merge!(:includes=>[ 'monkeys' ]).find(6)
+ assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message)
+
+ e = assert_raise(ActiveRecord::AssociationNotFoundError) {
+ Post.all.merge!(includes: [ "monkeys" ]).find(6)
}
- assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
- Post.all.merge!(:includes=>[ :monkeys, :elephants ]).find(6)
+ assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message)
+
+ e = assert_raise(ActiveRecord::AssociationNotFoundError) {
+ Post.all.merge!(includes: [ :monkeys, :elephants ]).find(6)
}
+ assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message)
end
def test_eager_has_many_through_with_order
- tag = OrderedTag.create(name: 'Foo')
- post1 = Post.create!(title: 'Beaches', body: "I like beaches!")
- post2 = Post.create!(title: 'Pools', body: "I like pools!")
+ tag = OrderedTag.create(name: "Foo")
+ post1 = Post.create!(title: "Beaches", body: "I like beaches!")
+ post2 = Post.create!(title: "Pools", body: "I like pools!")
- Tagging.create!(taggable_type: 'Post', taggable_id: post1.id, tag: tag)
- Tagging.create!(taggable_type: 'Post', taggable_id: post2.id, tag: tag)
+ Tagging.create!(taggable_type: "Post", taggable_id: post1.id, tag: tag)
+ 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
- tag1 = OrderedTag.create!(name: 'Bar')
- tag2 = OrderedTag.create!(name: 'Foo')
+ tag1 = OrderedTag.create!(name: "Bar")
+ tag2 = OrderedTag.create!(name: "Foo")
- post1 = Post.create!(title: 'Beaches', body: "I like beaches!")
- post2 = Post.create!(title: 'Pools', body: "I like pools!")
+ post1 = Post.create!(title: "Beaches", body: "I like beaches!")
+ post2 = Post.create!(title: "Pools", body: "I like pools!")
Tagging.create!(taggable: post1, tag: tag1)
Tagging.create!(taggable: post2, tag: tag1)
@@ -782,7 +896,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope
- developer = EagerDeveloperWithDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -790,7 +904,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_class_method
- developer = EagerDeveloperWithClassMethodDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithClassMethodDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -807,7 +921,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_class_method_using_find_by_method
- developer = EagerDeveloperWithClassMethodDefaultScope.find_by(name: 'David')
+ developer = EagerDeveloperWithClassMethodDefaultScope.find_by(name: "David")
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -815,7 +929,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_lambda
- developer = EagerDeveloperWithLambdaDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithLambdaDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -824,8 +938,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_with_default_scope_as_block
# warm up the habtm cache
- EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first.projects
- developer = EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first
+ EagerDeveloperWithBlockDefaultScope.where(name: "David").first.projects
+ developer = EagerDeveloperWithBlockDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -833,30 +947,26 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_default_scope_as_callable
- developer = EagerDeveloperWithCallableDefaultScope.where(:name => 'David').first
+ developer = EagerDeveloperWithCallableDefaultScope.where(name: "David").first
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
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
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ 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
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1
).to_a
)
end
@@ -865,15 +975,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal(
posts(:thinking, :sti_comments),
Post.all.merge!(
- :includes => [:author, :comments], :where => { 'authors.name' => 'David' },
- :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ 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
+ includes: [:author, :comments], where: { "authors.name" => "David" },
+ order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -882,25 +992,25 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal(
people(:david, :susan),
Person.references(:number1_fans_people).merge(
- :includes => [:readers, :primary_contact, :number1_fan],
- :where => "number1_fans_people.first_name like 'M%'",
- :order => 'people.id', :limit => 2, :offset => 0
+ includes: [:readers, :primary_contact, :number1_fan],
+ where: "number1_fans_people.first_name like 'M%'",
+ order: "people.id", limit: 2, offset: 0
).to_a
)
end
def test_polymorphic_type_condition
- post = Post.all.merge!(:includes => :taggings).find(posts(:thinking).id)
- assert post.taggings.include?(taggings(:thinking_general))
- post = SpecialPost.all.merge!(:includes => :taggings).find(posts(:thinking).id)
- assert post.taggings.include?(taggings(:thinking_general))
+ post = Post.all.merge!(includes: :taggings).find(posts(:thinking).id)
+ assert_includes post.taggings, taggings(:thinking_general)
+ post = SpecialPost.all.merge!(includes: :taggings).find(posts(:thinking).id)
+ assert_includes post.taggings, taggings(:thinking_general)
end
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
def assert_equal_after_sort(item1, item2, item3 = nil)
- assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
- assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
+ assert_equal(item1.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id })
+ assert_equal(item3.sort { |a, b| a.id <=> b.id }, item2.sort { |a, b| a.id <=> b.id }) if item3
end
# Test regular association, association with conditions, association with
# STI, and association with conditions assured not to be true
@@ -929,7 +1039,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
d2 = find_all_ordered(Firm, :account)
d1.each_index do |i|
assert_equal(d1[i], d2[i])
- assert_equal(d1[i].account, d2[i].account)
+ if d1[i].account.nil?
+ assert_nil(d2[i].account)
+ else
+ assert_equal(d1[i].account, d2[i].account)
+ end
end
end
@@ -939,28 +1053,34 @@ class EagerAssociationTest < ActiveRecord::TestCase
d2 = find_all_ordered(Client, firm_types)
d1.each_index do |i|
assert_equal(d1[i], d2[i])
- firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
+ firm_types.each do |type|
+ if (expected = d1[i].send(type)).nil?
+ assert_nil(d2[i].send(type))
+ else
+ assert_equal(expected, d2[i].send(type))
+ end
+ end
end
end
def test_eager_with_valid_association_as_string_not_symbol
- assert_nothing_raised { Post.all.merge!(:includes => 'comments').to_a }
+ assert_nothing_raised { Post.all.merge!(includes: "comments").to_a }
end
def test_eager_with_floating_point_numbers
assert_queries(2) do
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
- Comment.all.merge!(:where => "123.456 = 123.456", :includes => :post).to_a
+ Comment.all.merge!(where: "123.456 = 123.456", includes: :post).to_a
end
end
def test_preconfigured_includes_with_belongs_to
author = posts(:welcome).author_with_posts
- assert_no_queries {assert_equal 5, author.posts.size}
+ assert_no_queries { assert_equal 5, author.posts.size }
end
def test_preconfigured_includes_with_has_one
comment = posts(:sti_comments).very_special_comment_with_post
- assert_no_queries {assert_equal posts(:sti_comments), comment.post}
+ assert_no_queries { assert_equal posts(:sti_comments), comment.post }
end
def test_eager_association_with_scope_with_joins
@@ -1002,13 +1122,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_association_loading_notification
- notifications = messages_for('instantiation.active_record') do
- Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ notifications = messages_for("instantiation.active_record") do
+ Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
end
message = notifications.first
payload = message.last
- count = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ count = Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
# eagerloaded row count should be greater than just developer count
assert_operator payload[:record_count], :>, count
@@ -1016,7 +1136,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_base_messages
- notifications = messages_for('instantiation.active_record') do
+ notifications = messages_for("instantiation.active_record") do
Developer.all.to_a
end
message = notifications.first
@@ -1038,61 +1158,55 @@ 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
def test_conditions_on_join_table_with_include_and_limit
- assert_equal 3, Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ assert_equal 3, Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a.size
end
def test_dont_create_temporary_active_record_instances
Developer.instance_count = 0
- developers = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a
+ developers = Developer.all.merge!(includes: "projects", where: { "developers_projects.access_level" => 1 }, limit: 5).to_a
assert_equal developers.count, Developer.instance_count
end
def test_order_on_join_table_with_include_and_limit
- assert_equal 5, Developer.all.merge!(:includes => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).to_a.size
+ assert_equal 5, Developer.all.merge!(includes: "projects", order: "developers_projects.joined_on DESC", limit: 5).to_a.size
end
def test_eager_loading_with_order_on_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:joins => :comments, :includes => :author, :order => 'comments.id DESC').to_a
+ Post.all.merge!(joins: :comments, includes: :author, order: "comments.id DESC").to_a
end
assert_equal posts(:eager_other), posts[1]
- assert_equal authors(:mary), assert_no_queries { posts[1].author}
+ assert_equal authors(:mary), assert_no_queries { posts[1].author }
end
def test_eager_loading_with_conditions_on_joined_table_preloads
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!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ 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}
+ 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
+ Post.all.merge!(includes: :author, joins: { taggings: :tag }, where: "tags.name = 'General'", order: "posts.id").to_a
end
assert_equal posts(:welcome, :thinking), posts
posts = assert_queries(2) do
- Post.all.merge!(:includes => :author, :joins => {:taggings => {:tag => :taggings}}, :where => "taggings_tags.super_tag_id=2", :order => 'posts.id').to_a
+ Post.all.merge!(includes: :author, joins: { taggings: { tag: :taggings } }, where: "taggings_tags.super_tag_id=2", order: "posts.id").to_a
end
assert_equal posts(:welcome, :thinking), posts
end
def test_preload_has_many_with_association_condition_and_default_scope
- post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
- Reader.create! :person => people(:david), :post => post
- LazyReader.create! :person => people(:susan), :post => post
+ post = Post.create!(title: "Beaches", body: "I like beaches!")
+ Reader.create! person: people(:david), post: post
+ LazyReader.create! person: people(:susan), post: post
assert_equal 1, post.lazy_readers.to_a.size
assert_equal 2, post.lazy_readers_skimmers_or_not.to_a.size
@@ -1103,39 +1217,39 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_loading_with_conditions_on_string_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: "INNER JOIN comments on comments.post_id = posts.id", 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}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
posts = assert_queries(2) do
- Post.all.merge!(:select => 'distinct posts.*', :includes => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
+ Post.all.merge!(select: "distinct posts.*", includes: :author, joins: ["INNER JOIN comments on comments.post_id = posts.id"], 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}
+ assert_equal authors(:david), assert_no_queries { posts[0].author }
end
def test_eager_loading_with_select_on_joined_table_preloads
posts = assert_queries(2) do
- Post.all.merge!(:select => 'posts.*, authors.name as author_name', :includes => :comments, :joins => :author, :order => 'posts.id').to_a
+ Post.all.merge!(select: "posts.*, authors.name as author_name", includes: :comments, joins: :author, order: "posts.id").to_a
end
- assert_equal 'David', posts[0].author_name
- assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
+ assert_equal "David", posts[0].author_name
+ assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments }
end
def test_eager_loading_with_conditions_on_join_model_preloads
authors = assert_queries(2) do
- Author.all.merge!(:includes => :author_address, :joins => :comments, :where => "posts.title like 'Welcome%'").to_a
+ Author.all.merge!(includes: :author_address, joins: :comments, where: "posts.title like 'Welcome%'").to_a
end
assert_equal authors(:david), authors[0]
assert_equal author_addresses(:david_address), authors[0].author_address
end
def test_preload_belongs_to_uses_exclusive_scope
- people = Person.males.merge(:includes => :primary_contact).to_a
+ people = Person.males.merge(includes: :primary_contact).to_a
assert_not_equal people.length, 0
people.each do |person|
- assert_no_queries {assert_not_nil person.primary_contact}
+ assert_no_queries { assert_not_nil person.primary_contact }
assert_equal Person.find(person.id).primary_contact, person.primary_contact
end
end
@@ -1157,12 +1271,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_include_has_many_using_primary_key
expected = Firm.find(1).clients_using_primary_key.sort_by(&:name)
- # Oracle adapter truncates alias to 30 characters
- if current_adapter?(:OracleAdapter)
- firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies'[0,30]+'.name').find(1)
- else
- firm = Firm.all.merge!(:includes => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name').find(1)
- end
+ firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1)
assert_no_queries do
assert_equal expected, firm.clients_using_primary_key
end
@@ -1170,7 +1279,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_preload_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'companies.id').first
+ firm = Firm.all.merge!(includes: :account_using_primary_key, order: "companies.id").first
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
@@ -1178,35 +1287,37 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_include_has_one_using_primary_key
expected = accounts(:signals37)
- firm = Firm.all.merge!(:includes => :account_using_primary_key, :order => 'accounts.id').to_a.detect {|f| f.id == 1}
+ firm = Firm.all.merge!(includes: :account_using_primary_key, order: "accounts.id").to_a.detect { |f| f.id == 1 }
assert_no_queries do
assert_equal expected, firm.account_using_primary_key
end
end
def test_preloading_empty_belongs_to
- c = Client.create!(:name => 'Foo', :client_of => Company.maximum(:id) + 1)
+ c = Client.create!(name: "Foo", client_of: Company.maximum(:id) + 1)
client = assert_queries(2) { Client.preload(:firm).find(c.id) }
assert_no_queries { assert_nil client.firm }
+ assert_equal c.client_of, client.client_of
end
def test_preloading_empty_belongs_to_polymorphic
- t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general))
+ t = Tagging.create!(taggable_type: "Post", taggable_id: Post.maximum(:id) + 1, tag: tags(:general))
tagging = assert_queries(2) { Tagging.preload(:taggable).find(t.id) }
assert_no_queries { assert_nil tagging.taggable }
+ assert_equal t.taggable_id, tagging.taggable_id
end
def test_preloading_through_empty_belongs_to
- c = Client.create!(:name => 'Foo', :client_of => Company.maximum(:id) + 1)
+ c = Client.create!(name: "Foo", client_of: Company.maximum(:id) + 1)
client = assert_queries(2) { Client.preload(:accounts).find(c.id) }
assert_no_queries { assert client.accounts.empty? }
end
def test_preloading_has_many_through_with_distinct
- mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ mary = Author.includes(:unique_categorized_posts).where(id: authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
end
@@ -1234,31 +1345,31 @@ class EagerAssociationTest < ActiveRecord::TestCase
groucho = members(:groucho)
sponsor = assert_queries(2) {
- Sponsor.includes(:thing).where(:id => sponsor.id).first
+ Sponsor.includes(:thing).where(id: sponsor.id).first
}
assert_no_queries { assert_equal groucho, sponsor.thing }
end
def test_joins_with_includes_should_preload_via_joins
- post = assert_queries(1) { Post.includes(:comments).joins(:comments).order('posts.id desc').to_a.first }
+ post = assert_queries(1) { Post.includes(:comments).joins(:comments).order("posts.id desc").to_a.first }
- assert_queries(0) do
+ assert_no_queries do
assert_not_equal 0, post.comments.to_a.count
end
end
def test_join_eager_with_empty_order_should_generate_valid_sql
assert_nothing_raised do
- Post.includes(:comments).order("").where(:comments => {:body => "Thank you for the welcome"}).first
+ Post.includes(:comments).order("").where(comments: { body: "Thank you for the welcome" }).first
end
end
def test_deep_including_through_habtm
# warm up habtm cache
- posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a
posts[0].categories[0].categorizations.length
- posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
+ posts = Post.all.merge!(includes: { categories: :categorizations }, order: "posts.id").to_a
assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
@@ -1274,20 +1385,25 @@ 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) }
+ assert_equal Comment.find(1), Comment.preload(post: :comments).scoping { Comment.find(1) }
end
test "circular preload does not modify unscoped" do
expected = FirstPost.unscoped.find(2)
- FirstPost.preload(:comments => :first_post).find(1)
+ FirstPost.preload(comments: :first_post).find(1)
assert_equal expected, FirstPost.unscoped.find(2)
end
test "preload ignores the scoping" do
assert_equal(
Comment.find(1).post,
- Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post }
+ Post.where("1 = 0").scoping { Comment.preload(:post).find(1).post }
)
end
@@ -1312,10 +1428,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
test "works in combination with order(:symbol) and reorder(:symbol)" do
- author = Author.includes(:posts).references(:posts).order(:name).find_by('posts.title IS NOT NULL')
+ author = Author.includes(:posts).references(:posts).order(:name).find_by("posts.title IS NOT NULL")
assert_equal authors(:bob), author
- author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL')
+ author = Author.includes(:posts).references(:posts).reorder(:name).find_by("posts.title IS NOT NULL")
assert_equal authors(:bob), author
end
@@ -1341,6 +1457,8 @@ 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
@@ -1355,7 +1473,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
test "including associations with where.not adds implicit references" do
author = assert_queries(2) {
- Author.includes(:posts).where.not(posts: { title: 'Welcome to the weblog'} ).last
+ Author.includes(:posts).where.not(posts: { title: "Welcome to the weblog" }).last
}
assert_no_queries {
@@ -1389,7 +1507,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
exception = assert_raises(ArgumentError) do
Author.preload(10).to_a
end
- assert_equal('10 was not recognized for preload', exception.message)
+ assert_equal("10 was not recognized for preload", exception.message)
end
test "associations with extensions are not instance dependent" do
@@ -1408,48 +1526,107 @@ class EagerAssociationTest < ActiveRecord::TestCase
test "preloading readonly association" do
# has-one
firm = Firm.where(id: "1").preload(:readonly_account).first!
- assert firm.readonly_account.readonly?
+ assert_predicate firm.readonly_account, :readonly?
# has_and_belongs_to_many
project = Project.where(id: "2").preload(:readonly_developers).first!
- assert project.readonly_developers.first.readonly?
+ assert_predicate project.readonly_developers.first, :readonly?
# has-many :through
david = Author.where(id: "1").preload(:readonly_comments).first!
- assert david.readonly_comments.first.readonly?
+ assert_predicate david.readonly_comments.first, :readonly?
+ end
+
+ test "eager-loading non-readonly association" do
+ # has_one
+ firm = Firm.where(id: "1").eager_load(:account).first!
+ assert_not_predicate firm.account, :readonly?
+
+ # has_and_belongs_to_many
+ project = Project.where(id: "2").eager_load(:developers).first!
+ assert_not_predicate project.developers.first, :readonly?
+
+ # has_many :through
+ david = Author.where(id: "1").eager_load(:comments).first!
+ assert_not_predicate david.comments.first, :readonly?
+
+ # belongs_to
+ post = Post.where(id: "1").eager_load(:author).first!
+ assert_not_predicate post.author, :readonly?
end
test "eager-loading readonly association" do
# has-one
firm = Firm.where(id: "1").eager_load(:readonly_account).first!
- assert firm.readonly_account.readonly?
+ assert_predicate firm.readonly_account, :readonly?
# has_and_belongs_to_many
project = Project.where(id: "2").eager_load(:readonly_developers).first!
- assert project.readonly_developers.first.readonly?
+ assert_predicate project.readonly_developers.first, :readonly?
# has-many :through
david = Author.where(id: "1").eager_load(:readonly_comments).first!
- assert david.readonly_comments.first.readonly?
+ assert_predicate david.readonly_comments.first, :readonly?
# belongs_to
- post = Post.where(id: "1").eager_load(:author).first!
- assert post.author.readonly?
+ post = Post.where(id: "1").eager_load(:readonly_author).first!
+ assert_predicate post.readonly_author, :readonly?
end
test "preloading a polymorphic association with references to the associated table" do
- post = Post.includes(:tags).references(:tags).where('tags.name = ?', 'General').first
+ post = Post.includes(:tags).references(:tags).where("tags.name = ?", "General").first
assert_equal posts(:welcome), post
end
test "eager-loading a polymorphic association with references to the associated table" do
- post = Post.eager_load(:tags).where('tags.name = ?', 'General').first
+ post = Post.eager_load(:tags).where("tags.name = ?", "General").first
assert_equal posts(:welcome), post
end
+ test "eager-loading with a polymorphic association won't work consistently" do
+ assert_raise(ActiveRecord::EagerLoadPolymorphicError) { authors(:david).essays.eager_load(:writer).to_a }
+ assert_raise(ActiveRecord::EagerLoadPolymorphicError) { authors(:david).essays.eager_load(:writer).count }
+ assert_raise(ActiveRecord::EagerLoadPolymorphicError) { 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!
+ assert_not_called_on_instance_of(ActiveRecord::Associations::HasManyAssociation, :reader) do
+ Author.preload(:readonly_comments).first!
+ end
+ end
+
+ test "preloading through a polymorphic association doesn't require the association to exist" do
+ sponsors = []
+ assert_queries 5 do
+ sponsors = Sponsor.where(sponsorable_id: 1).preload(sponsorable: [:post, :membership]).to_a
+ end
+ # check the preload worked
+ assert_queries 0 do
+ sponsors.map(&:sponsorable).map { |s| s.respond_to?(:posts) ? s.post.author : s.membership }
+ end
end
+
+ test "preloading a regular association through a polymorphic association doesn't require the association to exist on all types" do
+ sponsors = []
+ assert_queries 6 do
+ sponsors = Sponsor.where(sponsorable_id: 1).preload(sponsorable: [{ post: :first_comment }, :membership]).to_a
+ end
+ # check the preload worked
+ assert_queries 0 do
+ sponsors.map(&:sponsorable).map { |s| s.respond_to?(:posts) ? s.post.author : s.membership }
+ end
+ end
+
+ test "preloading a regular association with a typo through a polymorphic association still raises" do
+ # this test contains an intentional typo of first -> fist
+ assert_raises(ActiveRecord::AssociationNotFoundError) do
+ Sponsor.where(sponsorable_id: 1).preload(sponsorable: [{ post: :fist_comment }, :membership]).to_a
+ end
+ 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 b161cde335..aef8f31112 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/project'
-require 'models/developer'
-require 'models/computer'
-require 'models/company_in_module'
+require "models/post"
+require "models/comment"
+require "models/project"
+require "models/developer"
+require "models/computer"
+require "models/company_in_module"
class AssociationsExtensionsTest < ActiveRecord::TestCase
fixtures :projects, :developers, :developers_projects, :comments, :posts
@@ -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
@@ -45,7 +52,7 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
# Marshaling an association shouldn't make it unusable by wiping its reflection.
assert_not_nil david.association(:projects).reflection
- david_too = Marshal.load(marshalled)
+ david_too = Marshal.load(marshalled)
assert_equal projects(:action_controller), david_too.projects.find_most_recent
end
@@ -63,14 +70,20 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
extend!(Developer)
extend!(MyApplication::Business::Developer)
- assert Object.const_get 'DeveloperAssociationNameAssociationExtension'
- assert MyApplication::Business.const_get 'DeveloperAssociationNameAssociationExtension'
+ assert Object.const_get "DeveloperAssociationNameAssociationExtension"
+ assert MyApplication::Business.const_get "DeveloperAssociationNameAssociationExtension"
end
def test_proxy_association_after_scoped
post = posts(:welcome)
assert_equal post.association(:comments), post.comments.the_association
- assert_equal post.association(:comments), post.comments.where('1=1').the_association
+ 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
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 1bbca84bb2..fe8bdd03ba 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,84 +1,89 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/course'
-require 'models/customer'
-require 'models/order'
-require 'models/categorization'
-require 'models/category'
-require 'models/post'
-require 'models/author'
-require 'models/tag'
-require 'models/tagging'
-require 'models/parrot'
-require 'models/person'
-require 'models/pirate'
-require 'models/professor'
-require 'models/treasure'
-require 'models/price_estimate'
-require 'models/club'
-require 'models/member'
-require 'models/membership'
-require 'models/sponsor'
-require 'models/country'
-require 'models/treaty'
-require 'models/vertex'
-require 'models/publisher'
-require 'models/publisher/article'
-require 'models/publisher/magazine'
-require 'active_support/core_ext/string/conversions'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/course"
+require "models/customer"
+require "models/order"
+require "models/categorization"
+require "models/category"
+require "models/post"
+require "models/author"
+require "models/tag"
+require "models/tagging"
+require "models/parrot"
+require "models/person"
+require "models/pirate"
+require "models/professor"
+require "models/treasure"
+require "models/price_estimate"
+require "models/club"
+require "models/user"
+require "models/member"
+require "models/membership"
+require "models/sponsor"
+require "models/lesson"
+require "models/student"
+require "models/country"
+require "models/treaty"
+require "models/vertex"
+require "models/publisher"
+require "models/publisher/article"
+require "models/publisher/magazine"
+require "active_support/core_ext/string/conversions"
class ProjectWithAfterCreateHook < ActiveRecord::Base
- self.table_name = 'projects'
+ self.table_name = "projects"
has_and_belongs_to_many :developers,
- :class_name => "DeveloperForProjectWithAfterCreateHook",
- :join_table => "developers_projects",
- :foreign_key => "project_id",
- :association_foreign_key => "developer_id"
+ class_name: "DeveloperForProjectWithAfterCreateHook",
+ join_table: "developers_projects",
+ foreign_key: "project_id",
+ association_foreign_key: "developer_id"
after_create :add_david
def add_david
- david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name("David")
david.projects << self
end
end
class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :projects,
- :class_name => "ProjectWithAfterCreateHook",
- :join_table => "developers_projects",
- :association_foreign_key => "project_id",
- :foreign_key => "developer_id"
+ class_name: "ProjectWithAfterCreateHook",
+ join_table: "developers_projects",
+ association_foreign_key: "project_id",
+ foreign_key: "developer_id"
end
class ProjectWithSymbolsForKeys < ActiveRecord::Base
- self.table_name = 'projects'
+ self.table_name = "projects"
has_and_belongs_to_many :developers,
- :class_name => "DeveloperWithSymbolsForKeys",
- :join_table => :developers_projects,
- :foreign_key => :project_id,
- :association_foreign_key => "developer_id"
+ class_name: "DeveloperWithSymbolsForKeys",
+ join_table: :developers_projects,
+ foreign_key: :project_id,
+ association_foreign_key: "developer_id"
end
class DeveloperWithSymbolsForKeys < ActiveRecord::Base
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :projects,
- :class_name => "ProjectWithSymbolsForKeys",
- :join_table => :developers_projects,
- :association_foreign_key => :project_id,
- :foreign_key => "developer_id"
+ class_name: "ProjectWithSymbolsForKeys",
+ join_table: :developers_projects,
+ association_foreign_key: :project_id,
+ foreign_key: "developer_id"
end
class SubDeveloper < Developer
- self.table_name = 'developers'
+ self.table_name = "developers"
has_and_belongs_to_many :special_projects,
- :join_table => 'developers_projects',
- :foreign_key => "project_id",
- :association_foreign_key => "developer_id"
+ join_table: "developers_projects",
+ foreign_key: "project_id",
+ association_foreign_key: "developer_id"
end
class DeveloperWithSymbolClassName < Developer
@@ -88,7 +93,7 @@ end
class DeveloperWithExtendOption < Developer
module NamedExtension
def category
- 'sns'
+ "sns"
end
end
@@ -96,27 +101,42 @@ class DeveloperWithExtendOption < Developer
end
class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base
- self.table_name = 'projects'
- has_and_belongs_to_many :developers, -> { unscope(where: 'name') },
+ self.table_name = "projects"
+ has_and_belongs_to_many :developers, -> { unscope(where: "name") },
class_name: "LazyBlockDeveloperCalledDavid",
join_table: "developers_projects",
foreign_key: "project_id",
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
def setup_data_for_habtm_case
- ActiveRecord::Base.connection.execute('delete from countries_treaties')
+ ActiveRecord::Base.connection.execute("delete from countries_treaties")
- country = Country.new(:name => 'India')
- country.country_id = 'c1'
+ country = Country.new(name: "India")
+ country.country_id = "c1"
country.save!
- treaty = Treaty.new(:name => 'peace')
- treaty.treaty_id = 't1'
+ treaty = Treaty.new(name: "peace")
+ treaty.treaty_id = "t1"
country.treaties << treaty
end
@@ -130,45 +150,45 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
setup_data_for_habtm_case
con = ActiveRecord::Base.connection
- sql = 'select * from countries_treaties'
+ sql = "select * from countries_treaties"
record = con.select_rows(sql).last
- assert_equal 'c1', record[0]
- assert_equal 't1', record[1]
+ assert_equal "c1", record[0]
+ assert_equal "t1", record[1]
end
def test_proper_usage_of_primary_keys_and_join_table
setup_data_for_habtm_case
- assert_equal 'country_id', Country.primary_key
- assert_equal 'treaty_id', Treaty.primary_key
+ assert_equal "country_id", Country.primary_key
+ assert_equal "treaty_id", Treaty.primary_key
country = Country.first
assert_equal 1, country.treaties.count
end
def test_join_table_composite_primary_key_should_not_warn
- country = Country.new(:name => 'India')
- country.country_id = 'c1'
+ country = Country.new(name: "India")
+ country.country_id = "c1"
country.save!
- treaty = Treaty.new(:name => 'peace')
- treaty.treaty_id = 't1'
+ treaty = Treaty.new(name: "peace")
+ treaty.treaty_id = "t1"
warning = capture(:stderr) do
country.treaties << treaty
end
- assert_no_match(/WARNING: Rails does not support composite primary key\./, warning)
+ assert_no_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
def test_has_and_belongs_to_many
david = Developer.find(1)
- assert !david.projects.empty?
+ assert_not_empty david.projects
assert_equal 2, david.projects.size
active_record = Project.find(1)
- assert !active_record.developers.empty?
+ assert_not_empty active_record.developers
assert_equal 3, active_record.developers.size
- assert active_record.developers.include?(david)
+ assert_includes active_record.developers, david
end
def test_adding_single
@@ -244,12 +264,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
no_of_projects = Project.count
aredridel = Developer.new("name" => "Aredridel")
aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
- assert !aredridel.persisted?
- assert !p.persisted?
+ assert_not_predicate aredridel, :persisted?
+ assert_not_predicate p, :persisted?
assert aredridel.save
- assert aredridel.persisted?
- assert_equal no_of_devels+1, Developer.count
- assert_equal no_of_projects+1, Project.count
+ assert_predicate aredridel, :persisted?
+ assert_equal no_of_devels + 1, Developer.count
+ assert_equal no_of_projects + 1, Project.count
assert_equal 2, aredridel.projects.size
assert_equal 2, aredridel.projects.reload.size
end
@@ -257,7 +277,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_habtm_saving_multiple_relationships
new_project = Project.new("name" => "Grimetime")
amount_of_developers = 4
- developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
+ developers = (0...amount_of_developers).reverse_each.map { |i| Developer.create(name: "JME #{i}") }
new_project.developer_ids = [developers[0].id, developers[1].id]
new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
@@ -282,54 +302,61 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_collection_size_from_params
- devel = Developer.new({
+ devel = Developer.new(
projects_attributes: {
- '0' => {}
- }
- })
+ "0" => {}
+ })
assert_equal 1, devel.projects.size
end
def test_build
devel = Developer.find(1)
- proj = assert_no_queries(ignore_none: false) { devel.projects.build("name" => "Projekt") }
- assert !devel.projects.loaded?
+
+ # Load schema information so we don't query below if running just this test.
+ Project.define_attribute_methods
+
+ proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert devel.projects.loaded?
+ assert_predicate devel.projects, :loaded?
- assert !proj.persisted?
+ assert_not_predicate proj, :persisted?
devel.save
- assert proj.persisted?
+ assert_predicate proj, :persisted?
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
def test_new_aliased_to_build
devel = Developer.find(1)
- proj = assert_no_queries(ignore_none: false) { devel.projects.new("name" => "Projekt") }
- assert !devel.projects.loaded?
+
+ # Load schema information so we don't query below if running just this test.
+ Project.define_attribute_methods
+
+ proj = assert_no_queries { devel.projects.new("name" => "Projekt") }
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert devel.projects.loaded?
+ assert_predicate devel.projects, :loaded?
- assert !proj.persisted?
+ assert_not_predicate proj, :persisted?
devel.save
- assert proj.persisted?
+ assert_predicate proj, :persisted?
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
def test_build_by_new_record
- devel = Developer.new(:name => "Marcel", :salary => 75000)
- devel.projects.build(:name => "Make bed")
- proj2 = devel.projects.build(:name => "Lie in it")
+ 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?
+ assert_not_predicate proj2, :persisted?
devel.save
- assert devel.persisted?
- assert proj2.persisted?
+ assert_predicate devel, :persisted?
+ assert_predicate proj2, :persisted?
assert_equal devel.projects.last, proj2
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
@@ -337,40 +364,27 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_create
devel = Developer.find(1)
proj = devel.projects.create("name" => "Projekt")
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
- assert proj.persisted?
+ assert_predicate proj, :persisted?
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 => ' ')
+ post = categories(:general).post_with_conditions.build(body: " ")
- assert post.save
- assert_equal 'Yet Another Testing Title', post.title
+ assert post.save
+ assert_equal "Yet Another Testing Title", post.title
# in Oracle '' is saved as null therefore need to save ' ' in not null column
- another_post = categories(:general).post_with_conditions.create(:body => ' ')
+ another_post = categories(:general).post_with_conditions.create(body: " ")
- assert another_post.persisted?
- assert_equal 'Yet Another Testing Title', another_post.title
+ assert_predicate another_post, :persisted?
+ assert_equal "Yet Another Testing Title", another_post.title
end
def test_distinct_after_the_fact
@@ -379,7 +393,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
dev.projects << projects(:active_record)
assert_equal 3, dev.projects.size
- assert_equal 1, dev.projects.distinct.size
+ assert_equal 1, dev.projects.uniq.size
end
def test_distinct_before_the_fact
@@ -437,10 +451,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_removing_associations_on_destroy
david = DeveloperWithBeforeDestroyRaise.find(1)
- assert !david.projects.empty?
+ assert_not_empty david.projects
david.destroy
- assert david.projects.empty?
- assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
+ assert_empty david.projects
+ assert_empty DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1")
end
def test_destroying
@@ -455,7 +469,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id} AND project_id = #{project.id}")
- assert join_records.empty?
+ assert_empty join_records
assert_equal 1, david.reload.projects.size
assert_equal 1, david.projects.reload.size
@@ -471,7 +485,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
- assert join_records.empty?
+ assert_empty join_records
assert_equal 0, david.reload.projects.size
assert_equal 0, david.projects.reload.size
@@ -480,23 +494,23 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_all
david = Developer.find(1)
david.projects.reload
- assert !david.projects.empty?
+ assert_not_empty david.projects
assert_no_difference "Project.count" do
david.projects.destroy_all
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
- assert join_records.empty?
+ assert_empty join_records
- assert david.projects.empty?
- assert david.projects.reload.empty?
+ assert_empty david.projects
+ assert_empty david.projects.reload
end
def test_destroy_associations_destroys_multiple_associations
george = parrots(:george)
- assert !george.pirates.empty?
- assert !george.treasures.empty?
+ assert_not_empty george.pirates
+ assert_not_empty george.treasures
assert_no_difference "Pirate.count" do
assert_no_difference "Treasure.count" do
@@ -505,12 +519,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
- assert join_records.empty?
- assert george.pirates.reload.empty?
+ assert_empty join_records
+ assert_empty george.pirates.reload
join_records = Parrot.connection.select_all("SELECT * FROM parrots_treasures WHERE parrot_id = #{george.id}")
- assert join_records.empty?
- assert george.treasures.reload.empty?
+ assert_empty join_records
+ assert_empty george.treasures.reload
end
def test_associations_with_conditions
@@ -542,9 +556,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
- assert_no_queries(ignore_none: false) do
- assert project.developers.loaded?
- assert project.developers.include?(developer)
+ assert_no_queries do
+ assert_predicate project.developers, :loaded?
+ assert_includes project.developers, developer
end
end
@@ -553,19 +567,19 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
project.reload
- assert ! project.developers.loaded?
+ assert_not_predicate project.developers, :loaded?
assert_queries(1) do
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
- assert ! project.developers.loaded?
+ assert_not_predicate project.developers, :loaded?
end
def test_include_returns_false_for_non_matching_record_to_verify_scoping
project = projects(:active_record)
- developer = Developer.create :name => "Bryan", :salary => 50_000
+ developer = Developer.create name: "Bryan", salary: 50_000
- assert ! project.developers.loaded?
- assert ! project.developers.include?(developer)
+ assert_not_predicate project.developers, :loaded?
+ assert_not project.developers.include?(developer)
end
def test_find_with_merged_options
@@ -576,32 +590,32 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_dynamic_find_should_respect_association_order
# Developers are ordered 'name DESC, id DESC'
- high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+ high_id_jamis = projects(:active_record).developers.create(name: "Jamis")
- assert_equal high_id_jamis, projects(:active_record).developers.merge(:where => "name = 'Jamis'").first
- assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
+ assert_equal high_id_jamis, projects(:active_record).developers.merge(where: "name = 'Jamis'").first
+ assert_equal high_id_jamis, projects(:active_record).developers.find_by_name("Jamis")
end
def test_find_should_append_to_association_order
- ordered_developers = projects(:active_record).developers.order('projects.id')
- assert_equal ['developers.name desc, developers.id desc', 'projects.id'], ordered_developers.order_values
+ ordered_developers = projects(:active_record).developers.order("projects.id")
+ assert_equal ["developers.name desc, developers.id desc", "projects.id"], ordered_developers.order_values
end
def test_dynamic_find_all_should_respect_readonly_access
- projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?}
+ projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid? }
projects(:active_record).readonly_developers.each(&:readonly?)
end
def test_new_with_values_in_collection
- jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
- david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
- project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
+ jamis = DeveloperForProjectWithAfterCreateHook.find_by_name("Jamis")
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name("David")
+ project = ProjectWithAfterCreateHook.new(name: "Cooking with Bertie")
project.developers << jamis
project.save!
project.reload
- assert project.developers.include?(jamis)
- assert project.developers.include?(david)
+ assert_includes project.developers, jamis
+ assert_includes project.developers, david
end
def test_find_in_association_with_options
@@ -612,8 +626,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_with_extend_option
- eponine = DeveloperWithExtendOption.create(name: 'Eponine')
- assert_equal 'sns', eponine.projects.category
+ eponine = DeveloperWithExtendOption.create(name: "Eponine")
+ assert_equal "sns", eponine.projects.category
end
def test_replace_with_less
@@ -628,7 +642,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
david.save
assert_equal 2, david.projects.length
- assert !david.projects.include?(projects(:active_record))
+ assert_not_includes david.projects, projects(:active_record)
end
def test_replace_on_new_object
@@ -646,9 +660,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer.special_projects << special_project
developer.reload
- assert developer.projects.include?(special_project)
- assert developer.special_projects.include?(special_project)
- assert !developer.special_projects.include?(other_project)
+ assert_includes developer.projects, special_project
+ assert_includes developer.special_projects, special_project
+ assert_not_includes developer.special_projects, other_project
end
def test_symbol_join_table
@@ -658,7 +672,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_includes developer.sym_special_projects, sp
end
- def test_update_attributes_after_push_without_duplicate_join_table_rows
+ def test_update_columns_after_push_without_duplicate_join_table_rows
developer = Developer.new("name" => "Kano")
project = SpecialProject.create("name" => "Special Project")
assert developer.save
@@ -689,28 +703,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_respects_select_query_method
- assert_equal ['id'], developers(:david).projects.select(:id).first.attributes.keys
+ assert_equal ["id"], developers(:david).projects.select(:id).first.attributes.keys
end
def test_join_table_alias
- # FIXME: `references` has no impact on the aliases generated for the join
- # query. The fact that we pass `:developers_projects_join` to `references`
- # and that the SQL string contains `developers_projects_join` is merely a
- # coincidence.
assert_equal(
3,
- Developer.references(:developers_projects_join).merge(
- :includes => {:projects => :developers},
- :where => 'projects_developers_projects_join.joined_on IS NOT NULL'
- ).to_a.size
+ Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).to_a.size
)
end
def test_join_with_group
- # FIXME: `references` has no impact on the aliases generated for the join
- # query. The fact that we pass `:developers_projects_join` to `references`
- # and that the SQL string contains `developers_projects_join` is merely a
- # coincidence.
group = Developer.columns.inject([]) do |g, c|
g << "developers.#{c.name}"
g << "developers_projects_2.#{c.name}"
@@ -719,16 +722,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
- Developer.references(:developers_projects_join).merge(
- :includes => {:projects => :developers}, :where => 'projects_developers_projects_join.joined_on IS NOT NULL',
- :group => group.join(",")
- ).to_a.size
+ Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).group(group.join(",")).to_a.size
)
end
def test_find_grouped
- all_posts_from_category1 = Post.all.merge!(:where => "category_id = 1", :joins => :categories).to_a
- grouped_posts_of_category1 = Post.all.merge!(:where => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories).to_a
+ all_posts_from_category1 = Post.all.merge!(where: "category_id = 1", joins: :categories).to_a
+ grouped_posts_of_category1 = Post.all.merge!(where: "category_id = 1", group: "author_id", select: "count(posts.id) as posts_count", joins: :categories).to_a
assert_equal 5, all_posts_from_category1.size
assert_equal 2, grouped_posts_of_category1.size
end
@@ -739,8 +739,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_scoped_grouped_having
- assert_equal 2, projects(:active_record).well_payed_salary_groups.to_a.size
- assert projects(:active_record).well_payed_salary_groups.all? { |g| g.salary > 10000 }
+ assert_equal 2, projects(:active_record).well_paid_salary_groups.to_a.size
+ assert projects(:active_record).well_paid_salary_groups.all? { |g| g.salary > 10000 }
end
def test_get_ids
@@ -751,7 +751,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
developer.projects.reload
- assert_queries(0) do
+ assert_no_queries do
developer.project_ids
developer.project_ids
end
@@ -759,9 +759,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
developer = developers(:david)
- assert !developer.projects.loaded?
+ assert_not_predicate developer.projects, :loaded?
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
- assert !developer.projects.loaded?
+ assert_not_predicate developer.projects, :loaded?
end
def test_assign_ids
@@ -775,13 +775,23 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_assign_ids_ignoring_blanks
developer = Developer.new("name" => "Joe")
- developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
+ developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, ""]
developer.save
developer.reload
assert_equal 2, developer.projects.length
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
end
+ def test_singular_ids_are_reloaded_after_collection_concat
+ student = Student.create(name: "Alberto Almagro")
+ student.lesson_ids
+
+ lesson = Lesson.create(name: "DSI")
+ student.lessons << lesson
+
+ assert_includes student.lesson_ids, lesson.id
+ end
+
def test_scoped_find_on_through_association_doesnt_return_read_only_records
tag = Post.find(1).tags.find_by_name("General")
@@ -791,12 +801,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_manys_works
- assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
+ assert_equal ["$10.00", "$20.00"].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
end
def test_symbols_as_keys
- developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
- project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
+ developer = DeveloperWithSymbolsForKeys.new(name: "David")
+ project = ProjectWithSymbolsForKeys.new(name: "Rails Testing")
project.developers << developer
project.save!
@@ -809,7 +819,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'authors.id'
- assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
+ assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title("Welcome to the weblog")
end
def test_count
@@ -841,12 +851,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_attributes_are_being_set_when_initialized_from_habtm_association_with_where_clause
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").build
+ new_developer = projects(:action_controller).developers.where(name: "Marcelo").build
assert_equal new_developer.name, "Marcelo"
end
def test_attributes_are_being_set_when_initialized_from_habtm_association_with_multiple_where_clauses
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").where(:salary => 90_000).build
+ new_developer = projects(:action_controller).developers.where(name: "Marcelo").where(salary: 90_000).build
assert_equal new_developer.name, "Marcelo"
assert_equal new_developer.salary, 90_000
end
@@ -854,7 +864,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
project = Project.new
developer = project.developers.build
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
def test_destruction_does_not_error_without_primary_key
@@ -869,9 +879,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_associations_on_new_records_use_null_relations
projects = Developer.new.projects
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], projects
- assert_equal [], projects.where(title: 'omg')
+ assert_equal [], projects.where(title: "omg")
assert_equal [], projects.pluck(:title)
assert_equal 0, projects.count
end
@@ -885,7 +895,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
treasure.valid?
assert_equal 1, treasure.rich_people.size
- assert_nil rich_person.first_name, 'should not run associated person validation on create when validate: false'
+ assert_nil rich_person.first_name, "should not run associated person validation on create when validate: false"
end
def test_association_with_validate_false_does_not_run_associated_validation_callbacks_on_update
@@ -898,11 +908,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
treasure.valid?
assert_equal 1, treasure.rich_people.size
- assert_equal person_first_name, rich_person.first_name, 'should not run associated person validation on update when validate: false'
+ assert_equal person_first_name, rich_person.first_name, "should not run associated person validation on update when validate: false"
end
def test_custom_join_table
- assert_equal 'edges', Vertex.reflect_on_association(:sources).join_table
+ assert_equal "edges", Vertex.reflect_on_association(:sources).join_table
end
def test_has_and_belongs_to_many_in_a_namespaced_model_pointing_to_a_namespaced_model
@@ -926,29 +936,24 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_redefine_habtm
child = SubDeveloper.new("name" => "Aredridel")
child.special_projects << SpecialProject.new("name" => "Special Project")
- assert child.save, 'child object should be saved'
+ assert child.save, "child object should be saved"
end
def test_habtm_with_reflection_using_class_name_and_fixtures
- assert_not_nil Developer._reflections['shared_computers']
+ 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
def test_with_symbol_class_name
assert_nothing_raised do
- DeveloperWithSymbolClassName.new
+ developer = DeveloperWithSymbolClassName.new
+ developer.projects
end
end
- def test_association_force_reload_with_only_true_is_deprecated
- developer = Developer.find(1)
-
- assert_deprecated { developer.projects(true) }
- end
-
def test_alternate_database
professor = Professor.create(name: "Plum")
course = Course.create(name: "Forensics")
@@ -995,4 +1000,25 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
Project.first.developers_required_by_default.create!(name: "Sean", salary: 50000)
end
end
+
+ def test_association_name_is_the_same_as_join_table_name
+ user = User.create!
+ assert_nothing_raised { user.jobs_pool.clear }
+ end
+
+ def test_has_and_belongs_to_many_while_partial_writes_false
+ original_partial_writes = ActiveRecord::Base.partial_writes
+ ActiveRecord::Base.partial_writes = false
+ developer = Developer.new(name: "Mehmet Emin İNAÇ")
+ developer.projects << Project.new(name: "Bounty")
+
+ assert developer.save
+ ensure
+ ActiveRecord::Base.partial_writes = original_partial_writes
+ 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 e975f4fbdd..6a7efe2121 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1,96 +1,109 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/contract'
-require 'models/topic'
-require 'models/reply'
-require 'models/category'
-require 'models/image'
-require 'models/post'
-require 'models/author'
-require 'models/essay'
-require 'models/comment'
-require 'models/person'
-require 'models/reader'
-require 'models/tagging'
-require 'models/tag'
-require 'models/invoice'
-require 'models/line_item'
-require 'models/car'
-require 'models/bulb'
-require 'models/engine'
-require 'models/categorization'
-require 'models/minivan'
-require 'models/speedometer'
-require 'models/reference'
-require 'models/job'
-require 'models/college'
-require 'models/student'
-require 'models/pirate'
-require 'models/ship'
-require 'models/ship_part'
-require 'models/treasure'
-require 'models/parrot'
-require 'models/tyre'
-require 'models/subscriber'
-require 'models/subscription'
-require 'models/zine'
-require 'models/interest'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/contract"
+require "models/topic"
+require "models/reply"
+require "models/category"
+require "models/image"
+require "models/post"
+require "models/author"
+require "models/essay"
+require "models/comment"
+require "models/person"
+require "models/reader"
+require "models/tagging"
+require "models/tag"
+require "models/invoice"
+require "models/line_item"
+require "models/car"
+require "models/bulb"
+require "models/engine"
+require "models/categorization"
+require "models/minivan"
+require "models/speedometer"
+require "models/reference"
+require "models/job"
+require "models/college"
+require "models/student"
+require "models/pirate"
+require "models/ship"
+require "models/ship_part"
+require "models/treasure"
+require "models/parrot"
+require "models/tyre"
+require "models/subscriber"
+require "models/subscription"
+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)
# this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression
# if the reorder clauses are not correctly handled
- assert author.posts_with_comments_sorted_by_comment_id.where('comments.id > 0').reorder('posts.comments_count DESC', 'posts.tags_count DESC').last
+ assert author.posts_with_comments_sorted_by_comment_id.where("comments.id > 0").reorder("posts.comments_count DESC", "posts.tags_count DESC").last
end
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')
- assert !subscriber.subscriptions.loaded?
+ subscriber = Subscriber.new(nick: "webster132")
+ assert_not_predicate subscriber.subscriptions, :loaded?
assert_queries 1 do
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
- author = Author.new(:name => "David")
- assert !author.essays.loaded?
+ author = Author.new(name: "David")
+ assert_not_predicate author.essays, :loaded?
assert_queries 1 do
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
david = people(:david)
assert_equal ["A Modest Proposal"], david.essays.map(&:name)
- david.essays = [Essay.create!(name: "Remote Work" )]
+ david.essays = [Essay.create!(name: "Remote Work")]
assert_equal ["Remote Work"], david.essays.map(&:name)
end
def test_blank_custom_primary_key_on_new_record_should_not_run_queries
author = Author.new
- assert !author.essays.loaded?
+ assert_not_predicate author.essays, :loaded?
assert_queries 0 do
assert_equal 0, author.essays.size
@@ -100,8 +113,8 @@ end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
- :developers_projects, :topics, :authors, :comments,
- :posts, :readers, :taggings, :cars, :jobs, :tags,
+ :developers_projects, :topics, :authors, :author_addresses, :comments,
+ :posts, :readers, :taggings, :cars, :tags,
:categorizations, :zines, :interests
def setup
@@ -116,14 +129,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_anonymous_has_many
developer = Class.new(ActiveRecord::Base) {
- self.table_name = 'developers'
+ self.table_name = "developers"
dev = self
developer_project = Class.new(ActiveRecord::Base) {
- self.table_name = 'developers_projects'
- belongs_to :developer, :anonymous_class => dev
+ self.table_name = "developers_projects"
+ belongs_to :developer, anonymous_class: dev
}
- has_many :developer_projects, :anonymous_class => developer_project, :foreign_key => 'developer_id'
+ has_many :developer_projects, anonymous_class: developer_project, foreign_key: "developer_id"
}
dev = developer.first
named = Developer.find(dev.id)
@@ -135,20 +148,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_default_scope_on_relations_is_not_cached
counter = 0
posts = Class.new(ActiveRecord::Base) {
- self.table_name = 'posts'
- self.inheritance_column = 'not_there'
+ self.table_name = "posts"
+ self.inheritance_column = "not_there"
post = self
comments = Class.new(ActiveRecord::Base) {
- self.table_name = 'comments'
- self.inheritance_column = 'not_there'
- belongs_to :post, :anonymous_class => post
+ self.table_name = "comments"
+ self.inheritance_column = "not_there"
+ belongs_to :post, anonymous_class: post
default_scope -> {
counter += 1
- where("id = :inc", :inc => counter)
+ where("id = :inc", inc: counter)
}
}
- has_many :comments, :anonymous_class => comments, :foreign_key => 'post_id'
+ has_many :comments, anonymous_class: comments, foreign_key: "post_id"
}
assert_equal 0, counter
post = posts.first
@@ -159,15 +172,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_build_with_options
- college = College.create(name: 'UFMT')
- Student.create(active: true, college_id: college.id, name: 'Sarah')
+ college = College.create(name: "UFMT")
+ Student.create(active: true, college_id: college.id, name: "Sarah")
assert_equal college.students, Student.where(active: true, college_id: college.id)
end
def test_add_record_to_collection_should_change_its_updated_at
- ship = Ship.create(name: 'dauntless')
- part = ShipPart.create(name: 'cockpit')
+ ship = Ship.create(name: "dauntless")
+ part = ShipPart.create(name: "cockpit")
updated_at = part.updated_at
travel(1.second) do
@@ -181,38 +194,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_clear_collection_should_not_change_updated_at
# GH#17161: .clear calls delete_all (and returns the association),
# which is intended to not touch associated objects's updated_at field
- ship = Ship.create(name: 'dauntless')
- part = ShipPart.create(name: 'cockpit', ship_id: ship.id)
+ ship = Ship.create(name: "dauntless")
+ part = ShipPart.create(name: "cockpit", ship_id: ship.id)
ship.parts.clear
part.reload
- assert_equal nil, part.ship
- assert !part.updated_at_changed?
+ assert_nil part.ship
+ assert_not_predicate part, :updated_at_changed?
end
def test_create_from_association_should_respect_default_scope
- car = Car.create(:name => 'honda')
- assert_equal 'honda', car.name
+ car = Car.create(name: "honda")
+ assert_equal "honda", car.name
bulb = Bulb.create
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.build
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.create
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
end
def test_build_and_create_from_association_should_respect_passed_attributes_over_default_scope
- car = Car.create(name: 'honda')
+ car = Car.create(name: "honda")
- bulb = car.bulbs.build(name: 'exotic')
- assert_equal 'exotic', bulb.name
+ bulb = car.bulbs.build(name: "exotic")
+ assert_equal "exotic", bulb.name
- bulb = car.bulbs.create(name: 'exotic')
- assert_equal 'exotic', bulb.name
+ bulb = car.bulbs.create(name: "exotic")
+ assert_equal "exotic", bulb.name
bulb = car.awesome_bulbs.build(frickinawesome: false)
assert_equal false, bulb.frickinawesome
@@ -225,36 +238,44 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
author = Author.new
post = author.thinking_posts.build
- assert_equal 'So I was thinking', post.title
+ assert_equal "So I was thinking", post.title
end
def test_create_from_association_with_nil_values_should_work
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.new(nil)
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.build(nil)
- assert_equal 'defaulty', bulb.name
+ assert_equal "defaulty", bulb.name
bulb = car.bulbs.create(nil)
- assert_equal 'defaulty', bulb.name
+ 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 = Car.create(name: "honda")
car.funky_bulbs.create!
- assert_nothing_raised { car.reload.funky_bulbs.delete_all }
- assert_equal 0, Bulb.count, "bulbs should have been deleted using :delete_all strategy"
+ assert_equal 1, car.funky_bulbs.count
+ assert_equal 1, car.reload.funky_bulbs.delete_all
+ assert_equal 0, car.funky_bulbs.count, "bulbs should have been deleted using :delete_all strategy"
end
def test_delete_all_on_association_is_the_same_as_not_loaded
author = authors :david
- author.thinking_posts.create!(:body => "test")
+ author.thinking_posts.create!(body: "test")
author.reload
expected_sql = capture_sql { author.thinking_posts.delete_all }
- author.thinking_posts.create!(:body => "test")
+ author.thinking_posts.create!(body: "test")
author.reload
author.thinking_posts.inspect
loaded_sql = capture_sql { author.thinking_posts.delete_all }
@@ -263,17 +284,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all_on_association_with_nil_dependency_is_the_same_as_not_loaded
author = authors :david
- author.posts.create!(:title => "test", :body => "body")
+ author.posts.create!(title: "test", body: "body")
author.reload
expected_sql = capture_sql { author.posts.delete_all }
- author.posts.create!(:title => "test", :body => "body")
+ author.posts.create!(title: "test", body: "body")
author.reload
author.posts.to_a
loaded_sql = capture_sql { author.posts.delete_all }
assert_equal(expected_sql, loaded_sql)
end
+ def test_delete_all_on_association_clears_scope
+ author = Author.create!(name: "Gannon")
+ posts = author.posts
+ posts.create!(title: "test", body: "body")
+ posts.delete_all
+ assert_nil posts.first
+ end
+
def test_building_the_associated_object_with_implicit_sti_base_class
firm = DependentFirm.new
company = firm.companies.build
@@ -282,29 +311,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_building_the_associated_object_with_explicit_sti_base_class
firm = DependentFirm.new
- company = firm.companies.build(:type => "Company")
+ company = firm.companies.build(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_associated_object_with_sti_subclass
firm = DependentFirm.new
- company = firm.companies.build(:type => "Client")
+ company = firm.companies.build(type: "Client")
assert_kind_of Client, company, "Expected #{company.class} to be a Client"
end
def test_building_the_associated_object_with_an_invalid_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(:type => "Invalid") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Invalid") }
end
def test_building_the_associated_object_with_an_unrelated_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.companies.build(type: "Account") }
end
test "building the association with an array" do
speedometer = Speedometer.new(speedometer_id: "a")
- data = [{name: "first"}, {name: "second"}]
+ data = [{ name: "first" }, { name: "second" }]
speedometer.minivans.build(data)
assert_equal 2, speedometer.minivans.size
@@ -313,24 +342,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_keys_bypass_attribute_protection
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.new
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.new :car_id => car.id + 1
+ bulb = car.bulbs.new car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.bulbs.build
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.build :car_id => car.id + 1
+ bulb = car.bulbs.build car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.bulbs.create
assert_equal car.id, bulb.car_id
- bulb = car.bulbs.create :car_id => car.id + 1
+ bulb = car.bulbs.create car_id: car.id + 1
assert_equal car.id, bulb.car_id
end
@@ -340,22 +369,43 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
line_item = invoice.line_items.new
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.new :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.new invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
line_item = invoice.line_items.build
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.build :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.build invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
line_item = invoice.line_items.create
assert_equal invoice.id, line_item.invoice_id
- line_item = invoice.line_items.create :invoice_id => invoice.id + 1
+ line_item = invoice.line_items.create invoice_id: invoice.id + 1
assert_equal invoice.id, line_item.invoice_id
end
+ class SpecialAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_many :books, class_name: "SpecialBook", foreign_key: :author_id
+ end
+
+ class SpecialBook < ActiveRecord::Base
+ self.table_name = "books"
+
+ belongs_to :author
+ enum read_status: { unread: 0, reading: 2, read: 3, forgotten: nil }
+ end
+
+ def test_association_enum_works_properly
+ author = SpecialAuthor.create!(name: "Test")
+ book = SpecialBook.create!(read_status: "reading")
+ author.books << book
+
+ assert_equal "reading", book.read_status
+ assert_not_equal 0, SpecialAuthor.joins(:books).where(books: { read_status: "reading" }).count
+ end
+
# When creating objects on the association, we must not do it within a scope (even though it
# would be convenient), because this would cause that scope to be applied to any callbacks etc.
def test_build_and_create_should_not_happen_within_scope
@@ -373,64 +423,106 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_no_sql_should_be_fired_if_association_already_loaded
- Car.create(:name => 'honda')
+ Car.create(name: "honda")
bulbs = Car.first.bulbs
bulbs.to_a # to load all instances of bulbs
assert_no_queries do
bulbs.first()
- bulbs.first({})
end
assert_no_queries do
bulbs.second()
- bulbs.second({})
end
assert_no_queries do
bulbs.third()
- bulbs.third({})
end
assert_no_queries do
bulbs.fourth()
- bulbs.fourth({})
end
assert_no_queries do
bulbs.fifth()
- bulbs.fifth({})
end
assert_no_queries do
bulbs.forty_two()
- bulbs.forty_two({})
end
assert_no_queries do
bulbs.third_to_last()
- bulbs.third_to_last({})
end
assert_no_queries do
bulbs.second_to_last()
- bulbs.second_to_last({})
end
assert_no_queries do
bulbs.last()
- bulbs.last({})
+ end
+ end
+
+ def test_finder_method_with_dirty_target
+ company = companies(:first_firm)
+ new_clients = []
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ assert_no_queries do
+ new_clients << company.clients_of_firm.build(name: "Another Client")
+ new_clients << company.clients_of_firm.build(name: "Another Client II")
+ new_clients << company.clients_of_firm.build(name: "Another Client III")
+ end
+
+ assert_not_predicate company.clients_of_firm, :loaded?
+ assert_queries(1) do
+ assert_same new_clients[0], company.clients_of_firm.third
+ assert_same new_clients[1], company.clients_of_firm.fourth
+ assert_same new_clients[2], company.clients_of_firm.fifth
+ assert_same new_clients[0], company.clients_of_firm.third_to_last
+ assert_same new_clients[1], company.clients_of_firm.second_to_last
+ assert_same new_clients[2], company.clients_of_firm.last
+ end
+ end
+
+ def test_finder_bang_method_with_dirty_target
+ company = companies(:first_firm)
+ new_clients = []
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ assert_no_queries do
+ new_clients << company.clients_of_firm.build(name: "Another Client")
+ new_clients << company.clients_of_firm.build(name: "Another Client II")
+ new_clients << company.clients_of_firm.build(name: "Another Client III")
+ end
+
+ assert_not_predicate company.clients_of_firm, :loaded?
+ assert_queries(1) do
+ assert_same new_clients[0], company.clients_of_firm.third!
+ assert_same new_clients[1], company.clients_of_firm.fourth!
+ assert_same new_clients[2], company.clients_of_firm.fifth!
+ assert_same new_clients[0], company.clients_of_firm.third_to_last!
+ assert_same new_clients[1], company.clients_of_firm.second_to_last!
+ assert_same new_clients[2], company.clients_of_firm.last!
end
end
def test_create_resets_cached_counters
- person = Person.create!(:first_name => 'tenderlove')
+ Reader.delete_all
+
+ person = Person.create!(first_name: "tenderlove")
+
post = Post.first
assert_equal [], person.readers
assert_nil person.readers.find_by_post_id(post.id)
- person.readers.create(:post_id => post.id)
+ person.readers.create(post_id: post.id)
assert_equal 1, person.readers.count
assert_equal 1, person.readers.length
@@ -438,25 +530,36 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal person, person.readers.first.person
end
- def force_signal37_to_load_all_clients_of_firm
- companies(:first_firm).clients_of_firm.each {|f| }
+ def test_update_all_respects_association_scope
+ person = Person.new
+ person.first_name = "Naruto"
+ person.references << Reference.new
+ person.save!
+ assert_equal 1, person.references.update_all(favourite: true)
+ end
+
+ def test_exists_respects_association_scope
+ person = Person.new
+ person.first_name = "Sasuke"
+ person.references << Reference.new
+ person.save!
+ 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
@@ -466,11 +569,11 @@ 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
- assert_equal 3, Firm.order(:id).find{|f| f.id > 0}.clients.length
+ assert_equal 3, Firm.order(:id).find { |f| f.id > 0 }.clients.length
end
def test_find_many_with_merged_options
@@ -480,13 +583,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_should_append_to_association_order
- ordered_clients = companies(:first_firm).clients_sorted_desc.order('companies.id')
- assert_equal ['id DESC', 'companies.id'], ordered_clients.order_values
+ ordered_clients = companies(:first_firm).clients_sorted_desc.order("companies.id")
+ assert_equal ["id DESC", "companies.id"], ordered_clients.order_values
end
def test_dynamic_find_should_respect_association_order
assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.where("type = 'Client'").first
- assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
+ assert_equal companies(:another_first_firm_client), companies(:first_firm).clients_sorted_desc.find_by_type("Client")
end
def test_taking
@@ -506,16 +609,28 @@ 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_predicate bob.posts, :loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
- bob = Author.find(authors(:bob).id)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
# taking from loaded Relation
- bob.posts.to_a
- assert_equal [posts(:misc_by_bob)], authors(:bob).posts.take(1)
- assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], authors(:bob).posts.take(2)
+ bob.posts.load
+ assert_predicate bob.posts, :loaded?
+ assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
+ assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
end
def test_taking_with_inverse_of
@@ -534,46 +649,41 @@ 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
- firm = Firm.new(name: 'Firm')
- clients_proxy_id = firm.clients.object_id
+ firm = Firm.new(name: "Firm")
firm.clients << Client.first
firm.save!
- assert_equal firm.clients.count, firm.clients.update_all(description: 'Great!')
- assert_not_equal clients_proxy_id, firm.clients.object_id
+ assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
end
def test_update_all_on_association_accessed_before_save_with_explicit_foreign_key
- # We can use the same cached proxy object because the id is available for the scope
- firm = Firm.new(name: 'Firm', id: 100)
- clients_proxy_id = firm.clients.object_id
+ firm = Firm.new(name: "Firm", id: 100)
firm.clients << Client.first
firm.save!
- assert_equal firm.clients.count, firm.clients.update_all(description: 'Great!')
- assert_equal clients_proxy_id, firm.clients.object_id
+ assert_equal firm.clients.count, firm.clients.update_all(description: "Great!")
end
def test_belongs_to_sanity
@@ -582,7 +692,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 }
@@ -601,9 +711,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
end
+ def test_find_one_message_on_primary_key
+ firm = Firm.first
+
+ e = assert_raises(ActiveRecord::RecordNotFound) do
+ firm.clients.find(0)
+ end
+ assert_equal 0, e.id
+ assert_equal "id", e.primary_key
+ assert_equal "Client", e.model
+ assert_match (/\ACouldn't find Client with 'id'=0/), e.message
+ end
+
def test_find_ids_and_inverse_of
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
firm = companies(:first_firm)
client = firm.clients_of_firm.find(3)
assert_kind_of Client, client
@@ -614,7 +738,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
@@ -622,67 +746,143 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_each
firm = companies(:first_firm)
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(4) do
- firm.clients.find_each(:batch_size => 1) {|c| assert_equal firm.id, c.firm_id }
+ firm.clients.find_each(batch_size: 1) { |c| assert_equal firm.id, c.firm_id }
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_find_each_with_conditions
firm = companies(:first_firm)
assert_queries(2) do
- firm.clients.where(name: 'Microsoft').find_each(batch_size: 1) do |c|
+ firm.clients.where(name: "Microsoft").find_each(batch_size: 1) do |c|
assert_equal firm.id, c.firm_id
assert_equal "Microsoft", c.name
end
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_find_in_batches
firm = companies(:first_firm)
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(2) do
- firm.clients.find_in_batches(:batch_size => 2) do |clients|
- clients.each {|c| assert_equal firm.id, c.firm_id }
+ firm.clients.find_in_batches(batch_size: 2) do |clients|
+ clients.each { |c| assert_equal firm.id, c.firm_id }
end
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
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
+ 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_reload_with_query_cache
+ connection = ActiveRecord::Base.connection
+ connection.enable_query_cache!
+ connection.clear_query_cache
+
+ # Populate the cache with a query
+ firm = Firm.first
+ # Populate the cache with a second query
+ firm.clients.load
+
+ assert_equal 2, connection.query_cache.size
+
+ # Clear the cache and fetch the clients again, populating the cache with a query
+ assert_queries(1) { firm.clients.reload }
+ # This query is cached, so it shouldn't make a real SQL query
+ assert_queries(0) { firm.clients.load }
+
+ assert_equal 1, connection.query_cache.size
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
+ end
+
+ def test_reloading_unloaded_associations_with_query_cache
+ connection = ActiveRecord::Base.connection
+ connection.enable_query_cache!
+ connection.clear_query_cache
+
+ firm = Firm.create!(name: "firm name")
+ client = firm.clients.create!(name: "client name")
+ firm.clients.to_a # add request to cache
+
+ connection.uncached do
+ client.update!(name: "new client name")
+ end
+
+ firm = Firm.find(firm.id)
+
+ assert_equal [client.name], firm.clients.reload.map(&:name)
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
end
def test_find_all_with_include_and_conditions
assert_nothing_raised do
- Developer.all.merge!(:joins => :audit_logs, :where => {'audit_logs.message' => nil, :name => 'Smith'}).to_a
+ Developer.all.merge!(joins: :audit_logs, where: { "audit_logs.message" => nil, :name => "Smith" }).to_a
end
end
@@ -692,8 +892,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_grouped
- all_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1").to_a
- grouped_clients_of_firm1 = Client.all.merge!(:where => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count').to_a
+ all_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1").to_a
+ grouped_clients_of_firm1 = Client.all.merge!(where: "firm_id = 1", group: "firm_id", select: "firm_id, count(id) as clients_count").to_a
assert_equal 3, all_clients_of_firm1.size
assert_equal 1, grouped_clients_of_firm1.size
end
@@ -706,7 +906,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
@@ -715,19 +915,28 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_select_query_method
- assert_equal ['id', 'body'], posts(:welcome).comments.select(:id, :body).first.attributes.keys
+ assert_equal ["id", "body"], posts(:welcome).comments.select(:id, :body).first.attributes.keys
end
def test_select_with_block
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
def test_adding
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
natural = Client.new("name" => "Natural Company")
companies(:first_firm).clients_of_firm << natural
assert_equal 3, companies(:first_firm).clients_of_firm.size # checking via the collection
@@ -738,7 +947,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_using_create
first_firm = companies(:first_firm)
assert_equal 3, first_firm.plain_clients.size
- first_firm.plain_clients.create(:name => "Natural Company")
+ first_firm.plain_clients.create(name: "Natural Company")
assert_equal 4, first_firm.plain_clients.length
assert_equal 4, first_firm.plain_clients.size
end
@@ -746,7 +955,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_when_parent_is_new_raises
error = assert_raise(ActiveRecord::RecordNotSaved) do
firm = Firm.new
- firm.plain_clients.create! :name=>"Whoever"
+ firm.plain_clients.create! name: "Whoever"
end
assert_equal "You cannot call create unless the parent is saved", error.message
@@ -755,7 +964,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_regular_create_on_has_many_when_parent_is_new_raises
error = assert_raise(ActiveRecord::RecordNotSaved) do
firm = Firm.new
- firm.plain_clients.create :name=>"Whoever"
+ firm.plain_clients.create name: "Whoever"
end
assert_equal "You cannot call create unless the parent is saved", error.message
@@ -763,7 +972,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
@@ -784,26 +993,33 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_a_collection
force_signal37_to_load_all_clients_of_firm
- companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
+ result = companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
assert_equal 4, companies(:first_firm).clients_of_firm.size
assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
+ assert_equal companies(:first_firm).clients_of_firm, result
end
def test_transactions_when_adding_to_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_save => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_save: true)
begin
companies(:first_firm).clients_of_firm.concat(good, bad)
rescue Client::RaisedOnSave
end
- assert !companies(:first_firm).clients_of_firm.reload.include?(good)
+ assert_not_includes companies(:first_firm).clients_of_firm.reload, good
end
def test_transactions_when_adding_to_new_record
- assert_no_queries(ignore_none: false) do
- firm = Firm.new
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ firm = Firm.new
+ assert_no_queries do
firm.clients_of_firm.concat(Client.new("name" => "Natural Company"))
end
end
@@ -817,21 +1033,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.new("name" => "Another Client") }
- assert !company.clients_of_firm.loaded?
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") }
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
def test_build
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
- assert !company.clients_of_firm.loaded?
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
@@ -840,13 +1064,34 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company.clients_of_firm.build("name" => "Another Client")
company.clients_of_firm.build("name" => "Yet Another Client")
assert_equal 4, company.clients_of_firm.size
+ assert_equal 4, company.clients_of_firm.uniq.size
end
def test_collection_not_empty_after_building
company = companies(:first_firm)
- assert_predicate company.contracts, :empty?
+ assert_empty company.contracts
company.contracts.build
- assert_not_predicate company.contracts, :empty?
+ assert_not_empty company.contracts
+ end
+
+ def test_collection_size_with_dirty_target
+ post = posts(:thinking)
+ assert_equal [], post.reader_ids
+ assert_equal 0, post.readers.size
+ post.readers.reset
+ post.readers.build
+ assert_equal [nil], post.reader_ids
+ assert_equal 1, post.readers.size
+ end
+
+ def test_collection_empty_with_dirty_target
+ post = posts(:thinking)
+ assert_equal [], post.reader_ids
+ assert_empty post.readers
+ post.readers.reset
+ post.readers.build
+ assert_equal [nil], post.reader_ids
+ assert_not_empty post.readers
end
def test_collection_size_twice_for_regressions
@@ -863,24 +1108,30 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_many
company = companies(:first_firm)
- new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ new_clients = assert_no_queries { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
assert_equal 2, new_clients.size
end
def test_build_followed_by_save_does_not_load_target
companies(:first_firm).clients_of_firm.build("name" => "Another Client")
assert companies(:first_firm).save
- assert !companies(:first_firm).clients_of_firm.loaded?
+ assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
end
def test_build_without_loading_association
first_topic = topics(:first)
- Reply.column_names
assert_equal 1, first_topic.replies.length
+ # Load schema information so we don't query below if running just this test.
+ Reply.define_attribute_methods
+
assert_no_queries do
- first_topic.replies.build(:title => "Not saved", :content => "Superstars")
+ first_topic.replies.build(title: "Not saved", content: "Superstars")
assert_equal 2, first_topic.replies.size
end
@@ -889,18 +1140,26 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_via_block
company = companies(:first_firm)
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build {|client| client.name = "Another Client" } }
- assert !company.clients_of_firm.loaded?
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ new_client = assert_no_queries { company.clients_of_firm.build { |client| client.name = "Another Client" } }
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
def test_build_many_via_block
company = companies(:first_firm)
- new_clients = assert_no_queries(ignore_none: false) do
- company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
+
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ new_clients = assert_no_queries do
+ company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
end
@@ -911,15 +1170,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_without_loading_association
- first_firm = companies(:first_firm)
- Firm.column_names
- Client.column_names
+ first_firm = companies(:first_firm)
assert_equal 2, first_firm.clients_of_firm.size
first_firm.clients_of_firm.reset
assert_queries(1) do
- first_firm.clients_of_firm.create(:name => "Superstars")
+ first_firm.clients_of_firm.create(name: "Superstars")
end
assert_equal 3, first_firm.clients_of_firm.size
@@ -927,25 +1184,31 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
- assert new_client.persisted?
+ assert_predicate new_client, :persisted?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
assert_equal new_client, companies(:first_firm).clients_of_firm.reload.last
end
def test_create_many
- companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
+ companies(:first_firm).clients_of_firm.create([{ "name" => "Another Client" }, { "name" => "Another Client II" }])
assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
end
def test_create_followed_by_save_does_not_load_target
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert companies(:first_firm).save
- assert !companies(:first_firm).clients_of_firm.loaded?
+ assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
end
def test_deleting
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
assert_equal 1, companies(:first_firm).clients_of_firm.size
assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
@@ -962,15 +1225,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_has_many_without_counter_cache_option
# Ship has a conventionally named `treasures_count` column, but the counter_cache
# option is not given on the association.
- ship = Ship.create(name: 'Countless', treasures_count: 10)
+ ship = Ship.create!(name: "Countless", treasures_count: 10)
- assert_not Ship.reflect_on_association(:treasures).has_cached_counter?
+ assert_not_predicate Ship.reflect_on_association(:treasures), :has_cached_counter?
# Count should come from sql count() of treasures rather than treasures_count attribute
assert_equal ship.treasures.size, 0
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
- ship.treasures.create(name: 'Gold')
+ ship.treasures.create(name: "Gold")
end
assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
@@ -1017,6 +1280,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, topic.reload.replies.size
end
+ def test_counter_cache_updates_in_memory_after_update_with_inverse_of_disabled
+ topic = Topic.create!(title: "Zoom-zoom-zoom")
+
+ assert_equal 0, topic.replies_count
+
+ reply1 = Reply.create!(title: "re: zoom", content: "speedy quick!")
+ reply2 = Reply.create!(title: "re: zoom 2", content: "OMG lol!")
+
+ assert_queries(4) do
+ topic.replies << [reply1, reply2]
+ end
+
+ assert_equal 2, topic.replies_count
+ assert_equal 2, topic.reload.replies_count
+ end
+
+ def test_counter_cache_updates_in_memory_after_update_with_inverse_of_enabled
+ category = Category.create!(name: "Counter Cache")
+
+ assert_nil category.categorizations_count
+
+ categorization1 = Categorization.create!
+ categorization2 = Categorization.create!
+
+ assert_queries(4) do
+ category.categorizations << [categorization1, categorization2]
+ end
+
+ assert_equal 2, category.categorizations_count
+ assert_equal 2, category.reload.categorizations_count
+ end
+
def test_pushing_association_updates_counter_cache
topic = Topic.order("id ASC").first
reply = Reply.create!
@@ -1054,8 +1349,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_empty_with_counter_cache
post = posts(:welcome)
- assert_queries(0) do
- assert_not post.comments.empty?
+ assert_no_queries do
+ assert_not_empty post.comments
end
end
@@ -1067,20 +1362,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- def test_calling_update_attributes_on_id_changes_the_counter_cache
+ def test_calling_update_on_id_changes_the_counter_cache
topic = Topic.order("id ASC").first
original_count = topic.replies.to_a.size
assert_equal original_count, topic.replies_count
first_reply = topic.replies.first
- first_reply.update_attributes(:parent_id => nil)
+ first_reply.update(parent_id: nil)
assert_equal original_count - 1, topic.reload.replies_count
- first_reply.update_attributes(:parent_id => topic.id)
+ first_reply.update(parent_id: topic.id)
assert_equal original_count, topic.reload.replies_count
end
- def test_calling_update_attributes_changing_ids_doesnt_change_counter_cache
+ def test_calling_update_changing_ids_doesnt_change_counter_cache
topic1 = Topic.find(1)
topic2 = Topic.find(3)
original_count1 = topic1.replies.to_a.size
@@ -1089,17 +1384,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
reply1 = topic1.replies.first
reply2 = topic2.replies.first
- reply1.update_attributes(:parent_id => topic2.id)
+ reply1.update(parent_id: topic2.id)
assert_equal original_count1 - 1, topic1.reload.replies_count
assert_equal original_count2 + 1, topic2.reload.replies_count
- reply2.update_attributes(:parent_id => topic1.id)
+ reply2.update(parent_id: topic1.id)
assert_equal original_count1, topic1.reload.replies_count
assert_equal original_count2, topic2.reload.replies_count
end
def test_deleting_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1], companies(:first_firm).clients_of_firm[2]])
@@ -1109,17 +1407,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).dependent_clients_of_firm.create("name" => "Another Client")
clients = companies(:first_firm).dependent_clients_of_firm.to_a
assert_equal 3, clients.count
assert_difference "Client.count", -(clients.count) do
- companies(:first_firm).dependent_clients_of_firm.delete_all
+ assert_equal clients.count, companies(:first_firm).dependent_clients_of_firm.delete_all
end
end
def test_delete_all_with_not_yet_loaded_association_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.reset
@@ -1129,8 +1433,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transaction_when_deleting_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_destroy => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_destroy: true)
companies(:first_firm).clients_of_firm = [good, bad]
@@ -1143,8 +1447,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transaction_when_deleting_new_record
- assert_no_queries(ignore_none: false) do
- firm = Firm.new
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ firm = Firm.new
+ assert_no_queries do
client = Client.new("name" => "New Client")
firm.clients_of_firm << client
firm.clients_of_firm.destroy(client)
@@ -1171,7 +1478,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_clearing_updates_counter_cache
topic = Topic.first
- assert_difference 'topic.reload.replies_count', -1 do
+ assert_difference "topic.reload.replies_count", -1 do
topic.replies.clear
end
end
@@ -1180,7 +1487,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
car = Car.first
car.engines.create!
- assert_difference 'car.reload.engines_count', -1 do
+ assert_difference "car.reload.engines_count", -1 do
car.engines.clear
end
end
@@ -1205,10 +1512,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all_with_option_delete_all
firm = companies(:first_firm)
client_id = firm.dependent_clients_of_firm.first.id
- firm.dependent_clients_of_firm.delete_all(:delete_all)
+ count = firm.dependent_clients_of_firm.count
+ assert_equal count, firm.dependent_clients_of_firm.delete_all(:delete_all)
assert_nil Client.find_by_id(client_id)
end
+ def test_delete_all_with_option_nullify
+ firm = companies(:first_firm)
+ client_id = firm.dependent_clients_of_firm.first.id
+ count = firm.dependent_clients_of_firm.count
+ assert_equal firm, Client.find(client_id).firm
+ assert_equal count, firm.dependent_clients_of_firm.delete_all(:nullify)
+ assert_nil Client.find(client_id).firm
+ end
+
def test_delete_all_accepts_limited_parameters
firm = companies(:first_firm)
assert_raise(ArgumentError) do
@@ -1238,8 +1555,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ 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_conditional_clients_of_firm.size
@@ -1250,8 +1567,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_sanitized_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ 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
@@ -1262,11 +1579,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_dependent_association_respects_optional_hash_conditions_on_delete
firm = companies(:odegy)
- Client.create(:client_of => firm.id, :name => "BigShot Inc.")
- Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ Client.create(client_of: firm.id, name: "BigShot Inc.")
+ 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
@@ -1288,13 +1605,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_creation_respects_hash_condition
ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
- assert ms_client.save
- assert_equal 'Microsoft', ms_client.name
+ assert ms_client.save
+ assert_equal "Microsoft", ms_client.name
another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create
- assert another_ms_client.persisted?
- assert_equal 'Microsoft', another_ms_client.name
+ assert_predicate another_ms_client, :persisted?
+ assert_equal "Microsoft", another_ms_client.name
end
def test_clearing_without_initial_access
@@ -1308,17 +1625,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_a_item_which_is_not_in_the_collection
force_signal37_to_load_all_clients_of_firm
- summit = Client.find_by_name('Summit')
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
+ summit = Client.find_by_name("Summit")
companies(:first_firm).clients_of_firm.delete(summit)
assert_equal 2, companies(:first_firm).clients_of_firm.size
assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
assert_equal 2, summit.client_of
end
- def test_deleting_by_fixnum_id
+ def test_deleting_by_integer_id
david = Developer.find(1)
- assert_difference 'david.projects.count', -1 do
+ assert_difference "david.projects.count", -1 do
assert_equal 1, david.projects.delete(1).size
end
@@ -1328,8 +1648,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_by_string_id
david = Developer.find(1)
- assert_difference 'david.projects.count', -1 do
- assert_equal 1, david.projects.delete('1').size
+ assert_difference "david.projects.count", -1 do
+ assert_equal 1, david.projects.delete("1").size
end
assert_equal 1, david.projects.size
@@ -1344,6 +1664,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first)
end
@@ -1352,9 +1674,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
- def test_destroying_by_fixnum_id
+ def test_destroying_by_integer_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id)
end
@@ -1366,6 +1690,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_by_string_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id.to_s)
end
@@ -1376,6 +1702,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
@@ -1389,8 +1718,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
clients = companies(:first_firm).clients_of_firm.to_a
- assert !clients.empty?, "37signals has clients after load"
+ assert_not clients.empty?, "37signals has clients after load"
destroyed = companies(:first_firm).clients_of_firm.destroy_all
assert_equal clients.sort_by(&:id), destroyed.sort_by(&:id)
assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
@@ -1398,21 +1730,52 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh"
end
+ def test_destroy_all_on_association_clears_scope
+ author = Author.create!(name: "Gannon")
+ posts = author.posts
+ posts.create!(title: "test", body: "body")
+ posts.destroy_all
+ assert_nil posts.first
+ end
+
+ def test_destroy_all_on_desynced_counter_cache_association
+ category = categories(:general)
+ assert_operator category.categorizations.count, :>, 0
+
+ category.categorizations.destroy_all
+ assert_equal 0, category.categorizations.count
+ end
+
+ def test_destroy_on_association_clears_scope
+ author = Author.create!(name: "Gannon")
+ posts = author.posts
+ post = posts.create!(title: "test", body: "body")
+ posts.destroy(post)
+ assert_nil posts.first
+ end
+
+ def test_delete_on_association_clears_scope
+ author = Author.create!(name: "Gannon")
+ posts = author.posts
+ post = posts.create!(title: "test", body: "body")
+ posts.delete(post)
+ assert_nil posts.first
+ end
+
def test_dependence
firm = companies(:first_firm)
assert_equal 3, firm.clients.size
firm.destroy
- assert Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.empty?
+ assert_empty Client.all.merge!(where: "firm_id=#{firm.id}").to_a
end
def test_dependence_for_associations_with_hash_condition
david = authors(:david)
- assert_difference('Post.count', -1) { assert david.destroy }
+ assert_difference("Post.count", -1) { assert david.destroy }
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
@@ -1439,7 +1802,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.destroy rescue "do nothing"
- assert_equal 3, Client.all.merge!(:where => "firm_id=#{firm.id}").to_a.size
+ assert_equal 3, Client.all.merge!(where: "firm_id=#{firm.id}").to_a.size
end
def test_dependence_on_account
@@ -1462,66 +1825,62 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal num_accounts, Account.count
end
- def test_restrict_with_exception
- firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
- firm.companies.create(:name => 'child')
-
- assert !firm.companies.empty?
- assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
- assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
- assert firm.companies.exists?(:name => 'child')
- end
-
- def test_restrict_with_error_is_deprecated_using_key_many
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { many: 'message for deprecated key' } } } }
+ def test_depends_and_nullify_on_polymorphic_assoc
+ author = PersonWithPolymorphicDependentNullifyComments.create!(first_name: "Laertis")
+ comment = posts(:welcome).comments.first
+ comment.author = author
+ comment.save!
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
- firm.companies.create(name: 'child')
+ assert_equal comment.author_id, author.id
+ assert_equal comment.author_type, author.class.name
- assert !firm.companies.empty?
+ author.destroy
+ comment.reload
- assert_deprecated { firm.destroy }
+ assert_nil comment.author_id
+ assert_nil comment.author_type
+ end
- assert !firm.errors.empty?
+ def test_restrict_with_exception
+ firm = RestrictedWithExceptionFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
- assert_equal 'message for deprecated key', firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.companies.exists?(name: 'child')
- ensure
- I18n.backend.reload!
+ assert_not_empty firm.companies
+ assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedWithExceptionFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
end
def test_restrict_with_error
- firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
- firm.companies.create(:name => 'child')
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
- assert !firm.companies.empty?
+ assert_not_empty firm.companies
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
- assert firm.companies.exists?(:name => 'child')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
end
def test_restrict_with_error_with_locale
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {companies: 'client companies'}}}
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
- firm.companies.create(name: 'child')
+ I18n.backend.store_translations "en", activerecord: { attributes: { restricted_with_error_firm: { companies: "client companies" } } }
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.companies.create(name: "child")
- assert !firm.companies.empty?
+ assert_not_empty firm.companies
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because dependent client companies exist", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.companies.exists?(name: 'child')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert firm.companies.exists?(name: "child")
ensure
I18n.backend.reload!
end
@@ -1531,10 +1890,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_included_in_collection_for_new_records
- client = Client.create(:name => 'Persisted')
+ client = Client.create(name: "Persisted")
assert_nil client.client_of
assert_equal false, Firm.new.clients_of_firm.include?(client),
- 'includes a client that does not belong to any firm'
+ "includes a client that does not belong to any firm"
end
def test_adding_array_and_collection
@@ -1542,7 +1901,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
@@ -1556,7 +1915,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
@@ -1569,8 +1928,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
account = Account.new
orig_accounts = firm.accounts.to_a
- assert !account.valid?
- assert !orig_accounts.empty?
+ assert_not_predicate account, :valid?
+ assert_not_empty orig_accounts
error = assert_raise ActiveRecord::RecordNotSaved do
firm.accounts = [account]
end
@@ -1585,16 +1944,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients = []
firm.save
- assert_queries(0, ignore_none: true) do
+ assert_no_queries do
firm.clients = []
end
- assert_equal [], firm.send('clients=', [])
+ assert_equal [], firm.send("clients=", [])
end
def test_transactions_when_replacing_on_persisted
- good = Client.new(:name => "Good")
- bad = Client.new(:name => "Bad", :raise_on_save => true)
+ good = Client.new(name: "Good")
+ bad = Client.new(name: "Bad", raise_on_save: true)
companies(:first_firm).clients_of_firm = [good]
@@ -1607,8 +1966,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_transactions_when_replacing_on_new_record
- assert_no_queries(ignore_none: false) do
- firm = Firm.new
+ # Load schema information so we don't query below if running just this test.
+ Client.define_attribute_methods
+
+ firm = Firm.new
+ assert_no_queries do
firm.clients_of_firm = [Client.new("name" => "New Client")]
end
end
@@ -1620,7 +1982,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
company = companies(:first_firm)
company.clients.reload
- assert_queries(0) do
+ assert_no_queries do
company.client_ids
company.client_ids
end
@@ -1628,9 +1990,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
company = companies(:first_firm)
- assert !company.clients.loaded?
+ assert_not_predicate company.clients, :loaded?
assert_equal [companies(:first_client).id, companies(:second_client).id, companies(:another_first_firm_client).id], company.client_ids
- assert !company.clients.loaded?
+ assert_not_predicate company.clients, :loaded?
+ end
+
+ def test_counter_cache_on_unloaded_association
+ car = Car.create(name: "My AppliCar")
+ assert_equal 0, car.engines.size
+ end
+
+ def test_ids_reader_cache_not_used_for_size_when_association_is_dirty
+ firm = Firm.create!(name: "Startup")
+ assert_equal 0, firm.client_ids.size
+ firm.clients.build
+ assert_equal 1, firm.clients.size
+ end
+
+ def test_ids_reader_cache_should_be_cleared_when_collection_is_deleted
+ firm = companies(:first_firm)
+ assert_equal [2, 3, 11], firm.client_ids
+ client = firm.clients.first
+ firm.clients.delete(client)
+ assert_equal [3, 11], firm.client_ids
end
def test_get_ids_ignores_include_option
@@ -1642,11 +2024,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_get_ids_for_association_on_new_record_does_not_try_to_find_records
- Company.columns # Load schema information so we don't query below
- Contract.columns # if running just this test.
+ # Load schema information so we don't query below if running just this test.
+ companies(:first_client).contract_ids
company = Company.new
- assert_queries(0) do
+ assert_no_queries do
company.contract_ids
end
@@ -1657,7 +2039,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
contract_a = Contract.create!
contract_b = Contract.create!
Contract.create! # another contract
- company = Company.new(:name => "Some Company")
+ company = Company.new(name: "Some Company")
company.contract_ids = [contract_a.id, contract_b.id]
assert_equal [contract_a.id, contract_b.id], company.contract_ids
@@ -1669,8 +2051,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_assign_ids_ignoring_blanks
- firm = Firm.create!(:name => 'Apple')
- firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
+ firm = Firm.create!(name: "Apple")
+ firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, ""]
firm.save!
assert_equal 2, firm.clients.reload.size
@@ -1685,19 +2067,28 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
[
lambda { authors(:mary).comment_ids = [comments(:greetings).id, comments(:more_greetings).id] },
lambda { authors(:mary).comments = [comments(:greetings), comments(:more_greetings)] },
- lambda { authors(:mary).comments << Comment.create!(:body => "Yay", :post_id => 424242) },
+ lambda { authors(:mary).comments << Comment.create!(body: "Yay", post_id: 424242) },
lambda { authors(:mary).comments.delete(authors(:mary).comments.first) },
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
+ ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
+ end
+
+ def test_associations_order_should_be_priority_over_throughs_order
+ original = authors(:david)
+ expected = [12, 10, 9, 8, 7, 6, 5, 3, 2, 1]
+ assert_equal expected, original.comments_desc.map(&:id)
+ preloaded = Author.includes(:comments_desc).find(original.id)
+ assert_equal expected, preloaded.comments_desc.map(&:id)
+ assert_equal original.posts_sorted_by_id.first.comments.map(&:id), preloaded.posts_sorted_by_id.first.comments.map(&:id)
end
def test_dynamic_find_should_respect_association_order_for_through
assert_equal Comment.find(10), authors(:david).comments_desc.where("comments.type = 'SpecialComment'").first
- assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
+ assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type("SpecialComment")
end
def test_has_many_through_respects_hash_conditions
- assert_equal authors(:david).hello_posts, authors(:david).hello_posts_with_hash_conditions
- assert_equal authors(:david).hello_post_comments, authors(:david).hello_post_comments_with_hash_conditions
+ assert_equal authors(:david).hello_posts.sort_by(&:id), authors(:david).hello_posts_with_hash_conditions.sort_by(&:id)
+ assert_equal authors(:david).hello_post_comments.sort_by(&:id), authors(:david).hello_post_comments_with_hash_conditions.sort_by(&:id)
end
def test_include_uses_array_include_after_loaded
@@ -1707,7 +2098,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
client = firm.clients.first
assert_no_queries do
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
assert_equal true, firm.clients.include?(client)
end
end
@@ -1717,18 +2108,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
client = firm.clients.first
firm.reload
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(1) do
assert_equal true, firm.clients.include?(client)
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_include_returns_false_for_non_matching_record_to_verify_scoping
firm = companies(:first_firm)
- client = Client.create!(:name => 'Not Associated')
+ client = Client.create!(name: "Not Associated")
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_equal false, firm.clients.include?(client)
end
@@ -1737,15 +2128,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.first
firm.clients.second
firm.clients.last
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
firm = companies(:first_firm)
firm.clients.load_target
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
firm.clients.first
assert_equal 2, firm.clients.first(2).size
firm.clients.last
@@ -1755,8 +2146,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_on_existing_record_with_build_should_load_association
firm = companies(:first_firm)
- firm.clients.build(:name => 'Foo')
- assert !firm.clients.loaded?
+ firm.clients.build(name: "Foo")
+ assert_not_predicate firm.clients, :loaded?
assert_queries 1 do
firm.clients.first
@@ -1764,13 +2155,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.last
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_first_nth_or_last_on_existing_record_with_create_should_not_load_association
firm = companies(:first_firm)
- firm.clients.create(:name => 'Foo')
- assert !firm.clients.loaded?
+ firm.clients.create(name: "Foo")
+ assert_not_predicate firm.clients, :loaded?
assert_queries 3 do
firm.clients.first
@@ -1778,7 +2169,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.last
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_first_nth_or_last_on_new_record_should_not_run_queries
@@ -1793,15 +2184,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_with_integer_on_association_should_not_load_association
firm = companies(:first_firm)
- firm.clients.create(:name => 'Foo')
- assert !firm.clients.loaded?
+ firm.clients.create(name: "Foo")
+ assert_not_predicate firm.clients, :loaded?
assert_queries 2 do
firm.clients.first(2)
firm.clients.last(2)
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_many_should_count_instead_of_loading_association
@@ -1809,37 +2200,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.many? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
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
def test_calling_many_should_defer_to_collection_if_using_a_block
firm = companies(:first_firm)
assert_queries(1) do
- firm.clients.expects(:size).never
- firm.clients.many? { true }
+ assert_not_called(firm.clients, :size) do
+ firm.clients.many? { true }
+ end
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_many_should_return_false_if_none_or_one
firm = companies(:another_firm)
- assert !firm.clients_like_ms.many?
+ assert_not_predicate firm.clients_like_ms, :many?
assert_equal 0, firm.clients_like_ms.size
firm = companies(:first_firm)
- assert !firm.limited_clients.many?
+ assert_not_predicate firm.limited_clients, :many?
assert_equal 1, firm.limited_clients.size
end
def test_calling_many_should_return_true_if_more_than_one
firm = companies(:first_firm)
- assert firm.clients.many?
+ assert_predicate firm.clients, :many?
assert_equal 3, firm.clients.size
end
@@ -1848,33 +2240,34 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.none? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_none_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
- assert_no_queries { assert ! firm.clients.none? }
+ firm.clients.load # force load
+ assert_no_queries { assert_not firm.clients.none? }
end
def test_calling_none_should_defer_to_collection_if_using_a_block
firm = companies(:first_firm)
assert_queries(1) do
- firm.clients.expects(:size).never
- firm.clients.none? { true }
+ assert_not_called(firm.clients, :size) do
+ firm.clients.none? { true }
+ end
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_none_should_return_true_if_none
firm = companies(:another_firm)
- assert firm.clients_like_ms.none?
+ assert_predicate firm.clients_like_ms, :none?
assert_equal 0, firm.clients_like_ms.size
end
def test_calling_none_should_return_false_if_any
firm = companies(:first_firm)
- assert !firm.limited_clients.none?
+ assert_not_predicate firm.limited_clients, :none?
assert_equal 1, firm.limited_clients.size
end
@@ -1883,39 +2276,40 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.one? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_one_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
- assert_no_queries { assert ! firm.clients.one? }
+ firm.clients.load # force load
+ assert_no_queries { assert_not firm.clients.one? }
end
def test_calling_one_should_defer_to_collection_if_using_a_block
firm = companies(:first_firm)
assert_queries(1) do
- firm.clients.expects(:size).never
- firm.clients.one? { true }
+ assert_not_called(firm.clients, :size) do
+ firm.clients.one? { true }
+ end
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_one_should_return_false_if_zero
firm = companies(:another_firm)
- assert ! firm.clients_like_ms.one?
+ assert_not_predicate firm.clients_like_ms, :one?
assert_equal 0, firm.clients_like_ms.size
end
def test_calling_one_should_return_true_if_one
firm = companies(:first_firm)
- assert firm.limited_clients.one?
+ assert_predicate firm.limited_clients, :one?
assert_equal 1, firm.limited_clients.size
end
def test_calling_one_should_return_false_if_more_than_one
firm = companies(:first_firm)
- assert ! firm.clients.one?
+ assert_not_predicate firm.clients, :one?
assert_equal 3, firm.clients.size
end
@@ -1923,13 +2317,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = true
- firm = Namespaced::Firm.create({ :name => 'Some Company' })
- firm.clients.create({ :name => 'Some Client' })
+ firm = Namespaced::Firm.create(name: "Some Company")
+ firm.clients.create(name: "Some Client")
stats = Namespaced::Firm.all.merge!(
- :select => "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
- :joins => :clients,
- :group => "#{Namespaced::Firm.table_name}.id"
+ select: "#{Namespaced::Firm.table_name}.id, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients",
+ joins: :clients,
+ group: "#{Namespaced::Firm.table_name}.id"
).find firm.id
assert_equal 1, stats.num_clients.to_i
ensure
@@ -1937,9 +2331,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Comment.expects(:transaction)
- Post.first.comments.transaction do
- # nothing
+ assert_called(Comment, :transaction) do
+ Post.first.comments.transaction do
+ # nothing
+ end
end
end
@@ -1948,43 +2343,45 @@ 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
- client = firm.clients_using_primary_key.create!(:name => 'test')
+ firm = Firm.first
+ client = firm.clients_using_primary_key.create!(name: "test")
assert_equal firm.name, client.firm_name
end
def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
- ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval(<<-EOF, __FILE__, __LINE__ + 1)
- class DeleteAllModel < ActiveRecord::Base
- has_many :nonentities, :dependent => :delete_all
- end
- EOF
+ assert_not_called_on_instance_of(
+ ActiveRecord::Reflection::AssociationReflection,
+ :class_name,
+ ) do
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
+ class DeleteAllModel < ActiveRecord::Base
+ has_many :nonentities, :dependent => :delete_all
+ end
+ EOF
+ end
end
def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
- ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval(<<-EOF, __FILE__, __LINE__ + 1)
- class NullifyModel < ActiveRecord::Base
- has_many :nonentities, :dependent => :nullify
- end
- EOF
+ assert_not_called_on_instance_of(
+ ActiveRecord::Reflection::AssociationReflection,
+ :class_name,
+ ) do
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
+ class NullifyModel < ActiveRecord::Base
+ has_many :nonentities, :dependent => :nullify
+ end
+ EOF
+ end
end
def test_attributes_are_being_set_when_initialized_from_has_many_association_with_where_clause
- new_comment = posts(:welcome).comments.where(:body => "Some content").build
+ new_comment = posts(:welcome).comments.where(body: "Some content").build
assert_equal new_comment.body, "Some content"
end
def test_attributes_are_being_set_when_initialized_from_has_many_association_with_multiple_where_clauses
- new_comment = posts(:welcome).comments.where(:body => "Some content").where(:type => 'SpecialComment').build
+ new_comment = posts(:welcome).comments.where(body: "Some content").where(type: "SpecialComment").build
assert_equal new_comment.body, "Some content"
assert_equal new_comment.type, "SpecialComment"
assert_equal new_comment.post_id, posts(:welcome).id
@@ -1998,7 +2395,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_load_target_respects_protected_attributes
topic = Topic.create!
- reply = topic.replies.create(:title => "reply 1")
+ reply = topic.replies.create(title: "reply 1")
reply.approved = false
reply.save!
@@ -2025,7 +2422,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_merging_with_custom_attribute_writer
- bulb = Bulb.new(:color => "red")
+ bulb = Bulb.new(color: "red")
assert_equal "RED!", bulb.color
car = Car.create!
@@ -2035,13 +2432,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_abstract_class_with_polymorphic_has_many
- post = SubStiPost.create! :title => "fooo", :body => "baa"
- tagging = Tagging.create! :taggable => post
+ post = SubStiPost.create! title: "fooo", body: "baa"
+ tagging = Tagging.create! taggable: post
assert_equal [tagging], post.taggings
end
def test_with_polymorphic_has_many_with_custom_columns_name
- post = Post.create! :title => 'foo', :body => 'bar'
+ post = Post.create! title: "foo", body: "bar"
image = Image.create!
post.images << image
@@ -2051,10 +2448,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_with_polymorphic_has_many_does_not_allow_to_override_type_and_id
welcome = posts(:welcome)
- tagging = welcome.taggings.build(:taggable_id => 99, :taggable_type => 'ShouldNotChange')
+ tagging = welcome.taggings.build(taggable_id: 99, taggable_type: "ShouldNotChange")
assert_equal welcome.id, tagging.taggable_id
- assert_equal 'Post', tagging.taggable_type
+ 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
@@ -2066,30 +2470,30 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_attributes_are_available_to_after_initialize
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.bulbs.build
- assert_equal car.id, bulb.attributes_after_initialize['car_id']
+ assert_equal car.id, bulb.attributes_after_initialize["car_id"]
end
def test_attributes_are_set_when_initialized_from_has_many_null_relationship
- car = Car.new name: 'honda'
- bulb = car.bulbs.where(name: 'headlight').first_or_initialize
- assert_equal 'headlight', bulb.name
+ car = Car.new name: "honda"
+ bulb = car.bulbs.where(name: "headlight").first_or_initialize
+ assert_equal "headlight", bulb.name
end
def test_attributes_are_set_when_initialized_from_polymorphic_has_many_null_relationship
- post = Post.new title: 'title', body: 'bar'
- tag = Tag.create!(name: 'foo')
+ post = Post.new title: "title", body: "bar"
+ tag = Tag.create!(name: "foo")
tagging = post.taggings.where(tag: tag).first_or_initialize
assert_equal tag.id, tagging.tag_id
- assert_equal 'Post', tagging.taggable_type
+ assert_equal "Post", tagging.taggable_type
end
def test_replace
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb1 = car.bulbs.create
bulb2 = Bulb.create
@@ -2100,7 +2504,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_returns_target
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb1 = car.bulbs.create
bulb2 = car.bulbs.create
bulb3 = Bulb.create
@@ -2114,18 +2518,31 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_collection_association_with_private_kernel_method
firm = companies(:first_firm)
assert_equal [accounts(:signals37)], firm.accounts.open
+ assert_equal [accounts(:signals37)], firm.accounts.available
+ end
+
+ def test_association_with_or_doesnt_set_inverse_instance_key
+ firm = companies(:first_firm)
+ accounts = firm.accounts.or(Account.where(firm_id: nil)).order(:id)
+ assert_equal [firm.id, nil], accounts.map(&:firm_id)
+ end
+
+ def test_association_with_rewhere_doesnt_set_inverse_instance_key
+ firm = companies(:first_firm)
+ accounts = firm.accounts.rewhere(firm_id: [firm.id, nil]).order(:id)
+ assert_equal [firm.id, nil], accounts.map(&:firm_id)
end
test "first_or_initialize adds the record to the association" do
- firm = Firm.create! name: 'omg'
+ firm = Firm.create! name: "omg"
client = firm.clients_of_firm.first_or_initialize
assert_equal [client], firm.clients_of_firm
end
test "first_or_create adds the record to the association" do
- firm = Firm.create! name: 'omg'
+ firm = Firm.create! name: "omg"
firm.clients_of_firm.load_target
- client = firm.clients_of_firm.first_or_create name: 'lol'
+ client = firm.clients_of_firm.first_or_create name: "lol"
assert_equal [client], firm.clients_of_firm
assert_equal [client], firm.reload.clients_of_firm
end
@@ -2134,7 +2551,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
assert post.taggings_with_delete_all.count > 0
- assert !post.taggings_with_delete_all.loaded?
+ assert_not_predicate post.taggings_with_delete_all, :loaded?
# 2 queries: one DELETE and another to update the counter cache
assert_queries(2) do
@@ -2145,9 +2562,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "has many associations on new records use null relations" do
post = Post.new
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], post.comments
- assert_equal [], post.comments.where(body: 'omg')
+ assert_equal [], post.comments.where(body: "omg")
assert_equal [], post.comments.pluck(:body)
assert_equal 0, post.comments.sum(:id)
assert_equal 0, post.comments.count
@@ -2156,7 +2573,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "collection proxy respects default scope" do
author = authors(:mary)
- assert !author.first_posts.exists?
+ assert_not_predicate author.first_posts, :exists?
end
test "association with extend option" do
@@ -2168,14 +2585,22 @@ 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
david = authors(:david)
post = david.posts.first
- post.type = 'PostWithSpecialCategorization'
+ post.type = "PostWithSpecialCategorization"
post.save
categorization = post.categorizations.first
@@ -2188,8 +2613,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
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 = Speedometer.create!(id: "4")
+ 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
@@ -2205,7 +2630,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "can unscope and where the default scope of the associated model" do
- Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: 'other') }, class_name: "Bulb"
+ Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: "other") }, class_name: "Bulb"
car = Car.create!
bulb1 = Bulb.create! name: "defaulty", car: car
bulb2 = Bulb.create! name: "other", car: car
@@ -2215,7 +2640,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
test "can rewhere the default scope of the associated model" do
- Car.has_many :old_bulbs, -> { rewhere(name: 'old') }, class_name: "Bulb"
+ Car.has_many :old_bulbs, -> { rewhere(name: "old") }, class_name: "Bulb"
car = Car.create!
bulb1 = Bulb.create! name: "defaulty", car: car
bulb2 = Bulb.create! name: "old", car: car
@@ -2224,12 +2649,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb2], car.old_bulbs
end
- test 'unscopes the default scope of associated model when used with include' do
+ test "unscopes the default scope of associated model when used with include" do
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
@@ -2244,7 +2670,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "Failed to destroy the record", error.message
end
- test 'updates counter cache when default scope is given' do
+ test "updates counter cache when default scope is given" do
topic = DefaultRejectedTopic.create approved: true
assert_difference "topic.reload.replies_count", 1 do
@@ -2252,8 +2678,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
has_many name
@@ -2262,16 +2688,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'passes custom context validation to validate children' do
+ test "passes custom context validation to validate children" do
pirate = FamousPirate.new
pirate.famous_ships << ship = FamousShip.new
- assert pirate.valid?
+ assert_predicate pirate, :valid?
assert_not pirate.valid?(:conference)
assert_equal "can't be blank", ship.errors[:name].first
end
- test 'association with instance dependent scope' do
+ test "association with instance dependent scope" do
bob = authors(:bob)
Post.create!(title: "signed post by bob", body: "stuff", author: authors(:bob))
Post.create!(title: "anonymous post", body: "more stuff", author: authors(:bob))
@@ -2281,7 +2707,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], authors(:david).posts_with_signature.map(&:title)
end
- test 'associations autosaves when object is already persisted' do
+ test "associations autosaves when object is already persisted" do
bulb = Bulb.create!
tyre = Tyre.create!
@@ -2294,7 +2720,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, car.tyres.count
end
- test 'associations replace in memory when records have the same id' do
+ test "associations replace in memory when records have the same id" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2305,7 +2731,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "foo", car.bulbs.first.name
end
- test 'in memory replacement executes no queries' do
+ test "in memory replacement executes no queries" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2316,7 +2742,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'in memory replacements do not execute callbacks' do
+ test "in memory replacements do not execute callbacks" do
raise_after_add = false
klass = Class.new(ActiveRecord::Base) do
self.table_name = :cars
@@ -2337,7 +2763,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- test 'in memory replacements sets inverse instance' do
+ test "in memory replacements sets inverse instance" do
bulb = Bulb.create!
car = Car.create!(bulbs: [bulb])
@@ -2347,7 +2773,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_same car, new_bulb.car
end
- test 'in memory replacement maintains order' do
+ 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!
car = Car.create!(bulbs: [first_bulb, second_bulb])
@@ -2358,16 +2793,99 @@ 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
+
+ test "calling size on an association that has not been loaded performs a query" do
car = Car.create!
- car.bulbs << TrickyBulb.new
- assert_equal 1, car.bulbs.size
+ Bulb.create(car_id: car.id)
+
+ car_two = Car.create!
+
+ assert_queries(1) do
+ assert_equal 1, car.bulbs.size
+ end
+
+ assert_queries(1) do
+ assert_equal 0, car_two.bulbs.size
+ end
end
- def test_association_force_reload_with_only_true_is_deprecated
- company = Company.find(1)
+ test "calling size on an association that has been loaded does not perform query" do
+ car = Car.create!
+ Bulb.create(car_id: car.id)
+ car.bulb_ids
+
+ car_two = Car.create!
+ car_two.bulb_ids
+
+ assert_no_queries do
+ assert_equal 1, car.bulbs.size
+ end
- assert_deprecated { company.clients_of_firm(true) }
+ assert_no_queries do
+ assert_equal 0, car_two.bulbs.size
+ end
+ end
+
+ test "calling empty on an association that has not been loaded performs a query" do
+ car = Car.create!
+ Bulb.create(car_id: car.id)
+
+ car_two = Car.create!
+
+ assert_queries(1) do
+ assert_not_empty car.bulbs
+ end
+
+ assert_queries(1) do
+ assert_empty car_two.bulbs
+ end
+ end
+
+ test "calling empty on an association that has been loaded does not performs query" do
+ car = Car.create!
+ Bulb.create(car_id: car.id)
+ car.bulb_ids
+
+ car_two = Car.create!
+ car_two.bulb_ids
+
+ assert_no_queries do
+ assert_not_empty car.bulbs
+ end
+
+ assert_no_queries do
+ assert_empty car_two.bulbs
+ end
end
class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
@@ -2401,10 +2919,62 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_ids_reader_memoization
- car = Car.create!(name: 'TofaÅŸ')
+ car = Car.create!(name: "TofaÅŸ")
bulb = Bulb.create!(car: car)
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 { |record| record.car.bulbs.load }
+
+ car = Car.create!(name: "Car")
+ bulb = car.bulbs.create!
+
+ assert_equal [bulb], car.bulbs
+ end
+ end
+
+ def test_create_children_could_be_rolled_back_by_after_save
+ firm = Firm.create!(name: "A New Firm, Inc")
+ assert_no_difference "Client.count" do
+ client = firm.clients.create(name: "New Client") do |cli|
+ cli.rollback_on_save = true
+ assert_not cli.rollback_on_create_called
+ end
+ assert client.rollback_on_create_called
+ end
end
+
+ def test_has_many_with_out_of_range_value
+ reference = Reference.create!(id: 2147483648) # out of range in the integer
+ assert_equal [], reference.ideal_jobs
+ end
+
+ private
+
+ def force_signal37_to_load_all_clients_of_firm
+ companies(:first_firm).clients_of_firm.load_target
+ end
+
+ def reset_callbacks(kind, klass)
+ old_callbacks = {}
+ old_callbacks[klass] = klass.send("_#{kind}_callbacks").dup
+ klass.subclasses.each do |subclass|
+ old_callbacks[subclass] = subclass.send("_#{kind}_callbacks").dup
+ end
+ yield
+ ensure
+ klass.send("_#{kind}_callbacks=", old_callbacks[klass])
+ klass.subclasses.each do |subclass|
+ subclass.send("_#{kind}_callbacks=", old_callbacks[subclass])
+ end
+ end
end
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 bb8c9fa19c..fa540c9dab 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -1,31 +1,38 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/person'
-require 'models/reference'
-require 'models/job'
-require 'models/reader'
-require 'models/comment'
-require 'models/rating'
-require 'models/tag'
-require 'models/tagging'
-require 'models/author'
-require 'models/owner'
-require 'models/pet'
-require 'models/toy'
-require 'models/contract'
-require 'models/company'
-require 'models/developer'
-require 'models/computer'
-require 'models/subscriber'
-require 'models/book'
-require 'models/subscription'
-require 'models/essay'
-require 'models/category'
-require 'models/categorization'
-require 'models/member'
-require 'models/membership'
-require 'models/club'
-require 'models/organization'
+require "models/post"
+require "models/person"
+require "models/reference"
+require "models/job"
+require "models/reader"
+require "models/comment"
+require "models/rating"
+require "models/tag"
+require "models/tagging"
+require "models/author"
+require "models/owner"
+require "models/pet"
+require "models/pet_treasure"
+require "models/toy"
+require "models/treasure"
+require "models/contract"
+require "models/company"
+require "models/developer"
+require "models/computer"
+require "models/subscriber"
+require "models/book"
+require "models/subscription"
+require "models/essay"
+require "models/category"
+require "models/categorization"
+require "models/member"
+require "models/membership"
+require "models/club"
+require "models/organization"
+require "models/user"
+require "models/family"
+require "models/family_tree"
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
@@ -35,8 +42,26 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Dummies to force column loads so query counts are clean.
def setup
- Person.create :first_name => 'gummy'
- Reader.create :person_id => 0, :post_id => 0
+ Person.create first_name: "gummy"
+ Reader.create person_id: 0, post_id: 0
+ end
+
+ def test_has_many_through_create_record
+ assert books(:awdr).subscribers.create!(nick: "bob")
+ end
+
+ def test_marshal_dump
+ preloaded = Post.includes(:first_blue_tags).first
+ assert_equal preloaded, Marshal.load(Marshal.dump(preloaded))
+ end
+
+ def test_preload_with_nested_association
+ posts = Post.preload(:author, :author_favorites_with_scope).to_a
+
+ assert_no_queries do
+ posts.each(&:author)
+ posts.each(&:author_favorites_with_scope)
+ end
end
def test_preload_sti_rhs_class
@@ -47,9 +72,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_preload_sti_middle_relation
- club = Club.create!(name: 'Aaron cool banana club')
- member1 = Member.create!(name: 'Aaron')
- member2 = Member.create!(name: 'Cat')
+ club = Club.create!(name: "Aaron cool banana club")
+ member1 = Member.create!(name: "Aaron")
+ member2 = Member.create!(name: "Cat")
SuperMembership.create! club: club, member: member1
CurrentMembership.create! club: club, member: member2
@@ -59,21 +84,26 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
club1.members.sort_by(&:id)
end
- def make_model(name)
- Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
+ def test_preload_multiple_instances_of_the_same_record
+ club = Club.create!(name: "Aaron cool banana club")
+ Membership.create! club: club, member: Member.create!(name: "Aaron")
+ Membership.create! club: club, member: Member.create!(name: "Bob")
+
+ preloaded_clubs = Club.joins(:memberships).preload(:membership).to_a
+ assert_no_queries { preloaded_clubs.each(&:membership) }
end
- def test_ordered_habtm
+ def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
- def self.name; 'Person'; end
+ def self.name; "Person"; end
has_many :readers
- has_many :posts, -> { order('posts.id DESC') }, :through => :readers
+ has_many :posts, -> { order("posts.id DESC") }, through: :readers
end
posts = person_prime.includes(:posts).first.posts
assert_operator posts.length, :>, 1
- posts.each_cons(2) do |left,right|
+ posts.each_cons(2) do |left, right|
assert_operator left.id, :>, right.id
end
end
@@ -83,7 +113,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
subscription = make_model "Subscription"
subscriber = make_model "Subscriber"
- subscriber.primary_key = 'nick'
+ subscriber.primary_key = "nick"
subscription.belongs_to :book, anonymous_class: book
subscription.belongs_to :subscriber, anonymous_class: subscriber
@@ -104,8 +134,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_no_pk_join_table_append
lesson, _, student = make_no_pk_hm_t
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
sicp.students << ben
assert sicp.save!
end
@@ -113,17 +143,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_no_pk_join_table_delete
lesson, lesson_student, student = make_no_pk_hm_t
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
- louis = student.new(:name => "Louis Reasoner")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
+ louis = student.new(name: "Louis Reasoner")
sicp.students << ben
sicp.students << louis
assert sicp.save!
sicp.students.reload
assert_operator lesson_student.count, :>=, 2
- assert_no_difference('student.count') do
- assert_difference('lesson_student.count', -2) do
+ assert_no_difference("student.count") do
+ assert_difference("lesson_student.count", -2) do
sicp.students.destroy(*student.all.to_a)
end
end
@@ -137,8 +167,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
after_destroy_called = true
end
- sicp = lesson.new(:name => "SICP")
- ben = student.new(:name => "Ben Bitdiddle")
+ sicp = lesson.new(name: "SICP")
+ ben = student.new(name: "Ben Bitdiddle")
sicp.students << ben
assert sicp.save!
@@ -147,20 +177,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
@@ -173,7 +189,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = Person.new
post = Post.new
person.posts << post
- assert person.posts.include?(post)
+ assert_includes person.posts, post
end
def test_associate_existing
@@ -185,19 +201,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert post.people.include?(person)
+ assert_includes post.people, person
end
- assert post.reload.people.reload.include?(person)
+ assert_includes post.reload.people.reload, person
end
def test_delete_all_for_with_dependent_option_destroy
person = people(:david)
assert_equal 1, person.jobs_with_dependent_destroy.count
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -1 do
- person.reload.jobs_with_dependent_destroy.delete_all
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
+ assert_equal 1, person.reload.jobs_with_dependent_destroy.delete_all
end
end
end
@@ -206,9 +222,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:david)
assert_equal 1, person.jobs_with_dependent_nullify.count
- assert_no_difference 'Job.count' do
- assert_no_difference 'Reference.count' do
- person.reload.jobs_with_dependent_nullify.delete_all
+ assert_no_difference "Job.count" do
+ assert_no_difference "Reference.count" do
+ assert_equal 1, person.reload.jobs_with_dependent_nullify.delete_all
end
end
end
@@ -217,26 +233,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:david)
assert_equal 1, person.jobs_with_dependent_delete_all.count
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -1 do
- person.reload.jobs_with_dependent_delete_all.delete_all
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
+ assert_equal 1, person.reload.jobs_with_dependent_delete_all.delete_all
end
end
end
+ def test_delete_all_on_association_clears_scope
+ post = Post.create!(title: "Rails 6", body: "")
+ people = post.people
+ people.create!(first_name: "Jeb")
+ people.delete_all
+ assert_nil people.first
+ end
+
def test_concat
person = people(:david)
post = posts(:thinking)
- post.people.concat [person]
+ result = post.people.concat [person]
assert_equal 1, post.people.size
assert_equal 1, post.people.reload.size
+ assert_equal post.people, result
end
def test_associate_existing_record_twice_should_add_to_target_twice
post = posts(:thinking)
person = people(:david)
- assert_difference 'post.people.to_a.count', 2 do
+ assert_difference "post.people.to_a.count", 2 do
post.people << person
post.people << person
end
@@ -246,7 +271,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:thinking)
person = people(:david)
- assert_difference 'post.people.count', 2 do
+ assert_difference "post.people.count", 2 do
post.people << person
post.people << person
end
@@ -259,20 +284,23 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post.people << person
post.people << person
- counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
+ counts = ["post.people.count", "post.people.to_a.count", "post.readers.count", "post.readers.to_a.count"]
assert_difference counts, -2 do
post.people.delete(person)
end
- assert !post.people.reload.include?(person)
+ assert_not_includes post.people.reload, person
end
def test_associating_new
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
- assert_queries(0) do
- new_person = Person.new :first_name => 'bob'
+ # Load schema information so we don't query below if running just this test.
+ Person.define_attribute_methods
+
+ assert_no_queries do
+ new_person = Person.new first_name: "bob"
end
# Associating new records always saves them
@@ -282,59 +310,73 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert posts(:thinking).people.include?(new_person)
+ assert_includes posts(:thinking).people, new_person
end
- assert posts(:thinking).reload.people.reload.include?(new_person)
+ assert_includes posts(:thinking).reload.people.reload, new_person
end
def test_associate_new_by_building
assert_queries(1) { posts(:thinking) }
- assert_queries(0) do
- posts(:thinking).people.build(:first_name => "Bob")
- posts(:thinking).people.new(:first_name => "Ted")
+ # Load schema information so we don't query below if running just this test.
+ Person.define_attribute_methods
+
+ assert_no_queries do
+ posts(:thinking).people.build(first_name: "Bob")
+ posts(:thinking).people.new(first_name: "Ted")
end
# Should only need to load the association once
assert_queries(1) do
- assert posts(:thinking).people.collect(&:first_name).include?("Bob")
- assert posts(:thinking).people.collect(&:first_name).include?("Ted")
+ assert_includes posts(:thinking).people.collect(&:first_name), "Bob"
+ assert_includes posts(:thinking).people.collect(&:first_name), "Ted"
end
# 2 queries for each new record (1 to save the record itself, 1 for the join model)
# * 2 new records = 4
# + 1 query to save the actual post = 5
assert_queries(5) do
- posts(:thinking).body += '-changed'
+ posts(:thinking).body += "-changed"
posts(:thinking).save
end
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Bob")
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Ted")
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Bob"
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Ted"
end
def test_build_then_save_with_has_many_inverse
post = posts(:thinking)
- person = post.people.build(:first_name => "Bob")
+ person = post.people.build(first_name: "Bob")
person.save
post.reload
- assert post.people.include?(person)
+ assert_includes post.people, person
end
def test_build_then_save_with_has_one_inverse
post = posts(:thinking)
- person = post.single_people.build(:first_name => "Bob")
+ person = post.single_people.build(first_name: "Bob")
person.save
post.reload
- assert post.single_people.include?(person)
+ 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')
+ post = Post.new(title: "Hello", body: "world")
+ person = Person.new(first_name: "Sean")
post.people = [person]
post.save
@@ -346,17 +388,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_delete_association
- assert_queries(2){posts(:welcome);people(:michael); }
+ assert_queries(2) { posts(:welcome); people(:michael); }
assert_queries(1) do
posts(:welcome).people.delete(people(:michael))
end
assert_queries(1) do
- assert posts(:welcome).people.empty?
+ assert_empty posts(:welcome).people
end
- assert posts(:welcome).reload.people.reload.empty?
+ assert_empty posts(:welcome).reload.people.reload
end
def test_destroy_association
@@ -366,8 +408,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people.reload.empty?
+ assert_empty posts(:welcome).reload.people
+ assert_empty posts(:welcome).people.reload
end
def test_destroy_all
@@ -377,8 +419,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people.reload.empty?
+ assert_empty posts(:welcome).reload.people
+ assert_empty posts(:welcome).people.reload
+ end
+
+ def test_destroy_all_on_association_clears_scope
+ post = Post.create!(title: "Rails 6", body: "")
+ people = post.people
+ people.create!(first_name: "Jeb")
+ people.destroy_all
+ assert_nil people.first
+ end
+
+ def test_destroy_on_association_clears_scope
+ post = Post.create!(title: "Rails 6", body: "")
+ people = post.people
+ person = people.create!(first_name: "Jeb")
+ people.destroy(person)
+ assert_nil people.first
+ end
+
+ def test_delete_on_association_clears_scope
+ post = Post.create!(title: "Rails 6", body: "")
+ people = post.people
+ person = people.create!(first_name: "Jeb")
+ people.delete(person)
+ assert_nil people.first
end
def test_should_raise_exception_for_destroying_mismatching_records
@@ -392,15 +458,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = people(:michael)
job = jobs(:magician)
- reference = Reference.where(:job_id => job.id, :person_id => person.id).first
+ reference = Reference.where(job_id: job.id, person_id: person.id).first
- assert_no_difference ['Job.count', 'Reference.count'] do
- assert_difference 'person.jobs.count', -1 do
+ assert_no_difference ["Job.count", "Reference.count"] do
+ assert_difference "person.jobs.count", -1 do
person.jobs_with_dependent_nullify.delete(job)
end
end
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
ensure
Reference.make_comments = false
end
@@ -414,14 +480,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Make sure we're not deleting everything
assert person.jobs.count >= 2
- assert_no_difference 'Job.count' do
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ assert_no_difference "Job.count" do
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
person.jobs_with_dependent_delete_all.delete(job)
end
end
# Check that the destroy callback on Reference did not run
- assert_equal nil, person.reload.comments
+ assert_nil person.reload.comments
ensure
Reference.make_comments = false
end
@@ -435,8 +501,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Make sure we're not deleting everything
assert person.jobs.count >= 2
- assert_no_difference 'Job.count' do
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
+ assert_no_difference "Job.count" do
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
person.jobs_with_dependent_destroy.delete(job)
end
end
@@ -453,8 +519,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Create a reference which is not linked to a job. This should not be destroyed.
person.references.create!
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -person.jobs.count do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -person.jobs.count do
person.destroy
end
end
@@ -466,8 +532,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# Create a reference which is not linked to a job. This should not be destroyed.
person.references.create!
- assert_no_difference 'Job.count' do
- assert_difference 'Reference.count', -person.jobs.count do
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -person.jobs.count do
person.destroy
end
end
@@ -478,41 +544,41 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
references = person.references.to_a
- assert_no_difference ['Reference.count', 'Job.count'] do
+ assert_no_difference ["Reference.count", "Job.count"] do
person.destroy
end
references.each do |reference|
- assert_equal nil, reference.reload.job_id
+ assert_nil reference.reload.job_id
end
end
def test_update_counter_caches_on_delete
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
- assert_difference ['post.reload.tags_count'], -1 do
+ assert_difference ["post.reload.tags_count"], -1 do
posts(:welcome).tags.delete(tag)
end
end
def test_update_counter_caches_on_delete_with_dependent_destroy
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
post.update_columns(tags_with_destroy_count: post.tags.count)
- assert_difference ['post.reload.tags_with_destroy_count'], -1 do
+ assert_difference ["post.reload.tags_with_destroy_count"], -1 do
posts(:welcome).tags_with_destroy.delete(tag)
end
end
def test_update_counter_caches_on_delete_with_dependent_nullify
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
post.update_columns(tags_with_nullify_count: post.tags.count)
- assert_no_difference 'post.reload.tags_count' do
- assert_difference 'post.reload.tags_with_nullify_count', -1 do
+ assert_no_difference "post.reload.tags_count" do
+ assert_difference "post.reload.tags_with_nullify_count", -1 do
posts(:welcome).tags_with_nullify.delete(tag)
end
end
@@ -520,7 +586,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_replace_association
post = posts(:welcome)
- tag = post.tags.create!(:name => 'doomed')
+ tag = post.tags.create!(name: "doomed")
tag.tagged_posts << posts(:thinking)
tag.tagged_posts = []
@@ -531,15 +597,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_update_counter_caches_on_destroy
post = posts(:welcome)
- tag = post.tags.create!(name: 'doomed')
+ tag = post.tags.create!(name: "doomed")
- assert_difference 'post.reload.tags_count', -1 do
+ assert_difference "post.reload.tags_count", -1 do
tag.tagged_posts.destroy(post)
end
end
+ def test_update_counter_caches_on_destroy_with_indestructible_through_record
+ post = posts(:welcome)
+ tag = post.indestructible_tags.create!(name: "doomed")
+ post.update_columns(indestructible_tags_count: post.indestructible_tags.count)
+
+ assert_no_difference "post.reload.indestructible_tags_count" do
+ posts(:welcome).indestructible_tags.destroy(tag)
+ end
+ end
+
def test_replace_association
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload}
+ assert_queries(4) { posts(:welcome); people(:david); people(:michael); posts(:welcome).people.reload }
# 1 query to delete the existing reader (michael)
# 1 query to associate the new reader (david)
@@ -547,35 +623,45 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:welcome).people = [people(:david)]
end
- assert_queries(0){
- assert posts(:welcome).people.include?(people(:david))
- assert !posts(:welcome).people.include?(people(:michael))
- }
+ assert_no_queries do
+ assert_includes posts(:welcome).people, people(:david)
+ assert_not_includes posts(:welcome).people, people(:michael)
+ end
+
+ assert_includes posts(:welcome).reload.people.reload, people(:david)
+ assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
+ end
+
+ def test_replace_association_with_duplicates
+ post = posts(:thinking)
+ person = people(:david)
- assert posts(:welcome).reload.people.reload.include?(people(:david))
- assert !posts(:welcome).reload.people.reload.include?(people(:michael))
+ assert_difference "post.people.count", 2 do
+ post.people = [person]
+ post.people = [person, person]
+ end
end
def test_replace_order_is_preserved
posts(:welcome).people.clear
posts(:welcome).people = [people(:david), people(:michael)]
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
# Test the inverse order in case the first success was a coincidence
posts(:welcome).people.clear
posts(:welcome).people = [people(:michael), people(:david)]
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
end
def test_replace_by_id_order_is_preserved
posts(:welcome).people.clear
posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
# Test the inverse order in case the first success was a coincidence
posts(:welcome).people.clear
posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
end
def test_associate_with_create
@@ -584,15 +670,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
# 1 query for the new record, 1 for the join table record
# No need to update the actual collection yet!
assert_queries(2) do
- posts(:thinking).people.create(:first_name=>"Jeb")
+ posts(:thinking).people.create(first_name: "Jeb")
end
# *Now* we actually need the collection so it's loaded
assert_queries(1) do
- assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
+ assert_includes posts(:thinking).people.collect(&:first_name), "Jeb"
end
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Jeb")
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Jeb"
end
def test_through_record_is_built_when_created_with_where
@@ -603,82 +689,82 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_associate_with_create_and_no_options
peeps = posts(:thinking).people.count
- posts(:thinking).people.create(:first_name => 'foo')
+ posts(:thinking).people.create(first_name: "foo")
assert_equal peeps + 1, posts(:thinking).people.count
end
def test_associate_with_create_with_through_having_conditions
impatient_people = posts(:thinking).impatient_people.count
- posts(:thinking).impatient_people.create!(:first_name => 'foo')
+ posts(:thinking).impatient_people.create!(first_name: "foo")
assert_equal impatient_people + 1, posts(:thinking).impatient_people.count
end
def test_associate_with_create_exclamation_and_no_options
peeps = posts(:thinking).people.count
- posts(:thinking).people.create!(:first_name => 'foo')
+ posts(:thinking).people.create!(first_name: "foo")
assert_equal peeps + 1, posts(:thinking).people.count
end
def test_create_on_new_record
p = Post.new
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") }
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(first_name: "mew") }
assert_equal "You cannot call create unless the parent is saved", error.message
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") }
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(first_name: "snow") }
assert_equal "You cannot call create unless the parent is saved", error.message
end
def test_associate_with_create_and_invalid_options
firm = companies(:first_firm)
- assert_no_difference('firm.developers.count') { assert_nothing_raised { firm.developers.create(:name => '0') } }
+ assert_no_difference("firm.developers.count") { assert_nothing_raised { firm.developers.create(name: "0") } }
end
def test_associate_with_create_and_valid_options
firm = companies(:first_firm)
- assert_difference('firm.developers.count', 1) { firm.developers.create(:name => 'developer') }
+ assert_difference("firm.developers.count", 1) { firm.developers.create(name: "developer") }
end
def test_associate_with_create_bang_and_invalid_options
firm = companies(:first_firm)
- assert_no_difference('firm.developers.count') { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(:name => '0') } }
+ assert_no_difference("firm.developers.count") { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(name: "0") } }
end
def test_associate_with_create_bang_and_valid_options
firm = companies(:first_firm)
- assert_difference('firm.developers.count', 1) { firm.developers.create!(:name => 'developer') }
+ assert_difference("firm.developers.count", 1) { firm.developers.create!(name: "developer") }
end
def test_push_with_invalid_record
firm = companies(:first_firm)
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(:name => '0') }
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(name: "0") }
end
def test_push_with_invalid_join_record
repair_validations(Contract) do
- Contract.validate {|r| r.errors[:base] << 'Invalid Contract' }
+ Contract.validate { |r| r.errors[:base] << "Invalid Contract" }
firm = companies(:first_firm)
- lifo = Developer.new(:name => 'lifo')
+ lifo = Developer.new(name: "lifo")
assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
- lifo = Developer.create!(:name => 'lifo')
+ lifo = Developer.create!(name: "lifo")
assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
end
end
def test_clear_associations
- assert_queries(2) { posts(:welcome);posts(:welcome).people.reload }
+ assert_queries(2) { posts(:welcome); posts(:welcome).people.reload }
assert_queries(1) do
posts(:welcome).people.clear
end
- assert_queries(0) do
- assert posts(:welcome).people.empty?
+ assert_no_queries do
+ assert_empty posts(:welcome).people
end
- assert posts(:welcome).reload.people.reload.empty?
+ assert_empty posts(:welcome).reload.people.reload
end
def test_association_callback_ordering
@@ -692,7 +778,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Michael"]
], log.last(2)
- post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
+ post.people_with_callbacks.push(people(:david), Person.create!(first_name: "Bob"), Person.new(first_name: "Lary"))
assert_equal [
[:added, :before, "David"],
[:added, :after, "David"],
@@ -700,21 +786,21 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :after, "Bob"],
[:added, :before, "Lary"],
[:added, :after, "Lary"]
- ],log.last(6)
+ ], log.last(6)
- post.people_with_callbacks.build(:first_name => "Ted")
+ post.people_with_callbacks.build(first_name: "Ted")
assert_equal [
[:added, :before, "Ted"],
[:added, :after, "Ted"]
], log.last(2)
- post.people_with_callbacks.create(:first_name => "Sam")
+ post.people_with_callbacks.create(first_name: "Sam")
assert_equal [
[:added, :before, "Sam"],
[:added, :after, "Sam"]
], log.last(2)
- post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
+ post.people_with_callbacks = [people(:michael), people(:david), Person.new(first_name: "Julian"), Person.create!(first_name: "Roger")]
assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
assert_equal [
[:added, :before, "Julian"],
@@ -722,12 +808,24 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :before, "Roger"],
[:added, :after, "Roger"]
], log.last(4)
+
+ post.people_with_callbacks.build { |person| person.first_name = "Ted" }
+ assert_equal [
+ [:added, :before, "Ted"],
+ [:added, :after, "Ted"]
+ ], log.last(2)
+
+ post.people_with_callbacks.create { |person| person.first_name = "Sam" }
+ assert_equal [
+ [:added, :before, "Sam"],
+ [:added, :after, "Sam"]
+ ], log.last(2)
end
def test_dynamic_find_should_respect_association_include
# SQL error in sort clause if :include is not included
# due to Unknown column 'comments.id'
- assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
+ assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title("Welcome to the weblog")
end
def test_count_with_include_should_alias_join_table
@@ -743,7 +841,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_get_ids_for_has_many_through_with_conditions_should_not_preload
- Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
+ Tagging.create!(taggable_type: "Post", taggable_id: posts(:welcome).id, tag: tags(:misc))
assert_not_called(ActiveRecord::Associations::Preloader, :new) do
posts(:welcome).misc_tag_ids
end
@@ -752,7 +850,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
person = people(:michael)
person.posts.reload
- assert_queries(0) do
+ assert_no_queries do
person.post_ids
person.post_ids
end
@@ -760,9 +858,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
person = people(:michael)
- assert !person.posts.loaded?
+ assert_not_predicate person.posts, :loaded?
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
- assert !person.posts.loaded?
+ assert_not_predicate person.posts, :loaded?
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
@@ -774,16 +872,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
- post = Post.create!(:title => "TITLE", :body => "BODY")
+ post = Post.create!(title: "TITLE", body: "BODY")
assert_equal [], post.author_favorites
end
def test_has_many_association_through_a_belongs_to_association
author = authors(:mary)
- post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
- author.author_favorites.create(:favorite_author_id => 1)
- author.author_favorites.create(:favorite_author_id => 2)
- author.author_favorites.create(:favorite_author_id => 3)
+ post = Post.create!(author: author, title: "TITLE", body: "BODY")
+ author.author_favorites.create(favorite_author_id: 1)
+ author.author_favorites.create(favorite_author_id: 2)
+ author.author_favorites.create(favorite_author_id: 3)
assert_equal post.author.author_favorites, post.author_favorites
end
@@ -807,37 +905,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_modifying_has_many_through_has_one_reflection_should_raise
[
- lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] },
- lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) },
+ lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(body: "Gorp!", post_id: 1011), VerySpecialComment.create!(body: "Eep!", post_id: 1012)] },
+ lambda { authors(:david).very_special_comments << VerySpecialComment.create!(body: "Hoohah!", post_id: 1013) },
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
+ ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
end
def test_has_many_association_through_a_has_many_association_to_self
- 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)
+ 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
- Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
+ Categorization.create(author: authors(:mary), named_category_name: categories(:general).name)
assert_equal categories(:general), authors(:mary).named_categories.first
end
def test_collection_build_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.build(:name => "Primary")
+ category = author.named_categories.build(name: "Primary")
author.save
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories.reload.include?(category)
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_includes author.named_categories.reload, category
end
def test_collection_create_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.create(:name => "Primary")
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories.reload.include?(category)
+ category = author.named_categories.create(name: "Primary")
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_includes author.named_categories.reload, category
end
def test_collection_exists
@@ -849,10 +947,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
- category = author.named_categories.create(:name => "Primary")
+ category = author.named_categories.create(name: "Primary")
author.named_categories.delete(category)
- assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories.reload.empty?
+ assert_not Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_empty author.named_categories.reload
end
def test_collection_singular_ids_getter_with_string_primary_keys
@@ -869,6 +967,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)
@@ -878,26 +984,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
book.subscriber_ids = []
assert_equal [], book.subscribers.reload
end
-
end
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
- ids = [Developer.first.id, -9999]
- assert_raises(ActiveRecord::AssociationTypeMismatch) {company.developer_ids= ids}
+ ids = [Developer.first.id, -9999]
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
+ 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 }
+ 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
- assert_nothing_raised { books(:awdr).subscribers.where(:nick => "marklazz").build }
+ assert_nothing_raised { books(:awdr).subscribers.where(nick: "marklazz").build }
end
def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_where_clause
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").build
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").build
assert_equal new_subscriber.nick, "marklazz"
end
def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_multiple_where_clauses
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").where(:name => 'Marcelo Giorgi').build
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").where(name: "Marcelo Giorgi").build
assert_equal new_subscriber.nick, "marklazz"
assert_equal new_subscriber.name, "Marcelo Giorgi"
end
@@ -906,19 +1021,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
person = Person.new
reference = person.references.build
job = reference.build_job
- assert person.jobs.include?(job)
+ assert_includes person.jobs, job
end
def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
author = Author.new
post = author.posts.build
comment = post.comments.build
- assert author.comments.include?(comment)
+ assert_includes author.comments, comment
end
def test_through_association_readonly_should_be_false
- assert !people(:michael).posts.first.readonly?
- assert !people(:michael).posts.to_a.first.readonly?
+ assert_not_predicate people(:michael).posts.first, :readonly?
+ assert_not_predicate people(:michael).posts.to_a.first, :readonly?
end
def test_can_update_through_association
@@ -927,10 +1042,17 @@ 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
- authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_categories).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
assert_equal [owners(:blackbeard)], authors(:david).essay_owners
@@ -942,7 +1064,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories_2
- authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_categories_2).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
end
@@ -954,30 +1076,30 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments.order('id').to_a, authors(:david).comments_on_first_posts
+ assert_equal posts(:welcome).comments.order("id").to_a, authors(:david).comments_on_first_posts
end
def test_create_has_many_through_with_default_scope_on_join_model
- category = authors(:david).special_categories.create(:name => "Foo")
- assert_equal 1, category.categorizations.where(:special => true).count
+ category = authors(:david).special_categories.create(name: "Foo")
+ assert_equal 1, category.categorizations.where(special: true).count
end
def test_joining_has_many_through_with_distinct
- mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ mary = Author.joins(:unique_categorized_posts).where(id: authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
end
def test_joining_has_many_through_belongs_to
- posts = Post.joins(:author_categorizations).order('posts.id').
- where('categorizations.id' => categorizations(:mary_thinking_sti).id)
+ posts = Post.joins(:author_categorizations).order("posts.id").
+ where("categorizations.id" => categorizations(:mary_thinking_sti).id)
assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts
end
def test_select_chosen_fields_only
author = authors(:david)
- assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
+ assert_equal ["body", "id"].sort, author.comments.select("comments.body").first.attributes.keys.sort
end
def test_get_has_many_through_belongs_to_ids_with_conditions
@@ -1000,12 +1122,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post.author_categorizations
proxy = post.send(:association_instance_get, :author_categorizations)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
post.author_id = authors(:david).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
end
@@ -1020,15 +1142,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
address = author_addresses(:david_address)
- assert post.author_addresses.include?(address)
+ assert_includes post.author_addresses, address
post.author_addresses.delete(address)
- assert post[:author_count].nil?
+ assert_predicate post[:author_count], :nil?
end
def test_primary_key_option_on_source
post = posts(:welcome)
category = categories(:general)
- Categorization.create!(:post_id => post.id, :named_category_name => category.name)
+ Categorization.create!(post_id: post.id, named_category_name: category.name)
assert_equal [category], post.named_categories
assert_equal [category.name], post.named_category_ids # checks when target loaded
@@ -1037,29 +1159,29 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_create_should_not_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- Category.create(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ Category.create(name: "Fishing", authors: [Author.first])
end
end
def test_assign_array_to_new_record_builds_join_records
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_equal 1, c.categorizations.size
end
def test_create_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
assert_raises(ActiveRecord::RecordInvalid) do
- Category.create!(:name => 'Fishing', :authors => [Author.first])
+ Category.create!(name: "Fishing", authors: [Author.first])
end
end
end
def test_save_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_raises(ActiveRecord::RecordInvalid) do
c.save!
end
@@ -1068,41 +1190,79 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_save_returns_falsy_when_join_record_has_errors
repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
+ c = Category.new(name: "Fishing", authors: [Author.first])
assert_not c.save
end
end
def test_preloading_empty_through_association_via_joins
- person = Person.create!(:first_name => "Gaga")
- person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
+ person = Person.create!(first_name: "Gaga")
+ person = Person.where(id: person.id).where("readers.id = 1 or 1=1").references(:readers).includes(:posts).to_a.first
- assert person.posts.loaded?, 'person.posts should be loaded'
+ assert person.posts.loaded?, "person.posts should be loaded"
assert_equal [], person.posts
end
+ def test_preloading_empty_through_with_polymorphic_source_association
+ owner = Owner.create!(name: "Rainbow Unicat")
+ pet = Pet.create!(owner: owner)
+ person = Person.create!(first_name: "Gaga")
+ treasure = Treasure.create!(looter: person)
+ non_looted_treasure = Treasure.create!()
+ PetTreasure.create!(pet: pet, treasure: treasure, rainbow_color: "Ultra violet indigo")
+ PetTreasure.create!(pet: pet, treasure: non_looted_treasure, rainbow_color: "Ultra violet indigo")
+
+ assert_equal [person], Owner.where(name: "Rainbow Unicat").includes(pets: :persons).first.persons.to_a
+ end
+
def test_explicitly_joining_join_table
assert_equal owners(:blackbeard).toys, owners(:blackbeard).toys.with_pet
end
def test_has_many_through_with_polymorphic_source
- post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
+ post = tags(:general).tagged_posts.create! title: "foo", body: "bar"
assert_equal [tags(:general)], post.reload.tags
end
def test_has_many_through_obeys_order_on_through_association
owner = owners(:blackbeard)
- assert owner.toys.to_sql.include?("pets.name desc")
+ assert_includes owner.toys.to_sql, "pets.name desc"
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
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [], person.posts
- assert_equal [], person.posts.where(body: 'omg')
+ assert_equal [], person.posts.where(body: "omg")
assert_equal [], person.posts.pluck(:body)
assert_equal 0, person.posts.sum(:tags_count)
assert_equal 0, person.posts.count
@@ -1134,9 +1294,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_unscope_default_scope
- post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
- Reader.create! :person => people(:david), :post => post
- LazyReader.create! :person => people(:susan), :post => post
+ post = Post.create!(title: "Beaches", body: "I like beaches!")
+ Reader.create! person: people(:david), post: post
+ LazyReader.create! person: people(:susan), post: post
assert_equal 2, post.people.to_a.size
assert_equal 1, post.lazy_people.to_a.size
@@ -1146,8 +1306,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_add_with_sti_middle_relation
- club = SuperClub.create!(name: 'Fight Club')
- member = Member.create!(name: 'Tyler Durden')
+ club = SuperClub.create!(name: "Fight Club")
+ member = Member.create!(name: "Tyler Durden")
club.members << member
assert_equal 1, SuperMembership.where(member_id: member.id, club_id: club.id).count
@@ -1168,12 +1328,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_nil Club.new.special_favourites.distinct_value
end
- def test_association_force_reload_with_only_true_is_deprecated
- post = Post.find(1)
-
- assert_deprecated { post.people(true) }
- end
-
def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
member = Member.create!
club = Club.create!
@@ -1201,4 +1355,159 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
ensure
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_left_joined_same_table_with_through_table
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.left_joins(:post)
+ 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! }
+ FamilyTree.create!(member: users[0], family: family)
+ FamilyTree.create!(member: users[1], family: family)
+ FamilyTree.create!(member: users[2], family: family, token: "wat")
+
+ assert_equal 2, users[0].family_members.to_a.size
+ 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(
+ companies: [Company.create]
+ )
+ 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
+
+ def test_child_is_visible_to_join_model_in_add_association_callbacks
+ [:before_add, :after_add].each do |callback_name|
+ sentient_treasure = Class.new(Treasure) do
+ def self.name; "SentientTreasure"; end
+
+ has_many :pet_treasures, foreign_key: :treasure_id, callback_name => :check_pet!
+ has_many :pets, through: :pet_treasures
+
+ def check_pet!(added)
+ raise "No pet!" if added.pet.nil?
+ end
+ end
+
+ treasure = sentient_treasure.new
+ assert_nothing_raised { treasure.pets << pets(:mochi) }
+ end
+ 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 c9d9e29f09..7bb629466d 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -1,48 +1,58 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-require 'models/ship'
-require 'models/pirate'
-require 'models/car'
-require 'models/bulb'
-require 'models/author'
-require 'models/image'
-require 'models/post'
+require "models/developer"
+require "models/computer"
+require "models/project"
+require "models/company"
+require "models/ship"
+require "models/pirate"
+require "models/car"
+require "models/bulb"
+require "models/author"
+require "models/image"
+require "models/post"
+require "models/drink_designer"
+require "models/chef"
+require "models/department"
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
end
def test_has_one
- assert_equal companies(:first_firm).account, Account.find(1)
- assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
+ firm = companies(:first_firm)
+ first_account = Account.find(1)
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) do
+ assert_equal first_account, firm.account
+ assert_equal first_account.credit_limit, firm.account.credit_limit
+ end
end
def test_has_one_does_not_use_order_by
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'
+ sql_log = ActiveRecord::SQLCounter.log
+ assert sql_log.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{sql_log}"
end
def test_has_one_cache_nils
firm = companies(:another_firm)
assert_queries(1) { assert_nil firm.account }
- assert_queries(0) { assert_nil firm.account }
+ assert_no_queries { assert_nil firm.account }
- firms = Firm.all.merge!(:includes => :account).to_a
- assert_queries(0) { firms.each(&:account) }
+ firms = Firm.includes(:account).to_a
+ assert_no_queries { firms.each(&:account) }
end
def test_with_select
assert_equal Firm.find(1).account_with_select.attributes.size, 2
- assert_equal Firm.all.merge!(:includes => :account_with_select).find(1).account_with_select.attributes.size, 2
+ assert_equal Firm.all.merge!(includes: :account_with_select).find(1).account_with_select.attributes.size, 2
end
def test_finding_using_primary_key
@@ -102,17 +112,32 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_nullification_on_association_change
firm = companies(:rails_core)
old_account_id = firm.account.id
- firm.account = Account.new(:credit_limit => 5)
+ firm.account = Account.new(credit_limit: 5)
# account is dependent with nullify, therefore its firm_id should be nil
assert_nil Account.find(old_account_id).firm_id
end
+ def test_nullify_on_polymorphic_association
+ department = Department.create!
+ designer = DrinkDesignerWithPolymorphicDependentNullifyChef.create!
+ chef = department.chefs.create!(employable: designer)
+
+ assert_equal chef.employable_id, designer.id
+ assert_equal chef.employable_type, designer.class.name
+
+ designer.destroy!
+ chef.reload
+
+ assert_nil chef.employable_id
+ assert_nil chef.employable_type
+ end
+
def test_nullification_on_destroyed_association
developer = Developer.create!(name: "Someone")
ship = Ship.create!(name: "Planet Caravan", developer: developer)
ship.destroy
- assert !ship.persisted?
- assert !developer.persisted?
+ assert_not_predicate ship, :persisted?
+ assert_not_predicate developer, :persisted?
end
def test_natural_assignment_to_nil_after_destroy
@@ -125,12 +150,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_association_change_calls_delete
- companies(:first_firm).deletable_account = Account.new(:credit_limit => 5)
+ companies(:first_firm).deletable_account = Account.new(credit_limit: 5)
assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id]
end
def test_association_change_calls_destroy
- companies(:first_firm).account = Account.new(:credit_limit => 5)
+ companies(:first_firm).account = Account.new(credit_limit: 5)
assert_equal [companies(:first_firm).id], Account.destroyed_account_ids[companies(:first_firm).id]
end
@@ -170,69 +195,50 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_dependence_with_nil_associate
- firm = DependentFirm.new(:name => 'nullify')
+ firm = DependentFirm.new(name: "nullify")
firm.save!
assert_nothing_raised { firm.destroy }
end
def test_restrict_with_exception
- firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
- firm.create_account(:credit_limit => 10)
-
- assert_not_nil firm.account
-
- assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
- assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
- assert firm.account.present?
- end
-
- def test_restrict_with_error_is_deprecated_using_key_one
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { one: 'message for deprecated key' } } } }
-
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ firm = RestrictedWithExceptionFirm.create!(name: "restrict")
firm.create_account(credit_limit: 10)
assert_not_nil firm.account
- assert_deprecated { firm.destroy }
-
- assert !firm.errors.empty?
- assert_equal 'message for deprecated key', firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.account.present?
- ensure
- I18n.backend.reload!
+ assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedWithExceptionFirm.exists?(name: "restrict")
+ assert_predicate firm.account, :present?
end
def test_restrict_with_error
- firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
- firm.create_account(:credit_limit => 10)
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
+ firm.create_account(credit_limit: 10)
assert_not_nil firm.account
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
- assert firm.account.present?
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert_predicate firm.account, :present?
end
def test_restrict_with_error_with_locale
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {account: 'firm account'}}}
- firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ I18n.backend.store_translations "en", activerecord: { attributes: { restricted_with_error_firm: { account: "firm account" } } }
+ firm = RestrictedWithErrorFirm.create!(name: "restrict")
firm.create_account(credit_limit: 10)
assert_not_nil firm.account
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because a dependent firm account exists", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(name: 'restrict')
- assert firm.account.present?
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert_predicate firm.account, :present?
ensure
I18n.backend.reload!
end
@@ -247,9 +253,13 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_build_association_dont_create_transaction
- assert_no_queries(ignore_none: false) {
- Firm.new.build_account
- }
+ # Load schema information so we don't query below if running just this test.
+ Account.define_attribute_methods
+
+ firm = Firm.new
+ assert_no_queries do
+ firm.build_account
+ end
end
def test_building_the_associated_object_with_implicit_sti_base_class
@@ -260,24 +270,24 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_building_the_associated_object_with_explicit_sti_base_class
firm = DependentFirm.new
- company = firm.build_company(:type => "Company")
+ company = firm.build_company(type: "Company")
assert_kind_of Company, company, "Expected #{company.class} to be a Company"
end
def test_building_the_associated_object_with_sti_subclass
firm = DependentFirm.new
- company = firm.build_company(:type => "Client")
+ company = firm.build_company(type: "Client")
assert_kind_of Client, company, "Expected #{company.class} to be a Client"
end
def test_building_the_associated_object_with_an_invalid_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(:type => "Invalid") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(type: "Invalid") }
end
def test_building_the_associated_object_with_an_unrelated_type
firm = DependentFirm.new
- assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(:type => "Account") }
+ assert_raise(ActiveRecord::SubclassNotFound) { firm.build_company(type: "Account") }
end
def test_build_and_create_should_not_happen_within_scope
@@ -295,19 +305,19 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_create_association
- firm = Firm.create(:name => "GlobalMegaCorp")
- account = firm.create_account(:credit_limit => 1000)
+ firm = Firm.create(name: "GlobalMegaCorp")
+ account = firm.create_account(credit_limit: 1000)
assert_equal account, firm.reload.account
end
def test_create_association_with_bang
- firm = Firm.create(:name => "GlobalMegaCorp")
- account = firm.create_account!(:credit_limit => 1000)
+ firm = Firm.create(name: "GlobalMegaCorp")
+ account = firm.create_account!(credit_limit: 1000)
assert_equal account, firm.reload.account
end
def test_create_association_with_bang_failing
- firm = Firm.create(:name => "GlobalMegaCorp")
+ firm = Firm.create(name: "GlobalMegaCorp")
assert_raise ActiveRecord::RecordInvalid do
firm.create_account!
end
@@ -319,13 +329,55 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_create_with_inexistent_foreign_key_failing
- firm = Firm.create(name: 'GlobalMegaCorp')
+ firm = Firm.create(name: "GlobalMegaCorp")
assert_raises(ActiveRecord::UnknownAttributeError) do
firm.create_account_with_inexistent_foreign_key
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)
+
+ assert_equal 53, odegy.account.credit_limit
+ Account.where(id: odegy.account.id).update_all(credit_limit: 80)
+ assert_equal 53, odegy.account.credit_limit
+
+ assert_equal 80, odegy.reload_account.credit_limit
+ end
+
+ def test_reload_association_with_query_cache
+ odegy_id = companies(:odegy).id
+
+ connection = ActiveRecord::Base.connection
+ connection.enable_query_cache!
+ connection.clear_query_cache
+
+ # Populate the cache with a query
+ odegy = Company.find(odegy_id)
+ # Populate the cache with a second query
+ odegy.account
+
+ assert_equal 2, connection.query_cache.size
+
+ # Clear the cache and fetch the account again, populating the cache with a query
+ assert_queries(1) { odegy.reload_account }
+
+ # This query is not cached anymore, so it should make a real SQL query
+ assert_queries(1) { Company.find(odegy_id) }
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
+ end
+
def test_build
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -365,7 +417,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_finding_with_interpolated_condition
firm = Firm.first
- superior = firm.clients.create(:name => 'SuperiorCo')
+ superior = firm.clients.create(name: "SuperiorCo")
superior.rating = 10
superior.save
assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
@@ -374,7 +426,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_assignment_before_child_saved
firm = Firm.find(1)
firm.account = a = Account.new("credit_limit" => 1000)
- assert a.persisted?
+ assert_predicate a, :persisted?
assert_equal a, firm.account
assert_equal a, firm.account
firm.association(:account).reload
@@ -382,7 +434,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_save_still_works_after_accessing_nil_has_one
- jp = Company.new :name => 'Jaded Pixel'
+ jp = Company.new name: "Jaded Pixel"
jp.dummy_account.nil?
assert_nothing_raised do
@@ -392,7 +444,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_cant_save_readonly_association
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save! }
- assert companies(:first_firm).readonly_account.readonly?
+ assert_predicate companies(:first_firm).readonly_account, :readonly?
end
def test_has_one_proxy_should_not_respond_to_private_methods
@@ -411,14 +463,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.all.merge!(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(includes: :account).find(@firm.id).save!
end
@firm.account.destroy
assert_nothing_raised do
Firm.find(@firm.id).save!
- Firm.all.merge!(:includes => :account).find(@firm.id).save!
+ Firm.all.merge!(includes: :account).find(@firm.id).save!
end
end
@@ -430,12 +482,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_create_respects_hash_condition
account = companies(:first_firm).create_account_limit_500_with_hash_conditions
- assert account.persisted?
+ assert_predicate account, :persisted?
assert_equal 500, account.credit_limit
end
def test_attributes_are_being_set_when_initialized_from_has_one_association_with_where_clause
- new_account = companies(:first_firm).build_account(:firm_name => 'Account')
+ new_account = companies(:first_firm).build_account(firm_name: "Account")
assert_equal new_account.firm_name, "Account"
end
@@ -447,9 +499,9 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
new_ship = pirate.create_ship
assert_not_equal ships(:black_pearl), new_ship
assert_equal new_ship, pirate.ship
- assert new_ship.new_record?
+ assert_predicate new_ship, :new_record?
assert_nil orig_ship.pirate_id
- assert !orig_ship.changed? # check it was saved
+ assert_not orig_ship.changed? # check it was saved
end
def test_creation_failure_with_dependent_option
@@ -457,8 +509,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
orig_ship = pirate.dependent_ship
new_ship = pirate.create_dependent_ship
- assert new_ship.new_record?
- assert orig_ship.destroyed?
+ assert_predicate new_ship, :new_record?
+ assert_predicate orig_ship, :destroyed?
end
def test_creation_failure_due_to_new_record_should_raise_error
@@ -478,14 +530,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
pirate = pirates(:blackbeard)
pirate.ship.name = nil
- assert !pirate.ship.valid?
+ assert_not_predicate pirate.ship, :valid?
error = assert_raise(ActiveRecord::RecordNotSaved) do
pirate.ship = ships(:interceptor)
end
assert_equal ships(:black_pearl), pirate.ship
assert_equal pirate.id, pirate.ship.pirate_id
- assert_equal "Failed to remove the existing associated ship. " +
+ assert_equal "Failed to remove the existing associated ship. " \
"The record failed to save after its foreign key was set to nil.", error.message
end
@@ -505,63 +557,63 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_association_keys_bypass_attribute_protection
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.build_bulb
assert_equal car.id, bulb.car_id
- bulb = car.build_bulb :car_id => car.id + 1
+ bulb = car.build_bulb car_id: car.id + 1
assert_equal car.id, bulb.car_id
bulb = car.create_bulb
assert_equal car.id, bulb.car_id
- bulb = car.create_bulb :car_id => car.id + 1
+ bulb = car.create_bulb car_id: car.id + 1
assert_equal car.id, bulb.car_id
end
def test_association_protect_foreign_key
- pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
ship = pirate.build_ship
assert_equal pirate.id, ship.pirate_id
- ship = pirate.build_ship :pirate_id => pirate.id + 1
+ ship = pirate.build_ship pirate_id: pirate.id + 1
assert_equal pirate.id, ship.pirate_id
ship = pirate.create_ship
assert_equal pirate.id, ship.pirate_id
- ship = pirate.create_ship :pirate_id => pirate.id + 1
+ ship = pirate.create_ship pirate_id: pirate.id + 1
assert_equal pirate.id, ship.pirate_id
end
def test_build_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.build_bulb{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.build_bulb { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_create_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.create_bulb{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.create_bulb { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_create_bang_with_block
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
- bulb = car.create_bulb!{ |b| b.color = 'Red' }
- assert_equal 'RED!', bulb.color
+ bulb = car.create_bulb! { |b| b.color = "Red" }
+ assert_equal "RED!", bulb.color
end
def test_association_attributes_are_available_to_after_initialize
- car = Car.create(:name => 'honda')
+ car = Car.create(name: "honda")
bulb = car.create_bulb
- assert_equal car.id, bulb.attributes_after_initialize['car_id']
+ assert_equal car.id, bulb.attributes_after_initialize["car_id"]
end
def test_has_one_transaction
@@ -581,36 +633,36 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_has_one_assignment_dont_trigger_save_on_change_of_same_object
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
- ship = pirate.build_ship(name: 'old name')
+ ship = pirate.build_ship(name: "old name")
ship.save!
- ship.name = 'new name'
- assert ship.changed?
+ ship.name = "new name"
+ assert_predicate ship, :changed?
assert_queries(1) do
# One query for updating name, not triggering query for updating pirate_id
pirate.ship = ship
end
- assert_equal 'new name', pirate.ship.reload.name
+ assert_equal "new name", pirate.ship.reload.name
end
def test_has_one_assignment_triggers_save_on_change_on_replacing_object
pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
- ship = pirate.build_ship(name: 'old name')
+ ship = pirate.build_ship(name: "old name")
ship.save!
- new_ship = Ship.create(name: 'new name')
+ new_ship = Ship.create(name: "new name")
assert_queries(2) do
- # One query for updating name and second query for updating pirate_id
+ # One query to nullify the old ship, one query to update the new ship
pirate.ship = new_ship
end
- assert_equal 'new name', pirate.ship.reload.name
+ assert_equal "new name", pirate.ship.reload.name
end
def test_has_one_autosave_with_primary_key_manually_set
- post = Post.create(id: 1234, title: "Some title", body: 'Some content')
- author = Author.new(id: 33, name: 'Hank Moody')
+ post = Post.create(id: 1234, title: "Some title", body: "Some content")
+ author = Author.new(id: 33, name: "Hank Moody")
author.post = post
author.save
@@ -621,7 +673,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_loading_for_new_record
- post = Post.create!(author_id: 42, title: 'foo', body: 'bar')
+ post = Post.create!(author_id: 42, title: "foo", body: "bar")
author = Author.new(id: 42)
assert_equal post, author.post
end
@@ -635,7 +687,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_with_polymorphic_has_one_with_custom_columns_name
- post = Post.create! :title => 'foo', :body => 'bar'
+ post = Post.create! title: "foo", body: "bar"
image = Image.create!
post.main_image = image
@@ -644,8 +696,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal image, post.main_image
end
- test 'dangerous association name raises ArgumentError' do
- [:errors, 'errors', :save, 'save'].each do |name|
+ test "dangerous association name raises ArgumentError" do
+ [:errors, "errors", :save, "save"].each do |name|
assert_raises(ArgumentError, "Association #{name} should not be allowed") do
Class.new(ActiveRecord::Base) do
has_one name
@@ -654,9 +706,99 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
- def test_association_force_reload_with_only_true_is_deprecated
- firm = Firm.find(1)
+ class SpecialBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "SpecialAuthor"
+ has_one :subscription, class_name: "SpecialSupscription", foreign_key: "subscriber_id"
+
+ enum status: [:proposed, :written, :published]
+ end
+
+ class SpecialAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_one :book, class_name: "SpecialBook", foreign_key: "author_id"
+ end
+
+ class SpecialSupscription < ActiveRecord::Base
+ self.table_name = "subscriptions"
+ belongs_to :book, class_name: "SpecialBook"
+ end
+
+ def test_association_enum_works_properly
+ author = SpecialAuthor.create!(name: "Test")
+ book = SpecialBook.create!(status: "published")
+ author.book = book
+
+ assert_equal "published", book.status
+ assert_not_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" }).count
+ end
+
+ def test_association_enum_works_properly_with_nested_join
+ author = SpecialAuthor.create!(name: "Test")
+ book = SpecialBook.create!(status: "published")
+ author.book = book
+
+ where_clause = { books: { subscriptions: { subscriber_id: nil } } }
+ assert_nothing_raised do
+ 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_deprecated { firm.account(true) }
+ assert_not DestroyByParentBook.exists?(book.id)
+ end
+
+ class UndestroyableBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "DestroyableAuthor"
+ before_destroy :dont
+
+ def dont
+ throw(:abort)
+ end
+ end
+
+ class DestroyableAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_one :book, class_name: "UndestroyableBook", foreign_key: "author_id", dependent: :destroy
+ end
+
+ def test_dependency_should_halt_parent_destruction
+ author = DestroyableAuthor.create!(name: "Test")
+ UndestroyableBook.create!(author: author)
+
+ assert_no_difference ["DestroyableAuthor.count", "UndestroyableBook.count"] do
+ assert_not author.destroy
+ end
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 b2b46812b9..69b4872519 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,29 +1,31 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/club'
-require 'models/member_type'
-require 'models/member'
-require 'models/membership'
-require 'models/sponsor'
-require 'models/organization'
-require 'models/member_detail'
-require 'models/minivan'
-require 'models/dashboard'
-require 'models/speedometer'
-require 'models/category'
-require 'models/author'
-require 'models/essay'
-require 'models/owner'
-require 'models/post'
-require 'models/comment'
-require 'models/categorization'
-require 'models/customer'
-require 'models/carrier'
-require 'models/shop_account'
-require 'models/customer_carrier'
+require "models/club"
+require "models/member_type"
+require "models/member"
+require "models/membership"
+require "models/sponsor"
+require "models/organization"
+require "models/member_detail"
+require "models/minivan"
+require "models/dashboard"
+require "models/speedometer"
+require "models/category"
+require "models/author"
+require "models/essay"
+require "models/owner"
+require "models/post"
+require "models/comment"
+require "models/categorization"
+require "models/customer"
+require "models/carrier"
+require "models/shop_account"
+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)
@@ -33,15 +35,34 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_equal clubs(:boring_club), @member.club
end
+ def test_has_one_through_executes_limited_query
+ boring_club = clubs(:boring_club)
+ assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) do
+ assert_equal boring_club, @member.general_club
+ end
+ end
+
def test_creating_association_creates_through_record
- new_member = Member.create(:name => "Chris")
- new_member.club = Club.create(:name => "LRUG")
+ new_member = Member.create(name: "Chris")
+ new_member.club = Club.create(name: "LRUG")
assert_not_nil new_member.current_membership
assert_not_nil new_member.club
end
+ def test_creating_association_builds_through_record
+ new_member = Member.create(name: "Chris")
+ new_club = new_member.association(:club).build
+ assert new_member.current_membership
+ assert_equal new_club, new_member.club
+ assert_predicate new_club, :new_record?
+ assert_predicate new_member.current_membership, :new_record?
+ assert new_member.save
+ assert_predicate new_club, :persisted?
+ assert_predicate new_member.current_membership, :persisted?
+ end
+
def test_creating_association_builds_through_record_for_new
- new_member = Member.new(:name => "Jane")
+ new_member = Member.new(name: "Jane")
new_member.club = clubs(:moustache_club)
assert new_member.current_membership
assert_equal clubs(:moustache_club), new_member.current_membership.club
@@ -50,9 +71,27 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_equal clubs(:moustache_club), new_member.club
end
+ def test_building_multiple_associations_builds_through_record
+ member_type = MemberType.create!
+ member = Member.create!
+ member_detail_with_one_association = MemberDetail.new(member_type: member_type)
+ assert_predicate member_detail_with_one_association.member, :new_record?
+ member_detail_with_two_associations = MemberDetail.new(member_type: member_type, admittable: member)
+ assert_predicate member_detail_with_two_associations.member, :new_record?
+ end
+
+ def test_creating_multiple_associations_creates_through_record
+ member_type = MemberType.create!
+ member = Member.create!
+ member_detail_with_one_association = MemberDetail.create!(member_type: member_type)
+ assert_not_predicate member_detail_with_one_association.member, :new_record?
+ member_detail_with_two_associations = MemberDetail.create!(member_type: member_type, admittable: member)
+ assert_not_predicate member_detail_with_two_associations.member, :new_record?
+ end
+
def test_creating_association_sets_both_parent_ids_for_new
- member = Member.new(name: 'Sean Griffin')
- club = Club.new(name: 'Da Club')
+ member = Member.new(name: "Sean Griffin")
+ club = Club.new(name: "Da Club")
member.club = club
@@ -65,7 +104,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_replace_target_record
- new_club = Club.create(:name => "Marx Bros")
+ new_club = Club.create(name: "Marx Bros")
@member.club = new_club
@member.reload
assert_equal new_club, @member.club
@@ -73,7 +112,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_replacing_target_record_deletes_old_association
assert_no_difference "Membership.count" do
- new_club = Club.create(:name => "Bananarama")
+ new_club = Club.create(name: "Bananarama")
@member.club = new_club
@member.reload
end
@@ -82,40 +121,47 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_set_record_to_nil_should_delete_association
@member.club = nil
@member.reload
- assert_equal nil, @member.current_membership
+ assert_nil @member.current_membership
assert_nil @member.club
end
+ def test_set_record_after_delete_association
+ @member.club = nil
+ @member.club = clubs(:moustache_club)
+ @member.reload
+ assert_equal clubs(:moustache_club), @member.club
+ end
+
def test_has_one_through_polymorphic
assert_equal clubs(:moustache_club), @member.sponsor_club
end
def test_has_one_through_eager_loading
- members = assert_queries(3) do #base table, through table, clubs table
- Member.all.merge!(:includes => :club, :where => ["name = ?", "Groucho Marx"]).to_a
+ 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
- assert_not_nil assert_no_queries {members[0].club}
+ assert_not_nil assert_no_queries { members[0].club }
end
def test_has_one_through_eager_loading_through_polymorphic
- members = assert_queries(3) do #base table, through table, clubs table
- Member.all.merge!(:includes => :sponsor_club, :where => ["name = ?", "Groucho Marx"]).to_a
+ 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
- assert_not_nil assert_no_queries {members[0].sponsor_club}
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
end
def test_has_one_through_with_conditions_eager_loading
# conditions on the through table
- assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :favourite_club).find(@member.id).favourite_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :favourite_club).find(@member.id).favourite_club
memberships(:membership_of_favourite_club).update_columns(favourite: false)
- assert_equal nil, Member.all.merge!(:includes => :favourite_club).find(@member.id).reload.favourite_club
+ assert_nil Member.all.merge!(includes: :favourite_club).find(@member.id).reload.favourite_club
# conditions on the source table
- assert_equal clubs(:moustache_club), Member.all.merge!(:includes => :hairy_club).find(@member.id).hairy_club
+ assert_equal clubs(:moustache_club), Member.all.merge!(includes: :hairy_club).find(@member.id).hairy_club
clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons")
- assert_equal nil, Member.all.merge!(:includes => :hairy_club).find(@member.id).reload.hairy_club
+ assert_nil Member.all.merge!(includes: :hairy_club).find(@member.id).reload.hairy_club
end
def test_has_one_through_polymorphic_with_source_type
@@ -123,31 +169,31 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_eager_has_one_through_polymorphic_with_source_type
- clubs = Club.all.merge!(:includes => :sponsored_member, :where => ["name = ?","Moustache and Eyebrow Fancier Club"]).to_a
+ clubs = Club.all.merge!(includes: :sponsored_member, where: ["name = ?", "Moustache and Eyebrow Fancier Club"]).to_a
# Only the eyebrow fanciers club has a sponsored_member
- assert_not_nil assert_no_queries {clubs[0].sponsored_member}
+ assert_not_nil assert_no_queries { clubs[0].sponsored_member }
end
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}
+ assert_not_nil assert_no_queries { members[0].club }
end
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}
+ assert_not_nil assert_no_queries { members[0].sponsor_club }
end
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!
+ 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 }
@@ -159,8 +205,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_assigning_association_correctly_assigns_target
- new_member = Member.create(:name => "Chris")
- new_member.club = new_club = Club.create(:name => "LRUG")
+ new_member = Member.create(name: "Chris")
+ new_member.club = new_club = Club.create(name: "LRUG")
assert_equal new_club, new_member.association(:club).target
end
@@ -176,37 +222,37 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_assigning_to_has_one_through_preserves_decorated_join_record
@organization = organizations(:nsa)
- assert_difference 'MemberDetail.count', 1 do
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.organization = @organization
end
assert_equal @organization, @member.organization
- assert @organization.members.include?(@member)
- assert_equal 'Extra', @member.member_detail.extra_data
+ assert_includes @organization.members, @member
+ assert_equal "Extra", @member.member_detail.extra_data
end
def test_reassigning_has_one_through
@organization = organizations(:nsa)
@new_organization = organizations(:discordians)
- assert_difference 'MemberDetail.count', 1 do
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ assert_difference "MemberDetail.count", 1 do
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.organization = @organization
end
assert_equal @organization, @member.organization
- assert_equal 'Extra', @member.member_detail.extra_data
- assert @organization.members.include?(@member)
- assert !@new_organization.members.include?(@member)
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_includes @organization.members, @member
+ assert_not_includes @new_organization.members, @member
- assert_no_difference 'MemberDetail.count' do
+ assert_no_difference "MemberDetail.count" do
@member.organization = @new_organization
end
assert_equal @new_organization, @member.organization
- assert_equal 'Extra', @member.member_detail.extra_data
- assert !@organization.members.include?(@member)
- assert @new_organization.members.include?(@member)
+ assert_equal "Extra", @member.member_detail.extra_data
+ assert_not_includes @organization.members, @member
+ assert_includes @new_organization.members, @member
end
def test_preloading_has_one_through_on_belongs_to
@@ -217,10 +263,10 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
@member.member_detail = @member_detail
@member.organization = @organization
@member_details = assert_queries(3) do
- MemberDetail.all.merge!(:includes => :member_type).to_a
+ MemberDetail.all.merge!(includes: :member_type).to_a
end
@new_detail = @member_details[0]
- assert @new_detail.send(:association, :member_type).loaded?
+ assert_predicate @new_detail.send(:association, :member_type), :loaded?
assert_no_queries { @new_detail.member_type }
end
@@ -230,19 +276,19 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
Club.find(@club.id).save!
- Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
end
@club.sponsor.destroy
assert_nothing_raised do
Club.find(@club.id).save!
- Club.all.merge!(:includes => :sponsored_member).find(@club.id).save!
+ Club.all.merge!(includes: :sponsored_member).find(@club.id).save!
end
end
def test_through_belongs_to_after_destroy
- @member_detail = MemberDetail.new(:extra_data => 'Extra')
+ @member_detail = MemberDetail.new(extra_data: "Extra")
@member.member_detail = @member_detail
@member.save!
@@ -261,7 +307,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_value_is_properly_quoted
- minivan = Minivan.find('m1')
+ minivan = Minivan.find("m1")
assert_nothing_raised do
minivan.dashboard
end
@@ -270,7 +316,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_polymorphic_with_primary_key_option
assert_equal categories(:general), authors(:david).essay_category
- authors = Author.joins(:essay_category).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_category).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
assert_equal owners(:blackbeard), authors(:david).essay_owner
@@ -282,12 +328,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_with_primary_key_option
assert_equal categories(:general), authors(:david).essay_category_2
- authors = Author.joins(:essay_category_2).where('categories.id' => categories(:general).id)
+ authors = Author.joins(:essay_category_2).where("categories.id" => categories(:general).id)
assert_equal authors(:david), authors.first
end
def test_has_one_through_with_default_scope_on_join_model
- assert_equal posts(:welcome).comments.order('id').first, authors(:david).comment_on_first_post
+ assert_equal posts(:welcome).comments.order("id").first, authors(:david).comment_on_first_post
end
def test_has_one_through_many_raises_exception
@@ -308,12 +354,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
minivan.dashboard
proxy = minivan.send(:association_instance_get, :dashboard)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal dashboards(:cool_first), minivan.dashboard
minivan.speedometer_id = speedometers(:second).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal dashboards(:second), minivan.dashboard
end
@@ -325,7 +371,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
minivan.speedometer_id = speedometers(:second).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal dashboards(:second), minivan.dashboard
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index b3fe759ad9..c33dcdee61 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -1,16 +1,18 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/category'
-require 'models/categorization'
-require 'models/person'
-require 'models/tagging'
-require 'models/tag'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/category"
+require "models/categorization"
+require "models/person"
+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
@@ -20,11 +22,29 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations
assert_nothing_raised do
- sql = Person.joins(:agents => {:agents => :agents}).joins(:agents => {:agents => {:primary_contact => :agents}}).to_sql
+ sql = Person.joins(agents: { agents: :agents }).joins(agents: { agents: { primary_contact: :agents } }).to_sql
assert_match(/agents_people_4/i, sql)
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)
@@ -47,7 +67,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_join_conditions_allow_nil_associations
- authors = Author.includes(:essays).where(:essays => {:id => nil})
+ authors = Author.includes(:essays).where(essays: { id: nil })
assert_equal 2, authors.count
end
@@ -58,56 +78,56 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_find_with_implicit_inner_joins_honors_readonly_with_select
- authors = Author.joins(:posts).select('authors.*').to_a
- assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ authors = Author.joins(:posts).select("authors.*").to_a
+ assert_not authors.empty?, "expected authors to be non-empty"
+ assert authors.all? { |a| !a.readonly? }, "expected no authors to be readonly"
end
def test_find_with_implicit_inner_joins_honors_readonly_false
authors = Author.joins(:posts).readonly(false).to_a
- assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ assert_not authors.empty?, "expected authors to be non-empty"
+ assert authors.all? { |a| !a.readonly? }, "expected no authors to be readonly"
end
def test_find_with_implicit_inner_joins_does_not_set_associations
- authors = Author.joins(:posts).select('authors.*').to_a
- assert !authors.empty?, "expected authors to be non-empty"
+ authors = Author.joins(:posts).select("authors.*").to_a
+ assert_not authors.empty?, "expected authors to be non-empty"
assert authors.all? { |a| !a.instance_variable_defined?(:@posts) }, "expected no authors to have the @posts association loaded"
end
def test_count_honors_implicit_inner_joins
- real_count = Author.all.to_a.sum{|a| a.posts.count }
+ real_count = Author.all.to_a.sum { |a| a.posts.count }
assert_equal real_count, Author.joins(:posts).count, "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins
- real_count = Author.all.to_a.sum{|a| a.posts.count }
- assert_equal real_count, Author.joins(:posts).calculate(:count, 'authors.id'), "plain inner join count should match the number of referenced posts records"
+ real_count = Author.all.to_a.sum { |a| a.posts.count }
+ assert_equal real_count, Author.joins(:posts).calculate(:count, "authors.id"), "plain inner join count should match the number of referenced posts records"
end
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
- real_count = Author.all.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
- authors_with_welcoming_post_titles = Author.all.merge!(joins: :posts, where: "posts.title like 'Welcome%'").distinct.calculate(:count, 'authors.id')
+ real_count = Author.all.to_a.select { |a| a.posts.any? { |p| p.title.start_with?("Welcome") } }.length
+ authors_with_welcoming_post_titles = Author.all.merge!(joins: :posts, where: "posts.title like 'Welcome%'").distinct.calculate(:count, "authors.id")
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
end
def test_find_with_sti_join
- scope = Post.joins(:special_comments).where(:id => posts(:sti_comments).id)
+ scope = Post.joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_find_with_conditions_on_reflection
- assert !posts(:welcome).comments.empty?
- assert Post.joins(:nonexistent_comments).where(:id => posts(:welcome).id).empty? # [sic!]
+ assert_not_empty posts(:welcome).comments
+ assert Post.joins(:nonexistent_comments).where(id: posts(:welcome).id).empty? # [sic!]
end
def test_find_with_conditions_on_through_reflection
- assert !posts(:welcome).tags.empty?
- assert Post.joins(:misc_tags).where(:id => posts(:welcome).id).empty?
+ assert_not_empty posts(:welcome).tags
+ assert_empty Post.joins(:misc_tags).where(id: posts(:welcome).id)
end
test "the default scope of the target is applied when joining associations" do
@@ -120,8 +140,8 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
test "the default scope of the target is correctly aliased when joining associations" do
author = Author.create! name: "Jon"
- author.categories.create! name: 'Not Special'
- author.special_categories.create! name: 'Special'
+ author.categories.create! name: "Not Special"
+ author.special_categories.create! name: "Special"
categories = author.categories.includes(:special_categorizations).references(:special_categorizations).to_a
assert_equal 2, categories.size
@@ -129,8 +149,8 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
test "the correct records are loaded when including an aliased association" do
author = Author.create! name: "Jon"
- author.categories.create! name: 'Not Special'
- author.special_categories.create! name: 'Special'
+ author.categories.create! name: "Not Special"
+ author.special_categories.create! name: "Special"
categories = author.categories.eager_load(:special_categorizations).order(:name).to_a
assert_equal 0, categories.first.special_categorizations.size
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index c9743e80d3..da3a42e2b5 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -1,21 +1,25 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/man'
-require 'models/face'
-require 'models/interest'
-require 'models/zine'
-require 'models/club'
-require 'models/sponsor'
-require 'models/rating'
-require 'models/comment'
-require 'models/car'
-require 'models/bulb'
-require 'models/mixed_case_monkey'
-require 'models/admin'
-require 'models/admin/account'
-require 'models/admin/user'
-require 'models/developer'
-require 'models/company'
-require 'models/project'
+require "models/man"
+require "models/face"
+require "models/interest"
+require "models/zine"
+require "models/club"
+require "models/sponsor"
+require "models/rating"
+require "models/comment"
+require "models/car"
+require "models/bulb"
+require "models/mixed_case_monkey"
+require "models/admin"
+require "models/admin/account"
+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,77 +119,53 @@ 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"
+ assert_not 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"
+ assert_not 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_predicate man_reflection, :has_inverse?
end
end
class InverseAssociationTests < ActiveRecord::TestCase
def test_should_allow_for_inverse_of_options_in_associations
assert_nothing_raised do
- Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
+ Class.new(ActiveRecord::Base).has_many(:wheels, inverse_of: :car)
end
assert_nothing_raised do
- Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
+ Class.new(ActiveRecord::Base).has_one(:engine, inverse_of: :car)
end
assert_nothing_raised do
- Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
+ Class.new(ActiveRecord::Base).belongs_to(:car, inverse_of: :driver)
end
end
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?
+ assert_predicate 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?
+ assert_predicate 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?
+ assert_predicate 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?
+ assert_not_predicate 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?
+ assert_not_predicate 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
+ assert_not_predicate belongs_to_without_inverse_ref, :has_inverse?
end
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
@@ -202,10 +190,20 @@ class InverseAssociationTests < ActiveRecord::TestCase
assert_nil belongs_to_ref.inverse_of
end
+ def test_polymorphic_associations_dont_attempt_to_find_inverse_of
+ belongs_to_ref = Sponsor.reflect_on_association(:sponsor)
+ assert_raise(ArgumentError) { belongs_to_ref.klass }
+ assert_nil belongs_to_ref.inverse_of
+
+ belongs_to_ref = Face.reflect_on_association(:human)
+ assert_raise(ArgumentError) { belongs_to_ref.klass }
+ assert_nil belongs_to_ref.inverse_of
+ end
+
def test_this_inverse_stuff
- firm = Firm.create!(name: 'Adequate Holdings')
- Project.create!(name: 'Project 1', firm: firm)
- Developer.create!(name: 'Gorbypuff', firm: firm)
+ firm = Firm.create!(name: "Adequate Holdings")
+ Project.create!(name: "Project 1", firm: firm)
+ Developer.create!(name: "Gorbypuff", firm: firm)
new_project = Project.last
assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
@@ -220,73 +218,72 @@ class InverseHasOneTests < ActiveRecord::TestCase
m = men(:gordon)
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
end
-
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face).first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :face).first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :face, :order => 'faces.id').first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :face, order: "faces.id").first
f = m.face
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_built_child
m = Man.first
- f = m.build_face(:description => 'haunted')
+ f = m.build_face(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child
m = Man.first
- f = m.create_face(:description => 'haunted')
+ f = m.create_face(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method
m = Man.first
- f = m.create_face!(:description => 'haunted')
+ f = m.create_face!(description: "haunted")
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
m = Man.first
- f = Face.new(:description => 'haunted')
+ f = Face.new(description: "haunted")
m.face = f
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
- f.man.name = 'Mungo'
+ f.man.name = "Mungo"
assert_equal m.name, f.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
end
@@ -296,74 +293,95 @@ 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)
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
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
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests).first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
- m = Man.all.merge!(:where => {:name => 'Gordon'}, :includes => :interests, :order => 'interests.id').first
+ m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests, order: "interests.id").first
is = m.interests
is.each do |i|
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_newly_block_style_built_child
m = Man.first
- i = m.interests.build {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
+ i = m.interests.build { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil i.topic, "Child attributes supplied to build via blocks should be populated"
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
m = Man.first
- i = m.interests.create!(:topic => 'Industrial Revolution Re-enactment')
+ i = m.interests.create!(topic: "Industrial Revolution Re-enactment")
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_block_style_created_child
m = Man.first
- i = m.interests.create {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
+ i = m.interests.create { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil i.topic, "Child attributes supplied to create via blocks should be populated"
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
@@ -385,25 +403,25 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_parent_instance_should_be_shared_with_poked_in_child
m = men(:gordon)
- i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.create(topic: "Industrial Revolution Re-enactment")
m.interests << i
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
m = Man.first
- i = Interest.new(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.new(topic: "Industrial Revolution Re-enactment")
m.interests = [i]
assert_not_nil i.man
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
- m.name = 'Bongo'
+ m.name = "Bongo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
- i.man.name = 'Mungo'
+ i.man.name = "Mungo"
assert_equal m.name, i.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
end
@@ -444,7 +462,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert man.equal?(man.interests.first.man), "Two inverses should lead back to the same object that was originally held"
assert man.equal?(man.interests.find(interest.id).man), "Two inversions should lead back to the same object that was originally held"
- assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match before the name is changed"
+ assert_nil man.interests.find(interest.id).man.name, "The name of the man should match before the name is changed"
man.name = "Ben Bitdiddle"
assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match after the parent name is changed"
man.interests.find(interest.id).man.name = "Alyssa P. Hacker"
@@ -456,7 +474,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
interest = Interest.create!(man: man)
man.interests.find(interest.id)
- assert_not man.interests.loaded?
+ assert_not_predicate man.interests, :loaded?
end
def test_raise_record_not_found_error_when_invalid_ids_are_passed
@@ -476,7 +494,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
@@ -485,7 +506,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_child_instance_should_point_to_parent_without_saving
man = Man.new
- i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
+ i = Interest.create(topic: "Industrial Revolution Re-enactment")
man.interests << i
assert_not_nil i.man
@@ -493,7 +514,34 @@ class InverseHasManyTests < ActiveRecord::TestCase
i.man.name = "Charles"
assert_equal i.man.name, man.name
- assert !man.persisted?
+ assert_not_predicate man, :persisted?
+ end
+
+ def test_inverse_instance_should_be_set_before_find_callbacks_are_run
+ reset_callbacks(Interest, :find) do
+ Interest.after_find { raise unless association(:man).loaded? && man.present? }
+
+ assert_predicate Man.first.interests.reload, :any?
+ assert_predicate Man.includes(:interests).first.interests, :any?
+ assert_predicate Man.joins(:interests).includes(:interests).first.interests, :any?
+ end
+ end
+
+ def test_inverse_instance_should_be_set_before_initialize_callbacks_are_run
+ reset_callbacks(Interest, :initialize) do
+ Interest.after_initialize { raise unless association(:man).loaded? && man.present? }
+
+ assert_predicate Man.first.interests.reload, :any?
+ assert_predicate Man.includes(:interests).first.interests, :any?
+ assert_predicate Man.joins(:interests).includes(:interests).first.interests, :any?
+ end
+ end
+
+ def reset_callbacks(target, type)
+ old_callbacks = target.send(:get_callbacks, type).deep_dup
+ yield
+ ensure
+ target.send(:set_callbacks, type, old_callbacks) if old_callbacks
end
end
@@ -504,49 +552,49 @@ class InverseBelongsToTests < ActiveRecord::TestCase
f = faces(:trusting)
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:includes => :man, :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(includes: :man, where: { description: "trusting" }).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.all.merge!(:includes => :man, :order => 'men.id', :where => {:description => 'trusting'}).first
+ f = Face.all.merge!(includes: :man, order: "men.id", where: { description: "trusting" }).first
m = f.man
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_built_parent
f = faces(:trusting)
- m = f.build_man(:name => 'Charles')
+ m = f.build_man(name: "Charles")
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_created_parent
f = faces(:trusting)
- m = f.create_man(:name => 'Charles')
+ m = f.create_man(name: "Charles")
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
end
@@ -554,24 +602,24 @@ class InverseBelongsToTests < ActiveRecord::TestCase
i = interests(:trainspotting)
m = i.man
assert_not_nil m.interests
- iz = m.interests.detect { |_iz| _iz.id == i.id}
+ iz = m.interests.detect { |_iz| _iz.id == i.id }
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
- i.topic = 'Eating cheese with a spoon'
+ i.topic = "Eating cheese with a spoon"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
- iz.topic = 'Cow tipping'
+ iz.topic = "Cow tipping"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
f = Face.first
- m = Man.new(:name => 'Charles')
+ m = Man.new(name: "Charles")
f.man = m
assert_not_nil m.face
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
- m.face.description = 'pleasing'
+ m.face.description = "pleasing"
assert_equal f.description, m.face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
@@ -584,30 +632,30 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
fixtures :men, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:where => {:description => 'confused'}).first
+ f = Face.all.merge!(where: { description: "confused" }).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
- f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man).first
+ f = Face.all.merge!(where: { description: "confused" }, includes: :man).first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
- f = Face.all.merge!(:where => {:description => 'confused'}, :includes => :man, :order => 'men.id').first
+ f = Face.all.merge!(where: { description: "confused" }, includes: :man, order: "men.id").first
m = f.polymorphic_man
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
- f.description = 'gormless'
+ f.description = "gormless"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
- m.polymorphic_face.description = 'pleasing'
+ m.polymorphic_face.description = "pleasing"
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
@@ -619,23 +667,9 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
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_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'
+ 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'
+ 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
@@ -651,16 +685,26 @@ 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
assert_not_nil m.polymorphic_interests
- iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id}
+ iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id }
assert_not_nil iz
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
- i.topic = 'Eating cheese with a spoon'
+ i.topic = "Eating cheese with a spoon"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
- iz.topic = 'Cow tipping'
+ iz.topic = "Cow tipping"
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
@@ -698,8 +742,8 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
assert_nothing_raised do
i = Interest.first
- i.build_zine(:title => 'Get Some in Winter! 2008')
- i.build_man(:name => 'Gordon')
+ i.build_zine(title: "Get Some in Winter! 2008")
+ i.build_man(name: "Gordon")
i.save!
end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index c7bd9d2119..9d1c73c33b 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -1,38 +1,40 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/tag'
-require 'models/tagging'
-require 'models/post'
-require 'models/rating'
-require 'models/item'
-require 'models/comment'
-require 'models/author'
-require 'models/category'
-require 'models/categorization'
-require 'models/vertex'
-require 'models/edge'
-require 'models/book'
-require 'models/citation'
-require 'models/aircraft'
-require 'models/engine'
-require 'models/car'
+require "models/tag"
+require "models/tagging"
+require "models/post"
+require "models/rating"
+require "models/item"
+require "models/comment"
+require "models/author"
+require "models/category"
+require "models/categorization"
+require "models/vertex"
+require "models/edge"
+require "models/book"
+require "models/citation"
+require "models/aircraft"
+require "models/engine"
+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
def test_has_many
- assert authors(:david).categories.include?(categories(:general))
+ assert_includes authors(:david).categories, categories(:general)
end
def test_has_many_inherited
- assert authors(:mary).categories.include?(categories(:sti_test))
+ assert_includes authors(:mary).categories, categories(:sti_test)
end
def test_inherited_has_many
- assert categories(:sti_test).authors.include?(authors(:mary))
+ assert_includes categories(:sti_test).authors, authors(:mary)
end
def test_has_many_distinct_through_join_model
@@ -42,11 +44,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_distinct_through_count
author = authors(:mary)
- assert !authors(:mary).unique_categorized_posts.loaded?
+ assert_not_predicate authors(:mary).unique_categorized_posts, :loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.where(title: nil).count(:title) }
- assert !authors(:mary).unique_categorized_posts.loaded?
+ assert_not_predicate authors(:mary).unique_categorized_posts, :loaded?
end
def test_has_many_distinct_through_find
@@ -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
+ tagging = tags(:misc).taggings.create(taggable: post)
+ assert_equal "SubAbstractStiPost", tagging.taggable_type
end
def test_polymorphic_has_many_going_through_join_model_with_inheritance
@@ -116,12 +118,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
post = posts(:thinking)
assert_instance_of SpecialPost, post
- tagging = tags(:misc).taggings.create(:taggable => post)
+ tagging = tags(:misc).taggings.create(taggable: post)
assert_equal "Post", tagging.taggable_type
end
def test_polymorphic_has_one_create_model_with_inheritance
- tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
+ tagging = tags(:misc).create_tagging(taggable: posts(:thinking))
assert_equal "Post", tagging.taggable_type
end
@@ -142,7 +144,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_set_polymorphic_has_one_on_new_record
tagging = tags(:misc).taggings.create
- post = Post.new :title => "foo", :body => "bar"
+ post = Post.new title: "foo", body: "bar"
post.tagging = tagging
post.save!
@@ -153,50 +155,50 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_create_polymorphic_has_many_with_scope
old_count = posts(:welcome).taggings.count
- tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
+ tagging = posts(:welcome).taggings.create(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_bang_polymorphic_with_has_many_scope
old_count = posts(:welcome).taggings.count
- tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
+ tagging = posts(:welcome).taggings.create!(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
+ assert_equal old_count + 1, posts(:welcome).taggings.count
end
def test_create_polymorphic_has_one_with_scope
old_count = Tagging.count
- tagging = posts(:welcome).create_tagging(:tag => tags(:misc))
+ tagging = posts(:welcome).create_tagging(tag: tags(:misc))
assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, Tagging.count
+ assert_equal old_count + 1, Tagging.count
end
def test_delete_polymorphic_has_many_with_delete_all
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDeleteAll'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyDeleteAll"
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_destroy
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDestroy'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyDestroy"
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
assert_equal 0, posts(:welcome).taggings.count
end
def test_delete_polymorphic_has_many_with_nullify
assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyNullify'
+ posts(:welcome).taggings.first.update_columns taggable_type: "PostWithHasManyNullify"
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
old_count = Tagging.count
@@ -207,19 +209,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_polymorphic_has_one_with_destroy
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneDestroy'
+ posts(:welcome).tagging.update_columns taggable_type: "PostWithHasOneDestroy"
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
old_count = Tagging.count
post.destroy
- assert_equal old_count-1, Tagging.count
+ assert_equal old_count - 1, Tagging.count
posts(:welcome).association(:tagging).reload
assert_nil posts(:welcome).tagging
end
def test_delete_polymorphic_has_one_with_nullify
assert posts(:welcome).tagging
- posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneNullify'
+ posts(:welcome).tagging.update_columns taggable_type: "PostWithHasOneNullify"
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
old_count = Tagging.count
@@ -235,15 +237,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_create_through_has_many_with_piggyback
category = categories(:sti_test)
- ernie = category.authors_with_select.create(:name => 'Ernie')
+ ernie = category.authors_with_select.create(name: "Ernie")
assert_nothing_raised do
- assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ assert_equal ernie, category.authors_with_select.detect { |a| a.name == "Ernie" }
end
end
def test_include_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_authors = Post.all.merge!(includes: :authors, order: "posts.id").to_a
assert_equal posts.length, posts_with_authors.length
posts.length.times do |i|
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
@@ -267,8 +269,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -276,8 +278,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_include_polymorphic_has_many
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -302,7 +304,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_array_methods_called_by_method_missing
- assert authors(:david).categories.any? { |category| category.name == 'General' }
+ assert authors(:david).categories.any? { |category| category.name == "General" }
assert_nothing_raised { authors(:david).categories.sort }
end
@@ -324,12 +326,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_with_custom_primary_key_on_has_many_source
- assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order('authors.id')
+ assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order("authors.id")
end
def test_belongs_to_polymorphic_with_counter_cache
assert_equal 1, posts(:welcome)[:tags_count]
- tagging = posts(:welcome).taggings.create(:tag => tags(:general))
+ tagging = posts(:welcome).taggings.create(tag: tags(:general))
assert_equal 2, posts(:welcome, :reload)[:tags_count]
tagging.destroy
assert_equal 1, posts(:welcome, :reload)[:tags_count]
@@ -354,7 +356,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
assert_raise ActiveRecord::EagerLoadPolymorphicError do
- tags(:general).taggings.includes(:taggable).where('bogus_table.column = 1').references(:bogus_table).to_a
+ tags(:general).taggings.includes(:taggable).where("bogus_table.column = 1").references(:bogus_table).to_a
end
end
@@ -365,13 +367,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_polymorphic_associations_merges_through_scope
Tag.has_many :null_taggings, -> { none }, class_name: :Tagging
- Tag.has_many :null_tagged_posts, :through => :null_taggings, :source => 'taggable', :source_type => 'Post'
+ Tag.has_many :null_tagged_posts, through: :null_taggings, source: "taggable", source_type: "Post"
assert_equal [], tags(:general).null_tagged_posts
- refute_equal [], tags(:general).tagged_posts
+ assert_not_equal [], tags(:general).tagged_posts
end
def test_eager_has_many_polymorphic_with_source_type
- tag_with_include = Tag.all.merge!(:includes => :tagged_posts).find(tags(:general).id)
+ tag_with_include = Tag.all.merge!(includes: :tagged_posts).find(tags(:general).id)
desired = posts(:welcome, :thinking)
assert_no_queries do
# added sort by ID as otherwise test using JRuby was failing as array elements were in different order
@@ -381,19 +383,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_has_many_find_all
- assert_equal comments(:greetings), authors(:david).comments.order('comments.id').to_a.first
+ assert_equal comments(:greetings), authors(:david).comments.order("comments.id").to_a.first
end
def test_has_many_through_has_many_find_all_with_custom_class
- assert_equal comments(:greetings), authors(:david).funky_comments.order('comments.id').to_a.first
+ assert_equal comments(:greetings), authors(:david).funky_comments.order("comments.id").to_a.first
end
def test_has_many_through_has_many_find_first
- assert_equal comments(:greetings), authors(:david).comments.order('comments.id').first
+ assert_equal comments(:greetings), authors(:david).comments.order("comments.id").first
end
def test_has_many_through_has_many_find_conditions
- options = { :where => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
+ options = { where: "comments.#{QUOTED_TYPE}='SpecialComment'", order: "comments.id" }
assert_equal comments(:does_it_hurt), authors(:david).comments.merge(options).first
end
@@ -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
@@ -413,20 +415,20 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
author = Author.includes(:taggings).find authors(:david).id
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
+ assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
end
def test_eager_load_has_many_through_has_many
- author = Author.all.merge!(:where => ['name = ?', 'David'], :includes => :comments, :order => 'comments.id').first
+ author = Author.all.merge!(where: ["name = ?", "David"], includes: :comments, order: "comments.id").first
SpecialComment.new; VerySpecialComment.new
assert_no_queries do
- assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], author.comments.collect(&:id)
end
end
def test_eager_load_has_many_through_has_many_with_conditions
- post = Post.all.merge!(:includes => :invalid_tags).first
+ post = Post.all.merge!(includes: :invalid_tags).first
assert_no_queries do
post.invalid_tags
end
@@ -434,8 +436,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_eager_belongs_to_and_has_one_not_singularized
assert_nothing_raised do
- Author.all.merge!(:includes => :author_address).first
- AuthorAddress.all.merge!(:includes => :author).first
+ Author.all.merge!(includes: :author_address).first
+ AuthorAddress.all.merge!(includes: :author).first
end
end
@@ -445,15 +447,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_add_to_self_referential_has_many_through
- new_author = Author.create(:name => "Bob")
- authors(:david).author_favorites.create :favorite_author => new_author
+ new_author = Author.create(name: "Bob")
+ authors(:david).author_favorites.create favorite_author: new_author
assert_equal new_author, authors(:david).reload.favorite_authors.first
end
def test_has_many_through_uses_conditions_specified_on_the_has_many_association
author = Author.first
- assert author.comments.present?
- assert author.nonexistent_comments.blank?
+ assert_predicate author.comments, :present?
+ assert_predicate author.nonexistent_comments, :blank?
end
def test_has_many_through_uses_correct_attributes
@@ -462,58 +464,57 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_associating_unsaved_records_with_has_many_through
saved_post = posts(:thinking)
- new_tag = Tag.new(:name => "new")
+ new_tag = Tag.new(name: "new")
saved_post.tags << new_tag
- assert new_tag.persisted? #consistent with habtm!
- assert saved_post.persisted?
- assert saved_post.tags.include?(new_tag)
-
- assert new_tag.persisted?
- assert saved_post.reload.tags.reload.include?(new_tag)
+ assert new_tag.persisted? # consistent with habtm!
+ assert_predicate saved_post, :persisted?
+ assert_includes saved_post.tags, new_tag
+ assert_predicate new_tag, :persisted?
+ assert_includes saved_post.reload.tags.reload, new_tag
- new_post = Post.new(:title => "Association replacement works!", :body => "You best believe it.")
+ new_post = Post.new(title: "Association replacement works!", body: "You best believe it.")
saved_tag = tags(:general)
new_post.tags << saved_tag
- assert !new_post.persisted?
- assert saved_tag.persisted?
- assert new_post.tags.include?(saved_tag)
+ assert_not_predicate new_post, :persisted?
+ assert_predicate saved_tag, :persisted?
+ assert_includes new_post.tags, saved_tag
new_post.save!
- assert new_post.persisted?
- assert new_post.reload.tags.reload.include?(saved_tag)
+ assert_predicate new_post, :persisted?
+ assert_includes new_post.reload.tags.reload, saved_tag
- assert !posts(:thinking).tags.build.persisted?
- assert !posts(:thinking).tags.new.persisted?
+ assert_not_predicate posts(:thinking).tags.build, :persisted?
+ assert_not_predicate posts(:thinking).tags.new, :persisted?
end
def test_create_associate_when_adding_to_has_many_through
count = posts(:thinking).tags.count
- push = Tag.create!(:name => 'pushme')
+ push = Tag.create!(name: "pushme")
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}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ "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}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ assert_kind_of Tag, post_thinking.tags.create!(name: "foo")
+ assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
+ "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ "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}.")
- assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ 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 },
+ "Expected a Tag in tags collection, got #{wrong.class}.")
+ assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
+ "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)
@@ -528,14 +529,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
author = authors(:david)
assert_equal 10, author.comments.size
- assert !author.comments.loaded?
+ assert_not_predicate author.comments, :loaded?
end
def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
c = categories(:general)
c.categorizations_count = 100
assert_equal 100, c.categorizations.size
- assert !c.categorizations.loaded?
+ assert_not_predicate c.categorizations, :loaded?
end
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
@@ -550,7 +551,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through_with_nonstandard_id
count = books(:awdr).references.count
references_before = books(:awdr).references
- book = Book.create!(:name => 'Getting Real')
+ book = Book.create!(name: "Getting Real")
book_awdr = books(:awdr)
book_awdr.references << book
assert_equal(count + 1, book_awdr.references.reload.size)
@@ -564,7 +565,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through
count = posts(:thinking).tags.count
tags_before = posts(:thinking).tags.sort
- tag = Tag.create!(:name => 'doomed')
+ tag = Tag.create!(name: "doomed")
post_thinking = posts(:thinking)
post_thinking.tags << tag
assert_equal(count + 1, post_thinking.taggings.reload.size)
@@ -581,9 +582,9 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
count = posts(:thinking).tags.count
tags_before = posts(:thinking).tags.sort
- doomed = Tag.create!(:name => 'doomed')
- doomed2 = Tag.create!(:name => 'doomed2')
- quaked = Tag.create!(:name => 'quaked')
+ doomed = Tag.create!(name: "doomed")
+ doomed2 = Tag.create!(name: "doomed2")
+ quaked = Tag.create!(name: "quaked")
post_thinking = posts(:thinking)
post_thinking.tags << doomed << doomed2
assert_equal(count + 2, post_thinking.reload.tags.reload.size)
@@ -598,10 +599,10 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete(Object.new) }
end
- def test_deleting_by_fixnum_id_from_has_many_through
+ def test_deleting_by_integer_id_from_has_many_through
post = posts(:thinking)
- assert_difference 'post.tags.count', -1 do
+ assert_difference "post.tags.count", -1 do
assert_equal 1, post.tags.delete(1).size
end
@@ -611,8 +612,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_deleting_by_string_id_from_has_many_through
post = posts(:thinking)
- assert_difference 'post.tags.count', -1 do
- assert_equal 1, post.tags.delete('1').size
+ assert_difference "post.tags.count", -1 do
+ assert_equal 1, post.tags.delete("1").size
end
assert_equal 0, post.tags.size
@@ -642,26 +643,26 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_polymorphic_has_many
expected = taggings(:welcome_general)
- p = Post.all.merge!(:includes => :taggings).find(posts(:welcome).id)
- assert_no_queries {assert p.taggings.include?(expected)}
- assert posts(:welcome).taggings.include?(taggings(:welcome_general))
+ p = Post.all.merge!(includes: :taggings).find(posts(:welcome).id)
+ assert_no_queries { assert_includes p.taggings, expected }
+ assert_includes posts(:welcome).taggings, taggings(:welcome_general)
end
def test_polymorphic_has_one
expected = posts(:welcome)
- tagging = Tagging.all.merge!(:includes => :taggable).find(taggings(:welcome_general).id)
- assert_no_queries { assert_equal expected, tagging.taggable}
+ tagging = Tagging.all.merge!(includes: :taggable).find(taggings(:welcome_general).id)
+ assert_no_queries { assert_equal expected, tagging.taggable }
end
def test_polymorphic_belongs_to
- p = Post.all.merge!(:includes => {:taggings => :taggable}).find(posts(:welcome).id)
- assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
+ p = Post.all.merge!(includes: { taggings: :taggable }).find(posts(:welcome).id)
+ assert_no_queries { assert_equal posts(:welcome), p.taggings.first.taggable }
end
def test_preload_polymorphic_has_many_through
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_tags = Post.all.merge!(:includes => :tags, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_tags = Post.all.merge!(includes: :tags, order: "posts.id").to_a
assert_equal posts.length, posts_with_tags.length
posts.length.times do |i|
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
@@ -669,26 +670,26 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_preload_polymorph_many_types
- taggings = Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type != ?', 'FakeModel']).to_a
+ taggings = Tagging.all.merge!(includes: :taggable, where: ["taggable_type != ?", "FakeModel"]).to_a
assert_no_queries do
taggings.first.taggable.id
taggings[1].taggable.id
end
taggables = taggings.map(&:taggable)
- assert taggables.include?(items(:dvd))
- assert taggables.include?(posts(:welcome))
+ assert_includes taggables, items(:dvd)
+ assert_includes taggables, posts(:welcome)
end
def test_preload_nil_polymorphic_belongs_to
assert_nothing_raised do
- Tagging.all.merge!(:includes => :taggable, :where => ['taggable_type IS NULL']).to_a
+ Tagging.all.merge!(includes: :taggable, where: ["taggable_type IS NULL"]).to_a
end
end
def test_preload_polymorphic_has_many
- posts = Post.all.merge!(:order => 'posts.id').to_a
- posts_with_taggings = Post.all.merge!(:includes => :taggings, :order => 'posts.id').to_a
+ posts = Post.all.merge!(order: "posts.id").to_a
+ posts_with_taggings = Post.all.merge!(includes: :taggings, order: "posts.id").to_a
assert_equal posts.length, posts_with_taggings.length
posts.length.times do |i|
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
@@ -696,7 +697,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_belongs_to_shared_parent
- comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 1').to_a
+ comments = Comment.all.merge!(includes: :post, where: "post_id = 1").to_a
assert_no_queries do
assert_equal comments.first.post, comments[1].post
end
@@ -709,8 +710,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
category = david.categories.first
assert_no_queries do
- assert david.categories.loaded?
- assert david.categories.include?(category)
+ assert_predicate david.categories, :loaded?
+ assert_includes david.categories, category
end
end
@@ -719,47 +720,47 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
category = david.categories.first
david.reload
- assert ! david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
assert_queries(1) do
- assert david.categories.include?(category)
+ assert_includes david.categories, category
end
- assert ! david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
end
def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
david = authors(:david)
- category = Category.create!(:name => 'Not Associated')
+ category = Category.create!(name: "Not Associated")
- assert ! david.categories.loaded?
- assert ! david.categories.include?(category)
+ assert_not_predicate david.categories, :loaded?
+ assert_not david.categories.include?(category)
end
def test_has_many_through_goes_through_all_sti_classes
- sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
- new_comment = sub_sti_post.comments.create(:body => 'test')
+ sub_sti_post = SubStiPost.create!(title: "test", body: "test", author_id: 1)
+ new_comment = sub_sti_post.comments.create(body: "test")
assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
end
def test_has_many_with_pluralize_table_names_false
- aircraft = Aircraft.create!(:name => "Airbus 380")
- engine = Engine.create!(:car_id => aircraft.id)
+ aircraft = Aircraft.create!(name: "Airbus 380")
+ engine = Engine.create!(car_id: aircraft.id)
assert_equal aircraft.engines, [engine]
end
def test_proper_error_message_for_eager_load_and_includes_association_errors
includes_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.includes(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.includes(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_error.message)
eager_load_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.eager_load(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.eager_load(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", eager_load_error.message)
includes_and_eager_load_error = assert_raises(ActiveRecord::ConfigurationError) {
- Post.eager_load(:nonexistent_relation).includes(:nonexistent_relation).where(nonexistent_relation: {name: 'Rochester'}).find(1)
+ Post.eager_load(:nonexistent_relation).includes(:nonexistent_relation).where(nonexistent_relation: { name: "Rochester" }).find(1)
}
assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_and_eager_load_error.message)
end
@@ -770,8 +771,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
Post.find(post_id).update_columns type: class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
- klass.table_name = 'posts'
- klass.send(association, association_name, :as => :taggable, :dependent => dependency)
+ klass.table_name = "posts"
+ klass.send(association, association_name, as: :taggable, dependent: dependency)
klass.find(post_id)
end
end
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 4af791b758..0e54e8c1b0 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/post'
-require 'models/comment'
-require 'models/author'
-require 'models/essay'
-require 'models/categorization'
-require 'models/person'
+require "models/post"
+require "models/comment"
+require "models/author"
+require "models/essay"
+require "models/category"
+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
@@ -17,56 +20,65 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations
assert_nothing_raised do
queries = capture_sql do
- Person.left_outer_joins(:agents => {:agents => :agents})
- .left_outer_joins(:agents => {:agents => {:primary_contact => :agents}}).to_a
+ Person.left_outer_joins(agents: { agents: :agents })
+ .left_outer_joins(agents: { agents: { primary_contact: :agents } }).to_a
end
- assert queries.any? { |sql| /agents_people_4/i =~ sql }
+ assert queries.any? { |sql| /agents_people_4/i.match?(sql) }
end
end
- def test_construct_finder_sql_executes_a_left_outer_join
- assert_not_equal Author.count, Author.joins(:posts).count
- assert_equal Author.count, Author.left_outer_joins(:posts).count
+ def test_left_outer_joins_count_is_same_as_size_of_loaded_results
+ assert_equal 17, Post.left_outer_joins(:comments).to_a.size
+ assert_equal 17, Post.left_outer_joins(:comments).count
+ end
+
+ def test_left_joins_aliases_left_outer_joins
+ assert_equal Post.left_outer_joins(:comments).to_sql, Post.left_joins(:comments).to_sql
+ end
+
+ def test_left_outer_joins_return_has_value_for_every_comment
+ all_post_ids = Post.pluck(:id)
+ assert_equal all_post_ids, all_post_ids & Post.left_outer_joins(:comments).pluck(:id)
end
- def test_left_outer_join_by_left_joins
- assert_not_equal Author.count, Author.joins(:posts).count
- assert_equal Author.count, Author.left_joins(:posts).count
+ def test_left_outer_joins_actually_does_a_left_outer_join
+ queries = capture_sql { Author.left_outer_joins(:posts).to_a }
+ assert queries.any? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_construct_finder_sql_ignores_empty_left_outer_joins_hash
- queries = capture_sql { Author.left_outer_joins({}) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ queries = capture_sql { Author.left_outer_joins({}).to_a }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_construct_finder_sql_ignores_empty_left_outer_joins_array
- queries = capture_sql { Author.left_outer_joins([]) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ queries = capture_sql { Author.left_outer_joins([]).to_a }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_left_outer_joins_forbids_to_use_string_as_argument
- assert_raise(ArgumentError){ Author.left_outer_joins('LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"').to_a }
+ assert_raise(ArgumentError) { Author.left_outer_joins('LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"').to_a }
end
def test_join_conditions_added_to_join_clause
queries = capture_sql { Author.left_outer_joins(:essays).to_a }
- assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1)/i =~ sql }
- assert queries.none? { |sql| /WHERE/i =~ sql }
+ assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i.match?(sql) }
+ assert queries.none? { |sql| /WHERE/i.match?(sql) }
end
def test_find_with_sti_join
- scope = Post.left_outer_joins(:special_comments).where(:id => posts(:sti_comments).id)
+ scope = Post.left_outer_joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_does_not_override_select
authors = Author.select("authors.name, #{%{(authors.author_address_id || ' ' || authors.author_address_extra_id) as addr_id}}").left_outer_joins(:posts)
- assert authors.any?
- assert authors.first.respond_to?(:addr_id)
+ assert_predicate authors, :any?
+ assert_respond_to authors.first, :addr_id
end
test "the default scope of the target is applied when joining associations" do
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index b040485d99..35da74102d 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -1,30 +1,37 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/author'
-require 'models/post'
-require 'models/person'
-require 'models/reference'
-require 'models/job'
-require 'models/reader'
-require 'models/comment'
-require 'models/tag'
-require 'models/tagging'
-require 'models/subscriber'
-require 'models/book'
-require 'models/subscription'
-require 'models/rating'
-require 'models/member'
-require 'models/member_detail'
-require 'models/member_type'
-require 'models/sponsor'
-require 'models/club'
-require 'models/organization'
-require 'models/category'
-require 'models/categorization'
-require 'models/membership'
-require 'models/essay'
+require "models/author"
+require "models/post"
+require "models/person"
+require "models/reference"
+require "models/job"
+require "models/reader"
+require "models/comment"
+require "models/tag"
+require "models/tagging"
+require "models/subscriber"
+require "models/book"
+require "models/subscription"
+require "models/rating"
+require "models/member"
+require "models/member_detail"
+require "models/member_type"
+require "models/sponsor"
+require "models/club"
+require "models/organization"
+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
@@ -65,13 +72,13 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Author.where('tags.id' => tags(:general).id),
+ Author.where("tags.id" => tags(:general).id),
[authors(:david)], :tags
)
# This ensures that the polymorphism of taggings is being observed correctly
- authors = Author.joins(:tags).where('taggings.taggable_type' => 'FakeModel')
- assert authors.empty?
+ authors = Author.joins(:tags).where("taggings.taggable_type" => "FakeModel")
+ assert_empty authors
end
# has_many through
@@ -79,7 +86,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# Through: has_many through
def test_has_many_through_has_many_through_with_has_many_source_reflection
luke, david = subscribers(:first), subscribers(:second)
- assert_equal [luke, david, david], authors(:david).subscribers.order('subscribers.nick')
+ assert_equal [luke, david, david], authors(:david).subscribers.order("subscribers.nick")
end
def test_has_many_through_has_many_through_with_has_many_source_reflection_preload
@@ -93,7 +100,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_through_with_has_many_source_reflection_preload_via_joins
# All authors with subscribers where one of the subscribers' nick is 'alterself'
assert_includes_and_joins_equal(
- Author.where('subscribers.nick' => 'alterself'),
+ Author.where("subscribers.nick" => "alterself"),
[authors(:david)], :subscribers
)
end
@@ -115,7 +122,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_with_has_one_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_types.id' => member_types(:founding).id),
+ Member.where("member_types.id" => member_types(:founding).id),
[members(:groucho)], :nested_member_types
)
end
@@ -130,14 +137,14 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_through_with_has_one_source_reflection_preload
members = assert_queries(4) { Member.includes(:nested_sponsors).to_a }
mustache = sponsors(:moustache_club_sponsor_for_groucho)
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [mustache], members.first.nested_sponsors
end
end
def test_has_many_through_has_one_through_with_has_one_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('sponsors.id' => sponsors(:moustache_club_sponsor_for_groucho).id),
+ Member.where("sponsors.id" => sponsors(:moustache_club_sponsor_for_groucho).id),
[members(:groucho)], :nested_sponsors
)
end
@@ -149,7 +156,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy)
assert_equal [groucho_details, other_details],
- members(:groucho).organization_member_details.order('member_details.id')
+ members(:groucho).organization_member_details.order("member_details.id")
end
def test_has_many_through_has_one_with_has_many_through_source_reflection_preload
@@ -164,13 +171,13 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_one_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'),
+ Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"),
[members(:groucho), members(:some_other_guy)], :organization_member_details
)
members = Member.joins(:organization_member_details).
- where('member_details.id' => 9)
- assert members.empty?
+ where("member_details.id" => 9)
+ assert_empty members
end
# has_many through
@@ -180,7 +187,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho_details, other_details = member_details(:groucho), member_details(:some_other_guy)
assert_equal [groucho_details, other_details],
- members(:groucho).organization_member_details_2.order('member_details.id')
+ members(:groucho).organization_member_details_2.order("member_details.id")
end
def test_has_many_through_has_one_through_with_has_many_source_reflection_preload
@@ -189,20 +196,20 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# postgresql test if randomly executed then executes "SHOW max_identifier_length". Hence
# the need to ignore certain predefined sqls that deal with system calls.
- assert_no_queries(ignore_none: false) do
+ assert_no_queries do
assert_equal [groucho_details, other_details], members.first.organization_member_details_2.sort_by(&:id)
end
end
def test_has_many_through_has_one_through_with_has_many_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_details.id' => member_details(:groucho).id).order('member_details.id'),
+ Member.where("member_details.id" => member_details(:groucho).id).order("member_details.id"),
[members(:groucho), members(:some_other_guy)], :organization_member_details_2
)
members = Member.joins(:organization_member_details_2).
- where('member_details.id' => 9)
- assert members.empty?
+ where("member_details.id" => 9)
+ assert_empty members
end
# has_many through
@@ -211,7 +218,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection
general, cooking = categories(:general), categories(:cooking)
- assert_equal [general, cooking], authors(:bob).post_categories.order('categories.id')
+ assert_equal [general, cooking], authors(:bob).post_categories.order("categories.id")
end
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload
@@ -228,7 +235,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Author.joins(:post_categories).first
assert_includes_and_joins_equal(
- Author.where('categories.id' => categories(:cooking).id),
+ Author.where("categories.id" => categories(:cooking).id),
[authors(:bob)], :post_categories
)
end
@@ -239,7 +246,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection
greetings, more = comments(:greetings), comments(:more_greetings)
- assert_equal [greetings, more], categories(:technology).post_comments.order('comments.id')
+ assert_equal [greetings, more], categories(:technology).post_comments.order("comments.id")
end
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload
@@ -257,7 +264,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Category.joins(:post_comments).first
assert_includes_and_joins_equal(
- Category.where('comments.id' => comments(:more_greetings).id).order('categories.id'),
+ Category.where("comments.id" => comments(:more_greetings).id).order("categories.id"),
[categories(:general), categories(:technology)], :post_comments
)
end
@@ -268,7 +275,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection
greetings, more = comments(:greetings), comments(:more_greetings)
- assert_equal [greetings, more], authors(:bob).category_post_comments.order('comments.id')
+ assert_equal [greetings, more], authors(:bob).category_post_comments.order("comments.id")
end
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload
@@ -285,7 +292,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
Author.joins(:category_post_comments).first
assert_includes_and_joins_equal(
- Author.where('comments.id' => comments(:does_it_hurt).id).order('authors.id'),
+ Author.where("comments.id" => comments(:does_it_hurt).id).order("authors.id"),
[authors(:david), authors(:mary)], :category_post_comments
)
end
@@ -308,7 +315,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_has_many_through_with_belongs_to_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Author.where('tags.id' => tags(:general).id),
+ Author.where("tags.id" => tags(:general).id),
[authors(:david)], :tagging_tags
)
end
@@ -320,7 +327,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
welcome_general, thinking_general = taggings(:welcome_general), taggings(:thinking_general)
assert_equal [welcome_general, thinking_general],
- categorizations(:david_welcome_general).post_taggings.order('taggings.id')
+ categorizations(:david_welcome_general).post_taggings.order("taggings.id")
end
def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload
@@ -334,7 +341,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_belongs_to_with_has_many_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Categorization.where('taggings.id' => taggings(:welcome_general).id).order('taggings.id'),
+ Categorization.where("taggings.id" => taggings(:welcome_general).id).order("taggings.id"),
[categorizations(:david_welcome_general)], :post_taggings
)
end
@@ -357,7 +364,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_has_one_with_has_one_through_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('member_types.id' => member_types(:founding).id),
+ Member.where("member_types.id" => member_types(:founding).id),
[members(:groucho)], :nested_member_type
)
end
@@ -391,7 +398,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload_via_joins
assert_includes_and_joins_equal(
- Member.where('categories.id' => categories(:technology).id),
+ Member.where("categories.id" => categories(:technology).id),
[members(:blarpy_winkup)], :club_category
)
end
@@ -404,7 +411,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_distinct_has_many_through_a_has_many_through_association_on_through_reflection
author = authors(:david)
assert_equal [subscribers(:first), subscribers(:second)],
- author.distinct_subscribers.order('subscribers.nick')
+ author.distinct_subscribers.order("subscribers.nick")
end
def test_nested_has_many_through_with_a_table_referenced_multiple_times
@@ -413,26 +420,31 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
author.similar_posts.sort_by(&:id)
# Mary and Bob both have posts in misc, but they are the only ones.
- authors = Author.joins(:similar_posts).where('posts.id' => posts(:misc_by_bob).id)
+ authors = Author.joins(:similar_posts).where("posts.id" => posts(:misc_by_bob).id)
assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id)
# Check the polymorphism of taggings is being observed correctly (in both joins)
- authors = Author.joins(:similar_posts).where('taggings.taggable_type' => 'FakeModel')
- assert authors.empty?
- authors = Author.joins(:similar_posts).where('taggings_authors_join.taggable_type' => 'FakeModel')
- assert authors.empty?
+ authors = Author.joins(:similar_posts).where("taggings.taggable_type" => "FakeModel")
+ assert_empty authors
+ authors = Author.joins(:similar_posts).where("taggings_authors_join.taggable_type" => "FakeModel")
+ assert_empty authors
+ 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 [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id")
assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors
- references = Reference.joins(:agents_posts_authors).where('authors.id' => authors(:david).id)
+ references = Reference.joins(:agents_posts_authors).where("authors.id" => authors(:david).id)
assert_equal [references(:david_unicyclist)], references
end
def test_has_many_through_with_foreign_key_option_on_source_reflection
- assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order('people.id')
+ assert_equal [people(:michael), people(:susan)], jobs(:unicyclist).agents.order("people.id")
jobs = Job.joins(:agents)
assert_equal [jobs(:unicyclist), jobs(:unicyclist)], jobs
@@ -443,19 +455,19 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [ratings(:special_comment_rating), ratings(:sub_special_comment_rating)], ratings
# Ensure STI is respected in the join
- scope = Post.joins(:special_comments_ratings).where(:id => posts(:sti_comments).id)
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ scope = Post.joins(:special_comments_ratings).where(id: posts(:sti_comments).id)
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_has_many_through_with_sti_on_nested_through_reflection
taggings = posts(:sti_comments).special_comments_ratings_taggings
assert_equal [taggings(:special_comment_rating)], taggings
- scope = Post.joins(:special_comments_ratings_taggings).where(:id => posts(:sti_comments).id)
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
+ scope = Post.joins(:special_comments_ratings_taggings).where(id: posts(:sti_comments).id)
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
end
def test_nested_has_many_through_writers_should_raise_error
@@ -505,7 +517,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_nested_has_many_through_with_conditions_on_through_associations_preload
- assert Author.where('tags.id' => 100).joins(:misc_post_first_blue_tags).empty?
+ assert_empty Author.where("tags.id" => 100).joins(:misc_post_first_blue_tags)
authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a.sort_by(&:id) }
blue = tags(:blue)
@@ -518,7 +530,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_through_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id').references(:tags),
+ Author.where("tags.id = tags.id").references(:tags),
[authors(:bob)], :misc_post_first_blue_tags
)
end
@@ -536,10 +548,19 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_through_association_preload_doesnt_reset_source_association_if_already_preloaded
+ blue = tags(:blue)
+ authors = Author.preload(posts: :first_blue_tags_2, misc_post_first_blue_tags_2: {}).to_a.sort_by(&:id)
+
+ assert_no_queries do
+ assert_equal [blue], authors[2].posts.first.first_blue_tags_2
+ end
+ end
+
def test_nested_has_many_through_with_conditions_on_source_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id').references(:tags),
+ Author.where("tags.id = tags.id").references(:tags),
[authors(:bob)], :misc_post_first_blue_tags_2
)
end
@@ -548,13 +569,13 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [categories(:general)], organizations(:nsa).author_essay_categories
organizations = Organization.joins(:author_essay_categories).
- where('categories.id' => categories(:general).id)
+ where("categories.id" => categories(:general).id)
assert_equal [organizations(:nsa)], organizations
assert_equal categories(:general), organizations(:nsa).author_owned_essay_category
organizations = Organization.joins(:author_owned_essay_category).
- where('categories.id' => categories(:general).id)
+ where("categories.id" => categories(:general).id)
assert_equal [organizations(:nsa)], organizations
end
@@ -562,9 +583,46 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
c = Categorization.new
c.author = authors(:david)
c.post_taggings.to_a
- assert !c.post_taggings.empty?
+ assert_not_empty c.post_taggings
c.save
- assert !c.post_taggings.empty?
+ assert_not_empty c.post_taggings
+ 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
+
+ def test_has_many_through_reset_source_reflection_after_loading_is_complete
+ preloaded = Category.preload(:ordered_post_comments).find(1, 2).last
+ original = Category.find(2)
+ assert_equal original.ordered_post_comments.ids, preloaded.ordered_post_comments.ids
end
private
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 3e5494e897..c7a78e6bc4 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
@@ -18,11 +20,14 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
end
teardown do
- @connection.drop_table 'parents', if_exists: true
- @connection.drop_table 'children', if_exists: true
+ @connection.drop_table "parents", if_exists: true
+ @connection.drop_table "children", if_exists: true
end
- test "belongs_to associations are not required by default" do
+ test "belongs_to associations can be optional by default" do
+ original_value = ActiveRecord::Base.belongs_to_required_by_default
+ ActiveRecord::Base.belongs_to_required_by_default = false
+
model = subclass_of(Child) do
belongs_to :parent, inverse_of: false,
class_name: "RequiredAssociationsTest::Parent"
@@ -30,6 +35,8 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
assert model.new.save
assert model.new(parent: Parent.new).save
+ ensure
+ ActiveRecord::Base.belongs_to_required_by_default = original_value
end
test "required belongs_to associations have presence validated" do
@@ -46,6 +53,25 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
assert record.save
end
+ test "belongs_to associations can be required by default" do
+ original_value = ActiveRecord::Base.belongs_to_required_by_default
+ ActiveRecord::Base.belongs_to_required_by_default = true
+
+ model = subclass_of(Child) do
+ belongs_to :parent, inverse_of: false,
+ class_name: "RequiredAssociationsTest::Parent"
+ end
+
+ record = model.new
+ assert_not record.save
+ assert_equal ["Parent must exist"], record.errors.full_messages
+
+ record.parent = Parent.new
+ assert record.save
+ ensure
+ ActiveRecord::Base.belongs_to_required_by_default = original_value
+ end
+
test "has_one associations are not required by default" do
model = subclass_of(Parent) do
has_one :child, inverse_of: false,
@@ -92,11 +118,11 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
private
- def subclass_of(klass, &block)
- subclass = Class.new(klass, &block)
- def subclass.name
- superclass.name
+ def subclass_of(klass, &block)
+ subclass = Class.new(klass, &block)
+ def subclass.name
+ superclass.name
+ end
+ subclass
end
- subclass
- end
end