Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backports to 9.0 (redux) #5887

Merged
merged 2 commits into from
Oct 21, 2024
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
15 changes: 14 additions & 1 deletion lib/mongoid/clients/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def persistence_context
else
PersistenceContext.get(self) ||
PersistenceContext.get(self.class) ||
PersistenceContext.new(self.class, storage_options)
PersistenceContext.new(self.class, default_storage_options)
end
end

Expand All @@ -112,6 +112,19 @@ def persistence_context?

private

def default_storage_options
# Nothing is overridden, we use either the default storage_options
# or storage_options defined for the document class.
return storage_options if Threaded.client_override.nil? && Threaded.database_override.nil?

storage_options.tap do |opts|
# Globally overridden client replaces client defined for the document class.
opts[:client] = Threaded.client_override unless Threaded.client_override.nil?
# Globally overridden database replaces database defined for the document class.
opts[:database] = Threaded.database_override unless Threaded.database_override.nil?
end
end

def set_persistence_context(options_or_context)
PersistenceContext.set(self, options_or_context)
end
Expand Down
16 changes: 15 additions & 1 deletion lib/mongoid/matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,25 @@ module Matcher
# from and behaves identically to association traversal for the purposes
# of, for example, subsequent array element retrieval.
#
# @param [ Document | Hash ] document The document to extract from.
# @param [ Document | Hash | String ] document The document to extract from.
# @param [ String ] key The key path to extract.
#
# @return [ Object | Array ] Field value or values.
module_function def extract_attribute(document, key)
# The matcher system will wind up sending atomic values to this as well,
# when attepting to match more complex types. If anything other than a
# Document or a Hash is given, we'll short-circuit the logic and just
# return an empty array.
return [] unless document.is_a?(Hash) || document.is_a?(Document)

# Performance optimization; if the key does not include a '.' character,
# it must reference an immediate attribute of the document.
unless key.include?('.')
hash = document.respond_to?(:attributes) ? document.attributes : document
key = find_exact_key(hash, key)
return key ? [ hash[key] ] : []
end

if document.respond_to?(:as_attributes, true)
# If a document has hash fields, as_attributes would keep those fields
# as Hash instances which do not offer indifferent access.
Expand Down
4 changes: 4 additions & 0 deletions spec/mongoid/association/referenced/belongs_to/proxy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@
person.save!
end

# NOTE: there as a bad interdependency here, with the auto_save_spec.rb
# file. If auto_save_spec.rb runs before this, the following specs fail
# with "undefined method `nullify' for an instance of Person".

context "when parent exists" do

context "when child is destroyed" do
Expand Down
125 changes: 125 additions & 0 deletions spec/mongoid/clients/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -522,4 +522,129 @@
end
end
end

context 'with global overrides' do
let(:default_subscriber) do
Mrss::EventSubscriber.new
end

let(:override_subscriber) do
Mrss::EventSubscriber.new
end

context 'when global client is overridden' do
before do
Mongoid.clients['override_client'] = { hosts: SpecConfig.instance.addresses, database: 'default_override_database' }
Mongoid.override_client('override_client')
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.client('override_client').subscribe(Mongo::Monitoring::COMMAND, override_subscriber)
end

after do
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.client('override_client').unsubscribe(Mongo::Monitoring::COMMAND, override_subscriber)
Mongoid.override_client(nil)
Mongoid.clients['override_client'] = nil
end

it 'uses the overridden client for create' do
Minim.create!

expect(override_subscriber.single_command_started_event('insert').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('insert')).to be_empty
end

it 'uses the overridden client for queries' do
Minim.where(name: 'Dmitry').to_a

expect(override_subscriber.single_command_started_event('find').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('find')).to be_empty
end

context 'when the client is set on the model level' do
let(:model_level_subscriber) do
Mrss::EventSubscriber.new
end

around(:example) do |example|
opts = Minim.storage_options
Minim.storage_options = Minim.storage_options.merge( { client: 'model_level_client' } )
Mongoid.clients['model_level_client'] = { hosts: SpecConfig.instance.addresses, database: 'model_level_database' }
Mongoid.client('model_level_client').subscribe(Mongo::Monitoring::COMMAND, override_subscriber)
example.run
Mongoid.client('model_level_client').unsubscribe(Mongo::Monitoring::COMMAND, override_subscriber)
Mongoid.clients['model_level_client'] = nil
Minim.storage_options = opts
end

# This behaviour is consistent with 8.x
it 'uses the overridden client for create' do
Minim.create!

expect(override_subscriber.single_command_started_event('insert').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('insert')).to be_empty
expect(model_level_subscriber.command_started_events('insert')).to be_empty
end

# This behaviour is consistent with 8.x
it 'uses the overridden client for queries' do
Minim.where(name: 'Dmitry').to_a

expect(override_subscriber.single_command_started_event('find').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('find')).to be_empty
expect(model_level_subscriber.command_started_events('find')).to be_empty
end
end
end

context 'when global database is overridden' do
before do
Mongoid.override_database('override_database')
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
end

after do
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.override_database(nil)
end

it 'uses the overridden database for create' do
Minim.create!

expect(default_subscriber.single_command_started_event('insert').database_name).to eq('override_database')
end

it 'uses the overridden database for queries' do
Minim.where(name: 'Dmitry').to_a

expect(default_subscriber.single_command_started_event('find').database_name).to eq('override_database')
end

context 'when the database is set on the model level' do
around(:example) do |example|
opts = Minim.storage_options
Minim.storage_options = Minim.storage_options.merge( { database: 'model_level_database' } )
Mongoid.clients['model_level_client'] = { hosts: SpecConfig.instance.addresses, database: 'model_level_database' }
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
example.run
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.clients['model_level_client'] = nil
Minim.storage_options = opts
end

# This behaviour is consistent with 8.x
it 'uses the overridden database for create' do
Minim.create!

expect(default_subscriber.single_command_started_event('insert').database_name).to eq('override_database')
end

it 'uses the overridden database for queries' do
Minim.where(name: 'Dmitry').to_a

expect(default_subscriber.single_command_started_event('find').database_name).to eq('override_database')
end
end
end
end
end
Loading