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.rb378
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb43
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb61
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb88
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb42
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb64
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb42
-rw-r--r--activerecord/test/cases/associations/eager_test.rb752
-rw-r--r--activerecord/test/cases/associations/extension_test.rb35
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb445
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb988
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb658
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb262
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb183
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb76
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb363
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb311
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb90
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb167
-rw-r--r--activerecord/test/cases/associations/required_test.rb60
21 files changed, 3291 insertions, 1833 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 47fd7345c8..0f7a249bf3 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,24 +1,30 @@
-require 'cases/helper'
-require 'models/developer'
-require 'models/computer'
-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'
+# 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,
@@ -31,21 +37,25 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal companies(:first_firm).name, firm.name
end
+ def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute
+ assert_raises(ActiveModel::MissingAttributeError) { Client.select(:id).first.firm }
+ end
+
def test_belongs_to_does_not_use_order_by
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'
+ assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query"
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
def test_belongs_to_with_primary_key_joins_on_correct_column
sql = Client.joins(:firm_with_primary_key).to_sql
- if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+ if current_adapter?(:Mysql2Adapter)
assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql)
assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql)
elsif current_adapter?(:OracleAdapter)
@@ -85,8 +95,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- refute account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_not account.valid?
+ assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -102,31 +112,69 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- refute account.valid?
- assert_equal [{error: :blank}], account.errors.details[:company]
+ assert_not 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, :class => comments
+ has_many :comments, anonymous_class: comments
}
- belongs_to :post, :class => posts, :inverse_of => false
+ belongs_to :post, anonymous_class: posts, inverse_of: false
}
assert_equal 0, counter
@@ -147,6 +195,32 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
end
+ def test_raises_type_mismatch_with_namespaced_class
+ assert_nil defined?(Region), "This test requires that there is no top-level Region class"
+
+ ActiveRecord::Base.connection.instance_eval do
+ create_table(:admin_regions) { |t| t.string :name }
+ add_column :admin_users, :region_id, :integer
+ end
+ Admin.const_set "RegionalUser", Class.new(Admin::User) { belongs_to(:region) }
+ Admin.const_set "Region", Class.new(ActiveRecord::Base)
+
+ e = assert_raise(ActiveRecord::AssociationTypeMismatch) {
+ Admin::RegionalUser.new(region: "wrong value")
+ }
+ 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")
+
+ ActiveRecord::Base.connection.instance_eval do
+ 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
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
@@ -171,14 +245,14 @@ 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
+ citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key).first
assert 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
+ 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?
end
@@ -192,7 +266,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
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
@@ -215,36 +289,36 @@ 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?
client.save
@@ -253,25 +327,37 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
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?
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_natural_assignment_to_nil
client = Client.find(3)
client.firm = nil
client.save
- assert_nil client.firm(true)
+ client.association(:firm).reload
+ assert_nil client.firm
assert_nil client.client_of
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
- assert_nil client.firm_with_primary_key(true)
+ client.association(:firm_with_primary_key).reload
+ assert_nil client.firm_with_primary_key
assert_nil client.client_of
end
@@ -288,18 +374,21 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_association_class
sponsor = Sponsor.new
assert_nil sponsor.association(:sponsorable).send(:klass)
+ 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
@@ -308,7 +397,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
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, Company.all.merge!(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")
+
+ 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.save
+ end
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed unless counter_cache is given on the relation" do
+ treasure = ship.treasures.first
+ treasure.destroy
+ end
end
def test_belongs_to_counter
@@ -407,8 +512,8 @@ 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")
+ topic.replies.create!(title: "re: monday night", content: "football")
assert_equal 1, Topic.find(topic.id)[:replies_count]
topic.save!
@@ -422,13 +527,33 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { line_item.touch }
end
+ def test_belongs_to_with_touch_on_multiple_records
+ line_item = LineItem.create!(amount: 1)
+ line_item2 = LineItem.create!(amount: 2)
+ Invoice.create!(line_items: [line_item, line_item2])
+
+ assert_queries(1) do
+ LineItem.transaction do
+ line_item.touch
+ line_item2.touch
+ end
+ end
+
+ assert_queries(2) do
+ line_item.touch
+ line_item2.touch
+ end
+ end
+
def test_belongs_to_with_touch_option_on_touch_without_updated_at_attributes
assert_not LineItem.column_names.include?("updated_at")
line_item = LineItem.create!
invoice = Invoice.create!(line_items: [line_item])
initial = invoice.updated_at
- line_item.touch
+ travel(1.second) do
+ line_item.touch
+ end
assert_not_equal initial, invoice.reload.updated_at
end
@@ -490,8 +615,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")
@@ -507,7 +632,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert final_cut.persisted?
assert firm.persisted?
assert_equal firm, final_cut.firm
- assert_equal firm, final_cut.firm(true)
+ final_cut.association(:firm).reload
+ assert_equal firm, final_cut.firm
end
def test_assignment_before_child_saved_with_primary_key
@@ -519,13 +645,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert final_cut.persisted?
assert firm.persisted?
assert_equal firm, final_cut.firm_with_primary_key
- assert_equal firm, final_cut.firm_with_primary_key(true)
+ final_cut.association(:firm_with_primary_key).reload
+ assert_equal firm, final_cut.firm_with_primary_key
end
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
@@ -550,16 +676,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_queries(0) { 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
computer = Computer.find(1)
assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
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
assert_equal 1, topic.reload[:replies_count]
@@ -570,10 +702,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]
@@ -590,10 +722,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]
@@ -611,10 +743,10 @@ 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
assert_equal 1, reply.reload[:replies_count]
@@ -624,10 +756,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
@@ -650,7 +793,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert companies(:first_client).readonly_firm.readonly?
end
- def test_test_polymorphic_assignment_foreign_key_type_string
+ def test_polymorphic_assignment_foreign_key_type_string
comment = Comment.first
comment.author = Author.first
comment.resource = Member.first
@@ -680,7 +823,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
@@ -705,7 +848,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
@@ -717,7 +860,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
@@ -733,7 +876,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
@@ -755,14 +898,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
@@ -783,18 +926,18 @@ 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'
+ 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
@@ -843,7 +986,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert !proxy.stale_target?
assert_equal members(:groucho), sponsor.sponsorable
- sponsor.sponsorable_type = 'Firm'
+ sponsor.sponsorable_type = "Firm"
assert proxy.stale_target?
assert_equal companies(:first_firm), sponsor.sponsorable
@@ -868,7 +1011,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
comment = comments(:greetings)
assert_difference lambda { post.reload.tags_count }, -1 do
- assert_difference 'comment.reload.tags_count', +1 do
+ assert_difference "comment.reload.tags_count", +1 do
tagging.taggable = comment
end
end
@@ -915,46 +1058,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
@@ -969,9 +1112,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(Comment) do
+ def self.name; "Temp"; end
+ validates :post, presence: true
+ end
+
+ comment = model.new
+ comment.post_id = 9223372036854775808 # out of range in the bigint
+
+ assert_nil comment.post
+ assert_not comment.valid?
+ assert_equal [{ error: :blank }], comment.errors.details[:post]
+ 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
@@ -990,7 +1147,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
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
@@ -999,8 +1156,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
@@ -1009,11 +1166,22 @@ 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_multiple_counter_cache_with_after_create_update
+ post = posts(:welcome)
+ parent = comments(:greetings)
+
+ assert_difference "parent.reload.children_count", +1 do
+ assert_difference "post.reload.comments_count", +1 do
+ CommentWithAfterCreateUpdate.create(body: "foo", post: post, parent: parent)
+ end
+ end
+ end
end
class BelongsToWithForeignKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
new file mode 100644
index 0000000000..88221b012e
--- /dev/null
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/content"
+
+class BidirectionalDestroyDependenciesTest < ActiveRecord::TestCase
+ fixtures :content, :content_positions
+
+ def setup
+ Content.destroyed_ids.clear
+ ContentPosition.destroyed_ids.clear
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_belongs_to_association
+ content_position = ContentPosition.find(1)
+ content = content_position.content
+ assert_not_nil content
+
+ content_position.destroy
+
+ assert_equal [content_position.id], ContentPosition.destroyed_ids
+ assert_equal [content.id], Content.destroyed_ids
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_has_one_association
+ content = Content.find(1)
+ content_position = content.content_position
+ assert_not_nil content_position
+
+ content.destroy
+
+ assert_equal [content.id], Content.destroyed_ids
+ assert_equal [content_position.id], ContentPosition.destroyed_ids
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_has_one_association_fails_first_time
+ content = ContentWhichRequiresTwoDestroyCalls.find(1)
+
+ 2.times { content.destroy }
+
+ assert_equal content.destroyed?, true
+ end
+end
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index a531e0e02c..e096cd4a0b 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -1,13 +1,15 @@
+# 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)
@@ -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
@@ -108,14 +110,14 @@ 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
@@ -126,18 +128,17 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
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')
+ 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)
@@ -160,14 +161,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
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?
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.unchangable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
begin
- @david.unchangable_posts << @authorless
+ @david.unchangeable_posts << @authorless
rescue Exception
end
assert @david.post_log.empty?
- assert !@david.unchangable_posts.include?(@authorless)
+ assert_not_includes @david.unchangeable_posts, @authorless
@david.reload
- assert !@david.unchangable_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..e717621928 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.all.merge!(includes: { posts: :comments }, 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_one_level
- 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 }
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 }).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,11 +117,11 @@ 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 = SillyReply.new(title: "gaga", content: "boo-boo", parent_id: 1)
silly.parent_id = 1
assert silly.save
- topics = Topic.all.merge!(:includes => :replies, :order => ['topics.id', 'replies_topics.id']).to_a
+ topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
@@ -133,14 +129,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
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 +145,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,7 +154,7 @@ 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
@@ -166,12 +162,12 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
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 +179,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..8754889143 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -1,36 +1,44 @@
-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"
end
end
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
-
def setup
- generate_test_objects
+ post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
+ @tagging = Tagging.create(taggable: post)
+ @old = ActiveRecord::Base.store_full_sti_class
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
end
- def test_class_names
- old = ActiveRecord::Base.store_full_sti_class
+ def test_class_names_with_includes
+ ActiveRecord::Base.store_full_sti_class = false
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
+ assert_nil post.tagging
+
+ ActiveRecord::Base.store_full_sti_class = true
+ post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
+ end
+ def test_class_names_with_eager_load
ActiveRecord::Base.store_full_sti_class = false
- post = Namespaced::Post.includes(:tagging).find_by_title('Great stuff')
+ 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
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
end
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index f571198079..c5b2b77bd4 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,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,19 +78,19 @@ 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
res.each do |se|
@@ -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 7d8b933992..9830917bc3 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,29 +1,33 @@
+# 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/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"
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :essays, :author_addresses, :categories, :categories_posts,
@@ -32,42 +36,53 @@ class EagerAssociationTest < ActiveRecord::TestCase
: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_with_scope_including_joins
+ assert_equal clubs(:boring_club), Member.preload(:general_club).find(1).general_club
+ assert_equal clubs(:boring_club), Member.eager_load(:general_club).find(1).general_club
+ end
+
def test_with_ordering
- list = Post.all.merge!(:includes => :comments, :order => "posts.id DESC").to_a
+ 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|
@@ -88,115 +103,123 @@ 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
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: 5) do
+ 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
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ 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
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: 5) do
+ 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
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: nil) do
+ posts = Post.all.merge!(includes: :categories).to_a
+ assert_equal 11, posts.size
+ end
end
def test_load_associated_records_in_one_query_when_adapter_has_no_limit
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ 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
+ end
end
end
def test_load_associated_records_in_several_queries_when_many_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(1)
-
- post1, post2 = posts(:welcome), posts(:thinking)
- assert_queries(3) do
- Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ 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
+ end
end
end
def test_load_associated_records_in_one_query_when_a_few_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(3)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ 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
+ 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!
@@ -207,7 +230,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
@@ -218,7 +241,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
@@ -228,53 +251,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 !Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
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
@@ -282,117 +302,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
@@ -400,61 +420,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
@@ -465,32 +485,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 }
@@ -498,35 +518,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
@@ -534,7 +562,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
@@ -545,67 +573,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
@@ -615,13 +643,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
@@ -681,32 +709,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
@@ -715,8 +743,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
@@ -729,22 +757,61 @@ 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!")
+
+ 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.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")
+
+ 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)
+ Tagging.create!(taggable: post2, tag: tag2)
+ Tagging.create!(taggable: post1, tag: tag2)
+
+ tags_with_includes = OrderedTag.where(id: [tag1, tag2].map(&:id)).includes(:tagged_posts).order(:id).to_a
+ tag1_with_includes = tags_with_includes.first
+ tag2_with_includes = tags_with_includes.last
+
+ assert_equal([post2, post1].map(&:title), tag1_with_includes.tagged_posts.map(&:title))
+ assert_equal([post1, post2].map(&:title), tag2_with_includes.tagged_posts.map(&:title))
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)
@@ -752,7 +819,24 @@ 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)
+ end
+ end
+
+ def test_eager_with_default_scope_as_class_method_using_find_method
+ david = developers(:david)
+ developer = EagerDeveloperWithClassMethodDefaultScope.find(david.id)
+ projects = Project.order(:id).to_a
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
+ def test_eager_with_default_scope_as_class_method_using_find_by_method
+ developer = EagerDeveloperWithClassMethodDefaultScope.find_by(name: "David")
projects = Project.order(:id).to_a
assert_no_queries do
assert_equal(projects, developer.projects)
@@ -760,7 +844,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)
@@ -769,8 +853,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)
@@ -778,30 +862,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
@@ -810,15 +890,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
@@ -827,25 +907,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
@@ -874,7 +954,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
@@ -884,28 +968,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
@@ -947,13 +1037,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
@@ -961,7 +1051,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
@@ -983,61 +1073,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
+ 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!(:select => 'distinct posts.*', :includes => :author, :joins => [:comments], :where => "comments.body like 'Thank you%'", :order => 'posts.id').to_a
- end
- assert_equal [posts(:welcome)], posts
- assert_equal authors(:david), assert_no_queries { posts[0].author}
-
- posts = assert_queries(2) do
- Post.all.merge!(:includes => :author, :joins => {:taggings => :tag}, :where => "tags.name = 'General'", :order => 'posts.id').to_a
+ 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
@@ -1048,39 +1132,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
@@ -1104,9 +1188,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
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)
+ 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)
+ firm = Firm.all.merge!(includes: :clients_using_primary_key, order: "clients_using_primary_keys_companies.name").find(1)
end
assert_no_queries do
assert_equal expected, firm.clients_using_primary_key
@@ -1115,7 +1199,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
@@ -1123,51 +1207,69 @@ 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 }
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 }
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_uniq
- mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ def test_preloading_has_many_through_with_distinct
+ 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
+ def test_preloading_has_one_using_reorder
+ klass = Class.new(ActiveRecord::Base) do
+ def self.name; "TempAuthor"; end
+ self.table_name = "authors"
+ has_one :post, class_name: "PostWithDefaultScope", foreign_key: :author_id
+ has_one :reorderd_post, -> { reorder(title: :desc) }, class_name: "PostWithDefaultScope", foreign_key: :author_id
+ end
+
+ author = klass.first
+ # PRECONDITION: make sure ordering results in different results
+ assert_not_equal author.post, author.reorderd_post
+
+ preloaded_reorderd_post = klass.preload(:reorderd_post).first.reorderd_post
+
+ assert_equal author.reorderd_post, preloaded_reorderd_post
+ assert_equal Post.order(title: :desc).first.title, preloaded_reorderd_post.title
+ end
+
def test_preloading_polymorphic_with_custom_foreign_type
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
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_not_equal 0, post.comments.to_a.count
@@ -1175,42 +1277,51 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_join_eager_with_empty_order_should_generate_valid_sql
- assert_nothing_raised(ActiveRecord::StatementInvalid) do
- Post.includes(:comments).order("").where(:comments => {:body => "Thank you for the welcome"}).first
- end
- end
-
- def test_join_eager_with_nil_order_should_generate_valid_sql
- assert_nothing_raised(ActiveRecord::StatementInvalid) do
- Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
+ assert_nothing_raised do
+ 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 }
end
+ def test_eager_load_multiple_associations_with_references
+ mentor = Mentor.create!(name: "Barış Can DAYLIK")
+ developer = Developer.create!(name: "Mehmet Emin İNAÇ", mentor: mentor)
+ Contract.create!(developer: developer)
+ project = Project.create!(name: "VNGRS", mentor: mentor)
+ project.developers << developer
+ projects = Project.references(:mentors).includes(mentor: { developers: :contracts }, developers: :contracts)
+ 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
@@ -1235,10 +1346,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
@@ -1264,6 +1375,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
@@ -1278,7 +1391,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 {
@@ -1308,6 +1421,26 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_match message, error.message
end
+ test "preload with invalid argument" do
+ exception = assert_raises(ArgumentError) do
+ Author.preload(10).to_a
+ end
+ assert_equal("10 was not recognized for preload", exception.message)
+ end
+
+ test "associations with extensions are not instance dependent" do
+ assert_nothing_raised do
+ Author.includes(:posts_with_extension).to_a
+ end
+ end
+
+ test "including associations with extensions and an instance dependent scope is not supported" do
+ e = assert_raises(ArgumentError) do
+ Author.includes(:posts_with_extension_and_instance).to_a
+ end
+ assert_match(/Preloading instance dependent scopes is not supported/, e.message)
+ end
+
test "preloading readonly association" do
# has-one
firm = Firm.where(id: "1").preload(:readonly_account).first!
@@ -1322,6 +1455,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert 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 firm.account.readonly?
+
+ # has_and_belongs_to_many
+ project = Project.where(id: "2").eager_load(:developers).first!
+ assert_not project.developers.first.readonly?
+
+ # has_many :through
+ david = Author.where(id: "1").eager_load(:comments).first!
+ assert_not david.comments.first.readonly?
+
+ # belongs_to
+ post = Post.where(id: "1").eager_load(:author).first!
+ assert_not post.author.readonly?
+ end
+
test "eager-loading readonly association" do
# has-one
firm = Firm.where(id: "1").eager_load(:readonly_account).first!
@@ -1336,17 +1487,32 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert 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 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 and using the existential predicate" do
+ assert_equal true, authors(:david).essays.eager_load(:writer).exists?
+ end
+
+ # CollectionProxy#reader is expensive, so the preloader avoids calling it.
+ test "preloading has_many_through association avoids calling association.reader" do
+ ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never
+ Author.preload(:readonly_comments).first!
+ end
+
+ private
+ def find_all_ordered(klass, include = nil)
+ klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
+ end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index b161cde335..5eacb5a3d8 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,19 +70,25 @@ 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
def extend!(model)
- ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { }
+ ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) {}
end
end
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 aea9207bfe..c817d7267b 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -1,101 +1,140 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require 'models/developer'
-require 'models/computer'
-require 'models/project'
-require 'models/company'
-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/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/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
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
+class DeveloperWithExtendOption < Developer
+ module NamedExtension
+ def category
+ "sns"
+ end
+ end
+
+ has_and_belongs_to_many :projects, extend: NamedExtension
+end
+
+class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base
+ 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
@@ -109,22 +148,35 @@ 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.save!
+
+ treaty = Treaty.new(name: "peace")
+ treaty.treaty_id = "t1"
+ warning = capture(:stderr) do
+ country.treaties << treaty
+ end
+ 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)
@@ -134,7 +186,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
active_record = Project.find(1)
assert !active_record.developers.empty?
assert_equal 3, active_record.developers.size
- assert active_record.developers.include?(david)
+ assert_includes active_record.developers, david
end
def test_adding_single
@@ -147,8 +199,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
jamis.projects << action_controller
assert_equal 2, jamis.projects.size
- assert_equal 2, jamis.projects(true).size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, jamis.projects.reload.size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_type_mismatch
@@ -166,9 +218,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
action_controller.developers << jamis
- assert_equal 2, jamis.projects(true).size
+ assert_equal 2, jamis.projects.reload.size
assert_equal 2, action_controller.developers.size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_from_the_project_fixed_timestamp
@@ -182,9 +234,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
action_controller.developers << jamis
assert_equal updated_at, jamis.updated_at
- assert_equal 2, jamis.projects(true).size
+ assert_equal 2, jamis.projects.reload.size
assert_equal 2, action_controller.developers.size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_multiple
@@ -193,7 +245,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
aredridel.projects.reload
aredridel.projects.push(Project.find(1), Project.find(2))
assert_equal 2, aredridel.projects.size
- assert_equal 2, aredridel.projects(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
def test_adding_a_collection
@@ -202,7 +254,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
aredridel.projects.reload
aredridel.projects.concat([Project.find(1), Project.find(2)])
assert_equal 2, aredridel.projects.size
- assert_equal 2, aredridel.projects(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
def test_habtm_adding_before_save
@@ -214,16 +266,16 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert !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_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(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
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).collect { |i| Developer.create(name: "JME #{i}") }.reverse
new_project.developer_ids = [developers[0].id, developers[1].id]
new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
@@ -234,7 +286,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers, new_project.developers
end
- def test_habtm_unique_order_preserved
+ def test_habtm_distinct_order_preserved
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
end
@@ -248,11 +300,10 @@ 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
@@ -288,9 +339,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
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?
devel.save
@@ -312,49 +363,36 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
- def test_create_by_new_record
- devel = Developer.new(:name => "Marcel", :salary => 75000)
- devel.projects.build(:name => "Make bed")
- proj2 = devel.projects.build(:name => "Lie in it")
- assert_equal devel.projects.last, proj2
- assert !proj2.persisted?
- devel.save
- assert devel.persisted?
- assert proj2.persisted?
- assert_equal devel.projects.last, proj2
- assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
- end
-
def test_creation_respects_hash_condition
# in Oracle '' is saved as null therefore need to save ' ' in not null column
- post = categories(:general).post_with_conditions.build(:body => ' ')
+ post = categories(:general).post_with_conditions.build(body: " ")
assert post.save
- assert_equal 'Yet Another Testing Title', post.title
+ 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_equal "Yet Another Testing Title", another_post.title
end
- def test_uniq_after_the_fact
+ def test_distinct_after_the_fact
dev = developers(:jamis)
dev.projects << projects(:active_record)
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_uniq_before_the_fact
+ def test_distinct_before_the_fact
projects(:active_record).developers << developers(:jamis)
projects(:active_record).developers << developers(:david)
assert_equal 3, projects(:active_record, :reload).developers.size
end
- def test_uniq_option_prevents_duplicate_push
+ def test_distinct_option_prevents_duplicate_push
project = projects(:active_record)
project.developers << developers(:jamis)
project.developers << developers(:david)
@@ -365,7 +403,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, project.developers.size
end
- def test_uniq_when_association_already_loaded
+ def test_distinct_when_association_already_loaded
project = projects(:active_record)
project.developers << [ developers(:jamis), developers(:david), developers(:jamis), developers(:david) ]
assert_equal 3, Project.includes(:developers).find(project.id).developers.size
@@ -381,8 +419,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.delete(active_record)
assert_equal 1, david.projects.size
- assert_equal 1, david.projects(true).size
- assert_equal 2, active_record.developers(true).size
+ assert_equal 1, david.projects.reload.size
+ assert_equal 2, active_record.developers.reload.size
end
def test_deleting_array
@@ -390,7 +428,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.reload
david.projects.delete(Project.all.to_a)
assert_equal 0, david.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_deleting_all
@@ -398,7 +436,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.reload
david.projects.clear
assert_equal 0, david.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_removing_associations_on_destroy
@@ -424,7 +462,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert_equal 1, david.reload.projects.size
- assert_equal 1, david.projects(true).size
+ assert_equal 1, david.projects.reload.size
end
def test_destroying_many
@@ -440,7 +478,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert_equal 0, david.reload.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_destroy_all
@@ -456,7 +494,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert david.projects.empty?
- assert david.projects(true).empty?
+ assert david.projects.reload.empty?
end
def test_destroy_associations_destroys_multiple_associations
@@ -472,11 +510,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
assert join_records.empty?
- assert george.pirates(true).empty?
+ assert george.pirates.reload.empty?
join_records = Parrot.connection.select_all("SELECT * FROM parrots_treasures WHERE parrot_id = #{george.id}")
assert join_records.empty?
- assert george.treasures(true).empty?
+ assert george.treasures.reload.empty?
end
def test_associations_with_conditions
@@ -510,7 +548,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) do
assert project.developers.loaded?
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
end
@@ -521,14 +559,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
project.reload
assert ! project.developers.loaded?
assert_queries(1) do
- assert project.developers.include?(developer)
+ assert_includes project.developers, developer
end
assert ! 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)
@@ -542,32 +580,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
@@ -577,6 +615,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first
end
+ def test_association_with_extend_option
+ eponine = DeveloperWithExtendOption.create(name: "Eponine")
+ assert_equal "sns", eponine.projects.category
+ end
+
def test_replace_with_less
david = developers(:david)
david.projects = [projects(:action_controller)]
@@ -589,7 +632,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
@@ -607,9 +650,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
@@ -639,7 +682,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_respects_select
- categories(:technology).select_testing_posts(true).each do |o|
+ categories(:technology).select_testing_posts.reload.each do |o|
assert_respond_to o, :correctness_marker
end
assert_respond_to categories(:technology).select_testing_posts.first, :correctness_marker
@@ -650,7 +693,7 @@ 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
@@ -661,8 +704,8 @@ 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'
+ includes: { projects: :developers },
+ where: "projects_developers_projects_join.joined_on IS NOT NULL"
).to_a.size
)
end
@@ -681,15 +724,15 @@ 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(",")
+ includes: { projects: :developers }, where: "projects_developers_projects_join.joined_on IS NOT NULL",
+ 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
@@ -700,8 +743,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
@@ -711,7 +754,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
- developer.projects(true)
+ developer.projects.reload
assert_queries(0) do
developer.project_ids
developer.project_ids
@@ -736,7 +779,7 @@ 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
@@ -756,8 +799,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
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!
@@ -770,7 +813,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
@@ -779,9 +822,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Post.expects(:transaction)
- Category.first.posts.transaction do
- # nothing
+ assert_called(Post, :transaction) do
+ Category.first.posts.transaction do
+ # nothing
+ end
end
end
@@ -800,13 +844,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries { david.projects.columns }
end
- def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").build
+ def test_attributes_are_being_set_when_initialized_from_habtm_association_with_where_clause
+ 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_habm_association_with_multiple_where_clauses
- new_developer = projects(:action_controller).developers.where(:name => "Marcelo").where(:salary => 90_000).build
+ 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
assert_equal new_developer.name, "Marcelo"
assert_equal new_developer.salary, 90_000
end
@@ -814,7 +858,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
@@ -831,7 +875,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
projects = Developer.new.projects
assert_no_queries(ignore_none: false) 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
@@ -845,7 +889,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
@@ -858,11 +902,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
@@ -886,20 +930,91 @@ 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 NoMethodError do
- DeveloperWithSymbolClassName.new
+ assert_nothing_raised do
+ developer = DeveloperWithSymbolClassName.new
+ developer.projects
+ end
+ end
+
+ def test_alternate_database
+ professor = Professor.create(name: "Plum")
+ course = Course.create(name: "Forensics")
+ assert_equal 0, professor.courses.count
+ assert_nothing_raised do
+ professor.courses << course
end
+ assert_equal 1, professor.courses.count
+ end
+
+ def test_habtm_scope_can_unscope
+ project = ProjectUnscopingDavidDefaultScope.new
+ project.save!
+
+ developer = LazyBlockDeveloperCalledDavid.new(name: "Not David")
+ developer.save!
+ project.developers << developer
+
+ projects = ProjectUnscopingDavidDefaultScope.includes(:developers).where(id: project.id)
+ assert_equal 1, projects.first.developers.size
+ end
+
+ def test_preloaded_associations_size
+ assert_equal Project.first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ assert_equal Project.includes(:salaried_developers).references(:salaried_developers).first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ # Nested HATBM
+ first_project = Developer.first.projects.first
+ preloaded_first_project =
+ Developer.preload(projects: :salaried_developers).
+ first.
+ projects.
+ detect { |p| p.id == first_project.id }
+
+ assert preloaded_first_project.salaried_developers.loaded?, true
+ assert_equal first_project.salaried_developers.size, preloaded_first_project.salaried_developers.size
+ end
+
+ def test_has_and_belongs_to_many_is_useable_with_belongs_to_required_by_default
+ assert_difference "Project.first.developers_required_by_default.size", 1 do
+ 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
+ begin
+ 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
+ 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 290b2a0d6b..18548f8516 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1,88 +1,103 @@
+# 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/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')
+ subscriber = Subscriber.new(nick: "webster132")
assert !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")
+ author = Author.new(name: "David")
assert !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
@@ -98,7 +113,7 @@ end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
- :developers_projects, :topics, :authors, :comments,
+ :developers_projects, :topics, :authors, :author_addresses, :comments,
:posts, :readers, :taggings, :cars, :jobs, :tags,
:categorizations, :zines, :interests
@@ -114,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, :class => dev
+ self.table_name = "developers_projects"
+ belongs_to :developer, anonymous_class: dev
}
- has_many :developer_projects, :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)
@@ -133,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, :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, :class => comments, :foreign_key => 'post_id'
+ has_many :comments, anonymous_class: comments, foreign_key: "post_id"
}
assert_equal 0, counter
post = posts.first
@@ -157,18 +172,20 @@ 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
- ship.parts << part
+ travel(1.second) do
+ ship.parts << part
+ end
assert_equal part.ship, ship
assert_not_equal part.updated_at, updated_at
@@ -177,67 +194,88 @@ 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_nil part.ship
assert !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")
+
+ bulb = car.bulbs.build(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
- bulb = car.bulbs.create(:name => 'exotic')
- assert_equal 'exotic', bulb.name
+ bulb = car.awesome_bulbs.create(frickinawesome: false)
+ assert_equal false, bulb.frickinawesome
end
def test_build_from_association_should_respect_scope
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_equal 1, car.funky_bulbs.count
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 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 }
@@ -246,11 +284,11 @@ 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 }
@@ -265,29 +303,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
@@ -296,24 +334,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
@@ -323,19 +361,19 @@ 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
@@ -356,54 +394,98 @@ 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()
+ end
+
+ assert_no_queries do
+ 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 = []
+ assert_no_queries(ignore_none: false) 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 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 = []
+ assert_no_queries(ignore_none: false) 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 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
@@ -411,25 +493,40 @@ 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.id = 10
+ person.references
+ 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.id = 10
+ person.references
+ 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
@@ -439,11 +536,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
@@ -453,13 +550,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
@@ -479,16 +576,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 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 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
@@ -507,46 +616,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
@@ -555,7 +659,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_ids
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
@@ -574,9 +678,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
@@ -587,7 +705,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
assert_equal 3, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
end
@@ -598,7 +716,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! 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?
@@ -608,7 +726,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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
@@ -623,8 +741,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! 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
@@ -632,30 +750,64 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all_sanitized
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
summit = firm.clients.where("name = 'Summit'").to_a
assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
- assert_equal summit, firm.clients.where("name = :name", { :name => "Summit" }).to_a
+ assert_equal summit, firm.clients.where("name = :name", name: "Summit").to_a
end
def test_find_first
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.order("id").first
assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
end
def test_find_first_sanitized
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
client2 = Client.find(2)
- assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = ?", 'Client'], :order => "id").first
- assert_equal client2, firm.clients.merge!(:where => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }], :order => "id").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = ?", "Client").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = :type", type: "Client").first
+ end
+
+ def test_find_first_after_reset_scope
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, firm.clients.first, "Expected #first to return a new object"
+ end
+
+ def test_find_first_after_reset
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reset
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reset to return a new object"
+ end
+
+ def test_find_first_after_reload
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reload
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reload to return a new object"
end
def test_find_all_with_include_and_conditions
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
@@ -665,8 +817,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
@@ -679,7 +831,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_scoped_grouped_having
- assert_equal 1, authors(:david).popular_grouped_posts.length
+ assert_equal 2, authors(:david).popular_grouped_posts.length
assert_equal 0, authors(:mary).popular_grouped_posts.length
end
@@ -688,30 +840,39 @@ 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
- assert_equal 3, companies(:first_firm).clients_of_firm(true).size # checking using the db
+ assert_equal 3, companies(:first_firm).clients_of_firm.reload.size # checking using the db
assert_equal natural, companies(:first_firm).clients_of_firm.last
end
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
@@ -719,7 +880,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
@@ -728,7 +889,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
@@ -736,7 +897,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
firm.plain_clients.create!
end
end
@@ -757,21 +918,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_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.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(true).size
+ assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
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(true).include?(good)
+ assert_not_includes companies(:first_firm).clients_of_firm.reload, good
end
def test_transactions_when_adding_to_new_record
@@ -813,6 +977,7 @@ 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
@@ -836,7 +1001,7 @@ 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"}]) }
+ new_clients = assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
assert_equal 2, new_clients.size
end
@@ -853,7 +1018,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, first_topic.replies.length
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
@@ -862,7 +1027,7 @@ 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" } }
+ new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
assert !company.clients_of_firm.loaded?
assert_equal "Another Client", new_client.name
@@ -873,7 +1038,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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|
+ company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
client.name = "changed"
end
end
@@ -884,7 +1049,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_without_loading_association
- first_firm = companies(:first_firm)
+ first_firm = companies(:first_firm)
Firm.column_names
Client.column_names
@@ -892,7 +1057,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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
@@ -900,15 +1065,18 @@ 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_equal new_client, companies(:first_firm).clients_of_firm.last
- assert_equal new_client, companies(:first_firm).clients_of_firm(true).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"}])
- assert_equal 4, companies(:first_firm).clients_of_firm(true).size
+ 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
@@ -919,9 +1087,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_deleting_before_save
@@ -932,6 +1103,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, new_firm.clients_of_firm.size
end
+ 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)
+
+ assert_not 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")
+ end
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
+ ship.treasures.destroy_all
+ end
+ end
+
def test_deleting_updates_counter_cache
topic = Topic.order("id ASC").first
assert_equal topic.replies.to_a.size, topic.replies_count
@@ -1027,10 +1217,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal original_count, topic.replies_count
first_reply = topic.replies.first
- first_reply.update_attributes(:parent_id => nil)
+ first_reply.update_attributes(parent_id: nil)
assert_equal original_count - 1, topic.reload.replies_count
- first_reply.update_attributes(:parent_id => topic.id)
+ first_reply.update_attributes(parent_id: topic.id)
assert_equal original_count, topic.reload.replies_count
end
@@ -1043,26 +1233,32 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
reply1 = topic1.replies.first
reply2 = topic2.replies.first
- reply1.update_attributes(:parent_id => topic2.id)
+ reply1.update_attributes(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_attributes(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]])
assert_equal 0, companies(:first_firm).clients_of_firm.size
- assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
end
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
@@ -1074,17 +1270,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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
companies(:first_firm).clients_of_firm.delete_all
assert_equal 0, companies(:first_firm).clients_of_firm.size
- assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
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]
@@ -1093,7 +1292,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
rescue Client::RaisedOnDestroy
end
- assert_equal [good, bad], companies(:first_firm).clients_of_firm(true)
+ assert_equal [good, bad], companies(:first_firm).clients_of_firm.reload
end
def test_transaction_when_deleting_new_record
@@ -1113,7 +1312,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients_of_firm.clear
assert_equal 0, firm.clients_of_firm.size
- assert_equal 0, firm.clients_of_firm(true).size
+ assert_equal 0, firm.clients_of_firm.reload.size
assert_equal [], Client.destroyed_client_ids[firm.id]
# Should not be destroyed since the association is not dependent.
@@ -1125,7 +1324,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
@@ -1134,7 +1333,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
@@ -1149,7 +1348,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.dependent_clients_of_firm.clear
assert_equal 0, firm.dependent_clients_of_firm.size
- assert_equal 0, firm.dependent_clients_of_firm(true).size
+ assert_equal 0, firm.dependent_clients_of_firm.reload.size
assert_equal [], Client.destroyed_client_ids[firm.id]
# Should be destroyed since the association is dependent.
@@ -1182,7 +1381,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.exclusively_dependent_clients_of_firm.clear
assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
- assert_equal 0, firm.exclusively_dependent_clients_of_firm(true).size
+ assert_equal 0, firm.exclusively_dependent_clients_of_firm.reload.size
# no destroy-filters should have been called
assert_equal [], Client.destroyed_client_ids[firm.id]
@@ -1192,8 +1391,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
@@ -1204,8 +1403,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
@@ -1216,11 +1415,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
@@ -1231,7 +1430,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# break the vanilla firm_id foreign key
assert_equal 3, firm.clients.count
firm.clients.first.update_columns(firm_id: nil)
- assert_equal 2, firm.clients(true).count
+ assert_equal 2, firm.clients.reload.count
assert_equal 2, firm.clients_using_primary_key_with_delete_all.count
old_record = firm.clients_using_primary_key_with_delete_all.first
firm = Firm.first
@@ -1243,12 +1442,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
assert ms_client.save
- assert_equal 'Microsoft', ms_client.name
+ 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_equal "Microsoft", another_ms_client.name
end
def test_clearing_without_initial_access
@@ -1257,22 +1456,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients_of_firm.clear
assert_equal 0, firm.clients_of_firm.size
- assert_equal 0, firm.clients_of_firm(true).size
+ assert_equal 0, firm.clients_of_firm.reload.size
end
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(true).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
@@ -1282,8 +1484,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
@@ -1298,38 +1500,47 @@ 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
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ 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
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
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
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
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
@@ -1338,35 +1549,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
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"
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"
assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all"
- assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh"
+ assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh"
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 Client.all.merge!(where: "firm_id=#{firm.id}").to_a.empty?
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
@@ -1393,7 +1606,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
@@ -1417,18 +1630,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_restrict_with_exception
- firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
- firm.companies.create(:name => 'child')
+ 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')
+ 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?
@@ -1437,8 +1650,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
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")
+
+ assert !firm.companies.empty?
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+
+ 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")
+ ensure
+ I18n.backend.reload!
end
def test_included_in_collection
@@ -1446,10 +1678,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
@@ -1457,7 +1689,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_less
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
@@ -1471,7 +1703,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_new
- firm = Firm.all.merge!(:order => "id").first
+ firm = Firm.first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
@@ -1503,11 +1735,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(0, ignore_none: true) do
firm.clients = []
end
+
+ 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]
@@ -1516,7 +1750,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
rescue Client::RaisedOnSave
end
- assert_equal [good], companies(:first_firm).clients_of_firm(true)
+ assert_equal [good], companies(:first_firm).clients_of_firm.reload
end
def test_transactions_when_replacing_on_new_record
@@ -1532,7 +1766,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
company = companies(:first_firm)
- company.clients(true)
+ company.clients.reload
assert_queries(0) do
company.client_ids
company.client_ids
@@ -1546,6 +1780,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !company.clients.loaded?
end
+ def test_counter_cache_on_unloaded_association
+ car = Car.create(name: "My AppliCar")
+ assert_equal car.engines.size, 0
+ end
+
def test_get_ids_ignores_include_option
assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
end
@@ -1570,7 +1809,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
@@ -1582,11 +1821,11 @@ 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(true).size
+ assert_equal 2, firm.clients.reload.size
assert_equal true, firm.clients.include?(companies(:second_client))
end
@@ -1598,14 +1837,14 @@ 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_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
@@ -1639,7 +1878,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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_equal false, firm.clients.include?(client)
@@ -1668,7 +1907,7 @@ 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')
+ firm.clients.build(name: "Foo")
assert !firm.clients.loaded?
assert_queries 1 do
@@ -1682,7 +1921,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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')
+ firm.clients.create(name: "Foo")
assert !firm.clients.loaded?
assert_queries 3 do
@@ -1706,7 +1945,7 @@ 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')
+ firm.clients.create(name: "Foo")
assert !firm.clients.loaded?
assert_queries 2 do
@@ -1727,7 +1966,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_many_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert firm.clients.many? }
end
@@ -1766,7 +2005,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_none_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert ! firm.clients.none? }
end
@@ -1801,7 +2040,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_one_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
- firm.clients.collect # force load
+ firm.clients.load # force load
assert_no_queries { assert ! firm.clients.one? }
end
@@ -1836,13 +2075,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
@@ -1861,15 +2100,9 @@ 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
@@ -1892,12 +2125,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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
@@ -1911,7 +2144,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!
@@ -1938,7 +2171,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!
@@ -1948,13 +2181,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
@@ -1964,10 +2197,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
@@ -1979,30 +2219,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
@@ -2013,7 +2253,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
@@ -2030,15 +2270,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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
@@ -2060,7 +2300,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries(ignore_none: false) 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
@@ -2081,14 +2321,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
@@ -2101,8 +2349,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
@@ -2117,26 +2365,48 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb1, bulb2], car.all_bulbs.sort_by(&:id)
end
- test 'unscopes the default scope of associated model when used with include' do
+ 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 = Car.create!
+ bulb1 = Bulb.create! name: "defaulty", car: car
+ bulb2 = Bulb.create! name: "other", car: car
+
+ assert_equal [bulb1], car.bulbs
+ assert_equal [bulb2], car.other_bulbs
+ end
+
+ test "can rewhere the default scope of the associated model" do
+ 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
+
+ assert_equal [bulb1], car.bulbs
+ assert_equal [bulb2], car.old_bulbs
+ end
+
+ 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
car = Car.create!
original_child = FailedBulb.create!(car: car)
- assert_raise(ActiveRecord::RecordNotDestroyed) do
+ error = assert_raise(ActiveRecord::RecordNotDestroyed) do
car.failed_bulbs = [FailedBulb.create!]
end
assert_equal [original_child], car.reload.failed_bulbs
+ 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
@@ -2144,8 +2414,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
@@ -2154,7 +2424,7 @@ 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
@@ -2163,7 +2433,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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))
@@ -2173,7 +2443,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], authors(:david).posts_with_signature.map(&:title)
end
- test 'associations autosaves when object is already persited' do
+ test "associations autosaves when object is already persisted" do
bulb = Bulb.create!
tyre = Tyre.create!
@@ -2186,7 +2456,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])
@@ -2197,7 +2467,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])
@@ -2208,7 +2478,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
@@ -2229,7 +2499,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])
@@ -2239,7 +2509,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])
@@ -2249,4 +2528,109 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
+
+ test "association size calculation works with default scoped selects when not previously fetched" do
+ firm = Firm.create!(name: "Firm")
+ 5.times { firm.developers_with_select << Developer.create!(name: "Developer") }
+
+ same_firm = Firm.find(firm.id)
+ assert_equal 5, same_firm.developers_with_select.size
+ end
+
+ test "prevent double insertion of new object when the parent association loaded in the after save callback" do
+ reset_callbacks(:save, Bulb) do
+ Bulb.after_save { |record| record.car.bulbs.load }
+
+ car = Car.create!
+ car.bulbs << Bulb.new
+
+ assert_equal 1, car.bulbs.size
+ end
+ end
+
+ test "prevent double firing the before save callback of new object when the parent association saved in the callback" do
+ reset_callbacks(:save, Bulb) do
+ count = 0
+ Bulb.before_save { |record| record.car.save && count += 1 }
+
+ car = Car.create!
+ car.bulbs.create!
+
+ assert_equal 1, count
+ end
+ end
+
+ class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
+ self.table_name = "authors"
+ has_many :posts_with_error_destroying,
+ class_name: "PostWithErrorDestroying",
+ foreign_key: :author_id,
+ dependent: :destroy
+ end
+
+ class PostWithErrorDestroying < ActiveRecord::Base
+ self.table_name = "posts"
+ self.inheritance_column = nil
+ before_destroy -> { throw :abort }
+ end
+
+ def test_destroy_does_not_raise_when_association_errors_on_destroy
+ assert_no_difference "AuthorWithErrorDestroyingAssociation.count" do
+ author = AuthorWithErrorDestroyingAssociation.first
+
+ assert_not author.destroy
+ end
+ end
+
+ def test_destroy_with_bang_bubbles_errors_from_associations
+ error = assert_raises ActiveRecord::RecordNotDestroyed do
+ AuthorWithErrorDestroyingAssociation.first.destroy!
+ end
+
+ assert_instance_of PostWithErrorDestroying, error.record
+ end
+
+ def test_ids_reader_memoization
+ 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
+
+ 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 83365884f5..a19cd9d365 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,8 @@ 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_preload_sti_rhs_class
@@ -47,9 +54,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 +66,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
club1.members.sort_by(&:id)
end
- def make_model(name)
- Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
- end
-
- def test_ordered_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,12 +86,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
subscription = make_model "Subscription"
subscriber = make_model "Subscriber"
- subscriber.primary_key = 'nick'
- subscription.belongs_to :book, class: book
- subscription.belongs_to :subscriber, class: subscriber
+ subscriber.primary_key = "nick"
+ subscription.belongs_to :book, anonymous_class: book
+ subscription.belongs_to :subscriber, anonymous_class: subscriber
- book.has_many :subscriptions, class: subscription
- book.has_many :subscribers, through: :subscriptions, class: subscriber
+ book.has_many :subscriptions, anonymous_class: subscription
+ book.has_many :subscribers, through: :subscriptions, anonymous_class: subscriber
anonbook = book.first
namebook = Book.find anonbook.id
@@ -104,8 +107,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 +116,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 +140,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 +150,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert after_destroy_called, "after destroy should be called"
end
- def make_no_pk_hm_t
- lesson = make_model 'Lesson'
- student = make_model 'Student'
-
- lesson_student = make_model 'LessonStudent'
- lesson_student.table_name = 'lessons_students'
-
- lesson_student.belongs_to :lesson, :class => lesson
- lesson_student.belongs_to :student, :class => student
- lesson.has_many :lesson_students, :class => lesson_student
- lesson.has_many :students, :through => :lesson_students, :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 +162,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,18 +174,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert post.people.include?(person)
+ assert_includes post.people, person
end
- assert post.reload.people(true).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
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
person.reload.jobs_with_dependent_destroy.delete_all
end
end
@@ -206,8 +195,8 @@ 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
+ assert_no_difference "Job.count" do
+ assert_no_difference "Reference.count" do
person.reload.jobs_with_dependent_nullify.delete_all
end
end
@@ -217,8 +206,8 @@ 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
+ assert_no_difference "Job.count" do
+ assert_difference "Reference.count", -1 do
person.reload.jobs_with_dependent_delete_all.delete_all
end
end
@@ -229,14 +218,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:thinking)
post.people.concat [person]
assert_equal 1, post.people.size
- assert_equal 1, post.people(true).size
+ assert_equal 1, post.people.reload.size
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 +235,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,12 +248,12 @@ 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
@@ -272,7 +261,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
new_person = nil # so block binding catches it
assert_queries(0) do
- new_person = Person.new :first_name => 'bob'
+ new_person = Person.new first_name: "bob"
end
# Associating new records always saves them
@@ -282,59 +271,70 @@ 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(true).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")
+ 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(true).collect(&:first_name).include?("Bob")
- assert posts(:thinking).reload.people(true).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,7 +346,7 @@ 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))
@@ -356,7 +356,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:welcome).people.empty?
end
- assert posts(:welcome).reload.people(true).empty?
+ assert posts(:welcome).reload.people.reload.empty?
end
def test_destroy_association
@@ -367,7 +367,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people(true).empty?
+ assert posts(:welcome).people.reload.empty?
end
def test_destroy_all
@@ -378,7 +378,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people(true).empty?
+ assert posts(:welcome).people.reload.empty?
end
def test_should_raise_exception_for_destroying_mismatching_records
@@ -392,15 +392,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 +414,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 +435,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 +453,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 +466,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 +478,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 +520,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 +531,15 @@ 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_replace_association
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
+ 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 +547,35 @@ 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_queries(0) {
+ assert_includes posts(:welcome).people, people(:david)
+ assert_not_includes posts(:welcome).people, people(:michael)
}
- assert posts(:welcome).reload.people(true).include?(people(:david))
- assert !posts(:welcome).reload.people(true).include?(people(:michael))
+ assert_includes posts(:welcome).reload.people.reload, people(:david)
+ assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
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 +584,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(true).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,72 +603,72 @@ 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(true) }
+ assert_queries(2) { posts(:welcome);posts(:welcome).people.reload }
assert_queries(1) do
posts(:welcome).people.clear
@@ -678,7 +678,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:welcome).people.empty?
end
- assert posts(:welcome).reload.people(true).empty?
+ assert posts(:welcome).reload.people.reload.empty?
end
def test_association_callback_ordering
@@ -692,7 +692,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 +700,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"],
@@ -727,7 +727,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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,14 +743,15 @@ 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))
- ActiveRecord::Associations::Preloader.expects(:new).never
- posts(:welcome).misc_tag_ids
+ 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
end
def test_get_ids_for_loaded_associations
person = people(:michael)
- person.posts(true)
+ person.posts.reload
assert_queries(0) do
person.post_ids
person.post_ids
@@ -765,23 +766,24 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Tag.expects(:transaction)
- Post.first.tags.transaction do
- # nothing
+ assert_called(Tag, :transaction) do
+ Post.first.tags.transaction do
+ # nothing
+ end
end
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
@@ -805,37 +807,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(true).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(true).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
@@ -847,10 +849,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(true).empty?
+ assert !Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert author.named_categories.reload.empty?
end
def test_collection_singular_ids_getter_with_string_primary_keys
@@ -867,35 +869,52 @@ 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)
book.subscriber_ids = [subscribers(:second).nick]
- assert_equal [subscribers(:second)], book.subscribers(true)
+ assert_equal [subscribers(:second)], book.subscribers.reload
book.subscriber_ids = []
- assert_equal [], book.subscribers(true)
+ 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::RecordNotFound) {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
@@ -904,14 +923,14 @@ 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
@@ -925,10 +944,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
@@ -940,7 +966,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
@@ -952,30 +978,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_uniq
- mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
+ def test_joining_has_many_through_with_distinct
+ 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
@@ -1018,7 +1044,7 @@ 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?
end
@@ -1026,7 +1052,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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
@@ -1035,80 +1061,110 @@ 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])
- end
- end
-
- def test_save_should_not_raise_exception_when_join_record_has_errors
- repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.create(:name => 'Fishing', :authors => [Author.first])
- c.save
+ 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
end
end
- def test_create_bang_returns_falsy_when_join_record_has_errors
+ 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])
- assert !c.save
+ 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_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
@@ -1117,10 +1173,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_default_scope_on_the_target
person = people(:michael)
- assert_equal [posts(:thinking)], person.first_posts
+ assert_equal [posts(:thinking).id], person.first_posts.map(&:id)
readers(:michael_authorless).update(first_post_id: 1)
- assert_equal [posts(:thinking)], person.reload.first_posts
+ assert_equal [posts(:thinking).id], person.reload.first_posts.map(&:id)
end
def test_has_many_through_with_includes_in_through_association_scope
@@ -1140,9 +1196,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
@@ -1152,8 +1208,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
@@ -1167,14 +1223,99 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal post_direct.author_id, post_through.author_id
end
+ def test_has_many_through_with_scope_that_should_not_be_fully_merged
+ Club.has_many :distinct_memberships, -> { distinct }, class_name: "Membership"
+ Club.has_many :special_favourites, through: :distinct_memberships, source: :member
+
+ assert_nil Club.new.special_favourites.distinct_value
+ end
+
+ def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
+ member = Member.create!
+ club = Club.create!
+ TenantMembership.create!(
+ member: member,
+ club: club
+ )
+
+ TenantMembership.current_member = member
+
+ tenant_clubs = member.tenant_clubs
+ assert_equal [club], tenant_clubs
+
+ TenantMembership.current_member = nil
+
+ other_member = Member.create!
+ other_club = Club.create!
+ TenantMembership.create!(
+ member: other_member,
+ club: other_club
+ )
+
+ tenant_clubs = other_member.tenant_clubs
+ assert_equal [other_club], tenant_clubs
+ 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_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_single_has_many_through_association_with_unpersisted_parent_instance
post_with_single_has_many_through = Class.new(Post) do
- def self.name; 'PostWithSingleHasManyThrough'; end
+ def self.name; "PostWithSingleHasManyThrough"; end
has_many :subscriptions, through: :author
end
post = post_with_single_has_many_through.new
- post.author = Author.create!(name: 'Federico Morissette')
- book = Book.create!(name: 'essays on single has many through associations')
+ post.author = Author.create!(name: "Federico Morissette")
+ book = Book.create!(name: "essays on single has many through associations")
post.author.books << book
subscription = Subscription.first
book.subscriptions << subscription
@@ -1183,16 +1324,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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
+ def self.name; "PostWithNestedHasManyThrough"; end
has_many :books, through: :author
has_many :subscriptions, through: :books
end
post = post_with_nested_has_many_through.new
- post.author = Author.create!(name: 'Obie Weissnat')
- book = Book.create!(name: 'essays on nested single has many through associations')
+ post.author = Author.create!(name: "Obie Weissnat")
+ book = Book.create!(name: "essays on nested has many through associations")
post.author.books << book
subscription = Subscription.first
book.subscriptions << subscription
assert_equal [subscription], post.subscriptions.to_a
end
+
+ private
+ def make_model(name)
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
+ end
+
+ def make_no_pk_hm_t
+ lesson = make_model "Lesson"
+ student = make_model "Student"
+
+ lesson_student = make_model "LessonStudent"
+ lesson_student.table_name = "lessons_students"
+
+ lesson_student.belongs_to :lesson, anonymous_class: lesson
+ lesson_student.belongs_to :student, anonymous_class: student
+ lesson.has_many :lesson_students, anonymous_class: lesson_student
+ lesson.has_many :students, through: :lesson_students, anonymous_class: student
+ [lesson, lesson_student, student]
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 5c2e5e7b43..ec5d95080b 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -1,19 +1,21 @@
+# 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"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates, :authors, :author_addresses
def setup
Account.destroyed_account_ids.clear
@@ -28,7 +30,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
companies(:first_firm).account
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, 'ORDER BY was used in the query'
+ log_all = ActiveRecord::SQLCounter.log_all
+ assert log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{log_all}"
end
def test_has_one_cache_nils
@@ -36,13 +39,13 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_queries(1) { assert_nil firm.account }
assert_queries(0) { assert_nil firm.account }
- firms = Firm.all.merge!(:includes => :account).to_a
+ firms = Firm.all.merge!(includes: :account).to_a
assert_queries(0) { 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,11 +105,19 @@ 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_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?
+ end
+
def test_natural_assignment_to_nil_after_destroy
firm = companies(:rails_core)
old_account_id = firm.account.id
@@ -117,12 +128,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
@@ -162,25 +173,25 @@ 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)
+ 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 RestrictedWithExceptionFirm.exists?(name: "restrict")
assert 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
@@ -188,8 +199,26 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
- assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
+ assert 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")
+ firm.create_account(credit_limit: 10)
+
+ assert_not_nil firm.account
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+ assert_equal "Cannot delete record because a dependent firm account exists", firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(name: "restrict")
assert firm.account.present?
+ ensure
+ I18n.backend.reload!
end
def test_successful_build_association
@@ -215,24 +244,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
@@ -250,19 +279,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
@@ -274,13 +303,32 @@ 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_build
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -320,7 +368,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
@@ -332,11 +380,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert a.persisted?
assert_equal a, firm.account
assert_equal a, firm.account
- assert_equal a, firm.account(true)
+ firm.association(:account).reload
+ assert_equal a, firm.account
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
@@ -365,14 +414,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
@@ -389,7 +438,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
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
@@ -439,7 +488,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
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
@@ -459,63 +508,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
@@ -535,36 +584,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'
+ ship.name = "new name"
assert 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
@@ -575,7 +624,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
@@ -589,7 +638,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
@@ -598,8 +647,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
@@ -607,4 +656,73 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ class SpecialBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "SpecialAuthor"
+ has_one :subscription, class_name: "SpecialSupscription", foreign_key: "subscriber_id"
+ 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
+
+ refute_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_not DestroyByParentBook.exists?(book.id)
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index f8772547a2..1d37457464 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,25 +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/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)
@@ -30,14 +36,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
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_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
@@ -47,8 +53,8 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
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
@@ -61,7 +67,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
@@ -69,7 +75,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
@@ -78,40 +84,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
@@ -119,31 +132,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 }
@@ -155,8 +168,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
@@ -172,37 +185,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
@@ -213,7 +226,7 @@ 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?
@@ -226,36 +239,38 @@ 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!
assert_not_nil @member_detail.member_type
@member_detail.destroy
assert_queries(1) do
- assert_not_nil @member_detail.member_type(true)
+ @member_detail.association(:member_type).reload
+ assert_not_nil @member_detail.member_type
end
@member_detail.member.destroy
assert_queries(1) do
- assert_nil @member_detail.member_type(true)
+ @member_detail.association(:member_type).reload
+ assert_nil @member_detail.member_type
end
end
def test_value_is_properly_quoted
- minivan = Minivan.find('m1')
+ minivan = Minivan.find("m1")
assert_nothing_raised do
minivan.dashboard
end
@@ -264,7 +279,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
@@ -276,12 +291,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
@@ -344,4 +359,34 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ def test_has_one_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
+ customer = Customer.create!
+ carrier = Carrier.create!
+ customer_carrier = CustomerCarrier.create!(
+ customer: customer,
+ carrier: carrier,
+ )
+ account = ShopAccount.create!(customer_carrier: customer_carrier)
+
+ CustomerCarrier.current_customer = customer
+
+ account_carrier = account.carrier
+ assert_equal carrier, account_carrier
+
+ CustomerCarrier.current_customer = nil
+
+ other_carrier = Carrier.create!
+ other_customer = Customer.create!
+ other_customer_carrier = CustomerCarrier.create!(
+ customer: other_customer,
+ carrier: other_carrier,
+ )
+ other_account = ShopAccount.create!(customer_carrier: other_customer_carrier)
+
+ account_carrier = other_account.carrier
+ assert_equal other_carrier, account_carrier
+ ensure
+ CustomerCarrier.current_customer = nil
+ end
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..7be875fec6 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,41 +78,41 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_find_with_implicit_inner_joins_honors_readonly_with_select
- authors = Author.joins(:posts).select('authors.*').to_a
+ 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"
+ 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 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
+ authors = Author.joins(:posts).select("authors.*").to_a
assert !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?
@@ -102,12 +122,12 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
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 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 Post.joins(:misc_tags).where(id: posts(:welcome).id).empty?
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 423b8238b1..c0d328ca8a 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -1,18 +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/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
@@ -21,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
@@ -34,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
@@ -43,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
@@ -56,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)
@@ -80,10 +95,10 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
- rating.comment.body = "Brogramming is the act of programming, like a bro."
+ rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
- comment.body = "Broseiden is the king of the sea of bros."
+ comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
@@ -94,89 +109,65 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
- rating.comment.body = "Brogramming is the act of programming, like a bro."
+ rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
- comment.body = "Broseiden is the king of the sea of bros."
+ comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
def test_polymorphic_and_has_many_through_relationships_should_not_have_inverses
sponsor_reflection = Sponsor.reflect_on_association(:sponsorable)
- assert_respond_to sponsor_reflection, :has_inverse?
assert !sponsor_reflection.has_inverse?, "A polymorphic association should not find an inverse automatically"
club_reflection = Club.reflect_on_association(:members)
- assert_respond_to club_reflection, :has_inverse?
assert !club_reflection.has_inverse?, "A has_many_through association should not find an inverse automatically"
end
- def test_polymorphic_relationships_should_still_not_have_inverses_when_non_polymorphic_relationship_has_the_same_name
+ def test_polymorphic_has_one_should_find_inverse_automatically
man_reflection = Man.reflect_on_association(:polymorphic_face_without_inverse)
- face_reflection = Face.reflect_on_association(:man)
-
- assert_respond_to face_reflection, :has_inverse?
- assert face_reflection.has_inverse?, "For this test, the non-polymorphic association must have an inverse"
- assert_respond_to man_reflection, :has_inverse?
- assert !man_reflection.has_inverse?, "The target of a polymorphic association should not find an inverse automatically"
+ assert man_reflection.has_inverse?
end
end
class InverseAssociationTests < ActiveRecord::TestCase
def test_should_allow_for_inverse_of_options_in_associations
- assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_many') do
- Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
+ assert_nothing_raised do
+ Class.new(ActiveRecord::Base).has_many(:wheels, inverse_of: :car)
end
- assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_one') do
- Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
+ assert_nothing_raised do
+ Class.new(ActiveRecord::Base).has_one(:engine, inverse_of: :car)
end
- assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on belongs_to') do
- Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
+ assert_nothing_raised do
+ 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?
has_many_with_inverse_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_with_inverse_ref, :has_inverse?
assert has_many_with_inverse_ref.has_inverse?
belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_with_inverse_ref, :has_inverse?
assert belongs_to_with_inverse_ref.has_inverse?
has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
- assert_respond_to has_one_without_inverse_ref, :has_inverse?
assert !has_one_without_inverse_ref.has_inverse?
has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
- assert_respond_to has_many_without_inverse_ref, :has_inverse?
assert !has_many_without_inverse_ref.has_inverse?
belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
- assert_respond_to belongs_to_without_inverse_ref, :has_inverse?
assert !belongs_to_without_inverse_ref.has_inverse?
end
- def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
- has_one_ref = Man.reflect_on_association(:face)
- assert_respond_to has_one_ref, :inverse_of
-
- has_many_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_ref, :inverse_of
-
- belongs_to_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_ref, :inverse_of
- end
-
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
has_one_ref = Man.reflect_on_association(:face)
assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of
@@ -198,6 +189,16 @@ class InverseAssociationTests < ActiveRecord::TestCase
belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
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)
+
+ new_project = Project.last
+ assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
+ assert new_project.lead_developer.present?, "Expected lead developer to be present on the project"
+ end
end
class InverseHasOneTests < ActiveRecord::TestCase
@@ -207,73 +208,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
@@ -283,74 +283,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
@@ -372,25 +393,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
@@ -431,7 +452,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"
@@ -463,7 +484,10 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_raise_record_not_found_error_when_no_ids_are_passed
man = Man.create!
- assert_raise(ActiveRecord::RecordNotFound) { man.interests.find() }
+ exception = assert_raise(ActiveRecord::RecordNotFound) { man.interests.load.find() }
+
+ assert_equal exception.model, "Interest"
+ assert_equal exception.primary_key, "id"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
@@ -472,7 +496,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
@@ -482,6 +506,33 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert !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 Man.first.interests.reload.any?
+ assert Man.includes(:interests).first.interests.any?
+ assert 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 Man.first.interests.reload.any?
+ assert Man.includes(:interests).first.interests.any?
+ assert 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
class InverseBelongsToTests < ActiveRecord::TestCase
@@ -491,49 +542,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
@@ -541,24 +592,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
@@ -571,30 +622,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
@@ -606,23 +657,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'
+ 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'
- 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
@@ -638,22 +675,32 @@ 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
def test_trying_to_access_inverses_that_dont_exist_shouldnt_raise_an_error
# Ideally this would, if only for symmetry's sake with other association types
- assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.horrible_polymorphic_man }
+ assert_nothing_raised { Face.first.horrible_polymorphic_man }
end
def test_trying_to_set_polymorphic_inverses_that_dont_exist_at_all_should_raise_an_error
@@ -663,7 +710,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
def test_trying_to_set_polymorphic_inverses_that_dont_exist_on_the_instance_being_set_should_raise_an_error
# passes because Man does have the correct inverse_of
- assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.polymorphic_man = Man.first }
+ assert_nothing_raised { Face.first.polymorphic_man = Man.first }
# fails because Interest does have the correct inverse_of
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.polymorphic_man = Interest.first }
end
@@ -675,7 +722,7 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
fixtures :men, :interests, :zines
def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
- assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
+ assert_nothing_raised do
i = Interest.first
i.zine
i.man
@@ -683,10 +730,10 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
end
def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
- assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
+ 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 213be50e67..5d83c9435b 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -1,46 +1,48 @@
+# 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_uniq_through_join_model
+ def test_has_many_distinct_through_join_model
assert_equal 2, authors(:mary).categorized_posts.size
assert_equal 1, authors(:mary).unique_categorized_posts.size
end
- def test_has_many_uniq_through_count
+ def test_has_many_distinct_through_count
author = authors(:mary)
assert !authors(:mary).unique_categorized_posts.loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
@@ -49,7 +51,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert !authors(:mary).unique_categorized_posts.loaded?
end
- def test_has_many_uniq_through_find
+ def test_has_many_distinct_through_find
assert_equal 1, authors(:mary).unique_categorized_posts.to_a.size
end
@@ -88,7 +90,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
- assert_nothing_raised(NoMethodError) { tag.author_id }
+ assert_nothing_raised { tag.author_id }
end
def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
@@ -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,24 +209,26 @@ 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_nil posts(:welcome).tagging(true)
+ 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
post.destroy
assert_equal old_count, Tagging.count
- assert_nil posts(:welcome).tagging(true)
+ posts(:welcome).association(:tagging).reload
+ assert_nil posts(:welcome).tagging
end
def test_has_many_with_piggyback
@@ -233,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 }
@@ -265,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 }
@@ -274,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 }
@@ -300,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
@@ -322,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]
@@ -352,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
@@ -361,8 +365,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal posts(:welcome, :thinking).sort_by(&:id), tags(:general).tagged_posts.sort_by(&:id)
end
+ 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"
+ assert_equal [], tags(:general).null_tagged_posts
+ refute_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
@@ -372,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
@@ -393,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
@@ -404,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
@@ -425,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
@@ -436,8 +447,8 @@ 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
@@ -453,28 +464,27 @@ 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 new_tag.persisted? # consistent with habtm!
assert saved_post.persisted?
- assert saved_post.tags.include?(new_tag)
+ assert_includes saved_post.tags, new_tag
assert new_tag.persisted?
- assert saved_post.reload.tags(true).include?(new_tag)
-
+ 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_includes new_post.tags, saved_tag
new_post.save!
assert new_post.persisted?
- assert new_post.reload.tags(true).include?(saved_tag)
+ assert_includes new_post.reload.tags.reload, saved_tag
assert !posts(:thinking).tags.build.persisted?
assert !posts(:thinking).tags.new.persisted?
@@ -482,31 +492,31 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
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(true).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(true).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(true).size)
+ assert_equal(count + 4, post_thinking.tags.reload.size)
# Raises if the wrong reflection name is used to set the Edge belongs_to
assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
@@ -541,47 +551,47 @@ 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(true).size)
+ assert_equal(count + 1, book_awdr.references.reload.size)
assert_nothing_raised { book_awdr.references.delete(book) }
assert_equal(count, book_awdr.references.size)
- assert_equal(count, book_awdr.references(true).size)
+ assert_equal(count, book_awdr.references.reload.size)
assert_equal(references_before.sort, book_awdr.references.sort)
end
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(true).size)
- assert_equal(count + 1, post_thinking.reload.tags(true).size)
+ assert_equal(count + 1, post_thinking.taggings.reload.size)
+ assert_equal(count + 1, post_thinking.reload.tags.reload.size)
assert_not_equal(tags_before, post_thinking.tags.sort)
assert_nothing_raised { post_thinking.tags.delete(tag) }
assert_equal(count, post_thinking.tags.size)
- assert_equal(count, post_thinking.tags(true).size)
- assert_equal(count, post_thinking.taggings(true).size)
+ assert_equal(count, post_thinking.tags.reload.size)
+ assert_equal(count, post_thinking.taggings.reload.size)
assert_equal(tags_before, post_thinking.tags.sort)
end
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(true).size)
+ assert_equal(count + 2, post_thinking.reload.tags.reload.size)
assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
assert_equal(count, post_thinking.tags.size)
- assert_equal(count, post_thinking.tags(true).size)
+ assert_equal(count, post_thinking.tags.reload.size)
assert_equal(tags_before, post_thinking.tags.sort)
end
@@ -589,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
@@ -602,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
@@ -625,7 +635,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
end
- def test_uniq_has_many_through_should_retain_order
+ def test_distinct_has_many_through_should_retain_order
comment_ids = authors(:david).comments.map(&:id)
assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
@@ -633,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 }
@@ -660,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 }
@@ -687,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
@@ -701,7 +711,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_no_queries do
assert david.categories.loaded?
- assert david.categories.include?(category)
+ assert_includes david.categories, category
end
end
@@ -712,40 +722,57 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
david.reload
assert ! david.categories.loaded?
assert_queries(1) do
- assert david.categories.include?(category)
+ assert_includes david.categories, category
end
assert ! 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)
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)
+ }
+ 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)
+ }
+ 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)
+ }
+ assert_equal("Can't join 'Post' to association named 'nonexistent_relation'; perhaps you misspelled it?", includes_and_eager_load_error.message)
+ end
+
private
# create dynamic Post models to allow different dependency options
def find_post_with_dependency(post_id, association, association_name, dependency)
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
new file mode 100644
index 0000000000..c95d0425cd
--- /dev/null
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -0,0 +1,90 @@
+# 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"
+
+class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
+ 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
+ assert_equal authors(:david), result.first
+ end
+
+ 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
+ end
+ assert queries.any? { |sql| /agents_people_4/i.match?(sql) }
+ end
+ end
+
+ 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_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({}).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([]).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 }
+ 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|\: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)
+
+ # 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?
+ 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)
+ end
+
+ test "the default scope of the target is applied when joining associations" do
+ author = Author.create! name: "Jon"
+ author.categorizations.create!
+ author.categorizations.create! special: true
+
+ assert_equal [author], Author.where(id: author).left_outer_joins(:special_categorizations)
+ end
+end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 31b68c940e..65d30d011b 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,12 +72,12 @@ 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')
+ authors = Author.joins(:tags).where("taggings.taggable_type" => "FakeModel")
assert authors.empty?
end
@@ -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
@@ -137,7 +144,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
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,12 +171,12 @@ 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)
+ where("member_details.id" => 9)
assert members.empty?
end
@@ -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
@@ -196,12 +203,12 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
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)
+ where("member_details.id" => 9)
assert members.empty?
end
@@ -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')
+ 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')
+ authors = Author.joins(:similar_posts).where("taggings_authors_join.taggable_type" => "FakeModel")
assert authors.empty?
end
+ def test_nested_has_many_through_with_scope_on_polymorphic_reflection
+ authors = Author.joins(:ordered_posts).where("posts.id" => posts(:misc_by_bob).id)
+ assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id)
+ end
+
def test_has_many_through_with_foreign_key_option_on_through_reflection
- assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order('posts.id')
+ assert_equal [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,7 +455,7 @@ 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)
+ 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?
@@ -453,7 +465,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
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)
+ 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?
end
@@ -495,7 +507,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho = members(:groucho)
founding = member_types(:founding)
- assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do
+ assert_raises(ActiveRecord::HasOneThroughNestedAssociationsAreReadonly) do
groucho.nested_member_type = founding
end
end
@@ -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 Author.where("tags.id" => 100).joins(:misc_post_first_blue_tags).empty?
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
@@ -539,7 +551,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
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 +560,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
@@ -567,6 +579,37 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert !c.post_taggings.empty?
end
+ def test_polymorphic_has_many_through_when_through_association_has_not_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_when_through_association_has_already_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_joined_different_table_twice
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ hotel = Hotel.create!(departments: [department])
+
+ assert_equal hotel, Hotel.joins(:cake_designers, :drink_designers).take
+ end
+
private
def assert_includes_and_joins_equal(query, expected, association)
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 3e5494e897..65a3bb5efe 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class RequiredAssociationsTest < ActiveRecord::TestCase
@@ -18,18 +20,25 @@ 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
- model = subclass_of(Child) do
- belongs_to :parent, inverse_of: false,
- class_name: "RequiredAssociationsTest::Parent"
- end
+ test "belongs_to associations can be optional by default" do
+ begin
+ original_value = ActiveRecord::Base.belongs_to_required_by_default
+ ActiveRecord::Base.belongs_to_required_by_default = false
- assert model.new.save
- assert model.new(parent: Parent.new).save
+ model = subclass_of(Child) do
+ belongs_to :parent, inverse_of: false,
+ class_name: "RequiredAssociationsTest::Parent"
+ end
+
+ assert model.new.save
+ assert model.new(parent: Parent.new).save
+ ensure
+ ActiveRecord::Base.belongs_to_required_by_default = original_value
+ end
end
test "required belongs_to associations have presence validated" do
@@ -46,6 +55,27 @@ class RequiredAssociationsTest < ActiveRecord::TestCase
assert record.save
end
+ test "belongs_to associations can be required by default" do
+ begin
+ 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
+ end
+
test "has_one associations are not required by default" do
model = subclass_of(Parent) do
has_one :child, inverse_of: false,
@@ -92,11 +122,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