From a37b1df4d3e9e7fcdb32ed1057057ef271fe48c0 Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Fri, 2 Aug 2024 14:17:42 -0400 Subject: [PATCH] Better detection of standard preload This ends up being an optimization. But when preloading `[:association]`, it runs a separate preloader on `:association`. This skips the interim step --- .../virtual_attributes/virtual_fields.rb | 8 +++- spec/virtual_includes_spec.rb | 48 +++++++++++++++---- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/active_record/virtual_attributes/virtual_fields.rb b/lib/active_record/virtual_attributes/virtual_fields.rb index c10b9e9..8f8b57e 100644 --- a/lib/active_record/virtual_attributes/virtual_fields.rb +++ b/lib/active_record/virtual_attributes/virtual_fields.rb @@ -152,8 +152,12 @@ def call # convert the includes with virtual attributes to includes with proper associations records_by_assoc = records.group_by { |rec| assoc_cache[rec.class] } - # if these are the same includes, then do the preloader work - return super if records_by_assoc.size == 1 && records_by_assoc.keys.first == associations + # If the association were already translated, then short circuit / do the standard preloader work. + # When replace_virtual_fields removes the outer array, match that too. + if records_by_assoc.size == 1 && + (associations == records_by_assoc.keys.first || associations == [records_by_assoc.keys.first]) + return super + end # for each of the associations, run a preloader records_by_assoc.each do |klass_associations, klass_records| diff --git a/spec/virtual_includes_spec.rb b/spec/virtual_includes_spec.rb index 6fff324..1d6f3dd 100644 --- a/spec/virtual_includes_spec.rb +++ b/spec/virtual_includes_spec.rb @@ -180,12 +180,6 @@ expect(Author.includes(:books => :author_name).references(:books)).to preload_values(:first_book_author_name, author_name) expect(Author.includes(:books => [:author_name]).references(:books)).to preload_values(:first_book_author_name, author_name) expect(Author.includes(:books => {:author_name => {}}).references(:books)).to preload_values(:first_book_author_name, author_name) - end - - it "uses preloaded fields" do - expect(Author.includes(:books => :author_name).references(:books)).to preload_values(:first_book_author_name, author_name) - expect(Author.includes(:books => [:author_name]).references(:books)).to preload_values(:first_book_author_name, author_name) - expect(Author.includes(:books => {:author_name => {}}).references(:books)).to preload_values(:first_book_author_name, author_name) inc = Author.virtual_includes(:first_book_author_name) expect(Author.includes(inc).references(:books)).to preload_values(:first_book_author_name, author_name) end @@ -308,10 +302,6 @@ it "uses included fields" do expect(preloaded(Author.all.to_a, :books => :author_name)).to preload_values(:first_book_author_name, author_name) end - - it "uses preloaded fields" do - expect(preloaded(Author.all.to_a, :books => :author_name)).to preload_values(:first_book_author_name, author_name) - end end context "preloads virtual_reflection with includes" do @@ -386,11 +376,49 @@ end end + describe ".eager_load" do + it "preloads standard associations (:books)" do + expect(Author.eager_load(:books)).to preload_values(:first_book_name, book_name) + expect(Author.eager_load([:books])).to preload_values(:first_book_name, book_name) + expect(Author.eager_load([[:books]])).to preload_values(:first_book_name, book_name) + expect(Author.eager_load(:books => {})).to preload_values(:first_book_name, book_name) + end + + it "preloads associations (:uses => :books)" do + expect(Author.eager_load(:first_book_name)).to preload_values(:first_book_name, book_name) + expect(Author.eager_load([:first_book_name])).to preload_values(:first_book_name, book_name) + expect(Author.eager_load([[:first_book_name]])).to preload_values(:first_book_name, book_name) + expect(Author.eager_load(:first_book_name => {})).to preload_values(:first_book_name, book_name) + end + end + + describe ".preload" do + it "preloads standard associations (:books)" do + expect(Author.preload(:books)).to preload_values(:first_book_name, book_name) + expect(Author.preload([:books])).to preload_values(:first_book_name, book_name) + expect(Author.preload([[:books]])).to preload_values(:first_book_name, book_name) + expect(Author.preload(:books => {})).to preload_values(:first_book_name, book_name) + end + + it "preloads associations (:uses => :books)" do + expect(Author.preload(:first_book_name)).to preload_values(:first_book_name, book_name) + expect(Author.preload([:first_book_name])).to preload_values(:first_book_name, book_name) + expect(Author.preload([[:first_book_name]])).to preload_values(:first_book_name, book_name) + expect(Author.preload(:first_book_name => {})).to preload_values(:first_book_name, book_name) + end + end + context "preloads virtual_reflection with preloader" do it "preloads virtual_reflection (:uses => :books)" do expect(preloaded(Author.all.to_a, :named_books)).to preload_values(:named_books, named_books) end + it "preloads virtual_reflection ([:books])" do + expect(preloaded(Author.all.to_a, :books)).to preload_values(:named_books, named_books) + expect(preloaded(Author.all.to_a, [:books])).to preload_values(:named_books, named_books) + expect(preloaded(Author.all.to_a, :books => {})).to preload_values(:named_books, named_books) + end + it "preloads virtual_reflection (:uses => {:books => :author_name})" do expect(preloaded(Author.all.to_a, :books_with_authors)).to preload_values(:books_with_authors, named_books) end