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

Support a dynamic collection name #4556

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 24 additions & 7 deletions lib/mongoid/clients/options.rb
Expand Up @@ -21,6 +21,12 @@ module Options
#
# @since 6.0.0
def with(options_or_context, &block)
# Only changing the collection name does not require the overhead of an entire new persistence context.
if options_or_context.is_a?(Hash) && (options_or_context.size == 1) && options_or_context.key?(:collection)
self.collection_name = options_or_context[:collection]
return block_given? ? yield(self) : self
end

original_cluster = persistence_context.cluster
set_persistence_context(options_or_context)
yield self
Expand All @@ -29,11 +35,15 @@ def with(options_or_context, &block)
end

def collection(parent = nil)
persistence_context.collection(parent)
@collection_name ? mongo_client[@collection_name] : persistence_context.collection(parent)
end

def collection_name
persistence_context.collection_name
@collection_name || persistence_context.collection_name
end

def collection_name=(collection_name)
@collection_name = collection_name.nil? ? nil : collection_name.to_sym
end

def mongo_client
Expand Down Expand Up @@ -93,11 +103,18 @@ def mongo_client
#
# @since 6.0.0
def with(options, &block)
original_cluster = persistence_context.cluster
PersistenceContext.set(self, options)
yield self
ensure
PersistenceContext.clear(self, original_cluster)
# Support changing just the collection name, when not used with a block
if !block_given? && options.is_a?(Hash) && (options.size == 1) && options.key?(:collection)
return all.with(options, &block)
end

begin
original_cluster = persistence_context.cluster
PersistenceContext.set(self, options)
yield self
ensure
PersistenceContext.clear(self, original_cluster)
end
end

def persistence_context
Expand Down
2 changes: 1 addition & 1 deletion lib/mongoid/contextual/mongo.rb
Expand Up @@ -342,7 +342,7 @@ def map(field = nil, &block)
# @since 3.0.0
def initialize(criteria)
@criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
@collection = @klass.collection
@collection = criteria.collection
criteria.send(:merge_type_selection)
@view = collection.find(criteria.selector, session: _session)
apply_options
Expand Down
7 changes: 6 additions & 1 deletion lib/mongoid/factory.rb
Expand Up @@ -47,6 +47,9 @@ def from_db(klass, attributes = nil, criteria = nil)
if criteria && criteria.association && criteria.parent_document
obj.set_relation(criteria.association.inverse, criteria.parent_document)
end
# Use the collection name from the criteria so that any changes to this model
# are saved back to the collection from which it was retrieved.
obj.collection_name = criteria.collection_name if criteria
obj
else
camelized = type.camelize
Expand All @@ -63,7 +66,9 @@ def from_db(klass, attributes = nil, criteria = nil)
raise Errors::UnknownModel.new(camelized, type)
end

constantized.instantiate(attributes, selected_fields)
obj = constantized.instantiate(attributes, selected_fields)
obj.collection_name = criteria.collection_name if criteria
obj
end
end
end
Expand Down
38 changes: 37 additions & 1 deletion spec/mongoid/clients/options_spec.rb
Expand Up @@ -74,6 +74,32 @@
end
end

context 'when not passing a block' do

let(:collection_name) do
Minim.with(options).collection_name
end

let(:persistence_context) do
Minim.with(options).persistence_context
end

let(:options) { { collection: 'another-collection' } }

it 'uses the collection' do
expect(collection_name).to eq(options[:collection].to_sym)
end

it 'does not change the context' do
expect(Minim.persistence_context).to eq(persistence_context)
end

it 'does not include the collection option in the client options' do
expect(persistence_context.client.options[:collection]).to be_nil
expect(persistence_context.client.options['collection']).to be_nil
end
end

context 'when passing a block', if: testing_locally? do

let!(:connections_before) do
Expand Down Expand Up @@ -184,6 +210,10 @@
it 'uses that collection' do
expect(persistence_context.collection.name).to eq(options[:collection])
end

it 'uses that collection when no block is supplied' do
expect(Minim.with(options).collection_name).to eq(options[:collection].to_sym)
end
end

context 'when returning a criteria' do
Expand Down Expand Up @@ -437,8 +467,14 @@
{ collection: 'other' }
end

let(:collection_name) do
test_model.with(options) do |object|
object.collection_name
end
end

it 'uses that collection' do
expect(persistence_context.collection.name).to eq(options[:collection])
expect(collection_name).to eq(options[:collection].to_sym)
end
end

Expand Down
14 changes: 14 additions & 0 deletions spec/mongoid/contextual/mongo_spec.rb
Expand Up @@ -1363,6 +1363,20 @@
it "sets the view selector" do
expect(context.view.selector).to eq({ "name" => "Depeche Mode" })
end

it "sets the collection" do
expect(context.collection).to eq(Band.collection)
end

context "with a different collection name" do
let(:criteria) do
Band.with(collection: 'other').where(name: "Depeche Mode").no_timeout
end

it "sets the collection" do
expect(context.collection.name).to eq("other")
end
end
end

[ :length, :size ].each do |method|
Expand Down
27 changes: 27 additions & 0 deletions spec/mongoid/factory_spec.rb
Expand Up @@ -236,5 +236,32 @@ def self.instantiate(*args)
end

end

context "when built from within the context of a criteria" do

let(:criteria) do
Person.with(collection: 'other').where(title: "Sir")
end

let(:attributes) do
{ "_type" => "Person", "title" => "Sir" }
end

let(:document) do
described_class.from_db(Person, attributes, criteria)
end

it "generates based on the type" do
expect(document).to be_a(Person)
end

it "sets the attributes" do
expect(document.title).to eq("Sir")
end

it "retains the collection name" do
expect(document.collection_name).to eq(:other)
end
end
end
end