diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 552c083b6a..d5aad8202b 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -27,6 +27,10 @@ require "models/parrot" require "models/bird" require "models/treasure" require "models/price_estimate" +require "models/invoice" +require "models/discount" +require "models/line_item" +require "models/shipping_line" class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, @@ -435,6 +439,60 @@ class PreloaderTest < ActiveRecord::TestCase end end + def test_some_already_loaded_associations + item_discount = Discount.create(amount: 5) + shipping_discount = Discount.create(amount: 20) + + invoice = Invoice.new + line_item = LineItem.new(amount: 20) + line_item.discount_applications << LineItemDiscountApplication.new(discount: item_discount) + invoice.line_items << line_item + + shipping_line = ShippingLine.new(amount: 50) + shipping_line.discount_applications << ShippingLineDiscountApplication.new(discount: shipping_discount) + invoice.shipping_lines << shipping_line + + invoice.save! + invoice.reload + + # SELECT "line_items".* FROM "line_items" WHERE "line_items"."invoice_id" = ? + # SELECT "shipping_lines".* FROM shipping_lines WHERE "shipping_lines"."invoice_id" = ? + # SELECT "line_item_discount_applications".* FROM "line_item_discount_applications" WHERE "line_item_discount_applications"."line_item_id" = ? + # SELECT "shipping_line_discount_applications".* FROM "shipping_line_discount_applications" WHERE "shipping_line_discount_applications"."shipping_line_id" = ? + # SELECT "discounts".* FROM "discounts" WHERE "discounts"."id" IN (?, ?). + assert_queries(5) do + preloader = ActiveRecord::Associations::Preloader.new(records: [invoice], associations: [ + line_items: { discount_applications: :discount }, + shipping_lines: { discount_applications: :discount }, + ]) + preloader.call + end + + assert_no_queries do + assert_not_nil invoice.line_items.first.discount_applications.first.discount + assert_not_nil invoice.shipping_lines.first.discount_applications.first.discount + end + + invoice.reload + invoice.line_items.map { |i| i.discount_applications.to_a } + # `line_items` and `line_item_discount_applications` are already preloaded, so we expect: + # SELECT "shipping_lines".* FROM shipping_lines WHERE "shipping_lines"."invoice_id" = ? + # SELECT "shipping_line_discount_applications".* FROM "shipping_line_discount_applications" WHERE "shipping_line_discount_applications"."shipping_line_id" = ? + # SELECT "discounts".* FROM "discounts" WHERE "discounts"."id" = ?. + assert_queries(3) do + preloader = ActiveRecord::Associations::Preloader.new(records: [invoice], associations: [ + line_items: { discount_applications: :discount }, + shipping_lines: { discount_applications: :discount }, + ]) + preloader.call + end + + assert_no_queries do + assert_not_nil invoice.line_items.first.discount_applications.first.discount + assert_not_nil invoice.shipping_lines.first.discount_applications.first.discount + end + end + def test_preload_through comments = [ comments(:eager_sti_on_associations_s_comment1), diff --git a/activerecord/test/models/discount.rb b/activerecord/test/models/discount.rb new file mode 100644 index 0000000000..06068df2fb --- /dev/null +++ b/activerecord/test/models/discount.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Discount < ActiveRecord::Base +end diff --git a/activerecord/test/models/invoice.rb b/activerecord/test/models/invoice.rb index 1851792ed5..1a62b7baa0 100644 --- a/activerecord/test/models/invoice.rb +++ b/activerecord/test/models/invoice.rb @@ -2,5 +2,6 @@ class Invoice < ActiveRecord::Base has_many :line_items, autosave: true + has_many :shipping_lines, -> { from("shipping_lines") }, autosave: true before_save { |record| record.balance = record.line_items.map(&:amount).sum } end diff --git a/activerecord/test/models/line_item.rb b/activerecord/test/models/line_item.rb index 3a51cf03b2..2c9876dac1 100644 --- a/activerecord/test/models/line_item.rb +++ b/activerecord/test/models/line_item.rb @@ -2,4 +2,10 @@ class LineItem < ActiveRecord::Base belongs_to :invoice, touch: true + has_many :discount_applications, class_name: "LineItemDiscountApplication" +end + +class LineItemDiscountApplication < ActiveRecord::Base + belongs_to :line_item + belongs_to :discount end diff --git a/activerecord/test/models/shipping_line.rb b/activerecord/test/models/shipping_line.rb new file mode 100644 index 0000000000..4044a5ee38 --- /dev/null +++ b/activerecord/test/models/shipping_line.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class ShippingLine < ActiveRecord::Base + belongs_to :invoice, touch: true + has_many :discount_applications, class_name: "ShippingLineDiscountApplication" +end + +class ShippingLineDiscountApplication < ActiveRecord::Base + belongs_to :shipping_line + belongs_to :discount +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index f72cda1e3c..c9fd47e512 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -313,6 +313,10 @@ ActiveRecord::Schema.define do t.boolean :deleted end + create_table :discounts, force: true do |t| + t.integer :amount + end + create_table :dl_keyed_belongs_tos, force: true, id: false do |t| t.primary_key :belongs_key t.references :destroy_async_parent @@ -547,6 +551,11 @@ ActiveRecord::Schema.define do t.integer :amount end + create_table :line_item_discount_applications, force: true do |t| + t.integer :line_item_id + t.integer :discount_id + end + create_table :lions, force: true do |t| t.integer :gender t.boolean :is_vegetarian, default: false @@ -922,6 +931,16 @@ ActiveRecord::Schema.define do t.integer :shape_id end + create_table :shipping_lines, force: true do |t| + t.integer :invoice_id + t.integer :amount + end + + create_table :shipping_line_discount_applications, force: true do |t| + t.integer :shipping_line_id + t.integer :discount_id + end + create_table :ships, force: true do |t| t.string :name t.integer :pirate_id