Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mongoid/includes/inclusion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def load_documents_for(foreign_key, foreign_key_values)
# Returns an Inclusion that can be eager loaded as usual.
def for_class_name(class_name)
Inclusion.new metadata.clone.instance_eval { |relation_metadata|
@options = @options.dup
@options[:class_name] = @class_name = class_name
@options[:polymorphic], @options[:as], @polymorphic, @klass = nil
self
Expand Down
112 changes: 112 additions & 0 deletions spec/mongoid/includes/polymorphic_includes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,117 @@
}.to raise_error(Mongoid::Includes::Errors::InvalidPolymorphicIncludes)
end
end

context 'eager loading polymorphic belongs_to associations with multiple concrete types' do
before(:context) do
class Main
include Mongoid::Document
belongs_to :related, polymorphic: true, optional: true
end

class Related
include Mongoid::Document
has_one :parent, as: :related
end

class Two < Related; end
class Three < Related; end
end

after(:context) do
%i[Main Related Two Three].each do |const|
Object.send(:remove_const, const) if Object.const_defined?(const, false)
end
end

it 'loads the related documents for each concrete type without raising' do
Main.create!(related: Two.create!)
Main.create!(related: Three.create!)

loaded = nil
expect { loaded = Main.includes(:related).entries }.not_to raise_error
expect(loaded.map { |doc| doc.related.class }).to match_array([Two, Three])
expect { Main.last.related.id }.not_to raise_error
end
end

context 'polymorphic eager loading in a fresh Ruby process' do
let(:project_root) { File.expand_path('../../..', __dir__) }

it 'does not error when includes is evaluated from the CLI' do
require 'open3'

database_name = "mongoid_includes_spec_#{SecureRandom.hex(6)}"
base_script = <<~RUBY
require 'bundler/setup'
require 'mongoid'
require 'mongoid_includes'

Mongoid.load_configuration(
clients: {
default: {
database: '#{database_name}',
hosts: %w[localhost:27017]
}
}
)

class Main
include Mongoid::Document
belongs_to :related, polymorphic: true, optional: true
end

class Related
include Mongoid::Document
has_one :parent, as: :related
end

class Two < Related; end
class Three < Related; end
RUBY

init_script = base_script + <<~RUBY
client = Mongoid::Clients.default
begin
client.database.drop
rescue Mongo::Error::OperationFailure
end

Main.destroy_all
Related.destroy_all

Main.create!(related: Two.create!)
Main.create!(related: Three.create!)
RUBY

bad_script = base_script + <<~RUBY
Main.includes(:related).entries
Main.last.related.id

Mongoid::Clients.default.database.drop
RUBY

bundle_gemfile = ENV.fetch(
'BUNDLE_GEMFILE',
File.join(project_root, 'Gemfile')
)

run_script = lambda do |script|
Open3.capture2e(
{ 'BUNDLE_GEMFILE' => bundle_gemfile },
RbConfig.ruby,
'-',
chdir: project_root,
stdin_data: script
)
end

init_out, init_status = run_script.call(init_script)
expect(init_status).to be_success, "failed to prepare polymorphic data: #{init_out}"

bad_out, bad_status = run_script.call(bad_script)
expect(bad_status).to be_success, "expected CLI reproduction to succeed, got #{bad_status.exitstatus}: #{bad_out}"
end
end
end
end