From fed822ae84f15755104ab0f975ee942f142ebf79 Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:24:50 +0900 Subject: [PATCH 01/43] MONGOID-5719 Cleanup version compatibility tables (#5760) --- docs/reference/compatibility.txt | 189 +++---------------------------- 1 file changed, 16 insertions(+), 173 deletions(-) diff --git a/docs/reference/compatibility.txt b/docs/reference/compatibility.txt index e30124b83..8b5a02340 100644 --- a/docs/reference/compatibility.txt +++ b/docs/reference/compatibility.txt @@ -34,52 +34,17 @@ specified Mongoid versions. - Driver 2.17-2.10 - Driver 2.9-2.7 - * - 9.0 + * - 8.0 thru 9.0 - |checkmark| - - - * - 8.1 + * - 7.2 thru 7.5 - |checkmark| - - - - - - * - 8.0 - |checkmark| - - - - * - 7.5 - - |checkmark| - - |checkmark| - - - - * - 7.4 - - |checkmark| - - |checkmark| - - - - * - 7.3 - - |checkmark| - - |checkmark| - - - - * - 7.2 - - |checkmark| - - |checkmark| - - - - * - 7.1 - - |checkmark| - - |checkmark| - - |checkmark| - - * - 7.0 - - |checkmark| - - |checkmark| - - |checkmark| - - * - 6.4 + * - 7.0 thru 7.1 - |checkmark| - |checkmark| - |checkmark| @@ -107,9 +72,9 @@ is deprecated. - Ruby 2.4 - Ruby 2.3 - Ruby 2.2 - - JRuby 9.2 - - JRuby 9.3 - JRuby 9.4 + - JRuby 9.3 + - JRuby 9.2 * - 9.0 - |checkmark| @@ -121,9 +86,9 @@ is deprecated. - - - + - |checkmark| - - - - |checkmark| * - 8.1 - |checkmark| @@ -139,7 +104,6 @@ is deprecated. - |checkmark| - - * - 8.0 - - |checkmark| @@ -164,9 +128,9 @@ is deprecated. - - - - - D - - |checkmark| - + - |checkmark| + - D * - 7.4 - @@ -178,9 +142,9 @@ is deprecated. - - - - - |checkmark| - - + - |checkmark| * - 7.3 - @@ -192,9 +156,9 @@ is deprecated. - D - D - - - |checkmark| - - + - |checkmark| * - 7.2 - @@ -206,9 +170,9 @@ is deprecated. - D - D - - - |checkmark| - - + - |checkmark| * - 7.1 - @@ -220,9 +184,9 @@ is deprecated. - |checkmark| [#ruby-2.4]_ - |checkmark| - - - |checkmark| - - + - |checkmark| * - 7.0 - @@ -234,23 +198,9 @@ is deprecated. - |checkmark| [#ruby-2.4]_ - |checkmark| - |checkmark| [#ruby-2.2]_ - - |checkmark| - - - - - - * - 6.4 - - - - - - - |checkmark| - - |checkmark| - - |checkmark| [#ruby-2.4]_ - - |checkmark| - - |checkmark| [#ruby-2.2]_ - - |checkmark| - - - - .. [#mongoid-7.3-ruby-3.0] Mongoid version 7.3.2 or higher is required. @@ -296,33 +246,7 @@ and will be removed in a next version. - MongoDB 3.0 - MongoDB 2.6 - * - 9.0 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 8.1 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 8.0 + * - 8.0 thru 9.0 - |checkmark| - |checkmark| - |checkmark| @@ -335,7 +259,7 @@ and will be removed in a next version. - - - * - 7.5 + * - 7.4 thru 7.5 - |checkmark| - |checkmark| - |checkmark| @@ -348,72 +272,7 @@ and will be removed in a next version. - D - D - * - 7.4 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.3 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.2 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.1 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.0 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 6.4 + * - 7.0 thru 7.3 - - - |checkmark| @@ -456,15 +315,7 @@ are supported by Mongoid. - - - * - 8.1 - - |checkmark| [#rails-7.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - - - * - 8.0 + * - 8.0 thru 8.1 - |checkmark| [#rails-7.1]_ - |checkmark| - |checkmark| @@ -520,14 +371,6 @@ are supported by Mongoid. - |checkmark| - |checkmark| - * - 6.4 - - - - - - - - - - |checkmark| - - |checkmark| - .. [#rails-5-ruby-3.0] Using Rails 5.x with Ruby 3 is not supported. .. [#rails-6] Rails 6.0 requires Mongoid 7.0.5 or later. From 38db7bf499b442a7dff66eedf7cac1f0d6929f0d Mon Sep 17 00:00:00 2001 From: Alex Bevilacqua Date: Wed, 17 Jan 2024 16:22:23 -0500 Subject: [PATCH 02/43] Fix formatting in configuration.txt (#5774) --- docs/reference/configuration.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index 92b1a44e2..b7864defc 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -746,11 +746,11 @@ It may be desirable to further configure TLS options in your application, for example by enabling or disabling certain ciphers. This can be done by setting TLS context hooks on the Ruby driver -- TLS context -hooks are user-provided ``Proc``s that will be invoked before any TLS socket +hooks are user-provided ``Proc``\(s) that will be invoked before any TLS socket connection in the driver and can be used to modify the underlying ``OpenSSL::SSL::SSLContext`` object used by the socket. -To set TLS context hooks, add ``Proc``s to the ``Mongo.tls_context_hooks`` +To set TLS context hooks, add ``Proc``\(s) to the ``Mongo.tls_context_hooks`` array. This can be done in an initializer. The example below adds a hook that only enables the "AES256-SHA" cipher. From 4635de7ed52e25927eede33a8991d1877e5619f1 Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov Date: Fri, 19 Jan 2024 17:17:58 +0100 Subject: [PATCH 03/43] MONGOID-5709 Fix atomic_path calculation (#5773) --- lib/mongoid/atomic.rb | 14 ++++--- .../has_and_belongs_to_many_spec.rb | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lib/mongoid/atomic.rb b/lib/mongoid/atomic.rb index 7dc970c53..3401f339a 100644 --- a/lib/mongoid/atomic.rb +++ b/lib/mongoid/atomic.rb @@ -179,11 +179,15 @@ def atomic_position # # @return [ Object ] The associated path. def atomic_paths - @atomic_paths ||= if _association - _association.path(self) - else - Atomic::Paths::Root.new(self) - end + return @atomic_paths if @atomic_paths + + paths = if _association + _association.path(self) + else + Atomic::Paths::Root.new(self) + end + + paths.tap { @atomic_paths = paths unless new_record? } end # Get all the attributes that need to be pulled. diff --git a/spec/integration/associations/has_and_belongs_to_many_spec.rb b/spec/integration/associations/has_and_belongs_to_many_spec.rb index e6d269e8e..838b4533f 100644 --- a/spec/integration/associations/has_and_belongs_to_many_spec.rb +++ b/spec/integration/associations/has_and_belongs_to_many_spec.rb @@ -3,6 +3,28 @@ require 'spec_helper' +module HabtmSpec + class Page + include Mongoid::Document + embeds_many :blocks, class_name: 'HabtmSpec::Block' + end + + class Block + include Mongoid::Document + embedded_in :page, class_name: 'HabtmSpec::Page' + end + + class ImageBlock < Block + has_and_belongs_to_many :attachments, inverse_of: nil, class_name: 'HabtmSpec::Attachment' + accepts_nested_attributes_for :attachments + end + + class Attachment + include Mongoid::Document + field :file, type: String + end +end + describe 'has_and_belongs_to_many associations' do context 'when an anonymous class defines a has_and_belongs_to_many association' do @@ -19,4 +41,22 @@ expect(klass.new.movies.build).to be_a Movie end end + + context 'when an embedded has habtm relation' do + let(:attachment) { HabtmSpec::Attachment.create!(file: 'foo.jpg') } + + let(:page) { HabtmSpec::Page.create! } + + let(:image_block) do + image_block = page.blocks.build({ + _type: 'HabtmSpec::ImageBlock', + attachment_ids: [ attachment.id.to_s ], + attachments_attributes: { '1234' => { file: 'bar.jpg', id: attachment.id.to_s } } + }) + end + + it 'does not raise on save' do + expect { image_block.save! }.not_to raise_error + end + end end From f876adbcb5f992983399a74cdbbbf505a8e8f6f8 Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:21:56 +0900 Subject: [PATCH 04/43] MONGOID-5601 - Cleanup specs for search index mgmt (#5772) * Fix failing spec on Ruby 3.0+ * Cleanup specs --- spec/mongoid/tasks/database_spec.rb | 32 +++++++++-------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/spec/mongoid/tasks/database_spec.rb b/spec/mongoid/tasks/database_spec.rb index 91a0ecc9a..375828204 100644 --- a/spec/mongoid/tasks/database_spec.rb +++ b/spec/mongoid/tasks/database_spec.rb @@ -231,44 +231,32 @@ class Note end context 'when wait is true' do - before do - allow(described_class).to receive(:wait_for_search_indexes) - described_class.create_search_indexes([ searchable_model_spy ], wait: true) - end - it 'invokes both create_search_indexes and wait_for_search_indexes' do - expect(searchable_model_spy).to have_received(:create_search_indexes) - expect(described_class).to have_received(:wait_for_search_indexes).with(searchable_model_spy => index_names) + expect(searchable_model_spy).to receive(:create_search_indexes) + expect(described_class).to receive(:wait_for_search_indexes).with({ searchable_model_spy => index_names }) + + described_class.create_search_indexes([searchable_model_spy], wait: true) end end context 'when wait is false' do - before do - allow(described_class).to receive(:wait_for_search_indexes) - described_class.create_search_indexes([ searchable_model_spy ], wait: false) - end - it 'invokes only create_search_indexes' do - expect(searchable_model_spy).to have_received(:create_search_indexes) - expect(described_class).not_to have_received(:wait_for_search_indexes) + expect(searchable_model_spy).to receive(:create_search_indexes) + expect(described_class).to_not receive(:wait_for_search_indexes) + + described_class.create_search_indexes([searchable_model_spy], wait: false) end end end describe '.remove_search_indexes' do - before do + it 'calls remove_search_indexes on all non-embedded models' do models.each do |model| - allow(model).to receive(:remove_search_indexes) unless model.embedded? + expect(model).to receive(:remove_search_indexes) unless model.embedded? end described_class.remove_search_indexes(models) end - - it 'calls remove_search_indexes on all non-embedded models' do - models.each do |model| - expect(model).to have_received(:remove_search_indexes) unless model.embedded? - end - end end describe ".undefined_indexes" do From 8154e7c14efeb45a88a58a11aa53c5702e8008ac Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov Date: Tue, 23 Jan 2024 17:43:26 +0100 Subject: [PATCH 05/43] MONGOID-5735 Deprecate aggregable API (#5778) --- docs/release-notes/mongoid-9.0.txt | 8 ++++++++ lib/mongoid/contextual/aggregable/memory.rb | 10 ++++++++++ lib/mongoid/contextual/aggregable/mongo.rb | 10 ++++++++++ lib/mongoid/contextual/aggregable/none.rb | 10 ++++++++++ 4 files changed, 38 insertions(+) diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index fa9c74160..375d2689e 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -17,6 +17,14 @@ The complete list of releases is available `on GitHub please consult GitHub releases for detailed release notes and JIRA for the complete list of issues fixed in each release, including bug fixes. +Aggregable API is deprecated +---------------------------- + +Mongoid 9 deprecated aggregable API in modules +``Mongoid::Contextual::Aggregable::Memory``, +``Mongoid::Contextual::Aggregable::Mongo``, and +``Mongoid::Contextual::Aggregable::None``. + Support for Ruby 2.6 and JRuby 9.3 Dropped ------------------------------------------- diff --git a/lib/mongoid/contextual/aggregable/memory.rb b/lib/mongoid/contextual/aggregable/memory.rb index 7e328fae9..c4cf0c87d 100644 --- a/lib/mongoid/contextual/aggregable/memory.rb +++ b/lib/mongoid/contextual/aggregable/memory.rb @@ -15,11 +15,13 @@ module Memory # @return [ Hash ] A Hash containing the aggregate values. # If no documents are present, then returned Hash will have # count, sum of 0 and max, min, avg of nil. + # @deprecated def aggregates(field) %w(count sum avg min max).each_with_object({}) do |method, hash| hash[method] = send(method, field) end end + Mongoid.deprecate(self, :aggregates) # Get the average value of the provided field. # @@ -29,6 +31,7 @@ def aggregates(field) # @param [ Symbol ] field The field to average. # # @return [ Numeric ] The average. + # @deprecated def avg(field) total = count { |doc| !doc.send(field).nil? } return nil unless total > 0 @@ -36,6 +39,7 @@ def avg(field) total = total.to_f if total.is_a?(Integer) sum(field) / total end + Mongoid.deprecate(self, :avg) # Get the max value of the provided field. If provided a block, will # return the Document with the greatest value for the field, in @@ -53,11 +57,13 @@ def avg(field) # # @return [ Numeric | Document ] The max value or document with the max # value. + # @deprecated def max(field = nil) return super() if block_given? aggregate_by(field, :max) end + Mongoid.deprecate(self, :max) # Get the min value of the provided field. If provided a block, will # return the Document with the smallest value for the field, in @@ -75,11 +81,13 @@ def max(field = nil) # # @return [ Numeric | Document ] The min value or document with the min # value. + # @deprecated def min(field = nil) return super() if block_given? aggregate_by(field, :min) end + Mongoid.deprecate(self, :min) # Get the sum value of the provided field. If provided a block, will # return the sum in accordance with Ruby's enumerable API. @@ -93,11 +101,13 @@ def min(field = nil) # @param [ Symbol ] field The field to sum. # # @return [ Numeric ] The sum value. + # @deprecated def sum(field = nil) return super() if block_given? aggregate_by(field, :sum) || 0 end + Mongoid.deprecate(self, :sum) private diff --git a/lib/mongoid/contextual/aggregable/mongo.rb b/lib/mongoid/contextual/aggregable/mongo.rb index 1b050281f..ed1bf6303 100644 --- a/lib/mongoid/contextual/aggregable/mongo.rb +++ b/lib/mongoid/contextual/aggregable/mongo.rb @@ -26,6 +26,7 @@ module Mongo # @return [ Hash ] A Hash containing the aggregate values. # If no documents are found, then returned Hash will have # count, sum of 0 and max, min, avg of nil. + # @deprecated def aggregates(field) result = collection.aggregate(pipeline(field), session: _session).to_a if result.empty? @@ -34,6 +35,7 @@ def aggregates(field) result.first end end + Mongoid.deprecate(self, :aggregates) # Get the average value of the provided field. # @@ -43,9 +45,11 @@ def aggregates(field) # @param [ Symbol ] field The field to average. # # @return [ Float ] The average. + # @deprecated def avg(field) aggregates(field)["avg"] end + Mongoid.deprecate(self, :avg) # Get the max value of the provided field. If provided a block, will # return the Document with the greatest value for the field, in @@ -63,9 +67,11 @@ def avg(field) # # @return [ Float | Document ] The max value or document with the max # value. + # @deprecated def max(field = nil) block_given? ? super() : aggregates(field)["max"] end + Mongoid.deprecate(self, :max) # Get the min value of the provided field. If provided a block, will # return the Document with the smallest value for the field, in @@ -83,9 +89,11 @@ def max(field = nil) # # @return [ Float | Document ] The min value or document with the min # value. + # @deprecated def min(field = nil) block_given? ? super() : aggregates(field)["min"] end + Mongoid.deprecate(self, :min) # Get the sum value of the provided field. If provided a block, will # return the sum in accordance with Ruby's enumerable API. @@ -99,9 +107,11 @@ def min(field = nil) # @param [ Symbol ] field The field to sum. # # @return [ Float ] The sum value. + # @deprecated def sum(field = nil) block_given? ? super() : aggregates(field)["sum"] || 0 end + Mongoid.deprecate(self, :sum) private diff --git a/lib/mongoid/contextual/aggregable/none.rb b/lib/mongoid/contextual/aggregable/none.rb index 13d410fae..fe2bd4b8f 100644 --- a/lib/mongoid/contextual/aggregable/none.rb +++ b/lib/mongoid/contextual/aggregable/none.rb @@ -15,9 +15,11 @@ module None # @param [ String | Symbol ] _field The field name. # # @return [ Hash ] A Hash with count, sum of 0 and max, min, avg of nil. + # @deprecated def aggregates(_field) Aggregable::EMPTY_RESULT.dup end + Mongoid.deprecate(self, :aggregates) # Always returns zero. # @@ -26,9 +28,11 @@ def aggregates(_field) # @param [ Symbol ] _field The field to sum. # # @return [ Integer ] Always zero. + # @deprecated def sum(_field = nil) 0 end + Mongoid.deprecate(self, :sum) # Always returns nil. # @@ -37,9 +41,11 @@ def sum(_field = nil) # @param [ Symbol ] _field The field to avg. # # @return [ nil ] Always nil. + # @deprecated def avg(_field) nil end + Mongoid.deprecate(self, :avg) # Always returns nil. # @@ -48,9 +54,11 @@ def avg(_field) # @param [ Symbol ] _field The field to min. # # @return [ nil ] Always nil. + # @deprecated def min(_field = nil) nil end + Mongoid.deprecate(self, :min) # Always returns nil. # @@ -59,7 +67,9 @@ def min(_field = nil) # @param [ Symbol ] _field The field to max. # # @return [ nil ] Always nil. + # @deprecated alias :max :min + Mongoid.deprecate(self, :max) end end end From 5e8f2b5f310d39c3b07669ae2afbd6e6f4592951 Mon Sep 17 00:00:00 2001 From: Alex Bevilacqua Date: Fri, 2 Feb 2024 17:00:29 -0500 Subject: [PATCH 06/43] MONGOID-5645: Incorporate railsmdb README into Mongoid 9 docs (#5782) * Add railsmdb to the Mongoid 9 release notes * Updated release notes * Updated installation instructions * Update release notes * Updated index --- docs/index.txt | 5 ++- docs/installation.txt | 35 +++++++++++----- docs/release-notes/mongoid-9.0.txt | 65 ++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 11 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index 76660e556..e4ef50e3e 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -6,8 +6,9 @@ Mongoid .. default-domain:: mongodb -Mongoid is the officially supported object-document mapper (ODM) -for MongoDB in Ruby. +Mongoid is the officially supported object-document mapper (ODM) for MongoDB in +Ruby. To work with Mongoid from the command line using ``rails``-like tooling, +the `railsmdb `_ utility can be used. .. toctree:: :titlesonly: diff --git a/docs/installation.txt b/docs/installation.txt index 7247273d6..2f4ecf0b4 100644 --- a/docs/installation.txt +++ b/docs/installation.txt @@ -32,16 +32,33 @@ To install the gem with bundler, include the following in your ``Gemfile``: Using Mongoid with a New Rails Application ========================================== -When creating a new Rails application and wish to use Mongoid for -data access, give the ``--skip-active-record`` flag to the ``rails new`` -command to avoid depending on and configuring ActiveRecord.. +By using the `railsmdb CLI `_ a new +Ruby on Rails application can be quickly generated using the same options as +``rails new``, but configured to work with MongoDB: + +.. code-block:: sh + + railsmdb new my_new_rails_app + +The ``rails`` CLI can also be used, however when creating a new Rails application +and where Mongoid will be used for data access, provide the ``--skip-active-record`` +flag to the ``rails new`` command to avoid depending on and configuring ActiveRecord. + +Additional examples can be found in the `tutorials `_. Using Mongoid with an Existing Rails Application ================================================ -When converting an existing Rails application to use Mongoid for data access, -the ``config/application.rb`` file needs to be updated to remove the -``require 'rails/all'`` line and explicitly include the required frameworks -(which could be all of the frameworks provided by Rails with the exception of -ActiveRecord). Any references to ActiveRecord in files in the ``config`` -directory and in the models also need to be removed. +Using the `railsmdb CLI `_ an existing +Rails application can easily be configured for use with Mongoid: + +.. code-block:: sh + + railsmdb setup + +Converting an existing Rails application without using ``railsmdb`` can be done +by updating the ``config/application.rb`` file to remove the ``require 'rails/all'`` +line and explicitly include the required frameworks (which could be all of the +frameworks provided by Rails with the exception ofActiveRecord). +Any references to ActiveRecord in files in the ``config`` directory and in the +models also need to be removed. diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index 375d2689e..6f04f807f 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -17,6 +17,71 @@ The complete list of releases is available `on GitHub please consult GitHub releases for detailed release notes and JIRA for the complete list of issues fixed in each release, including bug fixes. +Railsmdb for Mongoid +-------------------- + +To coincide with the release of Mongoid 9.0 a new command-line utility for creating, +updating, managing, and maintaining Rails applications has also +been made generally available! + +``railsmdb`` makes it easier to work with MongoDB from the command line through +common generators that Ruby on Rails developers are already familiar with. + +For example, you can use ``railsmdb`` to generate stubs for new Mongoid models: + +.. code-block:: sh + + $ bin/railsmdb generate model person + +This will create a new model at ``app/models/person.rb``: + +.. code-block:: ruby + + class Person + include Mongoid::Document + include Mongoid::Timestamp + end + +You can specify the fields of the model as well: + +.. code-block:: ruby + + # bin/railsmdb generate model person name:string birth:date + + class Person + include Mongoid::Document + include Mongoid::Timestamp + field :name, type: String + field :birth, type: Date + end + +You can instruct the generator to make the new model a subclass of another, +by passing the ``--parent`` option: + +.. code-block:: ruby + + # bin/railsmdb generate model student --parent=person + + class Student < Person + include Mongoid::Timestamp + end + +And if you need to store your models in a different collection than can be +inferred from the model name, you can specify ``--collection``: + +.. code-block:: ruby + + # bin/railsmdb generate model course --collection=classes + + class Course + include Mongoid::Document + include Mongoid::Timestamp + store_in collection: 'classes' + end + +For more information see the `GitHub Repository `_. + + Aggregable API is deprecated ---------------------------- From ad21404ba7db79b3d8c5d48990b6fc5ada9671dd Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Mon, 12 Feb 2024 08:26:06 -0700 Subject: [PATCH 07/43] RUBY-3397 update the public key for signing gems (#5785) --- gem-public_cert.pem | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gem-public_cert.pem b/gem-public_cert.pem index ab7c15c7c..68fa1da73 100644 --- a/gem-public_cert.pem +++ b/gem-public_cert.pem @@ -1,7 +1,7 @@ -----BEGIN CERTIFICATE----- MIIEeDCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBBMREwDwYDVQQDDAhkYngt cnVieTEXMBUGCgmSJomT8ixkARkWB21vbmdvZGIxEzARBgoJkiaJk/IsZAEZFgNj -b20wHhcNMjMwMTMxMTE1NjM1WhcNMjQwMTMxMTE1NjM1WjBBMREwDwYDVQQDDAhk +b20wHhcNMjQwMjA5MTc0NzIyWhcNMjUwMjA4MTc0NzIyWjBBMREwDwYDVQQDDAhk YngtcnVieTEXMBUGCgmSJomT8ixkARkWB21vbmdvZGIxEzARBgoJkiaJk/IsZAEZ FgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC0/Veq9l47cTfX tQ+kHq2NOCwJuJGt1iXWQ/vH/yp7pZ/bLej7gPDl2CfIngAXRjM7r1FkR9ya7VAm @@ -14,13 +14,13 @@ VYSiCHuc3yEDyq5M+98WGX2etbj6esYtzI3rDevpIAHPB6HQmtoJIA4dSl3gjFb+ D+YQSuB2qYu021FI9zeY9sbZyWysEXBxhwrmTk+XUV0qz+OQZkMCAwEAAaN7MHkw CQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFH4nnr4tYlatU57RbExW jG86YM5nMB8GA1UdEQQYMBaBFGRieC1ydWJ5QG1vbmdvZGIuY29tMB8GA1UdEgQY -MBaBFGRieC1ydWJ5QG1vbmdvZGIuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQAVSlgM -nFDWCCNLOCqG5/Lj4U62XoALkdCI+OZ30+WrA8qiRLSL9ZEziVK9AV7ylez+sriQ -m8XKZKsCN5ON4+zXw1S+6Ftz/R4zDg7nTb9Wgw8ibzsoiP6e4pRW3Fls3ZdaG4pW -+qMTbae9OiSrgI2bxNTII+v+1FcbQjOlMu8HPZ3ZfXnurXPgN5GxSyyclZI1QONO -HbUoKHRirZu0F7JCvQQq4EkSuLWPplRJfYEeJIYm05zhhFeEyqea2B/TTlCtXa42 -84vxXsxGzumuO8F2Q9m6/p95sNhqCp0B/SkKXIrRGJ7FBzupoORNRXHviS2OC3ty -4lwUzOlLTF/yO0wwYYfmtQOALQwKnW838vbYthMXvTjxB0EgVZ5PKto99WbjsXzy -wkeAWhd5b+5JS0zgDL4SvGB8/W2IY+y0zELkojBMgJPyrpAWHL/WSsSBMuhyI2Pv -xxaBVLklnJJ/qCCOZ3lG2MyVc/Nb0Mmq8ygWNsfwHmKKYuuWcviit0D0Tek= +MBaBFGRieC1ydWJ5QG1vbmdvZGIuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQBKGtHA +fpi3N/BL1J5O4CBsAjtF4jHDiw2r5MwK+66NzMh3uedjgPI7MoosemLy++SB+8BR +SE8bDkb6gfDQQzrI6KSXXyqH2TbQXpY5Tac7/yqXRiu8G2qOrOj4czB/Hq7j09CV +YoH88v6hL11i5jt6jPjFh8hXYG0hDQxhi3atRz5Wwd98tUf2DSbyJXJiRgCBeZjl +rP7AnKsWMu0C+zPlL+nXtQr+nTFtkKXRWfUJMqePpBqtriQvgQ+Y1ItqYVTSLuiM +iwUMcn/rGhdCMBSaKDXdFkIveCHQE2f2WBo2EdErrcTrgEKYYdNfzcb/43j7L1kx +AUwyTtk+HFrviBynQbKN82rjbZE+5gukVea5c7idQPkqacPYsoU37DI+hTlUyJkV +dcTtfEg44lLlfNukBslfiQf54r+uWbyB0m0rDUN/py7/Ghyzt5GLBU91uCO3dGoI +55uFRHMvEcJMTDeImC/nuucPCAiEGMHggr9+NPC0tqpxjGKTo7lS7GzUFjg= -----END CERTIFICATE----- From 621c78562f8b4933a07583435f5a30e2681cd9a6 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Tue, 13 Feb 2024 17:15:53 +0900 Subject: [PATCH 08/43] Wrap ActiveJob custom Serializers handling with on_load(:active_job) hook (#5780) Co-authored-by: Dmitry Rybakov --- lib/mongoid/railtie.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mongoid/railtie.rb b/lib/mongoid/railtie.rb index 14012a7e0..7a97d5917 100644 --- a/lib/mongoid/railtie.rb +++ b/lib/mongoid/railtie.rb @@ -120,9 +120,9 @@ def handle_configuration_error(e) # Add custom serializers for BSON::ObjectId initializer 'mongoid.active_job.custom_serializers' do - require 'mongoid/railties/bson_object_id_serializer' + ActiveSupport.on_load :active_job do + require 'mongoid/railties/bson_object_id_serializer' - config.after_initialize do ActiveJob::Serializers.add_serializers( [::Mongoid::Railties::ActiveJobSerializers::BsonObjectIdSerializer] ) From 78af4fb4b1eafad7ba76dbaa70170622169c598f Mon Sep 17 00:00:00 2001 From: Alex Bevilacqua Date: Thu, 15 Feb 2024 14:41:03 -0500 Subject: [PATCH 09/43] Remove legacy Mongoid 3 & 4 docs link --- docs/index.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index e4ef50e3e..0f320dd55 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -22,6 +22,3 @@ the `railsmdb `_ utility can be use contributing additional-resources ecosystem - - -For documentation on Mongoid 3 and 4, see ``_. From 861ce09993e145c7efc19a46aba04ceb2bb1efff Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Fri, 23 Feb 2024 12:53:38 -0500 Subject: [PATCH 10/43] DOCSP-37038: Fix broken links (#5788) --- docs/reference/configuration.txt | 11 +++++------ docs/reference/sharding.txt | 4 ++-- docs/reference/text-search.txt | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index b7864defc..35b90ab34 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -541,9 +541,8 @@ Place the following in one of environment configuration files, such as .. note:: - The ``log_level`` Mongoid `configuration option `_ - is not used when Mongoid operates in a Rails application, because Mongoid - inherits Rails' log level in this case. + The ``log_level`` Mongoid configuration option is not used when Mongoid operates + in a Rails application, because Mongoid inherits Rails' log level in this case. To configure either Mongoid or driver logger differently from the Rails logger, use an initializer as follows: @@ -590,8 +589,8 @@ Standalone ---------- When not loaded in a Ruby on Rails application, Mongoid respects the -``log_level`` top level `configuration option `_. -It can be given in the configuration file as follows: +``log_level`` top level configuration option. It can be given in the +configuration file as follows: .. code-block:: yaml @@ -830,7 +829,7 @@ Client-Side Encryption When loading the configuration file, Mongoid permits the file to contain ``BSON::Binary`` instances which are used for specifying ``keyId`` in the schema map for `client-side encryption -`_, +`_, as the following example shows: .. code-block:: yaml diff --git a/docs/reference/sharding.txt b/docs/reference/sharding.txt index ab451bf4a..675e62cae 100644 --- a/docs/reference/sharding.txt +++ b/docs/reference/sharding.txt @@ -35,8 +35,8 @@ Shard keys can be declared on models using the ``shard_key`` macro: end Note that in order to shard a collection, the collection must have an index -that starts with the shard key. Mongoid provides `index management -`_ functionality, which the examples here take +that starts with the shard key. Mongoid provides :ref:`index management +` functionality, which the examples here take advantage of. Mongoid supports two syntaxes for declaring shard keys. The standard syntax diff --git a/docs/reference/text-search.txt b/docs/reference/text-search.txt index c962b3667..b7c3013de 100644 --- a/docs/reference/text-search.txt +++ b/docs/reference/text-search.txt @@ -35,8 +35,8 @@ To perform text search with Mongoid, follow these steps: Defining Text Search Index -------------------------- -Index definition through Mongoid is described in detail on the `indexes -`_ page. Text search indexes are described in detail +Index definition through Mongoid is described in detail on the :ref:`indexes +` page. Text search indexes are described in detail under `text indexes `_ in the MongoDB manual. Below is an example definition of a Band model with a text index utilizing the description field: From dc4417a1ea18f50e269185840f858d9f3665f84f Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:08:23 +0100 Subject: [PATCH 11/43] Remove dependency on ruby2_keywords (#5789) Now that Ruby 2.7 is required (#5768) this is not needed anymore --- lib/mongoid.rb | 1 - mongoid.gemspec | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/lib/mongoid.rb b/lib/mongoid.rb index 8561c7346..7bc8ac8bf 100644 --- a/lib/mongoid.rb +++ b/lib/mongoid.rb @@ -4,7 +4,6 @@ require "forwardable" require "time" require "set" -require "ruby2_keywords" require "active_support" require "active_support/core_ext" diff --git a/mongoid.gemspec b/mongoid.gemspec index d6266ea5c..2884ad2e1 100644 --- a/mongoid.gemspec +++ b/mongoid.gemspec @@ -42,19 +42,6 @@ Gem::Specification.new do |s| s.add_dependency("mongo", ['>=2.18.0', '<3.0.0']) s.add_dependency("concurrent-ruby", ['>= 1.0.5', '< 2.0']) - # The ruby2_keywords gem is recommended for handling argument delegation issues, - # especially if support for 2.6 or prior is required. - # See https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/#delegation - # - # We have a bunch of complex delegation logic, including various method_missings. - # If we try to fix them "right", it will add too much logic. We will have to - # handle different Ruby versions (including minor ones, Ruby 2.6 and 2.7 - # behave differently), hash key types (strings vs symbols), ways of passing - # arguments (with curly braces vs without ones). - # - # Therefore, usage of this gem looks like a reasonable solution at the moment. - s.add_dependency("ruby2_keywords", "~> 0.0.5") - s.add_development_dependency("bson", ['>=4.14.0', '<5.0.0']) s.files = Dir.glob("lib/**/*") + %w(CHANGELOG.md LICENSE README.md Rakefile) From 919a9141302b99981f1b17fb02cbbf788ef3cadc Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Mon, 26 Feb 2024 14:26:46 -0500 Subject: [PATCH 12/43] DOCSP-37059: Fix build errors (#5790) * DOCSP-37059: Fix build errors * JS feedback --- docs/release-notes/mongoid-9.0.txt | 278 ++++++++++++------------ docs/release-notes/upgrading.txt | 4 +- docs/tutorials/automatic-encryption.txt | 14 +- 3 files changed, 150 insertions(+), 146 deletions(-) diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index 6f04f807f..b5d24842f 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -31,53 +31,53 @@ For example, you can use ``railsmdb`` to generate stubs for new Mongoid models: .. code-block:: sh - $ bin/railsmdb generate model person + $ bin/railsmdb generate model person This will create a new model at ``app/models/person.rb``: .. code-block:: ruby - class Person - include Mongoid::Document - include Mongoid::Timestamp - end + class Person + include Mongoid::Document + include Mongoid::Timestamp + end You can specify the fields of the model as well: .. code-block:: ruby - # bin/railsmdb generate model person name:string birth:date + # bin/railsmdb generate model person name:string birth:date - class Person - include Mongoid::Document - include Mongoid::Timestamp - field :name, type: String - field :birth, type: Date - end + class Person + include Mongoid::Document + include Mongoid::Timestamp + field :name, type: String + field :birth, type: Date + end You can instruct the generator to make the new model a subclass of another, by passing the ``--parent`` option: .. code-block:: ruby - # bin/railsmdb generate model student --parent=person + # bin/railsmdb generate model student --parent=person - class Student < Person - include Mongoid::Timestamp - end + class Student < Person + include Mongoid::Timestamp + end And if you need to store your models in a different collection than can be inferred from the model name, you can specify ``--collection``: .. code-block:: ruby - # bin/railsmdb generate model course --collection=classes + # bin/railsmdb generate model course --collection=classes - class Course - include Mongoid::Document - include Mongoid::Timestamp - store_in collection: 'classes' - end + class Course + include Mongoid::Document + include Mongoid::Timestamp + store_in collection: 'classes' + end For more information see the `GitHub Repository `_. @@ -121,10 +121,11 @@ If you want to restore the old behavior, you can set ``Mongoid.around_embedded_document_callbacks`` to true in your application. .. note:: - Enabling ``around_*`` callbacks for embedded documents is not recommended - as it may cause ``SystemStackError`` exceptions when a document has many - embedded documents. See `MONGOID-5658 `_ - for more details. + + Enabling ``around_*`` callbacks for embedded documents is not recommended + as it may cause ``SystemStackError`` exceptions when a document has many + embedded documents. See `MONGOID-5658 `_ + for more details. ``for_js`` method is deprecated @@ -176,22 +177,22 @@ In Mongoid 8.x and older ``touch`` method leaves models in the changed state: .. code-block:: ruby - # Mongoid 8.x behaviour - band = Band.create! - band.touch - band.changed? # => true - band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} + # Mongoid 8.x behaviour + band = Band.create! + band.touch + band.changed? # => true + band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} Starting from 9.0 Mongoid now correctly clears changed state after using ``touch`` method. .. code-block:: ruby - # Mongoid 9.0 behaviour - band = Band.create! - band.touch - band.changed? # => false - band.changes # => {} + # Mongoid 9.0 behaviour + band = Band.create! + band.touch + band.changed? # => false + band.changes # => {} Sandbox Mode for Rails Console ------------------------------ @@ -203,8 +204,9 @@ the commands executed in the console using the ``:default`` client won't be persisted in the database. .. note:: - If you execute commands in the sandbox mode *using any other client than default*, - these changes will be persisted as usual. + + If you execute commands in the sandbox mode *using any other client than default*, + these changes will be persisted as usual. New Transactions API -------------------- @@ -213,15 +215,15 @@ Mongoid 9.0 introduces new transactions API that is inspired by ActiveRecord: .. code-block:: ruby - Band.transaction do - Band.create(title: 'Led Zeppelin') - end + Band.transaction do + Band.create(title: 'Led Zeppelin') + end - band = Band.create(title: 'Deep Purple') - band.transaction do - band.active = false - band.save! - end + band = Band.create(title: 'Deep Purple') + band.transaction do + band.active = false + band.save! + end Please consult :ref:`transactions documentation ` for more details. @@ -262,11 +264,11 @@ when the instance was loaded, Mongoid will now raise a .. code-block:: ruby - Band.only(:name).first.label - #=> raises Mongoid::Errors::AttributeNotLoaded + Band.only(:name).first.label + #=> raises Mongoid::Errors::AttributeNotLoaded - Band.without(:label).first.label = 'Sub Pop Records' - #=> raises Mongoid::Errors::AttributeNotLoaded + Band.without(:label).first.label = 'Sub Pop Records' + #=> raises Mongoid::Errors::AttributeNotLoaded In earlier Mongoid versions, the same conditions would raise an ``ActiveModel::MissingAttributeError``. Please check your code for @@ -284,17 +286,17 @@ considers ``Time.zone`` to perform type conversion. .. code-block:: ruby - class Magazine - include Mongoid::Document + class Magazine + include Mongoid::Document - field :published_at, type: Time - end + field :published_at, type: Time + end - Time.zone = 'Asia/Tokyo' + Time.zone = 'Asia/Tokyo' - Magazine.gte(published_at: Date.parse('2022-09-26')) - #=> will return all results on or after Sept 26th, 2022 - # at 0:00 in Asia/Tokyo time zone. + Magazine.gte(published_at: Date.parse('2022-09-26')) + #=> will return all results on or after Sept 26th, 2022 + # at 0:00 in Asia/Tokyo time zone. In prior Mongoid versions, the above code would ignore the ``Time.zone`` (irrespective of the now-removed ``:use_activesupport_time_zone`` @@ -313,25 +315,25 @@ invoke ``#touch`` on its parent document. .. code-block:: ruby - class Address - include Mongoid::Document - include Mongoid::Timestamps + class Address + include Mongoid::Document + include Mongoid::Timestamps - embedded_in :mall, touch: false - end + embedded_in :mall, touch: false + end - class Mall - include Mongoid::Document - include Mongoid::Timestamps + class Mall + include Mongoid::Document + include Mongoid::Timestamps - embeds_many :addresses - end + embeds_many :addresses + end - mall = Mall.create! - address = mall.addresses.create! + mall = Mall.create! + address = mall.addresses.create! - address.touch - #=> updates address.updated_at but not mall.updated_at + address.touch + #=> updates address.updated_at but not mall.updated_at In addition, the ``#touch`` method has been optimized to perform one persistence operation per parent document, even when using multiple @@ -346,12 +348,12 @@ unless you explicitly set ``touch: false`` on the relation: .. code-block:: ruby - class Address - include Mongoid::Document - include Mongoid::Timestamps + class Address + include Mongoid::Document + include Mongoid::Timestamps - embedded_in :mall, touch: false - end + embedded_in :mall, touch: false + end For all other associations, the default remains ``touch: false``. @@ -383,7 +385,7 @@ defaults to ``true``. .. code-block:: ruby - Mongoid::Config.immutable_ids = true + Mongoid::Config.immutable_ids = true When set to false, the older, inconsistent behavior is restored. @@ -397,18 +399,18 @@ of the ``index`` macro: ``partial_filter_expression``, ``weights``, .. code-block:: ruby - class Person - include Mongoid::Document - field :a, as: :age - index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) - end + class Person + include Mongoid::Document + field :a, as: :age + index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) + end .. note:: - The expansion of field name aliases in index options such as - ``partial_filter_expression`` is performed according to the behavior of MongoDB - server 6.0. Future server versions may change how they interpret these options, - and Mongoid's functionality may not support such changes. + The expansion of field name aliases in index options such as + ``partial_filter_expression`` is performed according to the behavior of MongoDB + server 6.0. Future server versions may change how they interpret these options, + and Mongoid's functionality may not support such changes. BSON 5 and BSON::Decimal128 Fields @@ -420,32 +422,32 @@ declared as BSON::Decimal128 will return a BigDecimal value by default. .. code-block:: ruby - class Model - include Mongoid::Document + class Model + include Mongoid::Document - field :decimal_field, type: BSON::Decimal128 - end + field :decimal_field, type: BSON::Decimal128 + end - # under BSON <= 4 - Model.first.decimal_field.class #=> BSON::Decimal128 + # under BSON <= 4 + Model.first.decimal_field.class #=> BSON::Decimal128 - # under BSON >= 5 - Model.first.decimal_field.class #=> BigDecimal + # under BSON >= 5 + Model.first.decimal_field.class #=> BigDecimal If you need literal BSON::Decimal128 values with BSON 5, you may instruct Mongoid to allow literal BSON::Decimal128 fields: .. code-block:: ruby - Model.first.decimal_field.class #=> BigDecimal + Model.first.decimal_field.class #=> BigDecimal - Mongoid.allow_bson5_decimal128 = true - Model.first.decimal_field.class #=> BSON::Decimal128 + Mongoid.allow_bson5_decimal128 = true + Model.first.decimal_field.class #=> BSON::Decimal128 .. note:: - The ``allow_bson5_decimal128`` option only has any effect under - BSON 5 and later. BSON 4 and earlier ignore the setting entirely. + The ``allow_bson5_decimal128`` option only has any effect under + BSON 5 and later. BSON 4 and earlier ignore the setting entirely. Search Index Management with MongoDB Atlas @@ -457,23 +459,23 @@ API: .. code-block:: ruby - class SearchablePerson - include Mongoid::Document + class SearchablePerson + include Mongoid::Document - search_index { ... } # define the search index here - end + search_index { ... } # define the search index here + end - # create the declared search indexes; this returns immediately, but the - # search indexes may take several minutes before they are available. - SearchablePerson.create_search_indexes + # create the declared search indexes; this returns immediately, but the + # search indexes may take several minutes before they are available. + SearchablePerson.create_search_indexes - # query the available search indexes - SearchablePerson.search_indexes.each do |index| - # ... - end + # query the available search indexes + SearchablePerson.search_indexes.each do |index| + # ... + end - # remove all search indexes from the model's collection - SearchablePerson.remove_search_indexes + # remove all search indexes from the model's collection + SearchablePerson.remove_search_indexes If you are not connected to MongoDB Atlas, the search index definitions are ignored. Trying to create, enumerate, or remove search indexes will result in @@ -483,16 +485,16 @@ There are also rake tasks available, for convenience: .. code-block:: bash - # create search indexes for all models; waits for indexes to be created - # and shows progress on the terminal. - $ rake mongoid:db:create_search_indexes + # create search indexes for all models; waits for indexes to be created + # and shows progress on the terminal. + $ rake mongoid:db:create_search_indexes - # as above, but returns immediately and lets the indexes be created in the - # background - $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes + # as above, but returns immediately and lets the indexes be created in the + # background + $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes - # removes search indexes from all models - $ rake mongoid:db:remove_search_indexes + # removes search indexes from all models + $ rake mongoid:db:remove_search_indexes ``Time.configured`` has been removed @@ -506,33 +508,33 @@ Mongoid now requires that you set a time zone if you intend to do anything with time values (including using timestamps in your documents). Any uses of ``Time.configured`` must be replaced with ``Time.zone``. -... code-block:: ruby +.. code-block:: ruby - # before: - puts Time.configured.now + # before: + puts Time.configured.now - # after: - puts Time.zone.now + # after: + puts Time.zone.now - # or, better for finding the current Time specifically: - puts Time.current + # or, better for finding the current Time specifically: + puts Time.current If you do not set a time zone, you will see errors in your code related to ``nil`` values. If you are using Rails, the default time zone is already set to UTC. If you are not using Rails, you may set a time zone at the start of your program like this: -... code-block:: ruby +.. code-block:: ruby - Time.zone = 'UTC' + Time.zone = 'UTC' This will set the time zone to UTC. You can see all available time zone names by running the following command: -... code-block:: bash +.. code-block:: bash - $ ruby -ractive_support/values/time_zone \ - -e 'puts ActiveSupport::TimeZone::MAPPING.keys' + $ ruby -ractive_support/values/time_zone \ + -e 'puts ActiveSupport::TimeZone::MAPPING.keys' Records now remember the persistence context in which they were loaded/created @@ -540,10 +542,10 @@ Records now remember the persistence context in which they were loaded/created Consider the following code: -... code-block:: ruby +.. code-block:: ruby - record = Model.with(collection: 'other_collection') { Model.first } - record.update(field: 'value') + record = Model.with(collection: 'other_collection') { Model.first } + record.update(field: 'value') Prior to Mongoid 9.0, this could would silently fail to execute the update, because the storage options (here, the specification of an alternate @@ -560,9 +562,9 @@ a different database, or a different collection). If you need the legacy (pre-9.0) behavior, you can enable it with the following flag: -... code-block:: ruby +.. code-block:: ruby - Mongoid.legacy_persistence_context_behavior = true + Mongoid.legacy_persistence_context_behavior = true This flag defaults to false in Mongoid 9. diff --git a/docs/release-notes/upgrading.txt b/docs/release-notes/upgrading.txt index 1ae90c1cf..db27af622 100644 --- a/docs/release-notes/upgrading.txt +++ b/docs/release-notes/upgrading.txt @@ -49,10 +49,10 @@ Before you Upgrade ------------------ - *Test Coverage:* The best way to be sure that your application still works after upgrading -is to have good test coverage before you start the process. + is to have good test coverage before you start the process. - *Upgrade Ruby and Rails:* See `"Upgrading Ruby on Rails" `_ -for more information + for more information Upgrading Mongoid diff --git a/docs/tutorials/automatic-encryption.txt b/docs/tutorials/automatic-encryption.txt index dd7ce2890..4da882595 100644 --- a/docs/tutorials/automatic-encryption.txt +++ b/docs/tutorials/automatic-encryption.txt @@ -64,9 +64,11 @@ This can be done one of two ways. Install the automatic encryption shared library (Ruby driver 2.19+) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you use the Ruby driver version 2.19 and above the automatic encryption -shared library should be installed by following the instructions in the -`MongoDB manual `_. +If you use the Ruby driver version 2.19 and above, the automatic encryption +shared library should be installed by following the instructions on the +:manual:`Automatic Encryption Shared Library for Queryable Encryption +` +page in the Server manual. The steps required are as follows: @@ -253,9 +255,9 @@ Now we can tell Mongoid what should be encrypted: Known Limitations ~~~~~~~~~~~~~~~~~ -* MongoDB CSFLE has some limitations that are described in - `the server documentation. `_ - These limitations also apply to Mongoid. +* MongoDB CSFLE has some limitations that are described on the + :manual:`CSFLE Limitations ` + page in the Server manual. These limitations also apply to Mongoid. * Mongoid does not support encryption of ``embeds_many`` relations. * If you use ``:key_name_field`` option, the field must be encrypted using non-deterministic algorithm. To encrypt your field deterministically, you must From 82a89f35b30967be6c63948037d9b5ffeffba03d Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Tue, 27 Feb 2024 09:59:14 -0700 Subject: [PATCH 13/43] MONGOID-5704 Fix performance regression on belongs_to validation (#5787) * MONGOID-5704 don't try to validate persisted, unchanged associations * we just need the attribute name * fix failing specs * tread very carefully so we don't trigger a load while validating * hopefully fix has-many behavior * documentation * simplify * make sure attributes getter is defined * Fix typo -- records are not loaded *from the database* Co-authored-by: Dmitry Rybakov <160598371+comandeo-mongo@users.noreply.github.com> --------- Co-authored-by: Dmitry Rybakov <160598371+comandeo-mongo@users.noreply.github.com> --- lib/mongoid/validatable.rb | 8 ++ lib/mongoid/validatable/associated.rb | 114 ++++++++++++++++---- spec/mongoid/validatable/associated_spec.rb | 43 +++----- spec/support/models/name.rb | 10 ++ 4 files changed, 127 insertions(+), 48 deletions(-) diff --git a/lib/mongoid/validatable.rb b/lib/mongoid/validatable.rb index e43fe432f..ce7f5c4c1 100644 --- a/lib/mongoid/validatable.rb +++ b/lib/mongoid/validatable.rb @@ -38,6 +38,14 @@ def exit_validate Threaded.exit_validate(self) end + # Perform a validation within the associated block. + def validating + begin_validate + yield + ensure + exit_validate + end + # Given the provided options, are we performing validations? # # @example Are we performing validations? diff --git a/lib/mongoid/validatable/associated.rb b/lib/mongoid/validatable/associated.rb index f58b36a9a..b811cf132 100644 --- a/lib/mongoid/validatable/associated.rb +++ b/lib/mongoid/validatable/associated.rb @@ -16,32 +16,110 @@ module Validatable # # validates_associated :name, :addresses # end - class AssociatedValidator < ActiveModel::EachValidator + class AssociatedValidator < ActiveModel::Validator + # Required by `validates_with` so that the validator + # gets added to the correct attributes. + def attributes + options[:attributes] + end - # Validates that the associations provided are either all nil or all - # valid. If neither is true then the appropriate errors will be added to - # the parent document. + # Checks that the named associations of the given record + # (`attributes`) are valid. This does NOT load the associations + # from the database, and will only validate records that are dirty + # or unpersisted. # - # @example Validate the association. - # validator.validate_each(document, :name, name) + # If anything is not valid, appropriate errors will be added to + # the `document` parameter. + # + # @param [ Mongoid::Document ] document the document with the + # associations to validate. + def validate(document) + options[:attributes].each do |attr_name| + validate_association(document, attr_name) + end + end + + private + + # Validates that the given association provided is either nil, + # persisted and unchanged, or invalid. Otherwise, the appropriate errors + # will be added to the parent document. # # @param [ Document ] document The document to validate. # @param [ Symbol ] attribute The association to validate. - # @param [ Object ] value The value of the association. - def validate_each(document, attribute, value) - begin - document.begin_validate - valid = Array.wrap(value).collect do |doc| - if doc.nil? || doc.flagged_for_destroy? - true + def validate_association(document, attribute) + # grab the proxy from the instance variable directly; we don't want + # any loading logic to run; we just want to see if it's already + # been loaded. + proxy = document.ivar(attribute) + return unless proxy + + # if the variable exists, now we see if it is a proxy, or an actual + # document. It might be a literal document instead of a proxy if this + # document was created with a Document instance as a provided attribute, + # e.g. "Post.new(message: Message.new)". + target = proxy.respond_to?(:_target) ? proxy._target : proxy + + # Now, fetch the list of documents from the target. Target may be a + # single value, or a list of values, and in the case of HasMany, + # might be a rather complex collection. We need to do this without + # triggering a load, so it's a bit of a delicate dance. + list = get_target_documents(target) + + valid = document.validating do + # Now, treating the target as an array, look at each element + # and see if it is valid, but only if it has already been + # persisted, or changed, and hasn't been flagged for destroy. + list.all? do |value| + if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?) + value.validated? ? true : value.valid? else - doc.validated? ? true : doc.valid? + true end - end.all? - ensure - document.exit_validate + end + end + + document.errors.add(attribute, :invalid) unless valid + end + + private + + # Examine the given target object and return an array of + # documents (possibly empty) that the target represents. + # + # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy | HasMany::Enumerable ] target + # the target object to examine. + # + # @return [ Array ] the list of documents + def get_target_documents(target) + if target.respond_to?(:_loaded?) + get_target_documents_for_has_many(target) + else + get_target_documents_for_other(target) end - document.errors.add(attribute, :invalid, **options) unless valid + end + + # Returns the list of all currently in-memory values held by + # the target. The target will not be loaded. + # + # @param [ HasMany::Enumerable ] target the target that will + # be examined for in-memory documents. + # + # @return [ Array ] the in-memory documents + # held by the target. + def get_target_documents_for_has_many(target) + [ *target._loaded.values, *target._added.values ] + end + + # Returns the target as an array. If the target represents a single + # value, it is wrapped in an array. + # + # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy ] target + # the target to return. + # + # @return [ Array ] the target, as an array. + def get_target_documents_for_other(target) + Array.wrap(target) end end end diff --git a/spec/mongoid/validatable/associated_spec.rb b/spec/mongoid/validatable/associated_spec.rb index 4a86e9fbb..b9b72b7dc 100644 --- a/spec/mongoid/validatable/associated_spec.rb +++ b/spec/mongoid/validatable/associated_spec.rb @@ -76,7 +76,6 @@ end it "does not run validation on them" do - expect(description).to receive(:valid?).never expect(user).to be_valid end @@ -85,14 +84,14 @@ end end - describe "#validate_each" do + describe "#validate" do let(:person) do Person.new end let(:validator) do - described_class.new(attributes: person.attributes) + described_class.new(attributes: person.relations.keys) end context "when the association is a one to one" do @@ -100,7 +99,7 @@ context "when the association is nil" do before do - validator.validate_each(person, :name, nil) + validator.validate(person) end it "adds no errors" do @@ -109,14 +108,9 @@ end context "when the association is valid" do - - let(:associated) do - double(valid?: true, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :name, associated) + person.name = Name.new(first_name: 'A', last_name: 'B') + validator.validate(person) end it "adds no errors" do @@ -126,13 +120,9 @@ context "when the association is invalid" do - let(:associated) do - double(valid?: false, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :name, associated) + person.name = Name.new(first_name: 'Jamis', last_name: 'Buck') + validator.validate(person) end it "adds errors to the parent document" do @@ -150,7 +140,7 @@ context "when the association is empty" do before do - validator.validate_each(person, :addresses, []) + validator.validate(person) end it "adds no errors" do @@ -160,13 +150,9 @@ context "when the association has invalid documents" do - let(:associated) do - double(valid?: false, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :addresses, [ associated ]) + person.addresses << Address.new(street: '123') + validator.validate(person) end it "adds errors to the parent document" do @@ -176,13 +162,10 @@ context "when the association has all valid documents" do - let(:associated) do - double(valid?: true, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :addresses, [ associated ]) + person.addresses << Address.new(street: '123 First St') + person.addresses << Address.new(street: '456 Second St') + validator.validate(person) end it "adds no errors" do diff --git a/spec/support/models/name.rb b/spec/support/models/name.rb index 83a43c71f..9083504b7 100644 --- a/spec/support/models/name.rb +++ b/spec/support/models/name.rb @@ -5,6 +5,8 @@ class Name include Mongoid::Document include Mongoid::Attributes::Dynamic + validate :is_not_jamis + field :_id, type: String, overwrite: true, default: ->{ "#{first_name}-#{last_name}" } @@ -24,4 +26,12 @@ class Name def set_parent=(set = false) self.parent_title = namable.title if set end + + private + + def is_not_jamis + if first_name == 'Jamis' && last_name == 'Buck' + errors.add(:base, :invalid) + end + end end From 299043264cadc6fd95a93d3fead0533eae80dd59 Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov <160598371+comandeo-mongo@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:07:48 +0100 Subject: [PATCH 14/43] Remove unnecessary file (#5792) --- upload-api-docs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upload-api-docs b/upload-api-docs index 01b0534b5..eb23222bc 100755 --- a/upload-api-docs +++ b/upload-api-docs @@ -29,6 +29,7 @@ class FileUploader end def upload_docs + puts "Uploading to #{@bucket}" Dir.glob("#{@docs_path}/**/*").each do |file| next if File.directory?(file) @@ -115,6 +116,11 @@ def generate_docs(options) '--readme', './README.md', '-o', options[:docs_path] ) + begin + File.delete(File.join(options[:docs_path], 'frames.html')) + rescue StandardError + nil + end end options = Options.new From fbccb6a5fc78fb5cc15e99cb8594f41c1bdcc198 Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov <160598371+comandeo-mongo@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:11:45 +0100 Subject: [PATCH 15/43] Use latest YARD version (#5797) --- gemfiles/standard.rb | 2 +- upload-api-docs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gemfiles/standard.rb b/gemfiles/standard.rb index 59cdf8d0f..e89e2ae89 100644 --- a/gemfiles/standard.rb +++ b/gemfiles/standard.rb @@ -3,7 +3,7 @@ def standard_dependencies gem 'rake' group :development do - gem 'yard' + gem 'yard', '>= 0.9.35' platform :mri do # Debugger for VSCode. diff --git a/upload-api-docs b/upload-api-docs index eb23222bc..33f36f74a 100755 --- a/upload-api-docs +++ b/upload-api-docs @@ -7,7 +7,7 @@ gemfile true do source 'https://rubygems.org' gem 'nokogiri' gem 'aws-sdk-s3' - gem 'yard' + gem 'yard', '>= 0.9.35' end require 'aws-sdk-s3' From bd0ca4e5eea07f05106bbc6dce88aaacadb6fbf4 Mon Sep 17 00:00:00 2001 From: Nora Reidy Date: Tue, 5 Mar 2024 11:19:01 -0500 Subject: [PATCH 16/43] DOCSP-37327: Fix table format (#5798) --- docs/release-notes/mongoid-8.0.txt | 339 +++++++++++++++++------------ 1 file changed, 197 insertions(+), 142 deletions(-) diff --git a/docs/release-notes/mongoid-8.0.txt b/docs/release-notes/mongoid-8.0.txt index 69e17c6d9..b0d93132c 100644 --- a/docs/release-notes/mongoid-8.0.txt +++ b/docs/release-notes/mongoid-8.0.txt @@ -84,59 +84,69 @@ evolve an uncastable value, the inputted value is returned. See the section on Some ``mongoize``, ``demongoize`` and ``evolve`` methods were also changed to perform consistently with rails and the other ``mongoize``, ``demongoize`` and -``evolve`` methods. The following is a table of the changes in functionality: - -+--------------+------------------------+------------------------+-----------------------+ -| Field Type | Situation | Previous Functionality | New Functionality | -+==============+========================+========================+=======================+ -| Boolean | When a non-boolean | return ``false`` | return ``nil`` | -| | string is assigned: | | | -| | "bogus value" | | | -+--------------+------------------------+------------------------+-----------------------+ -| Array/Hash | When a value that is | raise ``InvalidValue`` | return ``nil`` | -| | not an array or hash | error | | -| | is assigned | | | -+--------------+------------------------+------------------------+-----------------------+ -| Set | When a value that is | raise ``NoMethodError``| return ``nil`` | -| | not a set is assigned: | Exception: undefined | | -| | 1 | method ``to_a`` for | | -| | | 1:Integer | | -+--------------+------------------------+------------------------+-----------------------+ -| Regexp | When persisting and | return a | return a | -| | reading a Regexp from | ``BSON::Regexp::Raw`` | ``Regexp`` | -| | the database | | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When assigning a | raise ``NoMethodError``| return ``nil`` | -| | bogus value: ``:bogus``| Exception: undefined | | -| | | method ``to_i`` | | -| | | for :bogus:Symbol | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When demongoizing a | raise ``NoMethodError``| "bogus": | -| | non-Time value: | Exception: undefined | return ``nil`` | -| | "bogus", | method ``getlocal`` | | -| | ``Date.today`` | for "bogus":String | ``Date.today``: | -| | | | return a | -| | | | ``Time/DateTime`` | -+--------------+------------------------+------------------------+-----------------------+ -| Date | When assigning or | raise ``NoMethodError``| return ``nil`` | -| | demongoizing a bogus | Exception: undefined | | -| | value: :bogus | method ``year`` | | -| | | for :bogus:Symbol | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When demongoizing a | raise ``NoMethodError``| return a | -| /Date | valid string: | Exception: undefined | ``Time/DateTime/Date``| -| | "2022-07-14 14:45:51 | method ``getlocal`` | | -| | -0400" | for "2022-07-14 | | -| | | 14:45:51 -0400":String | | -+--------------+------------------------+------------------------+-----------------------+ -| All Types | When an uncastable | undefined behavior, | return ``nil`` | -| | value is assigned or | occasionally raise | | -| | demongoized | ``NoMethodError`` | | -+--------------+------------------------+------------------------+-----------------------+ -| All Types | When an uncastable | undefined behavior, | return inputted value | -| | value is evolved | occasionally raise | | -| | | ``NoMethodError`` | | -+--------------+------------------------+------------------------+-----------------------+ +``evolve`` methods. The following table shows the changes in functionality: + +.. list-table:: + :widths: 1 2 2 2 + :stub-columns: 1 + :header-rows: 1 + + * - Field Type + - Situation + - Previous Functionality + - New Functionality + + * - | Boolean + - | When a non-boolean string is assigned: "bogus value" + - | return ``false`` + - | return ``nil`` + + * - Array/Hash + - When a value that is not an array or hash is assigned + - raise ``InvalidValue`` error + - return ``nil`` + + * - | Set + - | When a value that is not a set is assigned: 1 + - | raise ``NoMethodError`` Exception: undefined method ``to_a`` for 1:Integer + - | return ``nil`` + + * - Regexp + - When persisting and reading a Regexp from the database + - return a ``BSON::Regexp::Raw`` + - return a ``Regexp`` + + * - | Time/DateTime + - | When assigning a bogus value: ``:bogus`` + - | raise ``NoMethodError`` Exception: undefined method ``to_i`` for :bogus:Symbol + - | return ``nil`` + + * - Time/DateTime + - When demongoizing a non-Time value: "bogus", ``Date.today`` + - raise ``NoMethodError`` Exception: undefined method ``getlocal`` for "bogus":String + - "bogus": return ``nil`` + + ``Date.today``: return ``Time/DateTime`` + + * - | Date + - | When assigning or demongoizing a bogus value: :bogus + - | raise ``NoMethodError`` Exception: undefined method ``year`` for :bogus:Symbol + - | return ``nil`` + + * - Time/DateTime/Date + - When demongoizing a valid string: "2022-07-14 14:45:51 -0400" + - raise ``NoMethodError`` Exception: undefined method ``getlocal`` for "2022-07-14 14:45:51 -0400":String + - return a ``Time/DateTime/Date`` + + * - | All Types + - | When an uncastable value is assigned or demongoized + - | undefined behavior, occasionally raise ``NoMethodError`` + - | return ``nil`` + + * - All Types + - When an uncastable value is evolved + - undefined behavior, occasionally raise ``NoMethodError`` + - return inputted value .. note:: @@ -145,7 +155,6 @@ perform consistently with rails and the other ``mongoize``, ``demongoize`` and https://jira.mongodb.org/browse/MONGOID-2951 for a longer discussion on these bugs. - Changes to the ``attributes_before_type_cast`` Hash --------------------------------------------------- @@ -177,98 +186,144 @@ invocation for documents with associations. Referenced associations (``has_one`` and ``has_many``): -+---------------------------------------+---------------------------------------+ -| Mongoid 8.0 | Mongoid 7 | -+=======================================+=======================================+ -| Parent :before_save | Parent :before_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_open | Parent :around_save_open | -+---------------------------------------+---------------------------------------+ -| Parent :before_create | Parent :before_create | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_open | Parent :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Parent persisted in MongoDB** | **Parent persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :before_save | Parent :around_create_close | -+---------------------------------------+---------------------------------------+ -| Child :around_save_open | Parent :after_create | -+---------------------------------------+---------------------------------------+ -| Child :before_create | Child :before_save | -+---------------------------------------+---------------------------------------+ -| Child :around_create_open | Child :around_save_open | -+---------------------------------------+---------------------------------------+ -| | Child :before_create | -+---------------------------------------+---------------------------------------+ -| | Child :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Child persisted in MongoDB** | **Child persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :around_create_close | Child :around_create_close | -+---------------------------------------+---------------------------------------+ -| Child :after_create | Child :after_create | -+---------------------------------------+---------------------------------------+ -| Child :around_save_close | Child :around_save_close | -+---------------------------------------+---------------------------------------+ -| Child :after_save | Child :after_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_close | Parent :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_create | Parent :after_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_close | | -+---------------------------------------+---------------------------------------+ -| Parent :after_save | | -+---------------------------------------+---------------------------------------+ +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Mongoid 8.0 + - Mongoid 7 + + * - | Parent :before_save + - | Parent :before_save + + * - Parent :around_save_open + - Parent :around_save_open + + * - | Parent :before_create + - | Parent :before_create + + * - Parent :around_create_open + - Parent :around_create_open + + * - | **Parent persisted in MongoDB** + - | **Parent persisted in MongoDB** + + * - Child :before_save + - Parent :around_create_close + + * - | Child :around_save_open + - | Parent :after_create + + * - Child :before_create + - Child :before_save + + * - | Child :around_create_open + - | Child :around_save_open + + * - + - Child :before_create + + * - | + - | Child :around_create_open + + * - **Child persisted in MongoDB** + - **Child persisted in MongoDB** + + * - | Child :around_create_close + - | Child :around_create_close + + * - Child :after_create + - Child :after_create + + * - | Child :around_save_close + - | Child :around_save_close + + * - Child :after_save + - Child :after_save + + * - | Parent :around_create_close + - | Parent :around_save_close + + * - Parent :after_create + - Parent :after_save + + * - | Parent :around_save_close + - | + + * - Parent :after_save + - Embedded associations (``embeds_one`` and ``embeds_many``): -+---------------------------------------+---------------------------------------+ -| Mongoid 8.0 | Mongoid 7 | -+=======================================+=======================================+ -| Parent :before_save | Child :before_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_open | Child :around_save_open | -+---------------------------------------+---------------------------------------+ -| Parent :before_create | Child :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_open | Child :after_save | -+---------------------------------------+---------------------------------------+ -| Child :before_save | Parent :before_save | -+---------------------------------------+---------------------------------------+ -| Child :around_save_open | Parent :around_save_open | -+---------------------------------------+---------------------------------------+ -| Child :before_create | Child :before_create | -+---------------------------------------+---------------------------------------+ -| Child :around_create_open | Child :around_create_open | -+---------------------------------------+---------------------------------------+ -| | Child :around_create_close | -+---------------------------------------+---------------------------------------+ -| | Child :after_create | -+---------------------------------------+---------------------------------------+ -| | Parent :before_create | -+---------------------------------------+---------------------------------------+ -| | Parent :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Document persisted in MongoDB** | **Document persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :around_create_close | | -+---------------------------------------+---------------------------------------+ -| Child :after_create | | -+---------------------------------------+---------------------------------------+ -| Child :around_save_close | | -+---------------------------------------+---------------------------------------+ -| Child :after_save | | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_close | Parent :around_create_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_create | Parent :after_create | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_close | Parent :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_save | Parent :after_save | -+---------------------------------------+---------------------------------------+ +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Mongoid 8.0 + - Mongoid 7 + + * - | Parent :before_save + - | Child :before_save + + * - Parent :around_save_open + - Child :around_save_open + + * - | Parent :before_create + - | Child :around_save_close + + * - Parent :around_create_open + - Child :after_save + + * - | Child :before_save + - | Parent :before_save + + * - Child :around_save_open + - Parent :around_save_open + + * - | Child :before_create + - | Child :before_create + + * - Child :around_create_open + - Child :around_create_open + + * - | + - | Child :around_create_close + + * - + - Child :after_create + + * - | + - | Parent :before_create + + * - + - Parent :around_create_open + + * - | **Document persisted in MongoDB** + - | **Document persisted in MongoDB** + + * - Child :around_create_close + - + + * - | Child :after_create + - | + + * - Child :around_save_close + - + + * - | Child :after_save + - | + + * - Parent :around_create_close + - Parent :around_create_close + + * - | Parent :after_create + - | Parent :after_create + + * - Parent :around_save_close + - Parent :around_save_close + * - | Parent :after_save + - | Parent :after_save ``Changeable`` Module Behavior Made Compatible With ``ActiveModel::Dirty`` -------------------------------------------------------------------------- From ea01485624450e9076f86ade36f8e00f70db2550 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Thu, 7 Mar 2024 10:27:35 -0700 Subject: [PATCH 17/43] MONGOID-5552 warn on session/client mismatch (#5786) * MONGOID-5552 warn on session/client mismatch * clarify: bug *in your code* (and not in Mongoid) * test that client mismatches are logged --- lib/config/locales/en.yml | 11 ++++++++ lib/mongoid/clients/sessions.rb | 36 +++++++++++++++++++++++++++ lib/mongoid/errors/mongoid_error.rb | 6 ++--- lib/mongoid/persistable/creatable.rb | 1 + lib/mongoid/persistable/updatable.rb | 1 + spec/mongoid/clients/sessions_spec.rb | 18 ++++++++++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/lib/config/locales/en.yml b/lib/config/locales/en.yml index 43263b072..88b5541ca 100644 --- a/lib/config/locales/en.yml +++ b/lib/config/locales/en.yml @@ -704,3 +704,14 @@ en: %{document} when the child '%{relation}' still has documents in it." resolution: "Don't attempt to delete the parent %{document} when it has children, or change the dependent option on the association." + client_session_mismatch: + message: "%{model} used within another client's session" + summary: > + Each model is associated with a client. Each session (transaction) + is also associated with a client. You've attempted to create or + update a model within a session associated with a different client. + This may be a bug in your code. + resolution: > + Either process the %{model} model outside the enclosing + transaction, or open a new transaction for that model before + performing the create/update. diff --git a/lib/mongoid/clients/sessions.rb b/lib/mongoid/clients/sessions.rb index b85906978..faac2ea55 100644 --- a/lib/mongoid/clients/sessions.rb +++ b/lib/mongoid/clients/sessions.rb @@ -233,6 +233,42 @@ def transaction_include_any_action?(actions) end end end + + private + + # If at least one session is active, this ensures that the + # current model's client is compatible with one of them. + # + # "Compatible" is defined to mean: the same client was used + # to open one of the active sessions. + # + # Currently emits a warning. + def ensure_client_compatibility! + # short circuit: if no sessions are active, there's nothing + # to check. + return unless Threaded.sessions.any? + + # at this point, we know that at least one session is currently + # active. let's see if one of them was started with the model's + # client... + session = Threaded.get_session(client: persistence_context.client) + + # if not, then we have a case of the programmer trying to use + # a model within a transaction, where the model is not itself + # controlled by that transaction. this is potentially a bug, so + # let's tell them about it. + if session.nil? + # This is hacky; we're hijacking Mongoid::Errors::MongoidError in + # order to get the spiffy error message translation. If we later + # decide to raise an error instead of just writing a message, we can + # subclass MongoidError and raise that exception here. + message = Errors::MongoidError.new.compose_message( + 'client_session_mismatch', + model: self.class.name + ) + logger.info(message) + end + end end end end diff --git a/lib/mongoid/errors/mongoid_error.rb b/lib/mongoid/errors/mongoid_error.rb index 828c44dbb..d15e411af 100644 --- a/lib/mongoid/errors/mongoid_error.rb +++ b/lib/mongoid/errors/mongoid_error.rb @@ -28,9 +28,9 @@ def compose_message(key, attributes = {}) @resolution_title = translate("resolution_title", {}) - "\n#{@problem_title}:\n #{@problem}"+ - "\n#{@summary_title}:\n #{@summary}"+ - "\n#{@resolution_title}:\n #{@resolution}" + "\n#{@problem_title}:\n #{@problem&.strip}"+ + "\n#{@summary_title}:\n #{@summary&.strip}"+ + "\n#{@resolution_title}:\n #{@resolution&.strip}" end private diff --git a/lib/mongoid/persistable/creatable.rb b/lib/mongoid/persistable/creatable.rb index bf4f89f80..c519dcb17 100644 --- a/lib/mongoid/persistable/creatable.rb +++ b/lib/mongoid/persistable/creatable.rb @@ -105,6 +105,7 @@ def prepare_insert(options = {}) raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly return self if performing_validations?(options) && invalid?(options[:context] || :create) + ensure_client_compatibility! run_callbacks(:commit, with_children: true, skip_if: -> { in_transaction? }) do run_callbacks(:save, with_children: false) do run_callbacks(:create, with_children: false) do diff --git a/lib/mongoid/persistable/updatable.rb b/lib/mongoid/persistable/updatable.rb index f31ae88cd..0ba8dc08a 100644 --- a/lib/mongoid/persistable/updatable.rb +++ b/lib/mongoid/persistable/updatable.rb @@ -99,6 +99,7 @@ def init_atomic_updates def prepare_update(options = {}) raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly enforce_immutability_of_id_field! + ensure_client_compatibility! return false if performing_validations?(options) && invalid?(options[:context] || :update) process_flagged_destroys diff --git a/spec/mongoid/clients/sessions_spec.rb b/spec/mongoid/clients/sessions_spec.rb index 87783dd1c..eba32d8ea 100644 --- a/spec/mongoid/clients/sessions_spec.rb +++ b/spec/mongoid/clients/sessions_spec.rb @@ -4,6 +4,16 @@ require "spec_helper" describe Mongoid::Clients::Sessions do + let(:buffer) { StringIO.new } + let(:logger) { ::Logger.new(buffer, Logger::DEBUG) } + + around do |example| + old_logger = Mongoid.logger + Mongoid.logger = logger + example.run + ensure + Mongoid.logger = old_logger + end before(:all) do CONFIG[:clients][:other] = CONFIG[:clients][:default].dup @@ -232,6 +242,10 @@ expect(insert_lsids_sent.uniq.size).to eq(1) expect(update_lsids_sent.uniq).to eq(insert_lsids_sent.uniq) end + + it 'does not warn about a different client' do + expect(buffer.string).not_to include("used within another client's session") + end end context 'when the other class uses a different client' do @@ -260,6 +274,10 @@ update_lsids_sent = update_events.collect { |event| event.command['lsid'] } expect(update_lsids_sent.size).to eq(2) end + + it 'warns about a different client' do + expect(buffer.string).to include("used within another client's session") + end end context 'when sessions are nested' do From 39802ca6554dac9c8d3e81a8e5211d41a8a5b051 Mon Sep 17 00:00:00 2001 From: Dmitry Rybakov <160598371+comandeo-mongo@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:02:17 +0100 Subject: [PATCH 18/43] Revert "MONGOID-5735 Deprecate aggregable API (#5778)" (#5802) This reverts commit 8154e7c14efeb45a88a58a11aa53c5702e8008ac. --- docs/release-notes/mongoid-9.0.txt | 9 --------- lib/mongoid/contextual/aggregable/memory.rb | 10 ---------- lib/mongoid/contextual/aggregable/mongo.rb | 10 ---------- lib/mongoid/contextual/aggregable/none.rb | 10 ---------- 4 files changed, 39 deletions(-) diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index b5d24842f..2845f10c0 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -82,15 +82,6 @@ inferred from the model name, you can specify ``--collection``: For more information see the `GitHub Repository `_. -Aggregable API is deprecated ----------------------------- - -Mongoid 9 deprecated aggregable API in modules -``Mongoid::Contextual::Aggregable::Memory``, -``Mongoid::Contextual::Aggregable::Mongo``, and -``Mongoid::Contextual::Aggregable::None``. - - Support for Ruby 2.6 and JRuby 9.3 Dropped ------------------------------------------- diff --git a/lib/mongoid/contextual/aggregable/memory.rb b/lib/mongoid/contextual/aggregable/memory.rb index c4cf0c87d..7e328fae9 100644 --- a/lib/mongoid/contextual/aggregable/memory.rb +++ b/lib/mongoid/contextual/aggregable/memory.rb @@ -15,13 +15,11 @@ module Memory # @return [ Hash ] A Hash containing the aggregate values. # If no documents are present, then returned Hash will have # count, sum of 0 and max, min, avg of nil. - # @deprecated def aggregates(field) %w(count sum avg min max).each_with_object({}) do |method, hash| hash[method] = send(method, field) end end - Mongoid.deprecate(self, :aggregates) # Get the average value of the provided field. # @@ -31,7 +29,6 @@ def aggregates(field) # @param [ Symbol ] field The field to average. # # @return [ Numeric ] The average. - # @deprecated def avg(field) total = count { |doc| !doc.send(field).nil? } return nil unless total > 0 @@ -39,7 +36,6 @@ def avg(field) total = total.to_f if total.is_a?(Integer) sum(field) / total end - Mongoid.deprecate(self, :avg) # Get the max value of the provided field. If provided a block, will # return the Document with the greatest value for the field, in @@ -57,13 +53,11 @@ def avg(field) # # @return [ Numeric | Document ] The max value or document with the max # value. - # @deprecated def max(field = nil) return super() if block_given? aggregate_by(field, :max) end - Mongoid.deprecate(self, :max) # Get the min value of the provided field. If provided a block, will # return the Document with the smallest value for the field, in @@ -81,13 +75,11 @@ def max(field = nil) # # @return [ Numeric | Document ] The min value or document with the min # value. - # @deprecated def min(field = nil) return super() if block_given? aggregate_by(field, :min) end - Mongoid.deprecate(self, :min) # Get the sum value of the provided field. If provided a block, will # return the sum in accordance with Ruby's enumerable API. @@ -101,13 +93,11 @@ def min(field = nil) # @param [ Symbol ] field The field to sum. # # @return [ Numeric ] The sum value. - # @deprecated def sum(field = nil) return super() if block_given? aggregate_by(field, :sum) || 0 end - Mongoid.deprecate(self, :sum) private diff --git a/lib/mongoid/contextual/aggregable/mongo.rb b/lib/mongoid/contextual/aggregable/mongo.rb index ed1bf6303..1b050281f 100644 --- a/lib/mongoid/contextual/aggregable/mongo.rb +++ b/lib/mongoid/contextual/aggregable/mongo.rb @@ -26,7 +26,6 @@ module Mongo # @return [ Hash ] A Hash containing the aggregate values. # If no documents are found, then returned Hash will have # count, sum of 0 and max, min, avg of nil. - # @deprecated def aggregates(field) result = collection.aggregate(pipeline(field), session: _session).to_a if result.empty? @@ -35,7 +34,6 @@ def aggregates(field) result.first end end - Mongoid.deprecate(self, :aggregates) # Get the average value of the provided field. # @@ -45,11 +43,9 @@ def aggregates(field) # @param [ Symbol ] field The field to average. # # @return [ Float ] The average. - # @deprecated def avg(field) aggregates(field)["avg"] end - Mongoid.deprecate(self, :avg) # Get the max value of the provided field. If provided a block, will # return the Document with the greatest value for the field, in @@ -67,11 +63,9 @@ def avg(field) # # @return [ Float | Document ] The max value or document with the max # value. - # @deprecated def max(field = nil) block_given? ? super() : aggregates(field)["max"] end - Mongoid.deprecate(self, :max) # Get the min value of the provided field. If provided a block, will # return the Document with the smallest value for the field, in @@ -89,11 +83,9 @@ def max(field = nil) # # @return [ Float | Document ] The min value or document with the min # value. - # @deprecated def min(field = nil) block_given? ? super() : aggregates(field)["min"] end - Mongoid.deprecate(self, :min) # Get the sum value of the provided field. If provided a block, will # return the sum in accordance with Ruby's enumerable API. @@ -107,11 +99,9 @@ def min(field = nil) # @param [ Symbol ] field The field to sum. # # @return [ Float ] The sum value. - # @deprecated def sum(field = nil) block_given? ? super() : aggregates(field)["sum"] || 0 end - Mongoid.deprecate(self, :sum) private diff --git a/lib/mongoid/contextual/aggregable/none.rb b/lib/mongoid/contextual/aggregable/none.rb index fe2bd4b8f..13d410fae 100644 --- a/lib/mongoid/contextual/aggregable/none.rb +++ b/lib/mongoid/contextual/aggregable/none.rb @@ -15,11 +15,9 @@ module None # @param [ String | Symbol ] _field The field name. # # @return [ Hash ] A Hash with count, sum of 0 and max, min, avg of nil. - # @deprecated def aggregates(_field) Aggregable::EMPTY_RESULT.dup end - Mongoid.deprecate(self, :aggregates) # Always returns zero. # @@ -28,11 +26,9 @@ def aggregates(_field) # @param [ Symbol ] _field The field to sum. # # @return [ Integer ] Always zero. - # @deprecated def sum(_field = nil) 0 end - Mongoid.deprecate(self, :sum) # Always returns nil. # @@ -41,11 +37,9 @@ def sum(_field = nil) # @param [ Symbol ] _field The field to avg. # # @return [ nil ] Always nil. - # @deprecated def avg(_field) nil end - Mongoid.deprecate(self, :avg) # Always returns nil. # @@ -54,11 +48,9 @@ def avg(_field) # @param [ Symbol ] _field The field to min. # # @return [ nil ] Always nil. - # @deprecated def min(_field = nil) nil end - Mongoid.deprecate(self, :min) # Always returns nil. # @@ -67,9 +59,7 @@ def min(_field = nil) # @param [ Symbol ] _field The field to max. # # @return [ nil ] Always nil. - # @deprecated alias :max :min - Mongoid.deprecate(self, :max) end end end From c4fed245b94af9e40f29f36f41273c1c1d735191 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Tue, 12 Mar 2024 11:04:48 -0600 Subject: [PATCH 19/43] MONGOID-5739 Support BSON 5 (#5801) * remove bson version cap * prefer pessimistic version constraint for BSON --- mongoid.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoid.gemspec b/mongoid.gemspec index 2884ad2e1..c364c9fc9 100644 --- a/mongoid.gemspec +++ b/mongoid.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |s| s.add_dependency("mongo", ['>=2.18.0', '<3.0.0']) s.add_dependency("concurrent-ruby", ['>= 1.0.5', '< 2.0']) - s.add_development_dependency("bson", ['>=4.14.0', '<5.0.0']) + s.add_development_dependency("bson", '>=4.14.0', '<6.0.0') s.files = Dir.glob("lib/**/*") + %w(CHANGELOG.md LICENSE README.md Rakefile) s.test_files = Dir.glob("spec/**/*") From 5bdb998710edae23687f989cb3b07c569018177a Mon Sep 17 00:00:00 2001 From: John Williams <55147273+jwilliams-mongo@users.noreply.github.com> Date: Thu, 21 Mar 2024 09:00:45 -0500 Subject: [PATCH 20/43] DOCSP-37326: fix formatting (#5799) --- docs/reference/queries.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/queries.txt b/docs/reference/queries.txt index b8dd5c902..7cef79b94 100644 --- a/docs/reference/queries.txt +++ b/docs/reference/queries.txt @@ -1151,7 +1151,7 @@ examples. If any of the ``_id`` values are not found in the database, the behavior of ``find`` depends on the value of the ``raise_not_found_error`` configuration option. If the option is set to ``true``, ``find`` raises -``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``s are not found. +``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``\s are not found. If the option is set to ``false`` and ``find`` is given a single ``_id`` to find and there is no matching document, ``find`` returns ``nil``. If the option is set to ``false`` and ``find`` is given an array of ids to find From 85de45211365af9231f19e9a13d474e5a8f63b1a Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Thu, 21 Mar 2024 16:18:01 +0100 Subject: [PATCH 21/43] Add code of conduct (#5804) --- CODE_OF_CONDUCT.md | 131 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..42afacfef --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement through GitHub. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations From 7d752dca60d69e376d3a4ac1ced2ef5cabfbf4f0 Mon Sep 17 00:00:00 2001 From: stayweek <165480133+stayweek@users.noreply.github.com> Date: Mon, 1 Apr 2024 19:50:55 +0800 Subject: [PATCH 22/43] docs: fix typos in comment (#5807) --- docs/reference/configuration.txt | 4 ++-- docs/reference/fields.txt | 6 +++--- docs/reference/rails-integration.txt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index 35b90ab34..93e258177 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -396,7 +396,7 @@ for details on driver options. driver_options: # When this flag is off, an aggregation done on a view will be executed over # the documents included in that view, instead of all documents in the - # collection. When this flag is on, the view fiter is ignored. + # collection. When this flag is on, the view filter is ignored. # broken_view_aggregate: true # When this flag is set to false, the view options will be correctly @@ -404,7 +404,7 @@ for details on driver options. # broken_view_options: true # When this flag is set to true, the update and replace methods will - # validate the paramters and raise an error if they are invalid. + # validate the parameters and raise an error if they are invalid. # validate_update_replace: false diff --git a/docs/reference/fields.txt b/docs/reference/fields.txt index 6687a6a42..5e29bfeec 100644 --- a/docs/reference/fields.txt +++ b/docs/reference/fields.txt @@ -927,7 +927,7 @@ Reading Uncastable Values ````````````````````````` When documents in the database contain values of different types than their -represenations in Mongoid, if Mongoid cannot coerce them into the correct type, +representations in Mongoid, if Mongoid cannot coerce them into the correct type, it will replace the value with ``nil``. Consider the following model and document in the database: @@ -1105,7 +1105,7 @@ used for MongoDB serialization and deserialization as follows: end The instance method ``mongoize`` takes an instance of your custom type object, and -converts it into a represenation of how it will be stored in the database, i.e. to pass +converts it into a representation of how it will be stored in the database, i.e. to pass to the MongoDB Ruby driver. In our example above, we want to store our ``Point`` object as an ``Array`` in the form ``[ x, y ]``. @@ -1228,7 +1228,7 @@ which extend its behavior at the your time model classes are loaded. As an example, we will define a ``:max_length`` option which will add a length validator for the field. First, declare the new field option in an initializer, -specifiying its handler function as a block: +specifying its handler function as a block: .. code-block:: ruby diff --git a/docs/reference/rails-integration.txt b/docs/reference/rails-integration.txt index 4c7181cff..2f8f02ace 100644 --- a/docs/reference/rails-integration.txt +++ b/docs/reference/rails-integration.txt @@ -24,7 +24,7 @@ other Rails environment specific options by accessing config.mongoid. The ``mongoid:config`` generator will create an initializer in ``config/initializers/mongoid.rb`` which can also be used for configuring Mongoid. Note, though, that options set in your ``config/mongoid.yml`` will -take precendence over options set elsewhere; it is recommended that whenever +take precedence over options set elsewhere; it is recommended that whenever possible you use ``mongoid.yml`` as the default location for Mongoid configuration. From 3963eba1794926d031396a0d6c93ad1343438194 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Tue, 2 Apr 2024 08:08:48 -0600 Subject: [PATCH 23/43] MONGOID-5754 Making sure Mongoid works right with latest driver (#5806) * driver master relies on bson 5 (master) * make sure and recognize Mongo::Error::TransactionsNotSupported * bump spec/shared to latest (for jdk17) * work around the new exception class not being present in older driver versions * transaction check in with_sessions for server 3.6 --- gemfiles/driver_master.gemfile | 2 +- gemfiles/driver_stable.gemfile | 2 +- lib/mongoid/clients/sessions.rb | 20 ++++++++++++++++++++ spec/shared | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/gemfiles/driver_master.gemfile b/gemfiles/driver_master.gemfile index 062262798..2daf4c8a1 100644 --- a/gemfiles/driver_master.gemfile +++ b/gemfiles/driver_master.gemfile @@ -1,7 +1,7 @@ # rubocop:todo all source "https://rubygems.org" -gem 'bson', git: "https://github.com/mongodb/bson-ruby", branch: '4-stable' +gem 'bson', git: "https://github.com/mongodb/bson-ruby" gem 'mongo', git: "https://github.com/mongodb/mongo-ruby-driver" gem 'actionpack' diff --git a/gemfiles/driver_stable.gemfile b/gemfiles/driver_stable.gemfile index f28c5be54..8df367525 100644 --- a/gemfiles/driver_stable.gemfile +++ b/gemfiles/driver_stable.gemfile @@ -1,7 +1,7 @@ # rubocop:todo all source "https://rubygems.org" -gem 'mongo', git: "https://github.com/mongodb/mongo-ruby-driver", branch: '2.18-stable' +gem 'mongo', git: "https://github.com/mongodb/mongo-ruby-driver", branch: '2.19-stable' gem 'actionpack' diff --git a/lib/mongoid/clients/sessions.rb b/lib/mongoid/clients/sessions.rb index faac2ea55..9bf4f60b3 100644 --- a/lib/mongoid/clients/sessions.rb +++ b/lib/mongoid/clients/sessions.rb @@ -59,6 +59,8 @@ def with_session(options = {}) else raise ex end + rescue *transactions_not_supported_exceptions + raise Mongoid::Errors::TransactionsNotSupported ensure Threaded.clear_session(client: persistence_context.client) end @@ -90,6 +92,8 @@ def transaction(options = {}, session_options: {}) session.start_transaction(options) yield commit_transaction(session) + rescue *transactions_not_supported_exceptions + raise Mongoid::Errors::TransactionsNotSupported rescue Mongoid::Errors::Rollback abort_transaction(session) rescue Mongoid::Errors::InvalidSessionNesting @@ -150,6 +154,22 @@ def after_rollback(*args, &block) private + # Driver version 2.20 introduced a new exception for reporting that + # transactions are not supported. Prior to that, the condition was + # discovered by the rescue clause falling through to a different + # exception. + # + # This method ensures that Mongoid continues to work with older driver + # versions, by only returning the new exception. + # + # Once support is removed for all versions prior to 2.20.0, we can + # replace this method. + def transactions_not_supported_exceptions + return nil unless defined? Mongo::Error::TransactionsNotSupported + + Mongo::Error::TransactionsNotSupported + end + # @return [ Mongo::Session ] Session for the current client. def _session Threaded.get_session(client: persistence_context.client) diff --git a/spec/shared b/spec/shared index 53a38fe8f..cee4bc026 160000 --- a/spec/shared +++ b/spec/shared @@ -1 +1 @@ -Subproject commit 53a38fe8f165fd71687cf6205d2f1aac0dbde10b +Subproject commit cee4bc02649a573c8256b0505c1d23f503ac2609 From ca3e02edee8789b44282696f901f203d48414b0a Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Tue, 9 Apr 2024 05:57:32 +0900 Subject: [PATCH 24/43] Remove deprecated Document#as_json hack (#5755) Co-authored-by: Jamis Buck --- docs/release-notes/mongoid-9.0.txt | 5 +++++ lib/mongoid/document.rb | 27 --------------------------- lib/mongoid/warnings.rb | 1 - spec/mongoid/criteria_spec.rb | 1 - spec/mongoid/document_spec.rb | 29 ----------------------------- 5 files changed, 5 insertions(+), 58 deletions(-) diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index 2845f10c0..653eb4dc2 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -154,11 +154,16 @@ prior has been dropped (you must use a minimum of version 8.0.) Deprecated functionality removed -------------------------------- +**Breaking change:** The following deprecated functionality is now removed: + - The ``Mongoid::QueryCache`` module has been removed. Please replace any usages 1-for-1 with ``Mongo::QueryCache``. The method ``Mongoid::QueryCache#clear_cache`` should be replaced with ``Mongo::QueryCache#clear``. All other methods and submodules are identically named. Refer to the `driver query cache documentation `_ for more details. - ``Object#blank_criteria?`` method is removed (was previously deprecated.) +- ``Document#as_json :compact`` option is removed. Please call ```#compact`` on the + returned ``Hash`` object instead. +- The deprecated class ``Mongoid::Errors::InvalidStorageParent`` has been removed. ``touch`` method now clears changed state diff --git a/lib/mongoid/document.rb b/lib/mongoid/document.rb index e55d932c0..cf70ff955 100644 --- a/lib/mongoid/document.rb +++ b/lib/mongoid/document.rb @@ -135,33 +135,6 @@ def as_document BSON::Document.new(as_attributes) end - # Calls #as_json on the document with additional, Mongoid-specific options. - # - # @note Rails 6 changes return value of as_json for non-primitive types - # such as BSON::ObjectId. In Rails <= 5, as_json returned these as - # instances of the class. In Rails 6, these are returned serialized to - # primitive types (e.g. {'$oid'=>'5bcfc40bde340b37feda98e9'}). - # See https://github.com/rails/rails/commit/2e5cb980a448e7f4ab00df6e9ad4c1cc456616aa - # for more information. - # - # @example Get the document as json. - # document.as_json(compact: true) - # - # @param [ Hash ] options The options. - # - # @option options [ true | false ] :compact (Deprecated) Whether to include fields - # with nil values in the json document. - # - # @return [ Hash ] The document as json. - def as_json(options = nil) - rv = super - if options && options[:compact] - Mongoid::Warnings.warn_as_json_compact_deprecated - rv = rv.compact - end - rv - end - # Returns an instance of the specified class with the attributes, # errors, and embedded documents of the current document. # diff --git a/lib/mongoid/warnings.rb b/lib/mongoid/warnings.rb index 0a41abf3e..79333dcfc 100644 --- a/lib/mongoid/warnings.rb +++ b/lib/mongoid/warnings.rb @@ -30,7 +30,6 @@ def warning(id, message) end warning :geo_haystack_deprecated, 'The geoHaystack type is deprecated.' - warning :as_json_compact_deprecated, '#as_json :compact option is deprecated. Please call #compact on the returned Hash object instead.' warning :symbol_type_deprecated, 'The BSON Symbol type is deprecated by MongoDB. Please use String or StringifiedSymbol field types instead of the Symbol field type.' warning :legacy_readonly, 'The readonly! method will only mark the document readonly when the legacy_readonly feature flag is switched off.' warning :mutable_ids, 'Ignoring updates to immutable attribute `_id`. Please set Mongoid::Config.immutable_ids to true and update your code so that `_id` is never updated.' diff --git a/spec/mongoid/criteria_spec.rb b/spec/mongoid/criteria_spec.rb index 69d1dbb3f..65d1ca809 100644 --- a/spec/mongoid/criteria_spec.rb +++ b/spec/mongoid/criteria_spec.rb @@ -288,7 +288,6 @@ Band.where(name: "Depeche Mode") end - it "returns the criteria as a json hash" do expect(criteria.as_json).to eq([ band.serializable_hash.as_json ]) end diff --git a/spec/mongoid/document_spec.rb b/spec/mongoid/document_spec.rb index 938ac8a8f..03915b166 100644 --- a/spec/mongoid/document_spec.rb +++ b/spec/mongoid/document_spec.rb @@ -429,35 +429,6 @@ class << self; attr_accessor :name; end end end end - - context "when the Mongoid-specific options are provided" do - - let(:options) do - { compact: true } - end - - it "applies the Mongoid-specific options" do - expect(person.as_json(options)["title"]).to eq("Sir") - expect(person.as_json(options)["age"]).to eq(100) - expect(person.as_json(options).keys).not_to include("lunch_time") - end - - context "when options for the super method are provided" do - - let(:options) do - { compact: true, only: [:title, :pets, :ssn] } - end - - it "passes the options through to the super method" do - expect(person.as_json(options)["title"]).to eq("Sir") - expect(person.as_json(options)["pets"]).to eq(false) - end - - it "applies the Mongoid-specific options" do - expect(person.as_json(options).keys).not_to include("ssn") - end - end - end end describe "#as_document" do From a17643971f89ea18f1302d2b175a39f65de1d765 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 20 Apr 2024 23:40:52 +0900 Subject: [PATCH 25/43] Fix sessions --- lib/mongoid/clients/sessions.rb | 54 +++++++++++++++++++++++++++ lib/mongoid/persistable/creatable.rb | 1 + lib/mongoid/persistable/updatable.rb | 1 + spec/mongoid/clients/sessions_spec.rb | 44 +++++++++++++++------- 4 files changed, 87 insertions(+), 13 deletions(-) diff --git a/lib/mongoid/clients/sessions.rb b/lib/mongoid/clients/sessions.rb index e0edfd54c..e2a2cf9d4 100644 --- a/lib/mongoid/clients/sessions.rb +++ b/lib/mongoid/clients/sessions.rb @@ -59,6 +59,8 @@ def with_session(options = {}) end raise e + rescue *transactions_not_supported_exceptions + raise Mongoid::Errors::TransactionsNotSupported ensure Threaded.clear_session(client: persistence_context.client) end @@ -89,6 +91,8 @@ def transaction(options = {}, session_options: {}) session.start_transaction(options) yield commit_transaction(session) + rescue *transactions_not_supported_exceptions + raise Mongoid::Errors::TransactionsNotSupported rescue Mongoid::Errors::Rollback abort_transaction(session) rescue Mongoid::Errors::InvalidSessionNesting @@ -148,6 +152,22 @@ def after_rollback(*args, &block) private + # Driver version 2.20 introduced a new exception for reporting that + # transactions are not supported. Prior to that, the condition was + # discovered by the rescue clause falling through to a different + # exception. + # + # This method ensures that Mongoid continues to work with older driver + # versions, by only returning the new exception. + # + # Once support is removed for all versions prior to 2.20.0, we can + # replace this method. + def transactions_not_supported_exceptions + return nil unless defined? Mongo::Error::TransactionsNotSupported + + Mongo::Error::TransactionsNotSupported + end + # @return [ Mongo::Session ] Session for the current client. def _session Threaded.get_session(client: persistence_context.client) @@ -231,6 +251,40 @@ def transaction_include_any_action?(actions) end end end + + # If at least one session is active, this ensures that the + # current model's client is compatible with one of them. + # + # "Compatible" is defined to mean: the same client was used + # to open one of the active sessions. + # + # Currently emits a warning. + def ensure_client_compatibility! + # short circuit: if no sessions are active, there's nothing + # to check. + return unless Threaded.sessions.any? + + # at this point, we know that at least one session is currently + # active. let's see if one of them was started with the model's + # client... + session = Threaded.get_session(client: persistence_context.client) + return unless session.nil? + + # if the session is nil, then we have a case of the programmer trying to use + # a model within a transaction, where the model is not itself + # controlled by that transaction. this is potentially a bug, so + # let's tell them about it. + # + # This is hacky; we're hijacking Mongoid::Errors::MongoidError in + # order to get the spiffy error message translation. If we later + # decide to raise an error instead of just writing a message, we can + # subclass MongoidError and raise that exception here. + message = Errors::MongoidError.new.compose_message( + 'client_session_mismatch', + model: self.class.name + ) + logger.info(message) + end end end end diff --git a/lib/mongoid/persistable/creatable.rb b/lib/mongoid/persistable/creatable.rb index ad9f3e893..017bced7e 100644 --- a/lib/mongoid/persistable/creatable.rb +++ b/lib/mongoid/persistable/creatable.rb @@ -107,6 +107,7 @@ def prepare_insert(options = {}) return self if performing_validations?(options) && invalid?(options[:context] || :create) + ensure_client_compatibility! run_callbacks(:commit, with_children: true, skip_if: -> { in_transaction? }) do run_callbacks(:save, with_children: false) do run_callbacks(:create, with_children: false) do diff --git a/lib/mongoid/persistable/updatable.rb b/lib/mongoid/persistable/updatable.rb index 20a2d4b61..ec8a5efe1 100644 --- a/lib/mongoid/persistable/updatable.rb +++ b/lib/mongoid/persistable/updatable.rb @@ -99,6 +99,7 @@ def prepare_update(options = {}) raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly enforce_immutability_of_id_field! + ensure_client_compatibility! return false if performing_validations?(options) && invalid?(options[:context] || :update) diff --git a/spec/mongoid/clients/sessions_spec.rb b/spec/mongoid/clients/sessions_spec.rb index 3453ceaa3..6619aabd8 100644 --- a/spec/mongoid/clients/sessions_spec.rb +++ b/spec/mongoid/clients/sessions_spec.rb @@ -3,19 +3,8 @@ require 'spec_helper' describe Mongoid::Clients::Sessions do - - before(:all) do - CONFIG[:clients][:other] = CONFIG[:clients][:default].dup - CONFIG[:clients][:other][:database] = 'other' - Mongoid::Clients.clients.each_value(&:close) - Mongoid::Config.send(:clients=, CONFIG[:clients]) - Mongoid::Clients.with_name(:other).subscribe(Mongo::Monitoring::COMMAND, EventSubscriber.new) - end - - after(:all) do - Mongoid::Clients.with_name(:other).close - Mongoid::Clients.clients.delete(:other) - end + let(:buffer) { StringIO.new } + let(:logger) { Logger.new(buffer, Logger::DEBUG) } let(:subscriber) do client = Mongoid::Clients.with_name(:other) @@ -35,6 +24,27 @@ subscriber.started_events.select { |event| event.command_name.to_s == 'update' } end + around do |example| + old_logger = Mongoid.logger + Mongoid.logger = logger + example.run + ensure + Mongoid.logger = old_logger + end + + before(:all) do + CONFIG[:clients][:other] = CONFIG[:clients][:default].dup + CONFIG[:clients][:other][:database] = 'other' + Mongoid::Clients.clients.each_value(&:close) + Mongoid::Config.send(:clients=, CONFIG[:clients]) + Mongoid::Clients.with_name(:other).subscribe(Mongo::Monitoring::COMMAND, EventSubscriber.new) + end + + after(:all) do + Mongoid::Clients.with_name(:other).close + Mongoid::Clients.clients.delete(:other) + end + context 'when a session is used on a model class' do context 'when sessions are supported' do @@ -229,6 +239,10 @@ expect(insert_lsids_sent.uniq.size).to eq(1) expect(update_lsids_sent.uniq).to eq(insert_lsids_sent.uniq) end + + it 'does not warn about a different client' do + expect(buffer.string).to_not include("used within another client's session") + end end context 'when the other class uses a different client' do @@ -257,6 +271,10 @@ update_lsids_sent = update_events.collect { |event| event.command['lsid'] } expect(update_lsids_sent.size).to eq(2) end + + it 'warns about a different client' do + expect(buffer.string).to include("used within another client's session") + end end context 'when sessions are nested' do From 4e3fcc6d9a669ec95acefad2be16ca86fea36b7e Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 00:06:20 +0900 Subject: [PATCH 26/43] More merge --- CODE_OF_CONDUCT.md | 15 ++ docs/index.txt | 3 - docs/installation.txt | 2 +- docs/reference/compatibility.txt | 189 ++----------- docs/reference/configuration.txt | 19 +- docs/reference/fields.txt | 6 +- docs/reference/queries.txt | 2 +- docs/reference/rails-integration.txt | 2 +- docs/reference/sharding.txt | 4 +- docs/reference/text-search.txt | 4 +- docs/release-notes/mongoid-8.0.txt | 339 ++++++++++++++---------- docs/release-notes/mongoid-9.0.txt | 254 +++++++++--------- docs/release-notes/upgrading.txt | 4 +- docs/tutorials/automatic-encryption.txt | 14 +- lib/config/locales/en.yml | 11 + lib/mongoid/errors/mongoid_error.rb | 6 +- upload-api-docs | 9 +- 17 files changed, 401 insertions(+), 482 deletions(-) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..477f3506b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,15 @@ +# Conduct of Code + +1. "I fight for the Users." - Tron + +2. "Talk is cheap. Show me the code." - Linus Torvalds + +3. "Everything should be made as simple as possible, but not simpler." - Albert Einstein + +4. "The most dangerous phrase in the language is: We've always done it this way." - Grace Hopper + +5. "A feeling of aversion or attachment toward something is your clue that there's work to be done." - Ram Dass + +6. "I'm smart enough to know that I'm dumb." - Richard Feynman + +7. "Be excellent to each other." - Abraham Lincoln diff --git a/docs/index.txt b/docs/index.txt index 76660e556..e7fed377a 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -21,6 +21,3 @@ for MongoDB in Ruby. contributing additional-resources ecosystem - - -For documentation on Mongoid 3 and 4, see ``_. diff --git a/docs/installation.txt b/docs/installation.txt index 7247273d6..44ffdaba6 100644 --- a/docs/installation.txt +++ b/docs/installation.txt @@ -34,7 +34,7 @@ Using Mongoid with a New Rails Application When creating a new Rails application and wish to use Mongoid for data access, give the ``--skip-active-record`` flag to the ``rails new`` -command to avoid depending on and configuring ActiveRecord.. +command to avoid depending on and configuring ActiveRecord. Using Mongoid with an Existing Rails Application ================================================ diff --git a/docs/reference/compatibility.txt b/docs/reference/compatibility.txt index 7f616c315..79299b23e 100644 --- a/docs/reference/compatibility.txt +++ b/docs/reference/compatibility.txt @@ -34,52 +34,17 @@ specified Mongoid versions. - Driver 2.17-2.10 - Driver 2.9-2.7 - * - 9.0 + * - 8.0 thru 9.0 - |checkmark| - - - * - 8.1 + * - 7.2 thru 7.5 - |checkmark| - - - - - - * - 8.0 - |checkmark| - - - - * - 7.5 - - |checkmark| - - |checkmark| - - - - * - 7.4 - - |checkmark| - - |checkmark| - - - - * - 7.3 - - |checkmark| - - |checkmark| - - - - * - 7.2 - - |checkmark| - - |checkmark| - - - - * - 7.1 - - |checkmark| - - |checkmark| - - |checkmark| - - * - 7.0 - - |checkmark| - - |checkmark| - - |checkmark| - - * - 6.4 + * - 7.0 thru 7.1 - |checkmark| - |checkmark| - |checkmark| @@ -107,9 +72,9 @@ is deprecated. - Ruby 2.4 - Ruby 2.3 - Ruby 2.2 - - JRuby 9.2 - - JRuby 9.3 - JRuby 9.4 + - JRuby 9.3 + - JRuby 9.2 * - 9.0 - |checkmark| @@ -121,9 +86,9 @@ is deprecated. - - - + - |checkmark| - - - - |checkmark| * - 8.1 - |checkmark| @@ -139,7 +104,6 @@ is deprecated. - |checkmark| - - * - 8.0 - - |checkmark| @@ -164,9 +128,9 @@ is deprecated. - - - - - D - - |checkmark| - + - |checkmark| + - D * - 7.4 - @@ -178,9 +142,9 @@ is deprecated. - - - - - |checkmark| - - + - |checkmark| * - 7.3 - @@ -192,9 +156,9 @@ is deprecated. - D - D - - - |checkmark| - - + - |checkmark| * - 7.2 - @@ -206,9 +170,9 @@ is deprecated. - D - D - - - |checkmark| - - + - |checkmark| * - 7.1 - @@ -220,9 +184,9 @@ is deprecated. - |checkmark| [#ruby-2.4]_ - |checkmark| - - - |checkmark| - - + - |checkmark| * - 7.0 - @@ -234,23 +198,9 @@ is deprecated. - |checkmark| [#ruby-2.4]_ - |checkmark| - |checkmark| [#ruby-2.2]_ - - |checkmark| - - - - - - * - 6.4 - - - - - - - |checkmark| - - |checkmark| - - |checkmark| [#ruby-2.4]_ - - |checkmark| - - |checkmark| [#ruby-2.2]_ - - |checkmark| - - - - .. [#mongoid-7.3-ruby-3.0] Mongoid version 7.3.2 or higher is required. @@ -296,33 +246,7 @@ and will be removed in a next version. - MongoDB 3.0 - MongoDB 2.6 - * - 9.0 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 8.1 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - - - - - - - - - * - 8.0 + * - 8.0 thru 9.0 - |checkmark| - |checkmark| - |checkmark| @@ -335,7 +259,7 @@ and will be removed in a next version. - - - * - 7.5 + * - 7.4 thru 7.5 - |checkmark| - |checkmark| - |checkmark| @@ -348,72 +272,7 @@ and will be removed in a next version. - D - D - * - 7.4 - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.3 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.2 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.1 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 7.0 - - - - - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| - - D - - D - - D - - D - - * - 6.4 + * - 7.0 thru 7.3 - - - |checkmark| @@ -456,15 +315,7 @@ are supported by Mongoid. - - - * - 8.1 - - |checkmark| [#rails-7.1]_ - - |checkmark| - - |checkmark| - - |checkmark| - - |checkmark| [#rails-5-ruby-3.0]_ - - - - * - 8.0 + * - 8.0 thru 8.1 - |checkmark| [#rails-7.1]_ - |checkmark| - |checkmark| @@ -520,14 +371,6 @@ are supported by Mongoid. - |checkmark| - |checkmark| - * - 6.4 - - - - - - - - - - |checkmark| - - |checkmark| - .. [#rails-5-ruby-3.0] Using Rails 5.x with Ruby 3 is not supported. .. [#rails-6] Rails 6.0 requires Mongoid 7.0.5 or later. diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index 0014cb452..852f32194 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -396,7 +396,7 @@ for details on driver options. driver_options: # When this flag is off, an aggregation done on a view will be executed over # the documents included in that view, instead of all documents in the - # collection. When this flag is on, the view fiter is ignored. + # collection. When this flag is on, the view filter is ignored. # broken_view_aggregate: true # When this flag is set to false, the view options will be correctly @@ -404,7 +404,7 @@ for details on driver options. # broken_view_options: true # When this flag is set to true, the update and replace methods will - # validate the paramters and raise an error if they are invalid. + # validate the parameters and raise an error if they are invalid. # validate_update_replace: false @@ -541,9 +541,8 @@ Place the following in one of environment configuration files, such as .. note:: - The ``log_level`` Mongoid `configuration option `_ - is not used when Mongoid operates in a Rails application, because Mongoid - inherits Rails' log level in this case. + The ``log_level`` Mongoid configuration option is not used when Mongoid operates + in a Rails application, because Mongoid inherits Rails' log level in this case. To configure either Mongoid or driver logger differently from the Rails logger, use an initializer as follows: @@ -590,8 +589,8 @@ Standalone ---------- When not loaded in a Ruby on Rails application, Mongoid respects the -``log_level`` top level `configuration option `_. -It can be given in the configuration file as follows: +``log_level`` top level configuration option. It can be given in the +configuration file as follows: .. code-block:: yaml @@ -746,11 +745,11 @@ It may be desirable to further configure TLS options in your application, for example by enabling or disabling certain ciphers. This can be done by setting TLS context hooks on the Ruby driver -- TLS context -hooks are user-provided ``Proc``s that will be invoked before any TLS socket +hooks are user-provided ``Proc``\(s) that will be invoked before any TLS socket connection in the driver and can be used to modify the underlying ``OpenSSL::SSL::SSLContext`` object used by the socket. -To set TLS context hooks, add ``Proc``s to the ``Mongo.tls_context_hooks`` +To set TLS context hooks, add ``Proc``\(s) to the ``Mongo.tls_context_hooks`` array. This can be done in an initializer. The example below adds a hook that only enables the "AES256-SHA" cipher. @@ -830,7 +829,7 @@ Client-Side Encryption When loading the configuration file, Mongoid permits the file to contain ``BSON::Binary`` instances which are used for specifying ``keyId`` in the schema map for `client-side encryption -`_, +`_, as the following example shows: .. code-block:: yaml diff --git a/docs/reference/fields.txt b/docs/reference/fields.txt index 6687a6a42..5e29bfeec 100644 --- a/docs/reference/fields.txt +++ b/docs/reference/fields.txt @@ -927,7 +927,7 @@ Reading Uncastable Values ````````````````````````` When documents in the database contain values of different types than their -represenations in Mongoid, if Mongoid cannot coerce them into the correct type, +representations in Mongoid, if Mongoid cannot coerce them into the correct type, it will replace the value with ``nil``. Consider the following model and document in the database: @@ -1105,7 +1105,7 @@ used for MongoDB serialization and deserialization as follows: end The instance method ``mongoize`` takes an instance of your custom type object, and -converts it into a represenation of how it will be stored in the database, i.e. to pass +converts it into a representation of how it will be stored in the database, i.e. to pass to the MongoDB Ruby driver. In our example above, we want to store our ``Point`` object as an ``Array`` in the form ``[ x, y ]``. @@ -1228,7 +1228,7 @@ which extend its behavior at the your time model classes are loaded. As an example, we will define a ``:max_length`` option which will add a length validator for the field. First, declare the new field option in an initializer, -specifiying its handler function as a block: +specifying its handler function as a block: .. code-block:: ruby diff --git a/docs/reference/queries.txt b/docs/reference/queries.txt index 79ecd9cc9..8dcae9b27 100644 --- a/docs/reference/queries.txt +++ b/docs/reference/queries.txt @@ -1154,7 +1154,7 @@ examples. If any of the ``_id`` values are not found in the database, the behavior of ``find`` depends on the value of the ``raise_not_found_error`` configuration option. If the option is set to ``true``, ``find`` raises -``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``s are not found. +``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``\s are not found. If the option is set to ``false`` and ``find`` is given a single ``_id`` to find and there is no matching document, ``find`` returns ``nil``. If the option is set to ``false`` and ``find`` is given an array of ids to find diff --git a/docs/reference/rails-integration.txt b/docs/reference/rails-integration.txt index 4c7181cff..2f8f02ace 100644 --- a/docs/reference/rails-integration.txt +++ b/docs/reference/rails-integration.txt @@ -24,7 +24,7 @@ other Rails environment specific options by accessing config.mongoid. The ``mongoid:config`` generator will create an initializer in ``config/initializers/mongoid.rb`` which can also be used for configuring Mongoid. Note, though, that options set in your ``config/mongoid.yml`` will -take precendence over options set elsewhere; it is recommended that whenever +take precedence over options set elsewhere; it is recommended that whenever possible you use ``mongoid.yml`` as the default location for Mongoid configuration. diff --git a/docs/reference/sharding.txt b/docs/reference/sharding.txt index ab451bf4a..675e62cae 100644 --- a/docs/reference/sharding.txt +++ b/docs/reference/sharding.txt @@ -35,8 +35,8 @@ Shard keys can be declared on models using the ``shard_key`` macro: end Note that in order to shard a collection, the collection must have an index -that starts with the shard key. Mongoid provides `index management -`_ functionality, which the examples here take +that starts with the shard key. Mongoid provides :ref:`index management +` functionality, which the examples here take advantage of. Mongoid supports two syntaxes for declaring shard keys. The standard syntax diff --git a/docs/reference/text-search.txt b/docs/reference/text-search.txt index c962b3667..b7c3013de 100644 --- a/docs/reference/text-search.txt +++ b/docs/reference/text-search.txt @@ -35,8 +35,8 @@ To perform text search with Mongoid, follow these steps: Defining Text Search Index -------------------------- -Index definition through Mongoid is described in detail on the `indexes -`_ page. Text search indexes are described in detail +Index definition through Mongoid is described in detail on the :ref:`indexes +` page. Text search indexes are described in detail under `text indexes `_ in the MongoDB manual. Below is an example definition of a Band model with a text index utilizing the description field: diff --git a/docs/release-notes/mongoid-8.0.txt b/docs/release-notes/mongoid-8.0.txt index 69e17c6d9..b0d93132c 100644 --- a/docs/release-notes/mongoid-8.0.txt +++ b/docs/release-notes/mongoid-8.0.txt @@ -84,59 +84,69 @@ evolve an uncastable value, the inputted value is returned. See the section on Some ``mongoize``, ``demongoize`` and ``evolve`` methods were also changed to perform consistently with rails and the other ``mongoize``, ``demongoize`` and -``evolve`` methods. The following is a table of the changes in functionality: - -+--------------+------------------------+------------------------+-----------------------+ -| Field Type | Situation | Previous Functionality | New Functionality | -+==============+========================+========================+=======================+ -| Boolean | When a non-boolean | return ``false`` | return ``nil`` | -| | string is assigned: | | | -| | "bogus value" | | | -+--------------+------------------------+------------------------+-----------------------+ -| Array/Hash | When a value that is | raise ``InvalidValue`` | return ``nil`` | -| | not an array or hash | error | | -| | is assigned | | | -+--------------+------------------------+------------------------+-----------------------+ -| Set | When a value that is | raise ``NoMethodError``| return ``nil`` | -| | not a set is assigned: | Exception: undefined | | -| | 1 | method ``to_a`` for | | -| | | 1:Integer | | -+--------------+------------------------+------------------------+-----------------------+ -| Regexp | When persisting and | return a | return a | -| | reading a Regexp from | ``BSON::Regexp::Raw`` | ``Regexp`` | -| | the database | | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When assigning a | raise ``NoMethodError``| return ``nil`` | -| | bogus value: ``:bogus``| Exception: undefined | | -| | | method ``to_i`` | | -| | | for :bogus:Symbol | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When demongoizing a | raise ``NoMethodError``| "bogus": | -| | non-Time value: | Exception: undefined | return ``nil`` | -| | "bogus", | method ``getlocal`` | | -| | ``Date.today`` | for "bogus":String | ``Date.today``: | -| | | | return a | -| | | | ``Time/DateTime`` | -+--------------+------------------------+------------------------+-----------------------+ -| Date | When assigning or | raise ``NoMethodError``| return ``nil`` | -| | demongoizing a bogus | Exception: undefined | | -| | value: :bogus | method ``year`` | | -| | | for :bogus:Symbol | | -+--------------+------------------------+------------------------+-----------------------+ -| Time/DateTime| When demongoizing a | raise ``NoMethodError``| return a | -| /Date | valid string: | Exception: undefined | ``Time/DateTime/Date``| -| | "2022-07-14 14:45:51 | method ``getlocal`` | | -| | -0400" | for "2022-07-14 | | -| | | 14:45:51 -0400":String | | -+--------------+------------------------+------------------------+-----------------------+ -| All Types | When an uncastable | undefined behavior, | return ``nil`` | -| | value is assigned or | occasionally raise | | -| | demongoized | ``NoMethodError`` | | -+--------------+------------------------+------------------------+-----------------------+ -| All Types | When an uncastable | undefined behavior, | return inputted value | -| | value is evolved | occasionally raise | | -| | | ``NoMethodError`` | | -+--------------+------------------------+------------------------+-----------------------+ +``evolve`` methods. The following table shows the changes in functionality: + +.. list-table:: + :widths: 1 2 2 2 + :stub-columns: 1 + :header-rows: 1 + + * - Field Type + - Situation + - Previous Functionality + - New Functionality + + * - | Boolean + - | When a non-boolean string is assigned: "bogus value" + - | return ``false`` + - | return ``nil`` + + * - Array/Hash + - When a value that is not an array or hash is assigned + - raise ``InvalidValue`` error + - return ``nil`` + + * - | Set + - | When a value that is not a set is assigned: 1 + - | raise ``NoMethodError`` Exception: undefined method ``to_a`` for 1:Integer + - | return ``nil`` + + * - Regexp + - When persisting and reading a Regexp from the database + - return a ``BSON::Regexp::Raw`` + - return a ``Regexp`` + + * - | Time/DateTime + - | When assigning a bogus value: ``:bogus`` + - | raise ``NoMethodError`` Exception: undefined method ``to_i`` for :bogus:Symbol + - | return ``nil`` + + * - Time/DateTime + - When demongoizing a non-Time value: "bogus", ``Date.today`` + - raise ``NoMethodError`` Exception: undefined method ``getlocal`` for "bogus":String + - "bogus": return ``nil`` + + ``Date.today``: return ``Time/DateTime`` + + * - | Date + - | When assigning or demongoizing a bogus value: :bogus + - | raise ``NoMethodError`` Exception: undefined method ``year`` for :bogus:Symbol + - | return ``nil`` + + * - Time/DateTime/Date + - When demongoizing a valid string: "2022-07-14 14:45:51 -0400" + - raise ``NoMethodError`` Exception: undefined method ``getlocal`` for "2022-07-14 14:45:51 -0400":String + - return a ``Time/DateTime/Date`` + + * - | All Types + - | When an uncastable value is assigned or demongoized + - | undefined behavior, occasionally raise ``NoMethodError`` + - | return ``nil`` + + * - All Types + - When an uncastable value is evolved + - undefined behavior, occasionally raise ``NoMethodError`` + - return inputted value .. note:: @@ -145,7 +155,6 @@ perform consistently with rails and the other ``mongoize``, ``demongoize`` and https://jira.mongodb.org/browse/MONGOID-2951 for a longer discussion on these bugs. - Changes to the ``attributes_before_type_cast`` Hash --------------------------------------------------- @@ -177,98 +186,144 @@ invocation for documents with associations. Referenced associations (``has_one`` and ``has_many``): -+---------------------------------------+---------------------------------------+ -| Mongoid 8.0 | Mongoid 7 | -+=======================================+=======================================+ -| Parent :before_save | Parent :before_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_open | Parent :around_save_open | -+---------------------------------------+---------------------------------------+ -| Parent :before_create | Parent :before_create | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_open | Parent :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Parent persisted in MongoDB** | **Parent persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :before_save | Parent :around_create_close | -+---------------------------------------+---------------------------------------+ -| Child :around_save_open | Parent :after_create | -+---------------------------------------+---------------------------------------+ -| Child :before_create | Child :before_save | -+---------------------------------------+---------------------------------------+ -| Child :around_create_open | Child :around_save_open | -+---------------------------------------+---------------------------------------+ -| | Child :before_create | -+---------------------------------------+---------------------------------------+ -| | Child :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Child persisted in MongoDB** | **Child persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :around_create_close | Child :around_create_close | -+---------------------------------------+---------------------------------------+ -| Child :after_create | Child :after_create | -+---------------------------------------+---------------------------------------+ -| Child :around_save_close | Child :around_save_close | -+---------------------------------------+---------------------------------------+ -| Child :after_save | Child :after_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_close | Parent :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_create | Parent :after_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_close | | -+---------------------------------------+---------------------------------------+ -| Parent :after_save | | -+---------------------------------------+---------------------------------------+ +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Mongoid 8.0 + - Mongoid 7 + + * - | Parent :before_save + - | Parent :before_save + + * - Parent :around_save_open + - Parent :around_save_open + + * - | Parent :before_create + - | Parent :before_create + + * - Parent :around_create_open + - Parent :around_create_open + + * - | **Parent persisted in MongoDB** + - | **Parent persisted in MongoDB** + + * - Child :before_save + - Parent :around_create_close + + * - | Child :around_save_open + - | Parent :after_create + + * - Child :before_create + - Child :before_save + + * - | Child :around_create_open + - | Child :around_save_open + + * - + - Child :before_create + + * - | + - | Child :around_create_open + + * - **Child persisted in MongoDB** + - **Child persisted in MongoDB** + + * - | Child :around_create_close + - | Child :around_create_close + + * - Child :after_create + - Child :after_create + + * - | Child :around_save_close + - | Child :around_save_close + + * - Child :after_save + - Child :after_save + + * - | Parent :around_create_close + - | Parent :around_save_close + + * - Parent :after_create + - Parent :after_save + + * - | Parent :around_save_close + - | + + * - Parent :after_save + - Embedded associations (``embeds_one`` and ``embeds_many``): -+---------------------------------------+---------------------------------------+ -| Mongoid 8.0 | Mongoid 7 | -+=======================================+=======================================+ -| Parent :before_save | Child :before_save | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_open | Child :around_save_open | -+---------------------------------------+---------------------------------------+ -| Parent :before_create | Child :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_open | Child :after_save | -+---------------------------------------+---------------------------------------+ -| Child :before_save | Parent :before_save | -+---------------------------------------+---------------------------------------+ -| Child :around_save_open | Parent :around_save_open | -+---------------------------------------+---------------------------------------+ -| Child :before_create | Child :before_create | -+---------------------------------------+---------------------------------------+ -| Child :around_create_open | Child :around_create_open | -+---------------------------------------+---------------------------------------+ -| | Child :around_create_close | -+---------------------------------------+---------------------------------------+ -| | Child :after_create | -+---------------------------------------+---------------------------------------+ -| | Parent :before_create | -+---------------------------------------+---------------------------------------+ -| | Parent :around_create_open | -+---------------------------------------+---------------------------------------+ -| **Document persisted in MongoDB** | **Document persisted in MongoDB** | -+---------------------------------------+---------------------------------------+ -| Child :around_create_close | | -+---------------------------------------+---------------------------------------+ -| Child :after_create | | -+---------------------------------------+---------------------------------------+ -| Child :around_save_close | | -+---------------------------------------+---------------------------------------+ -| Child :after_save | | -+---------------------------------------+---------------------------------------+ -| Parent :around_create_close | Parent :around_create_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_create | Parent :after_create | -+---------------------------------------+---------------------------------------+ -| Parent :around_save_close | Parent :around_save_close | -+---------------------------------------+---------------------------------------+ -| Parent :after_save | Parent :after_save | -+---------------------------------------+---------------------------------------+ +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Mongoid 8.0 + - Mongoid 7 + + * - | Parent :before_save + - | Child :before_save + + * - Parent :around_save_open + - Child :around_save_open + + * - | Parent :before_create + - | Child :around_save_close + + * - Parent :around_create_open + - Child :after_save + + * - | Child :before_save + - | Parent :before_save + + * - Child :around_save_open + - Parent :around_save_open + + * - | Child :before_create + - | Child :before_create + + * - Child :around_create_open + - Child :around_create_open + + * - | + - | Child :around_create_close + + * - + - Child :after_create + + * - | + - | Parent :before_create + + * - + - Parent :around_create_open + + * - | **Document persisted in MongoDB** + - | **Document persisted in MongoDB** + + * - Child :around_create_close + - + + * - | Child :after_create + - | + + * - Child :around_save_close + - + + * - | Child :after_save + - | + + * - Parent :around_create_close + - Parent :around_create_close + + * - | Parent :after_create + - | Parent :after_create + + * - Parent :around_save_close + - Parent :around_save_close + * - | Parent :after_save + - | Parent :after_save ``Changeable`` Module Behavior Made Compatible With ``ActiveModel::Dirty`` -------------------------------------------------------------------------- diff --git a/docs/release-notes/mongoid-9.0.txt b/docs/release-notes/mongoid-9.0.txt index 09fa47fb2..8a32c94a6 100644 --- a/docs/release-notes/mongoid-9.0.txt +++ b/docs/release-notes/mongoid-9.0.txt @@ -43,19 +43,6 @@ Consider using `MongoDB Atlas `_ to automate your MongoDB server upgrades. -Support for Ruby 2.6 and JRuby 9.3 Dropped -------------------------------------------- - -Mongoid 9 requires Ruby 2.7 or newer or JRuby 9.4. Earlier Ruby and JRuby -versions are not supported. - - -Support for Rails 5 Dropped ------------------------------ - -Mongoid 9 requires Rails 6.0 or newer. Earlier Rails versions are not supported. - - Deprecated class ``Mongoid::Errors::InvalidStorageParent`` removed ------------------------------------------------------------------ @@ -73,10 +60,11 @@ If you want to restore the old behavior, you can set ``Mongoid.around_embedded_document_callbacks`` to true in your application. .. note:: - Enabling ``around_*`` callbacks for embedded documents is not recommended - as it may cause ``SystemStackError`` exceptions when a document has many - embedded documents. See `MONGOID-5658 `_ - for more details. + + Enabling ``around_*`` callbacks for embedded documents is not recommended + as it may cause ``SystemStackError`` exceptions when a document has many + embedded documents. See `MONGOID-5658 `_ + for more details. ``for_js`` method is deprecated @@ -114,15 +102,16 @@ prior has been dropped (you must use a minimum of version 8.0.) Deprecated functionality removed -------------------------------- -The following previously deprecated functionality is now removed: +**Breaking change:** The following deprecated functionality is now removed: - The ``Mongoid::QueryCache`` module has been removed. Please replace any usages 1-for-1 with ``Mongo::QueryCache``. The method ``Mongoid::QueryCache#clear_cache`` should be replaced with ``Mongo::QueryCache#clear``. All other methods and submodules are identically named. Refer to the `driver query cache documentation `_ for more details. - ``Object#blank_criteria?`` method is removed (was previously deprecated.) -- ``Document#as_json :compact`` option is removed. Please call ```#compact`` - on the returned ``Hash`` object instead. +- ``Document#as_json :compact`` option is removed. Please call ```#compact`` on the + returned ``Hash`` object instead. +- The deprecated class ``Mongoid::Errors::InvalidStorageParent`` has been removed. - ``Criteria#geo_near`` is removed as MongoDB server versions 4.2 and later no longer support the ``$geoNear`` command. Please use the `$geoNear stage of the aggregation pipeline @@ -145,22 +134,22 @@ In Mongoid 8.x and older ``touch`` method leaves models in the changed state: .. code-block:: ruby - # Mongoid 8.x behaviour - band = Band.create! - band.touch - band.changed? # => true - band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} + # Mongoid 8.x behaviour + band = Band.create! + band.touch + band.changed? # => true + band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]} Starting from 9.0 Mongoid now correctly clears changed state after using ``touch`` method. .. code-block:: ruby - # Mongoid 9.0 behaviour - band = Band.create! - band.touch - band.changed? # => false - band.changes # => {} + # Mongoid 9.0 behaviour + band = Band.create! + band.touch + band.changed? # => false + band.changes # => {} Sandbox Mode for Rails Console ------------------------------ @@ -172,8 +161,9 @@ the commands executed in the console using the ``:default`` client won't be persisted in the database. .. note:: - If you execute commands in the sandbox mode *using any other client than default*, - these changes will be persisted as usual. + + If you execute commands in the sandbox mode *using any other client than default*, + these changes will be persisted as usual. New Transactions API -------------------- @@ -182,15 +172,15 @@ Mongoid 9.0 introduces new transactions API that is inspired by ActiveRecord: .. code-block:: ruby - Band.transaction do - Band.create(title: 'Led Zeppelin') - end + Band.transaction do + Band.create(title: 'Led Zeppelin') + end - band = Band.create(title: 'Deep Purple') - band.transaction do - band.active = false - band.save! - end + band = Band.create(title: 'Deep Purple') + band.transaction do + band.active = false + band.save! + end Please consult :ref:`transactions documentation ` for more details. @@ -231,11 +221,11 @@ when the instance was loaded, Mongoid will now raise a .. code-block:: ruby - Band.only(:name).first.label - #=> raises Mongoid::Errors::AttributeNotLoaded + Band.only(:name).first.label + #=> raises Mongoid::Errors::AttributeNotLoaded - Band.without(:label).first.label = 'Sub Pop Records' - #=> raises Mongoid::Errors::AttributeNotLoaded + Band.without(:label).first.label = 'Sub Pop Records' + #=> raises Mongoid::Errors::AttributeNotLoaded In earlier Mongoid versions, the same conditions would raise an ``ActiveModel::MissingAttributeError``. Please check your code for @@ -253,17 +243,17 @@ considers ``Time.zone`` to perform type conversion. .. code-block:: ruby - class Magazine - include Mongoid::Document + class Magazine + include Mongoid::Document - field :published_at, type: Time - end + field :published_at, type: Time + end - Time.zone = 'Asia/Tokyo' + Time.zone = 'Asia/Tokyo' - Magazine.gte(published_at: Date.parse('2022-09-26')) - #=> will return all results on or after Sept 26th, 2022 - # at 0:00 in Asia/Tokyo time zone. + Magazine.gte(published_at: Date.parse('2022-09-26')) + #=> will return all results on or after Sept 26th, 2022 + # at 0:00 in Asia/Tokyo time zone. In prior Mongoid versions, the above code would ignore the ``Time.zone`` (irrespective of the now-removed ``:use_activesupport_time_zone`` @@ -282,25 +272,25 @@ invoke ``#touch`` on its parent document. .. code-block:: ruby - class Address - include Mongoid::Document - include Mongoid::Timestamps + class Address + include Mongoid::Document + include Mongoid::Timestamps - embedded_in :mall, touch: false - end + embedded_in :mall, touch: false + end - class Mall - include Mongoid::Document - include Mongoid::Timestamps + class Mall + include Mongoid::Document + include Mongoid::Timestamps - embeds_many :addresses - end + embeds_many :addresses + end - mall = Mall.create! - address = mall.addresses.create! + mall = Mall.create! + address = mall.addresses.create! - address.touch - #=> updates address.updated_at but not mall.updated_at + address.touch + #=> updates address.updated_at but not mall.updated_at In addition, the ``#touch`` method has been optimized to perform one persistence operation per parent document, even when using multiple @@ -315,12 +305,12 @@ unless you explicitly set ``touch: false`` on the relation: .. code-block:: ruby - class Address - include Mongoid::Document - include Mongoid::Timestamps + class Address + include Mongoid::Document + include Mongoid::Timestamps - embedded_in :mall, touch: false - end + embedded_in :mall, touch: false + end For all other associations, the default remains ``touch: false``. @@ -352,7 +342,7 @@ defaults to ``true``. .. code-block:: ruby - Mongoid::Config.immutable_ids = true + Mongoid::Config.immutable_ids = true When set to false, the older, inconsistent behavior is restored. @@ -395,18 +385,18 @@ of the ``index`` macro: ``partial_filter_expression``, ``weights``, .. code-block:: ruby - class Person - include Mongoid::Document - field :a, as: :age - index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) - end + class Person + include Mongoid::Document + field :a, as: :age + index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) + end .. note:: - The expansion of field name aliases in index options such as - ``partial_filter_expression`` is performed according to the behavior of MongoDB - server 6.0. Future server versions may change how they interpret these options, - and Mongoid's functionality may not support such changes. + The expansion of field name aliases in index options such as + ``partial_filter_expression`` is performed according to the behavior of MongoDB + server 6.0. Future server versions may change how they interpret these options, + and Mongoid's functionality may not support such changes. BSON 5 and BSON::Decimal128 Fields @@ -418,32 +408,32 @@ declared as BSON::Decimal128 will return a BigDecimal value by default. .. code-block:: ruby - class Model - include Mongoid::Document + class Model + include Mongoid::Document - field :decimal_field, type: BSON::Decimal128 - end + field :decimal_field, type: BSON::Decimal128 + end - # under BSON <= 4 - Model.first.decimal_field.class #=> BSON::Decimal128 + # under BSON <= 4 + Model.first.decimal_field.class #=> BSON::Decimal128 - # under BSON >= 5 - Model.first.decimal_field.class #=> BigDecimal + # under BSON >= 5 + Model.first.decimal_field.class #=> BigDecimal If you need literal BSON::Decimal128 values with BSON 5, you may instruct Mongoid to allow literal BSON::Decimal128 fields: .. code-block:: ruby - Model.first.decimal_field.class #=> BigDecimal + Model.first.decimal_field.class #=> BigDecimal - Mongoid.allow_bson5_decimal128 = true - Model.first.decimal_field.class #=> BSON::Decimal128 + Mongoid.allow_bson5_decimal128 = true + Model.first.decimal_field.class #=> BSON::Decimal128 .. note:: - The ``allow_bson5_decimal128`` option only has any effect under - BSON 5 and later. BSON 4 and earlier ignore the setting entirely. + The ``allow_bson5_decimal128`` option only has any effect under + BSON 5 and later. BSON 4 and earlier ignore the setting entirely. Search Index Management with MongoDB Atlas @@ -455,23 +445,23 @@ API: .. code-block:: ruby - class SearchablePerson - include Mongoid::Document + class SearchablePerson + include Mongoid::Document - search_index { ... } # define the search index here - end + search_index { ... } # define the search index here + end - # create the declared search indexes; this returns immediately, but the - # search indexes may take several minutes before they are available. - SearchablePerson.create_search_indexes + # create the declared search indexes; this returns immediately, but the + # search indexes may take several minutes before they are available. + SearchablePerson.create_search_indexes - # query the available search indexes - SearchablePerson.search_indexes.each do |index| - # ... - end + # query the available search indexes + SearchablePerson.search_indexes.each do |index| + # ... + end - # remove all search indexes from the model's collection - SearchablePerson.remove_search_indexes + # remove all search indexes from the model's collection + SearchablePerson.remove_search_indexes If you are not connected to MongoDB Atlas, the search index definitions are ignored. Trying to create, enumerate, or remove search indexes will result in @@ -481,16 +471,16 @@ There are also rake tasks available, for convenience: .. code-block:: bash - # create search indexes for all models; waits for indexes to be created - # and shows progress on the terminal. - $ rake mongoid:db:create_search_indexes + # create search indexes for all models; waits for indexes to be created + # and shows progress on the terminal. + $ rake mongoid:db:create_search_indexes - # as above, but returns immediately and lets the indexes be created in the - # background - $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes + # as above, but returns immediately and lets the indexes be created in the + # background + $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes - # removes search indexes from all models - $ rake mongoid:db:remove_search_indexes + # removes search indexes from all models + $ rake mongoid:db:remove_search_indexes ``Time.configured`` has been removed @@ -504,33 +494,33 @@ Mongoid now requires that you set a time zone if you intend to do anything with time values (including using timestamps in your documents). Any uses of ``Time.configured`` must be replaced with ``Time.zone``. -... code-block:: ruby +.. code-block:: ruby - # before: - puts Time.configured.now + # before: + puts Time.configured.now - # after: - puts Time.zone.now + # after: + puts Time.zone.now - # or, better for finding the current Time specifically: - puts Time.current + # or, better for finding the current Time specifically: + puts Time.current If you do not set a time zone, you will see errors in your code related to ``nil`` values. If you are using Rails, the default time zone is already set to UTC. If you are not using Rails, you may set a time zone at the start of your program like this: -... code-block:: ruby +.. code-block:: ruby - Time.zone = 'UTC' + Time.zone = 'UTC' This will set the time zone to UTC. You can see all available time zone names by running the following command: -... code-block:: bash +.. code-block:: bash - $ ruby -ractive_support/values/time_zone \ - -e 'puts ActiveSupport::TimeZone::MAPPING.keys' + $ ruby -ractive_support/values/time_zone \ + -e 'puts ActiveSupport::TimeZone::MAPPING.keys' Records now remember the persistence context in which they were loaded/created @@ -538,10 +528,10 @@ Records now remember the persistence context in which they were loaded/created Consider the following code: -... code-block:: ruby +.. code-block:: ruby - record = Model.with(collection: 'other_collection') { Model.first } - record.update(field: 'value') + record = Model.with(collection: 'other_collection') { Model.first } + record.update(field: 'value') Prior to Mongoid 9.0, this could would silently fail to execute the update, because the storage options (here, the specification of an alternate @@ -558,9 +548,9 @@ a different database, or a different collection). If you need the legacy (pre-9.0) behavior, you can enable it with the following flag: -... code-block:: ruby +.. code-block:: ruby - Mongoid.legacy_persistence_context_behavior = true + Mongoid.legacy_persistence_context_behavior = true This flag defaults to false in Mongoid 9. diff --git a/docs/release-notes/upgrading.txt b/docs/release-notes/upgrading.txt index 1ae90c1cf..db27af622 100644 --- a/docs/release-notes/upgrading.txt +++ b/docs/release-notes/upgrading.txt @@ -49,10 +49,10 @@ Before you Upgrade ------------------ - *Test Coverage:* The best way to be sure that your application still works after upgrading -is to have good test coverage before you start the process. + is to have good test coverage before you start the process. - *Upgrade Ruby and Rails:* See `"Upgrading Ruby on Rails" `_ -for more information + for more information Upgrading Mongoid diff --git a/docs/tutorials/automatic-encryption.txt b/docs/tutorials/automatic-encryption.txt index dd7ce2890..4da882595 100644 --- a/docs/tutorials/automatic-encryption.txt +++ b/docs/tutorials/automatic-encryption.txt @@ -64,9 +64,11 @@ This can be done one of two ways. Install the automatic encryption shared library (Ruby driver 2.19+) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you use the Ruby driver version 2.19 and above the automatic encryption -shared library should be installed by following the instructions in the -`MongoDB manual `_. +If you use the Ruby driver version 2.19 and above, the automatic encryption +shared library should be installed by following the instructions on the +:manual:`Automatic Encryption Shared Library for Queryable Encryption +` +page in the Server manual. The steps required are as follows: @@ -253,9 +255,9 @@ Now we can tell Mongoid what should be encrypted: Known Limitations ~~~~~~~~~~~~~~~~~ -* MongoDB CSFLE has some limitations that are described in - `the server documentation. `_ - These limitations also apply to Mongoid. +* MongoDB CSFLE has some limitations that are described on the + :manual:`CSFLE Limitations ` + page in the Server manual. These limitations also apply to Mongoid. * Mongoid does not support encryption of ``embeds_many`` relations. * If you use ``:key_name_field`` option, the field must be encrypted using non-deterministic algorithm. To encrypt your field deterministically, you must diff --git a/lib/config/locales/en.yml b/lib/config/locales/en.yml index dcf17ef46..02edd9e83 100644 --- a/lib/config/locales/en.yml +++ b/lib/config/locales/en.yml @@ -710,3 +710,14 @@ en: %{document} when the child '%{relation}' still has documents in it." resolution: "Don't attempt to delete the parent %{document} when it has children, or change the dependent option on the association." + client_session_mismatch: + message: "%{model} used within another client's session" + summary: > + Each model is associated with a client. Each session (transaction) + is also associated with a client. You've attempted to create or + update a model within a session associated with a different client. + This may be a bug in your code. + resolution: > + Either process the %{model} model outside the enclosing + transaction, or open a new transaction for that model before + performing the create/update. diff --git a/lib/mongoid/errors/mongoid_error.rb b/lib/mongoid/errors/mongoid_error.rb index e4b6a5917..161f65eb3 100644 --- a/lib/mongoid/errors/mongoid_error.rb +++ b/lib/mongoid/errors/mongoid_error.rb @@ -26,9 +26,9 @@ def compose_message(key, attributes = {}) @summary_title = translate('summary_title', {}) @resolution_title = translate('resolution_title', {}) - "\n#{@problem_title}:\n #{@problem}" \ - "\n#{@summary_title}:\n #{@summary}" \ - "\n#{@resolution_title}:\n #{@resolution}" + "\n#{@problem_title}:\n #{@problem&.strip}" \ + "\n#{@summary_title}:\n #{@summary&.strip}" \ + "\n#{@resolution_title}:\n #{@resolution&.strip}" end private diff --git a/upload-api-docs b/upload-api-docs index a24d24613..589a23355 100755 --- a/upload-api-docs +++ b/upload-api-docs @@ -7,7 +7,7 @@ gemfile true do source 'https://rubygems.org' gem 'nokogiri' gem 'aws-sdk-s3' - gem 'yard' + gem 'yard', '>= 0.9.35' end require 'aws-sdk-s3' @@ -29,6 +29,7 @@ class FileUploader end def upload_docs + puts "Uploading to #{@bucket}" Dir.glob("#{@docs_path}/**/*").each do |file| next if File.directory?(file) @@ -115,6 +116,12 @@ def generate_docs(options) '--readme', './README.md', '-o', options[:docs_path] ) + + begin + File.delete(File.join(options[:docs_path], 'frames.html')) + rescue StandardError + nil + end end options = Options.new From f5b693a7dd45efb78e8c03eca06c1c7fac17ff89 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 10:46:31 +0900 Subject: [PATCH 27/43] Upgrade gemfile --- gemfiles/driver_master.gemfile | 2 +- gemfiles/driver_stable.gemfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gemfiles/driver_master.gemfile b/gemfiles/driver_master.gemfile index b823f21c6..c0a23181d 100644 --- a/gemfiles/driver_master.gemfile +++ b/gemfiles/driver_master.gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gemspec path: '..' -gem 'bson', git: 'https://github.com/mongodb/bson-ruby', branch: '4-stable' +gem 'bson', git: 'https://github.com/mongodb/bson-ruby' gem 'mongo', git: 'https://github.com/mongodb/mongo-ruby-driver' gem 'actionpack', '>= 6.0' gem 'activemodel', '>= 6.0' diff --git a/gemfiles/driver_stable.gemfile b/gemfiles/driver_stable.gemfile index 6a84330de..53b0fed57 100644 --- a/gemfiles/driver_stable.gemfile +++ b/gemfiles/driver_stable.gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gemspec path: '..' -gem 'mongo', git: 'https://github.com/mongodb/mongo-ruby-driver', branch: '2.18-stable' +gem 'mongo', git: 'https://github.com/mongodb/mongo-ruby-driver', branch: '2.19-stable' gem 'actionpack', '>= 6.0' gem 'activemodel', '>= 6.0' From 552a89cb1cbf62855d730f61ca57418612555d1e Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 11:31:50 +0900 Subject: [PATCH 28/43] Gemspec --- mongoid.gemspec | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mongoid.gemspec b/mongoid.gemspec index cf9a0cf10..2d295cd08 100644 --- a/mongoid.gemspec +++ b/mongoid.gemspec @@ -34,12 +34,7 @@ Gem::Specification.new do |s| s.add_dependency('mongo', ['>=2.18.0', '<3.0.0']) s.add_dependency('concurrent-ruby', ['>= 1.0.5', '< 2.0']) - # The ruby2_keywords gem normalizes Ruby 2.7's arg delegation. - # It can be removed when Ruby 2.7 is removed. - # See: https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/#delegation - s.add_dependency('ruby2_keywords', '~> 0.0.5') - - s.add_development_dependency('bson', ['>= 4.14.0', '< 5.0.0']) + s.add_development_dependency('bson', ['>= 4.14.0', '< 6.0.0']) s.files = Dir.glob('lib/**/*') + %w[LICENSE README.md Rakefile] s.require_path = 'lib' From 041bb9e79cba269f740ae7ded8e429de87ab764f Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:01:24 +0900 Subject: [PATCH 29/43] Changes to person model --- spec/support/models/name.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/support/models/name.rb b/spec/support/models/name.rb index 64e422bc2..857c8e718 100644 --- a/spec/support/models/name.rb +++ b/spec/support/models/name.rb @@ -4,6 +4,8 @@ class Name include Mongoid::Document include Mongoid::Attributes::Dynamic + validate :is_not_jamis + field :_id, type: String, overwrite: true, default: lambda { "#{first_name}-#{last_name}" } @@ -23,4 +25,11 @@ class Name def set_parent=(set = false) self.parent_title = namable.title if set end + + private + + def is_not_jamis + return unless first_name == 'Jamis' && last_name == 'Buck' + errors.add(:base, :invalid) + end end From c0c84f3da4a615826967c835d758427e6b77f7b1 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:17:59 +0900 Subject: [PATCH 30/43] Harmless changes --- lib/mongoid/atomic.rb | 14 +++++++++----- lib/mongoid/railtie.rb | 4 ++-- .../referenced/belongs_to/proxy_spec.rb | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/mongoid/atomic.rb b/lib/mongoid/atomic.rb index c0cdcfefd..7486a05aa 100644 --- a/lib/mongoid/atomic.rb +++ b/lib/mongoid/atomic.rb @@ -178,11 +178,15 @@ def atomic_position # # @return [ Object ] The associated path. def atomic_paths - @atomic_paths ||= if _association - _association.path(self) - else - Atomic::Paths::Root.new(self) - end + return @atomic_paths if @atomic_paths + + paths = if _association + _association.path(self) + else + Atomic::Paths::Root.new(self) + end + + paths.tap { @atomic_paths = paths unless new_record? } end # Get all the attributes that need to be pulled. diff --git a/lib/mongoid/railtie.rb b/lib/mongoid/railtie.rb index 77a218ca0..5e5b0f723 100644 --- a/lib/mongoid/railtie.rb +++ b/lib/mongoid/railtie.rb @@ -114,9 +114,9 @@ def handle_configuration_error(error) # Add custom serializers for BSON::ObjectId initializer 'mongoid.active_job.custom_serializers' do - require 'mongoid/railties/bson_object_id_serializer' + ActiveSupport.on_load :active_job do + require 'mongoid/railties/bson_object_id_serializer' - config.after_initialize do ActiveJob::Serializers.add_serializers( [::Mongoid::Railties::ActiveJobSerializers::BsonObjectIdSerializer] ) diff --git a/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb b/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb index 3dffb7ad2..dab566c26 100644 --- a/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +++ b/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb @@ -755,7 +755,7 @@ expect(drug).to be_destroyed end - it "doesn't deletes parent" do + it "doesn't delete parent" do expect(person).to_not be_destroyed end From 4ef4335b87a78deada842d81d0d222800399aa8b Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sat, 20 Apr 2024 09:28:13 +0900 Subject: [PATCH 31/43] Fix rubocop --- lib/mongoid/clients/sessions.rb | 24 +++++------ lib/mongoid/validatable/associated.rb | 4 +- .../has_and_belongs_to_many_spec.rb | 6 +-- .../referenced/belongs_to/proxy_spec.rb | 2 +- spec/mongoid/clients/sessions_spec.rb | 40 +++++++++---------- spec/support/models/name.rb | 9 ++--- 6 files changed, 41 insertions(+), 44 deletions(-) diff --git a/lib/mongoid/clients/sessions.rb b/lib/mongoid/clients/sessions.rb index 9264b5249..89e3ef191 100644 --- a/lib/mongoid/clients/sessions.rb +++ b/lib/mongoid/clients/sessions.rb @@ -270,22 +270,22 @@ def ensure_client_compatibility! # active. let's see if one of them was started with the model's # client... session = Threaded.get_session(client: persistence_context.client) + return unless session.nil? - # if not, then we have a case of the programmer trying to use + # if the session is nil, then we have a case of the programmer trying to use # a model within a transaction, where the model is not itself # controlled by that transaction. this is potentially a bug, so # let's tell them about it. - if session.nil? - # This is hacky; we're hijacking Mongoid::Errors::MongoidError in - # order to get the spiffy error message translation. If we later - # decide to raise an error instead of just writing a message, we can - # subclass MongoidError and raise that exception here. - message = Errors::MongoidError.new.compose_message( - 'client_session_mismatch', - model: self.class.name - ) - logger.info(message) - end + # + # This is hacky; we're hijacking Mongoid::Errors::MongoidError in + # order to get the spiffy error message translation. If we later + # decide to raise an error instead of just writing a message, we can + # subclass MongoidError and raise that exception here. + message = Errors::MongoidError.new.compose_message( + 'client_session_mismatch', + model: self.class.name + ) + logger.info(message) end end end diff --git a/lib/mongoid/validatable/associated.rb b/lib/mongoid/validatable/associated.rb index c647794aa..bf89043d6 100644 --- a/lib/mongoid/validatable/associated.rb +++ b/lib/mongoid/validatable/associated.rb @@ -81,8 +81,6 @@ def validate_association(document, attribute) document.errors.add(attribute, :invalid) unless valid end - private - # Examine the given target object and return an array of # documents (possibly empty) that the target represents. # @@ -107,7 +105,7 @@ def get_target_documents(target) # @return [ Array ] the in-memory documents # held by the target. def get_target_documents_for_has_many(target) - [ *target._loaded.values, *target._added.values ] + [*target._loaded.values, *target._added.values] end # Returns the target as an array. If the target represents a single diff --git a/spec/integration/associations/has_and_belongs_to_many_spec.rb b/spec/integration/associations/has_and_belongs_to_many_spec.rb index a84e9d2c8..5d03fe1b3 100644 --- a/spec/integration/associations/has_and_belongs_to_many_spec.rb +++ b/spec/integration/associations/has_and_belongs_to_many_spec.rb @@ -47,15 +47,15 @@ class Attachment let(:page) { HabtmSpec::Page.create! } let(:image_block) do - image_block = page.blocks.build({ + page.blocks.build({ _type: 'HabtmSpec::ImageBlock', - attachment_ids: [ attachment.id.to_s ], + attachment_ids: [attachment.id.to_s], attachments_attributes: { '1234' => { file: 'bar.jpg', id: attachment.id.to_s } } }) end it 'does not raise on save' do - expect { image_block.save! }.not_to raise_error + expect { image_block.save! }.to_not raise_error end end end diff --git a/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb b/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb index 3dffb7ad2..dab566c26 100644 --- a/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +++ b/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb @@ -755,7 +755,7 @@ expect(drug).to be_destroyed end - it "doesn't deletes parent" do + it "doesn't delete parent" do expect(person).to_not be_destroyed end diff --git a/spec/mongoid/clients/sessions_spec.rb b/spec/mongoid/clients/sessions_spec.rb index 28eff1011..6619aabd8 100644 --- a/spec/mongoid/clients/sessions_spec.rb +++ b/spec/mongoid/clients/sessions_spec.rb @@ -4,7 +4,25 @@ describe Mongoid::Clients::Sessions do let(:buffer) { StringIO.new } - let(:logger) { ::Logger.new(buffer, Logger::DEBUG) } + let(:logger) { Logger.new(buffer, Logger::DEBUG) } + + let(:subscriber) do + client = Mongoid::Clients.with_name(:other) + monitoring = client.send(:monitoring) + monitoring.subscribers['Command'].find do |s| + s.is_a?(EventSubscriber) + end + end + + let(:insert_events) do + # Driver 2.5 sends command_name as a symbol + subscriber.started_events.select { |event| event.command_name.to_s == 'insert' } + end + + let(:update_events) do + # Driver 2.5 sends command_name as a symbol + subscriber.started_events.select { |event| event.command_name.to_s == 'update' } + end around do |example| old_logger = Mongoid.logger @@ -27,24 +45,6 @@ Mongoid::Clients.clients.delete(:other) end - let(:subscriber) do - client = Mongoid::Clients.with_name(:other) - monitoring = client.send(:monitoring) - monitoring.subscribers['Command'].find do |s| - s.is_a?(EventSubscriber) - end - end - - let(:insert_events) do - # Driver 2.5 sends command_name as a symbol - subscriber.started_events.select { |event| event.command_name.to_s == 'insert' } - end - - let(:update_events) do - # Driver 2.5 sends command_name as a symbol - subscriber.started_events.select { |event| event.command_name.to_s == 'update' } - end - context 'when a session is used on a model class' do context 'when sessions are supported' do @@ -241,7 +241,7 @@ end it 'does not warn about a different client' do - expect(buffer.string).not_to include("used within another client's session") + expect(buffer.string).to_not include("used within another client's session") end end diff --git a/spec/support/models/name.rb b/spec/support/models/name.rb index 9083504b7..e81c24831 100644 --- a/spec/support/models/name.rb +++ b/spec/support/models/name.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -# rubocop:todo all class Name include Mongoid::Document @@ -7,7 +6,7 @@ class Name validate :is_not_jamis - field :_id, type: String, overwrite: true, default: ->{ + field :_id, type: String, overwrite: true, default: lambda { "#{first_name}-#{last_name}" } @@ -30,8 +29,8 @@ def set_parent=(set = false) private def is_not_jamis - if first_name == 'Jamis' && last_name == 'Buck' - errors.add(:base, :invalid) - end + return unless first_name == 'Jamis' && last_name == 'Buck' + + errors.add(:base, :invalid) end end From 28f4f8b37e1bcee74f41a7fe6c04f39aa0019795 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:19:17 +0900 Subject: [PATCH 32/43] Fix rubocop --- lib/mongoid/clients/sessions.rb | 2 ++ spec/support/models/name.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/mongoid/clients/sessions.rb b/lib/mongoid/clients/sessions.rb index e2a2cf9d4..89e3ef191 100644 --- a/lib/mongoid/clients/sessions.rb +++ b/lib/mongoid/clients/sessions.rb @@ -252,6 +252,8 @@ def transaction_include_any_action?(actions) end end + private + # If at least one session is active, this ensures that the # current model's client is compatible with one of them. # diff --git a/spec/support/models/name.rb b/spec/support/models/name.rb index 857c8e718..e81c24831 100644 --- a/spec/support/models/name.rb +++ b/spec/support/models/name.rb @@ -30,6 +30,7 @@ def set_parent=(set = false) def is_not_jamis return unless first_name == 'Jamis' && last_name == 'Buck' + errors.add(:base, :invalid) end end From 27d699db450f60fd3e9031404e1e26e1d399ec11 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:23:41 +0900 Subject: [PATCH 33/43] Add validating block method --- lib/mongoid/validatable.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/mongoid/validatable.rb b/lib/mongoid/validatable.rb index c95a520ed..821ff2c24 100644 --- a/lib/mongoid/validatable.rb +++ b/lib/mongoid/validatable.rb @@ -37,6 +37,14 @@ def exit_validate Threaded.exit_validate(self) end + # Perform a validation within the associated block. + def validating + begin_validate + yield + ensure + exit_validate + end + # Given the provided options, are we performing validations? # # @example Are we performing validations? From 56fb5ede1eaea6ca6c0dfbc329d30b73ebc6c984 Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Sun, 21 Apr 2024 21:42:22 +0900 Subject: [PATCH 34/43] Commit HABTM spec --- .../has_and_belongs_to_many_spec.rb | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/spec/integration/associations/has_and_belongs_to_many_spec.rb b/spec/integration/associations/has_and_belongs_to_many_spec.rb index 30ba61b8e..5d03fe1b3 100644 --- a/spec/integration/associations/has_and_belongs_to_many_spec.rb +++ b/spec/integration/associations/has_and_belongs_to_many_spec.rb @@ -2,6 +2,28 @@ require 'spec_helper' +module HabtmSpec + class Page + include Mongoid::Document + embeds_many :blocks, class_name: 'HabtmSpec::Block' + end + + class Block + include Mongoid::Document + embedded_in :page, class_name: 'HabtmSpec::Page' + end + + class ImageBlock < Block + has_and_belongs_to_many :attachments, inverse_of: nil, class_name: 'HabtmSpec::Attachment' + accepts_nested_attributes_for :attachments + end + + class Attachment + include Mongoid::Document + field :file, type: String + end +end + describe 'has_and_belongs_to_many associations' do context 'when an anonymous class defines a has_and_belongs_to_many association' do @@ -18,4 +40,22 @@ expect(klass.new.movies.build).to be_a Movie end end + + context 'when an embedded has habtm relation' do + let(:attachment) { HabtmSpec::Attachment.create!(file: 'foo.jpg') } + + let(:page) { HabtmSpec::Page.create! } + + let(:image_block) do + page.blocks.build({ + _type: 'HabtmSpec::ImageBlock', + attachment_ids: [attachment.id.to_s], + attachments_attributes: { '1234' => { file: 'bar.jpg', id: attachment.id.to_s } } + }) + end + + it 'does not raise on save' do + expect { image_block.save! }.to_not raise_error + end + end end From aef7114f16da89b0aa174b7bccf0342374e99f14 Mon Sep 17 00:00:00 2001 From: Johnny Shields <27655+johnnyshields@users.noreply.github.com> Date: Tue, 23 Apr 2024 00:17:32 +0900 Subject: [PATCH 35/43] MONGOID-5758: Add Mongoid.reconnect_clients and improve forking webserver documentation (#5808) * This PR does the following: - Add Mongoid.reconnect_clients (analogous to Mongoid.disconnect_clients). The reason for adding this is to simply web server hooks (see added docs.) - Corrects the @return in the docs for disconnect_clients. Also added specs for the existing behavior. - Updates documentation related to web server forking. * Fix method name * More terse syntax * Preserve old return type * Update configuration.txt * Update configuration.txt --- docs/reference/configuration.txt | 109 ++++++++++++++----------------- lib/mongoid.rb | 10 +++ lib/mongoid/clients.rb | 16 ++++- spec/mongoid/clients_spec.rb | 44 +++++++++++++ spec/mongoid_spec.rb | 26 ++++++++ 5 files changed, 141 insertions(+), 64 deletions(-) diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index 93e258177..2d82faf24 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -863,58 +863,57 @@ as the following example shows: Usage with Forking Servers ========================== -When using Mongoid with a forking web server such as Puma, Unicorn or -Passenger, it is recommended to not perform any operations on Mongoid models -in the parent process prior to the fork. - -When a process forks, Ruby threads are not transferred to the child processes -and the Ruby driver Client objects lose their background monitoring. The -application will typically seem to work just fine until the deployment -state changes (for example due to network errors, a maintenance event) at -which point the application is likely to start getting ``NoServerAvailable`` -exception when performing MongoDB operations. - -If the parent process needs to perform operations on the MongoDB database, -reset all clients in the workers after they forked. How to do so depends -on the web server being used. - -If the parent process does not need to perform operations on the MongoDB -database after child processes are forked, close the clients in the parent -prior to forking children. If the parent process performs operations on a Mongo -client and does not close it, the parent process will continue consuming a -connection slot in the cluster and will continue monitoring the cluster for -as long as the parent remains alive. - -.. note:: - - The close/reconnect pattern described here should be used with Ruby driver - version 2.6.2 or higher. Previous driver versions did not recreate - monitoring threads when reconnecting. +When using Mongoid with a forking web server such as Puma, or any application +that otherwise forks to spawn child processes, special considerations apply. + +If possible, we recommend to not perform any MongoDB operations in the parent +process prior to forking, which will avoid any forking-related pitfalls. + +A detailed technical explanation of how the Mongo Ruby Driver handles forking +is given in the `driver's "Usage with Forking Servers" documentation +`. +In a nutshell, to avoid various connection errors such as ``Mongo::Error::SocketError`` +and ``Mongo::Error::NoServerAvailable``, you must do the following: + +1. Disconnect MongoDB clients in the parent Ruby process immediately *before* + forking using ``Mongoid.disconnect_clients``. This ensures the parent and child + process do not accidentally reuse the same sockets and have I/O conflicts. + Note that ``Mongoid.disconnect_clients`` does not disrupt any in-flight + MongoDB operations, and will automatically reconnect when you perform new + operations. +2. Reconnect your MongoDB clients in the child Ruby process immediately *after* + forking using ``Mongoid.reconnect_clients``. This is required to respawn + the driver's monitoring threads in the child process. + +Most web servers provide hooks that can be used by applications to +perform actions when the worker processes are forked. The following +are configuration examples for several common Ruby web servers. Puma ---- Use the ``on_worker_boot`` hook to reconnect clients in the workers and -the ``before_fork`` hook to close clients in the parent process -(`Puma documentation `_): +the ``before_fork`` and ``on_refork`` hooks to close clients in the +parent process (`Puma documentation `_). .. code-block:: ruby - on_worker_boot do - if defined?(Mongoid) - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - else - raise "Mongoid is not loaded. You may have forgotten to enable app preloading." - end - end + # config/puma.rb + # Runs in the Puma master process before it forks a child worker. before_fork do - if defined?(Mongoid) - Mongoid.disconnect_clients - end + Mongoid.disconnect_clients + end + + # Required when using Puma's fork_worker option. Runs in the + # child worker 0 process before it forks grandchild workers. + on_refork do + Mongoid.disconnect_clients + end + + # Runs in each Puma child process after it forks from its parent. + on_worker_boot do + Mongoid.reconnect_clients end Unicorn @@ -926,21 +925,14 @@ the ``before_fork`` hook to close clients in the parent process .. code-block:: ruby - after_fork do |server, worker| - if defined?(Mongoid) - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - else - raise "Mongoid is not loaded. You may have forgotten to enable app preloading." - end + # config/unicorn.rb + + before_fork do |_server, _worker| + Mongoid.disconnect_clients end - before_fork do |server, worker| - if defined?(Mongoid) - Mongoid.disconnect_clients - end + after_fork do |_server, _worker| + Mongoid.reconnect_clients end Passenger @@ -956,12 +948,7 @@ before the workers are forked. if defined?(PhusionPassenger) PhusionPassenger.on_event(:starting_worker_process) do |forked| - if forked - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - end + Mongoid.reconnect_clients if forked end end diff --git a/lib/mongoid.rb b/lib/mongoid.rb index 7bc8ac8bf..4d305f178 100644 --- a/lib/mongoid.rb +++ b/lib/mongoid.rb @@ -100,6 +100,16 @@ def disconnect_clients Clients.disconnect end + # Reconnect all active clients. + # + # @example Reconnect all active clients. + # Mongoid.reconnect_clients + # + # @return [ true ] True. + def reconnect_clients + Clients.reconnect + end + # Convenience method for getting a named client. # # @example Get a named client. diff --git a/lib/mongoid/clients.rb b/lib/mongoid/clients.rb index feaa889df..25e22afa8 100644 --- a/lib/mongoid/clients.rb +++ b/lib/mongoid/clients.rb @@ -47,9 +47,19 @@ def default # # @return [ true ] True. def disconnect - clients.values.each do |client| - client.close - end + clients.each_value(&:close) + true + end + + # Reconnect all active clients. + # + # @example Reconnect all active clients. + # Mongoid::Clients.reconnect + # + # @return [ true ] True. + def reconnect + clients.each_value(&:reconnect) + true end # Get a stored client with the provided name. If no client exists diff --git a/spec/mongoid/clients_spec.rb b/spec/mongoid/clients_spec.rb index 055500cf8..0e923ee44 100644 --- a/spec/mongoid/clients_spec.rb +++ b/spec/mongoid/clients_spec.rb @@ -1216,4 +1216,48 @@ class StoreChild2 < StoreParent end end end + + context "#disconnect" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "disconnects from all active clients" do + clients.each do |client| + expect(client).to receive(:close).and_call_original + end + Mongoid::Clients.disconnect + end + + it "returns true" do + expect(Mongoid::Clients.disconnect).to eq(true) + end + end + + context "#reconnect" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "reconnects all active clients" do + clients.each do |client| + expect(client).to receive(:reconnect).and_call_original + end + Mongoid::Clients.reconnect + end + + it "returns true" do + expect(Mongoid::Clients.reconnect).to eq(true) + end + end end diff --git a/spec/mongoid_spec.rb b/spec/mongoid_spec.rb index c3a1be773..038f8f9b9 100644 --- a/spec/mongoid_spec.rb +++ b/spec/mongoid_spec.rb @@ -81,6 +81,32 @@ end Mongoid.disconnect_clients end + + it "returns true" do + expect(Mongoid.disconnect_clients).to eq(true) + end + end + + describe ".reconnect_clients" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "reconnects all active clients" do + clients.each do |client| + expect(client).to receive(:reconnect).and_call_original + end + Mongoid.reconnect_clients + end + + it "returns true" do + expect(Mongoid.reconnect_clients).to eq(true) + end end describe ".client" do From bbbd82bddbea2833a51dda9b6c76b652f1ca8fce Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:35:39 +0900 Subject: [PATCH 36/43] Commit AssociatedValidator --- lib/mongoid/validatable/associated.rb | 112 ++++++++++++++++---- spec/mongoid/validatable/associated_spec.rb | 45 +++----- 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/lib/mongoid/validatable/associated.rb b/lib/mongoid/validatable/associated.rb index 1aba1be99..bf89043d6 100644 --- a/lib/mongoid/validatable/associated.rb +++ b/lib/mongoid/validatable/associated.rb @@ -15,32 +15,108 @@ module Validatable # # validates_associated :name, :addresses # end - class AssociatedValidator < ActiveModel::EachValidator + class AssociatedValidator < ActiveModel::Validator + # Required by `validates_with` so that the validator + # gets added to the correct attributes. + def attributes + options[:attributes] + end - # Validates that the associations provided are either all nil or all - # valid. If neither is true then the appropriate errors will be added to - # the parent document. + # Checks that the named associations of the given record + # (`attributes`) are valid. This does NOT load the associations + # from the database, and will only validate records that are dirty + # or unpersisted. # - # @example Validate the association. - # validator.validate_each(document, :name, name) + # If anything is not valid, appropriate errors will be added to + # the `document` parameter. + # + # @param [ Mongoid::Document ] document the document with the + # associations to validate. + def validate(document) + options[:attributes].each do |attr_name| + validate_association(document, attr_name) + end + end + + private + + # Validates that the given association provided is either nil, + # persisted and unchanged, or invalid. Otherwise, the appropriate errors + # will be added to the parent document. # # @param [ Mongoid::Document ] document The document to validate. # @param [ Symbol ] attribute The association to validate. - # @param [ Object ] value The value of the association. - def validate_each(document, attribute, value) - begin - document.begin_validate - valid = Array.wrap(value).collect do |doc| - if doc.nil? || doc.flagged_for_destroy? - true + def validate_association(document, attribute) + # grab the proxy from the instance variable directly; we don't want + # any loading logic to run; we just want to see if it's already + # been loaded. + proxy = document.ivar(attribute) + return unless proxy + + # if the variable exists, now we see if it is a proxy, or an actual + # document. It might be a literal document instead of a proxy if this + # document was created with a Document instance as a provided attribute, + # e.g. "Post.new(message: Message.new)". + target = proxy.respond_to?(:_target) ? proxy._target : proxy + + # Now, fetch the list of documents from the target. Target may be a + # single value, or a list of values, and in the case of HasMany, + # might be a rather complex collection. We need to do this without + # triggering a load, so it's a bit of a delicate dance. + list = get_target_documents(target) + + valid = document.validating do + # Now, treating the target as an array, look at each element + # and see if it is valid, but only if it has already been + # persisted, or changed, and hasn't been flagged for destroy. + list.all? do |value| + if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?) + value.validated? ? true : value.valid? else - doc.validated? ? true : doc.valid? + true end - end.all? - ensure - document.exit_validate + end end - document.errors.add(attribute, :invalid, **options) unless valid + + document.errors.add(attribute, :invalid) unless valid + end + + # Examine the given target object and return an array of + # documents (possibly empty) that the target represents. + # + # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy | HasMany::Enumerable ] target + # the target object to examine. + # + # @return [ Array ] the list of documents + def get_target_documents(target) + if target.respond_to?(:_loaded?) + get_target_documents_for_has_many(target) + else + get_target_documents_for_other(target) + end + end + + # Returns the list of all currently in-memory values held by + # the target. The target will not be loaded. + # + # @param [ HasMany::Enumerable ] target the target that will + # be examined for in-memory documents. + # + # @return [ Array ] the in-memory documents + # held by the target. + def get_target_documents_for_has_many(target) + [*target._loaded.values, *target._added.values] + end + + # Returns the target as an array. If the target represents a single + # value, it is wrapped in an array. + # + # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy ] target + # the target to return. + # + # @return [ Array ] the target, as an array. + def get_target_documents_for_other(target) + Array.wrap(target) end end end diff --git a/spec/mongoid/validatable/associated_spec.rb b/spec/mongoid/validatable/associated_spec.rb index 83eb52dd9..180a73a93 100644 --- a/spec/mongoid/validatable/associated_spec.rb +++ b/spec/mongoid/validatable/associated_spec.rb @@ -75,23 +75,20 @@ end it 'does not run validation on them' do - expect(description).to_not receive(:valid?) expect(user).to be_valid end - end - end end - describe '#validate_each' do + describe '#validate' do let(:person) do Person.new end let(:validator) do - described_class.new(attributes: person.attributes) + described_class.new(attributes: person.relations.keys) end context 'when the association is a one to one' do @@ -99,7 +96,7 @@ context 'when the association is nil' do before do - validator.validate_each(person, :name, nil) + validator.validate(person) end it 'adds no errors' do @@ -108,14 +105,9 @@ end context 'when the association is valid' do - - let(:associated) do - double(valid?: true, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :name, associated) + person.name = Name.new(first_name: 'A', last_name: 'B') + validator.validate(person) end it 'adds no errors' do @@ -125,13 +117,9 @@ context 'when the association is invalid' do - let(:associated) do - double(valid?: false, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :name, associated) + person.name = Name.new(first_name: 'Jamis', last_name: 'Buck') + validator.validate(person) end it 'adds errors to the parent document' do @@ -149,7 +137,7 @@ context 'when the association is empty' do before do - validator.validate_each(person, :addresses, []) + validator.validate(person) end it 'adds no errors' do @@ -159,13 +147,9 @@ context 'when the association has invalid documents' do - let(:associated) do - double(valid?: false, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :addresses, [associated]) + person.addresses << Address.new(street: '123') + validator.validate(person) end it 'adds errors to the parent document' do @@ -175,13 +159,10 @@ context 'when the association has all valid documents' do - let(:associated) do - double(valid?: true, flagged_for_destroy?: false) - end - before do - expect(associated).to receive(:validated?).and_return(false) - validator.validate_each(person, :addresses, [associated]) + person.addresses << Address.new(street: '123 First St') + person.addresses << Address.new(street: '456 Second St') + validator.validate(person) end it 'adds no errors' do From a35d481ef41ef738be1821d82bebbb52915ef576 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Mon, 29 Apr 2024 11:13:16 -0600 Subject: [PATCH 37/43] MONGOID-5760 Update feature flags defaults and remove deprecations (#5811) * configure defaults for <= 8.1 * deprecate "running_with_passenger?" * remove code deprecated <= 8.1 * fix failing specs --- lib/mongoid/config.rb | 4 + lib/mongoid/config/defaults.rb | 2 + lib/mongoid/contextual/geo_near.rb | 239 --------- lib/mongoid/contextual/mongo.rb | 26 - lib/mongoid/extensions/date.rb | 4 +- lib/mongoid/extensions/object.rb | 16 - lib/mongoid/extensions/time.rb | 2 +- lib/mongoid/findable.rb | 1 - lib/mongoid/scopable.rb | 20 +- spec/mongoid/config/defaults_spec.rb | 4 + spec/mongoid/contextual/geo_near_spec.rb | 472 ------------------ .../queryable/selectable_logical_spec.rb | 6 +- spec/mongoid/criteria_spec.rb | 20 - spec/mongoid/extensions/object_spec.rb | 7 - spec/mongoid/extensions/time_spec.rb | 16 +- .../mongoid/extensions/time_with_zone_spec.rb | 16 +- spec/mongoid/monkey_patches_spec.rb | 9 +- spec/mongoid/scopable_spec.rb | 19 - 18 files changed, 50 insertions(+), 833 deletions(-) delete mode 100644 lib/mongoid/contextual/geo_near.rb delete mode 100644 spec/mongoid/contextual/geo_near_spec.rb diff --git a/lib/mongoid/config.rb b/lib/mongoid/config.rb index 352f36bc1..978948f07 100644 --- a/lib/mongoid/config.rb +++ b/lib/mongoid/config.rb @@ -376,10 +376,14 @@ def time_zone # config.running_with_passenger? # # @return [ true | false ] If the app is deployed on Passenger. + # + # @deprecated def running_with_passenger? @running_with_passenger ||= defined?(PhusionPassenger) end + Mongoid.deprecate(self, :running_with_passenger?) + private def set_log_levels diff --git a/lib/mongoid/config/defaults.rb b/lib/mongoid/config/defaults.rb index b1981646b..eaa00499f 100644 --- a/lib/mongoid/config/defaults.rb +++ b/lib/mongoid/config/defaults.rb @@ -25,6 +25,8 @@ def load_defaults(version) when "8.1" self.immutable_ids = false self.legacy_persistence_context_behavior = true + self.around_callbacks_for_embeds = true + self.prevent_multiple_calls_of_embedded_callbacks = false load_defaults "9.0" when "9.0" diff --git a/lib/mongoid/contextual/geo_near.rb b/lib/mongoid/contextual/geo_near.rb deleted file mode 100644 index f0bda793c..000000000 --- a/lib/mongoid/contextual/geo_near.rb +++ /dev/null @@ -1,239 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -module Mongoid - module Contextual - - # Represents a $geoNear database command instruction. - class GeoNear - extend Forwardable - include Enumerable - include Command - - def_delegator :results, :[] - def_delegators :entries, :==, :empty? - - # Get the average distance for all documents from the point in the - # command. - # - # @example Get the average distance. - # geo_near.average_distance - # - # @return [ Float | nil ] The average distance. - def average_distance - average = stats["avgDistance"] - (average.nil? || average.nan?) ? nil : average - end - - # Iterates over each of the documents in the $geoNear, excluding the - # extra information that was passed back from the database. - # - # @example Iterate over the results. - # geo_near.each do |doc| - # p doc - # end - # - # @return [ Enumerator ] The enumerator. - def each - if block_given? - documents.each do |doc| - yield doc - end - else - to_enum - end - end - - # Provide a distance multiplier to be used for each returned distance. - # - # @example Provide the distance multiplier. - # geo_near.distance_multiplier(13113.1) - # - # @param [ Integer | Float ] value The distance multiplier. - # - # @return [ GeoNear ] The GeoNear wrapper. - def distance_multiplier(value) - command[:distanceMultiplier] = value - self - end - - # Initialize the new map/reduce directive. - # - # @example Initialize the new map/reduce. - # MapReduce.new(criteria, map, reduce) - # - # @param [ Mongo::Collection ] collection The collection to run the - # operation on. - # @param [ Criteria ] criteria The Mongoid criteria. - # @param [ String ] near - def initialize(collection, criteria, near) - @collection, @criteria = collection, criteria - command[:geoNear] = collection.name.to_s - command[:near] = near - apply_criteria_options - end - - # Get a pretty string representation of the command. - # - # @example Inspect the geoNear. - # geo_near.inspect - # - # @return [ String ] The inspection string. - def inspect -%Q{# -} - end - - # Specify the maximum distance to find documents for, or get the value of - # the document with the furthest distance. - # - # @example Set the max distance. - # geo_near.max_distance(0.5) - # - # @example Get the max distance. - # geo_near.max_distance - # - # @param [ Integer | Float ] value The maximum distance. - # - # @return [ GeoNear | Float ] The GeoNear command or the value. - def max_distance(value = nil) - if value - command[:maxDistance] = value - self - else - stats["maxDistance"] - end - end - - # Specify the minimum distance to find documents for. - # - # @example Set the min distance. - # geo_near.min_distance(0.5) - # - # @param [ Integer | Float ] value The minimum distance. - # - # @return [ GeoNear ] The GeoNear command. - def min_distance(value) - command[:minDistance] = value - self - end - - # Tell the command to calculate based on spherical distances. - # - # @example Add the spherical flag. - # geo_near.spherical - # - # @return [ GeoNear ] The command. - def spherical - command[:spherical] = true - self - end - - # Tell the command whether or not the returned results should be unique. - # - # @example Set the unique flag. - # geo_near.unique(false) - # - # @param [ true | false ] value Whether to return unique documents. - # - # @return [ GeoNear ] The command. - def unique(value = true) - command[:unique] = value - self - end - - # Execute the $geoNear, returning the raw output. - # - # @example Run the $geoNear - # geo_near.execute - # - # @return [ Hash ] The raw output - def execute - results - end - - # Get the stats for the command run. - # - # @example Get the stats. - # geo_near.stats - # - # @return [ Hash ] The stats from the command run. - def stats - results["stats"] - end - - # Get the execution time of the command. - # - # @example Get the execution time. - # geo_near.time - # - # @return [ Float ] The execution time. - def time - stats["time"] - end - - # Is this context's criteria considered empty? - # - # @example Is this context's criteria considered empty? - # geo_near.empty_and_chainable? - # - # @return [ true ] Always true. - def empty_and_chainable? - true - end - - private - - # Apply criteria specific options - query, limit. - # - # @api private - # - # @example Apply the criteria options - # geo_near.apply_criteria_options - # - # @return [ nil ] Nothing. - def apply_criteria_options - command[:query] = criteria.selector - if limit = criteria.options[:limit] - command[:num] = limit - end - end - - # Get the result documents from the $geoNear. - # - # @api private - # - # @example Get the documents. - # geo_near.documents - # - # @return [ Array | Cursor ] The documents. - def documents - results["results"].map do |attributes| - doc = Factory.from_db(criteria.klass, attributes["obj"], criteria) - doc.attributes["geo_near_distance"] = attributes["dis"] - doc - end - end - - # Execute the $geoNear command and get the results. - # - # @api private - # - # @example Get the results. - # geo_near.results - # - # @return [ Hash ] The results of the command. - def results - @results ||= client.command(command).first - end - end - end -end diff --git a/lib/mongoid/contextual/mongo.rb b/lib/mongoid/contextual/mongo.rb index 3fceba9df..b93fa32d5 100644 --- a/lib/mongoid/contextual/mongo.rb +++ b/lib/mongoid/contextual/mongo.rb @@ -6,7 +6,6 @@ require "mongoid/contextual/atomic" require "mongoid/contextual/aggregable/mongo" require "mongoid/contextual/command" -require "mongoid/contextual/geo_near" require "mongoid/contextual/map_reduce" require "mongoid/association/eager_loadable" @@ -24,8 +23,6 @@ class Mongo include Association::EagerLoadable include Queryable - Mongoid.deprecate(self, :geo_near) - # Options constant. OPTIONS = [ :hint, :limit, @@ -263,29 +260,6 @@ def find_first end end - # Execute a $geoNear command against the database. - # - # @example Find documents close to 10, 10. - # context.geo_near([ 10, 10 ]) - # - # @example Find with spherical distance. - # context.geo_near([ 10, 10 ]).spherical - # - # @example Find with a max distance. - # context.geo_near([ 10, 10 ]).max_distance(0.5) - # - # @example Provide a distance multiplier. - # context.geo_near([ 10, 10 ]).distance_multiplier(1133) - # - # @param [ Array ] coordinates The coordinates. - # - # @return [ GeoNear ] The GeoNear command. - # - # @deprecated - def geo_near(coordinates) - GeoNear.new(collection, criteria, coordinates) - end - # Create the new Mongo context. This delegates operations to the # underlying driver. # diff --git a/lib/mongoid/extensions/date.rb b/lib/mongoid/extensions/date.rb index d28e181d1..231fbf03d 100644 --- a/lib/mongoid/extensions/date.rb +++ b/lib/mongoid/extensions/date.rb @@ -71,8 +71,10 @@ def mongoize(object) if object.is_a?(String) # https://jira.mongodb.org/browse/MONGOID-4460 time = ::Time.parse(object) - else + elsif object.respond_to?(:__mongoize_time__) time = object.__mongoize_time__ + else + nil end rescue ArgumentError nil diff --git a/lib/mongoid/extensions/object.rb b/lib/mongoid/extensions/object.rb index 879602f39..67b91bc98 100644 --- a/lib/mongoid/extensions/object.rb +++ b/lib/mongoid/extensions/object.rb @@ -32,22 +32,6 @@ def __find_args__ end Mongoid.deprecate(self, :__find_args__) - # Mongoize a plain object into a time. - # - # @note This method should not be used, because it does not - # return correct results for non-Time objects. Override - # __mongoize_time__ in classes that are time-like to return an - # instance of Time or ActiveSupport::TimeWithZone. - # - # @example Mongoize the object. - # object.__mongoize_time__ - # - # @return [ Object ] self. - # @deprecated - def __mongoize_time__ - self - end - # Try to form a setter from this object. # # @example Try to form a setter. diff --git a/lib/mongoid/extensions/time.rb b/lib/mongoid/extensions/time.rb index a2689d664..464a8d60a 100644 --- a/lib/mongoid/extensions/time.rb +++ b/lib/mongoid/extensions/time.rb @@ -71,7 +71,7 @@ def demongoize(object) def mongoize(object) return if object.blank? begin - time = object.__mongoize_time__ + time = object.respond_to?(:__mongoize_time__) ? object.__mongoize_time__ : nil rescue ArgumentError return end diff --git a/lib/mongoid/findable.rb b/lib/mongoid/findable.rb index fccfe2b1c..b533d425b 100644 --- a/lib/mongoid/findable.rb +++ b/lib/mongoid/findable.rb @@ -38,7 +38,6 @@ module Findable :for_js, :fourth, :fourth!, - :geo_near, :includes, :last!, :map_reduce, diff --git a/lib/mongoid/scopable.rb b/lib/mongoid/scopable.rb index 35635acdc..dac877ad9 100644 --- a/lib/mongoid/scopable.rb +++ b/lib/mongoid/scopable.rb @@ -289,17 +289,15 @@ def check_scope_validity(value) # @return [ Method ] The defined method. def define_scope_method(name) singleton_class.class_eval do - ruby2_keywords( - define_method(name) do |*args, **kwargs| - scoping = _declared_scopes[name] - scope = instance_exec(*args, **kwargs, &scoping[:scope]) - extension = scoping[:extension] - to_merge = scope || queryable - criteria = to_merge.empty_and_chainable? ? to_merge : with_default_scope.merge(to_merge) - criteria.extend(extension) - criteria - end - ) + define_method(name) do |*args, **kwargs| + scoping = _declared_scopes[name] + scope = instance_exec(*args, **kwargs, &scoping[:scope]) + extension = scoping[:extension] + to_merge = scope || queryable + criteria = to_merge.empty_and_chainable? ? to_merge : with_default_scope.merge(to_merge) + criteria.extend(extension) + criteria + end end end diff --git a/spec/mongoid/config/defaults_spec.rb b/spec/mongoid/config/defaults_spec.rb index ddff45b45..cbf8904e4 100644 --- a/spec/mongoid/config/defaults_spec.rb +++ b/spec/mongoid/config/defaults_spec.rb @@ -27,6 +27,8 @@ it "uses settings for 8.1" do expect(Mongoid.immutable_ids).to be false expect(Mongoid.legacy_persistence_context_behavior).to be true + expect(Mongoid.around_callbacks_for_embeds).to be true + expect(Mongoid.prevent_multiple_calls_of_embedded_callbacks).to be false end end @@ -34,6 +36,8 @@ it "does not use settings for 8.1" do expect(Mongoid.immutable_ids).to be true expect(Mongoid.legacy_persistence_context_behavior).to be false + expect(Mongoid.around_callbacks_for_embeds).to be false + expect(Mongoid.prevent_multiple_calls_of_embedded_callbacks).to be true end end diff --git a/spec/mongoid/contextual/geo_near_spec.rb b/spec/mongoid/contextual/geo_near_spec.rb deleted file mode 100644 index 53f90e1a0..000000000 --- a/spec/mongoid/contextual/geo_near_spec.rb +++ /dev/null @@ -1,472 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -require "spec_helper" - -describe Mongoid::Contextual::GeoNear do - max_server_version '4.0' - - describe "#average_distance" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the average distance" do - expect(geo_near.average_distance).to_not be_nil - end - end - - context "when no results are returned" do - - let(:criteria) do - Bar.where(name: "Green Door") - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:expected_value) do - if ClusterConfig.instance.fcv_ish == '4.0' && ClusterConfig.instance.topology == :sharded - # https://jira.mongodb.org/browse/SERVER-50074 - 0.0 - else - nil - end - end - - it "is nil except for 4.0 sharded when it is 0" do - expect(geo_near.average_distance).to eq expected_value - end - end - end - - describe "#each" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when no options are provided" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.entries - end - - it "returns all the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - - context "when the criteria has a limit" do - - let(:criteria) do - Bar.limit(1) - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.entries - end - - it "returns the limited documents" do - expect(results).to eq([ bar_one ]) - end - end - - context "when providing a max distance" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.max_distance(0.40).entries - end - - it "returns the limited documents" do - expect(results).to eq([ bar_one ]) - end - end - - context "when specifying spherical" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.spherical.entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - - context "when providing a distance multiplier" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.distance_multiplier(6378.1).entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - - it "multiplies the distance factor" do - expect(results.first.geo_near_distance.to_i).to eq(2490) - end - end - - context "when unique is false" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.unique(false).entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - end - - describe "#empty?" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - context "when the $geoNear has results" do - - let(:criteria) do - Bar.all - end - - it "returns false" do - expect(geo_near).to_not be_empty - end - end - - context "when the map/reduce has no results" do - - let(:criteria) do - Bar.where(name: "Halo") - end - - it "returns true" do - expect(geo_near).to be_empty - end - end - end - - describe "#execute" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:execution_results) do - geo_near.execute - end - - it "returns a hash" do - expect(execution_results).to be_a_kind_of(Hash) - end - end - - describe "#max_distance" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the max distance" do - expect(geo_near.max_distance).to_not be_nil - end - end - - context "when no results are returned" do - - let(:criteria) do - Bar.where(name: "Green Door") - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns 0.0" do - expect(geo_near.max_distance).to eq(0.0) - end - end - end - - describe "#min_distance" do - - let!(:collection) do - Pub.collection - end - - before do - Pub.create_indexes - end - - let!(:bar_one) do - Pub.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Pub.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Pub.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]).spherical.min_distance(0.0) - end - - it "sets the min distance" do - expect(geo_near.to_a).to include(bar_one) - expect(geo_near.to_a).to include(bar_two) - end - end - end - - describe "#inspect" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "contains the selector" do - expect(geo_near.inspect).to include("selector") - end - - it "contains the class" do - expect(geo_near.inspect).to include("class") - end - - it "contains the near" do - expect(geo_near.inspect).to include("near") - end - - it "contains the multiplier" do - expect(geo_near.inspect).to include("multiplier") - end - - it "contains the max" do - expect(geo_near.inspect).to include("max") - end - - it "contains the unique" do - expect(geo_near.inspect).to include("unique") - end - - it "contains the spherical" do - expect(geo_near.inspect).to include("spherical") - end - end - - describe "#time" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the execution time" do - expect(geo_near.time).to_not be_nil - end - end - - describe "#empty_and_chainable" do - - let!(:collection) do - Bar.collection - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns true" do - expect(geo_near.empty_and_chainable?).to be(true) - end - end -end diff --git a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb index b312b5076..4ac7c4f73 100644 --- a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +++ b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb @@ -1663,7 +1663,7 @@ # Date instance is converted to a Time instance in local time, # because we are querying on a Time field and dates are interpreted # in local time when assigning to Time fields - {'published' => {'$gt' => Time.local(2020, 2, 3)}}, + {'published' => {'$gt' => Time.zone.local(2020, 2, 3)}}, ]} end end @@ -2427,7 +2427,7 @@ context 'when a criterion has an aliased field' do let(:selection) { query.none_of({ id: 1 }) } - + it 'adds the $nor selector and aliases the field' do expect(selection.selector).to eq('$nor' => [{ '_id' => 1 }]) end @@ -2514,7 +2514,7 @@ # Date instance is converted to a Time instance in local time, # because we are querying on a Time field and dates are interpreted # in local time when assigning to Time fields - {'published' => {'$gt' => Time.local(2020, 2, 3) } }, + {'published' => {'$gt' => Time.zone.local(2020, 2, 3) } }, ] } end end diff --git a/spec/mongoid/criteria_spec.rb b/spec/mongoid/criteria_spec.rb index 65d1ca809..c4ef5ddf3 100644 --- a/spec/mongoid/criteria_spec.rb +++ b/spec/mongoid/criteria_spec.rb @@ -1030,26 +1030,6 @@ end end - describe "#geo_near" do - max_server_version '4.0' - - before do - Bar.create_indexes - end - - let!(:match) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let(:criteria) do - Bar.geo_near([ 52, 13 ]).max_distance(10).spherical - end - - it "returns the matching documents" do - expect(criteria).to eq([ match ]) - end - end - describe "#eq" do let!(:match) do diff --git a/spec/mongoid/extensions/object_spec.rb b/spec/mongoid/extensions/object_spec.rb index 9d5aae2d2..ada6e0ce7 100644 --- a/spec/mongoid/extensions/object_spec.rb +++ b/spec/mongoid/extensions/object_spec.rb @@ -23,13 +23,6 @@ end end - describe "#__mongoize_time__" do - - it "returns self" do - expect(object.__mongoize_time__).to eq(object) - end - end - describe ".demongoize" do let(:object) do diff --git a/spec/mongoid/extensions/time_spec.rb b/spec/mongoid/extensions/time_spec.rb index a68bf1547..3b06c0b7c 100644 --- a/spec/mongoid/extensions/time_spec.rb +++ b/spec/mongoid/extensions/time_spec.rb @@ -8,7 +8,7 @@ describe ".demongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when the time zone is not defined" do @@ -22,7 +22,7 @@ it "returns the local time" do expect(Time.demongoize(time).utc_offset).to eq( - Time.local(2010, 11, 19).utc_offset + Time.zone.local(2010, 11, 19).utc_offset ) end end @@ -41,7 +41,7 @@ context "when we have a time close to midnight" do let(:time) do - Time.local(2010, 11, 19, 0, 30).utc + Time.zone.local(2010, 11, 19, 0, 30).utc end it "changes it back to the equivalent local time" do @@ -322,7 +322,7 @@ describe ".mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when given nil" do @@ -608,7 +608,7 @@ end it "converts to a utc time" do - expect(Time.mongoize(date)).to eq(Time.local(date.year, date.month, date.day)) + expect(Time.mongoize(date)).to eq(Time.zone.local(date.year, date.month, date.day)) end it "has a zero utc offset" do @@ -635,7 +635,7 @@ end it "returns a time" do - expect(Time.mongoize(array)).to eq(Time.local(*array)) + expect(Time.mongoize(array)).to eq(Time.zone.local(*array)) end context "when setting ActiveSupport time zone" do @@ -653,11 +653,11 @@ describe "#mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end let!(:eom_time) do - Time.local(2012, 11, 30, 23, 59, 59, 999999.999) + Time.zone.local(2012, 11, 30, 23, 59, 59, 999999.999) end let!(:eom_time_mongoized) do diff --git a/spec/mongoid/extensions/time_with_zone_spec.rb b/spec/mongoid/extensions/time_with_zone_spec.rb index f35897d94..027fd4025 100644 --- a/spec/mongoid/extensions/time_with_zone_spec.rb +++ b/spec/mongoid/extensions/time_with_zone_spec.rb @@ -8,7 +8,7 @@ describe ".demongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when the time zone is not defined" do @@ -22,7 +22,7 @@ it "returns the local time" do expect(ActiveSupport::TimeWithZone.demongoize(time).utc_offset).to eq( - Time.local(2010, 11, 19).utc_offset + Time.zone.local(2010, 11, 19).utc_offset ) end end @@ -41,7 +41,7 @@ context "when we have a time close to midnight" do let(:time) do - Time.local(2010, 11, 19, 0, 30).utc + Time.zone.local(2010, 11, 19, 0, 30).utc end it "changes it back to the equivalent local time" do @@ -136,7 +136,7 @@ describe ".mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when given nil" do @@ -171,7 +171,7 @@ it "returns a local date from the string" do expect(ActiveSupport::TimeWithZone.mongoize(time.to_s)).to eq( - Time.local(time.year, time.month, time.day, time.hour, time.min, time.sec) + Time.zone.local(time.year, time.month, time.day, time.hour, time.min, time.sec) ) end end @@ -275,7 +275,7 @@ end it "converts to a utc time" do - expect(ActiveSupport::TimeWithZone.mongoize(date)).to eq(Time.local(date.year, date.month, date.day)) + expect(ActiveSupport::TimeWithZone.mongoize(date)).to eq(Time.zone.local(date.year, date.month, date.day)) end it "has a zero utc offset" do @@ -302,7 +302,7 @@ end it "returns a time" do - expect(ActiveSupport::TimeWithZone.mongoize(array)).to eq(Time.local(*array)) + expect(ActiveSupport::TimeWithZone.mongoize(array)).to eq(Time.zone.local(*array)) end context "when setting ActiveSupport time zone" do @@ -320,7 +320,7 @@ describe "#mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end it "converts to a utc time" do diff --git a/spec/mongoid/monkey_patches_spec.rb b/spec/mongoid/monkey_patches_spec.rb index 8fdded9af..90086d4d9 100644 --- a/spec/mongoid/monkey_patches_spec.rb +++ b/spec/mongoid/monkey_patches_spec.rb @@ -45,7 +45,6 @@ __intersect_from_array__ __intersect_from_object__ __mongoize_object_id__ - __mongoize_time__ __union__ __union_from_object__ ivar @@ -58,6 +57,7 @@ Array => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ __sort_option__ __sort_pair__ delete_one @@ -65,15 +65,18 @@ Date => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], DateTime => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], FalseClass => %i[is_a?], Float => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], Hash => %i[ __sort_option__ @@ -81,6 +84,7 @@ Integer => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], Module => %i[ re_define_method @@ -102,6 +106,7 @@ __evolve_time__ __expr_part__ __mongo_expression__ + __mongoize_time__ __sort_option__ before_type_cast? collectionize @@ -150,10 +155,12 @@ Time => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], ActiveSupport::TimeWithZone => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ _bson_to_i ], BSON::Decimal128 => %i[ diff --git a/spec/mongoid/scopable_spec.rb b/spec/mongoid/scopable_spec.rb index 4f971c07f..93502c62d 100644 --- a/spec/mongoid/scopable_spec.rb +++ b/spec/mongoid/scopable_spec.rb @@ -354,25 +354,6 @@ def self.default_scope end end - context "when the lambda includes a geo_near query" do - - before do - Bar.scope(:near_by, lambda{ |location| geo_near(location) }) - end - - after do - class << Bar - undef_method :near_by - end - Bar._declared_scopes.clear - end - - it "allows the scope to be defined" do - expect(Bar.near_by([ 51.545099, -0.0106 ])).to be_a(Mongoid::Contextual::GeoNear) - end - - end - context "when a block is provided" do before do From ff4c3179f3c1091cfe8250ac54fcda704be0b7ca Mon Sep 17 00:00:00 2001 From: Sten Larsson Date: Tue, 30 Apr 2024 22:54:30 +0200 Subject: [PATCH 38/43] Add pretty print support (#5810) * Add pretty print support This adds the `pretty_print` method, which pretty prints the same information as the `inspect` method. This is meant to be called by the default 'pp' gem, e.g. by calling `pp(person)` or `person.pretty_inspect`. The specs are an almost identical to the specs for the `inspect` method to ensure the same information is included. * Simplify code with if modifier Co-authored-by: Jamis Buck * Avoid "default gem" terminology Co-authored-by: Jamis Buck --------- Co-authored-by: Jamis Buck Co-authored-by: Jamis Buck --- lib/mongoid/inspectable.rb | 31 +++++++++++++ spec/mongoid/inspectable_spec.rb | 80 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/lib/mongoid/inspectable.rb b/lib/mongoid/inspectable.rb index f432ab32a..90678eac1 100644 --- a/lib/mongoid/inspectable.rb +++ b/lib/mongoid/inspectable.rb @@ -19,6 +19,37 @@ def inspect "#<#{self.class.name} _id: #{_id}, #{inspection * ', '}>" end + # This pretty prints the same information as the inspect method. This is + # meant to be called by the standard 'pp' library. + # + # @param [ PP ] pretty_printer The pretty printer. + # + # @example Pretty print the document. + # person.pretty_inspect + # + # @api private + def pretty_print(pretty_printer) + keys = fields.keys | attributes.keys + pretty_printer.group(1, "#<#{self.class.name}", '>') do + sep = lambda { pretty_printer.text(',') } + pretty_printer.seplist(keys, sep) do |key| + pretty_printer.breakable + field = fields[key] + as = "(#{field.options[:as]})" if field && field.options[:as] + pretty_printer.text("#{key}#{as}") + pretty_printer.text(':') + pretty_printer.group(1) do + pretty_printer.breakable + if key == "_id" + pretty_printer.text(_id.to_s) + else + pretty_printer.pp(@attributes[key]) + end + end + end + end + end + private # Get an array of inspected fields for the document. diff --git a/spec/mongoid/inspectable_spec.rb b/spec/mongoid/inspectable_spec.rb index 203fd5ea1..a471fa5a8 100644 --- a/spec/mongoid/inspectable_spec.rb +++ b/spec/mongoid/inspectable_spec.rb @@ -84,4 +84,84 @@ end end end + + describe "#pretty_inspect" do + + context "when not allowing dynamic fields" do + + let(:person) do + Person.new(title: "CEO") + end + + let(:pretty_inspected) do + person.pretty_inspect + end + + it "includes the model type" do + expect(pretty_inspected).to include("#\n" + end + end + end end From 88f4b1ae86fab094b553a443dc5067493210c384 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Wed, 1 May 2024 09:17:48 -0600 Subject: [PATCH 39/43] bump version number(s) (#5813) --- README.md | 4 ++-- lib/mongoid/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f2394f4e0..0eaa465a9 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ Compatibility Mongoid supports and is tested against: -- MRI 2.7 - 3.1 +- MRI 2.7 - 3.2 - JRuby 9.4 -- MongoDB server 3.6 - 6.0 +- MongoDB server 3.6 - 7.0 Issues ------ diff --git a/lib/mongoid/version.rb b/lib/mongoid/version.rb index bd850fa5a..a757d0eef 100644 --- a/lib/mongoid/version.rb +++ b/lib/mongoid/version.rb @@ -2,5 +2,5 @@ # rubocop:todo all module Mongoid - VERSION = "9.0.0.alpha" + VERSION = "9.0.0" end From 8e7494394043e408feb84f9b10fa60a301aa3554 Mon Sep 17 00:00:00 2001 From: Anton Savitskiy Date: Mon, 6 May 2024 12:47:10 +0200 Subject: [PATCH 40/43] fix CI: prevent models mutations in auto_save_spec --- spec/mongoid/association/auto_save_spec.rb | 38 ++-- spec/support/models/account_auto.rb | 38 ++++ spec/support/models/drug_auto.rb | 8 + spec/support/models/person_auto.rb | 236 +++++++++++++++++++++ 4 files changed, 301 insertions(+), 19 deletions(-) create mode 100644 spec/support/models/account_auto.rb create mode 100644 spec/support/models/drug_auto.rb create mode 100644 spec/support/models/person_auto.rb diff --git a/spec/mongoid/association/auto_save_spec.rb b/spec/mongoid/association/auto_save_spec.rb index a700a76ba..e5a0e972a 100644 --- a/spec/mongoid/association/auto_save_spec.rb +++ b/spec/mongoid/association/auto_save_spec.rb @@ -9,16 +9,16 @@ describe '.auto_save' do before(:all) do - Person.has_many :drugs, validate: false, autosave: true - Person.has_one :account, validate: false, autosave: true + PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true + PersonAuto.has_one :account, class_name: 'AccountAuto', validate: false, autosave: true end after(:all) do - Person.reset_callbacks(:save) + PersonAuto.reset_callbacks(:save) end let(:person) do - Person.new + PersonAuto.new end context 'when the option is not provided' do @@ -85,11 +85,11 @@ context 'when the relation has already had the autosave callback added' do before do - Person.has_many :drugs, validate: false, autosave: true + PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true end let(:drug) do - Drug.new(name: 'Percocet') + DrugAuto.new(name: 'Percocet') end it 'does not add the autosave callback twice' do @@ -102,7 +102,7 @@ context 'when the relation is a references many' do let(:drug) do - Drug.new(name: 'Percocet') + DrugAuto.new(name: 'Percocet') end context 'when saving a new parent document' do @@ -110,7 +110,7 @@ context 'when persistence options are not set on the parent' do before do - Person.has_many :drugs, validate: false, autosave: true + PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true end before do @@ -130,8 +130,8 @@ end after do - Person.with(database: other_database, &:delete_all) - Drug.with(database: other_database, &:delete_all) + PersonAuto.with(database: other_database, &:delete_all) + DrugAuto.with(database: other_database, &:delete_all) end before do @@ -142,7 +142,7 @@ end it 'saves the relation with the persistence options' do - Drug.with(database: other_database) do |drug_class| + DrugAuto.with(database: other_database) do |drug_class| expect(drug_class.count).to eq(1) end end @@ -165,7 +165,7 @@ context 'when not updating the document' do let(:from_db) do - Person.find person.id + PersonAuto.find person.id end before do @@ -183,7 +183,7 @@ context 'when the relation is a references one' do let(:account) do - Account.new(name: 'Testing') + AccountAuto.new(name: 'Testing') end context 'when saving a new parent document' do @@ -237,7 +237,7 @@ context 'when not updating the document' do let(:from_db) do - Person.find person.id + PersonAuto.find person.id end before do @@ -291,25 +291,25 @@ context 'when it has two relations with autosaves' do let!(:person) do - Person.create!(drugs: [percocet], account: account) + PersonAuto.create!(drugs: [percocet], account: account) end let(:from_db) do - Person.find person.id + PersonAuto.find person.id end let(:percocet) do - Drug.new(name: 'Percocet') + DrugAuto.new(name: 'Percocet') end let(:account) do - Account.new(name: 'Testing') + AccountAuto.new(name: 'Testing') end context 'when updating one document' do let(:placebo) do - Drug.new(name: 'Placebo') + DrugAuto.new(name: 'Placebo') end before do diff --git a/spec/support/models/account_auto.rb b/spec/support/models/account_auto.rb new file mode 100644 index 000000000..781b19663 --- /dev/null +++ b/spec/support/models/account_auto.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class AccountAuto + include Mongoid::Document + + field :_id, type: String, overwrite: true, default: -> { name.try(:parameterize) } + + field :number, type: String + field :balance, type: Integer + field :nickname, type: String + field :name, type: String + field :balanced, type: Mongoid::Boolean, default: -> { balance? } + + field :overridden, type: String + + embeds_many :memberships + belongs_to :creator, class_name: 'User' + belongs_to :person, class_name: 'PersonAuto' + has_many :alerts, autosave: false + has_and_belongs_to_many :agents + has_one :comment, validate: false + + validates_presence_of :name + validates_presence_of :nickname, on: :upsert + validates_length_of :name, maximum: 10, on: :create + + def overridden + self[:overridden] = 'not recommended' + end + + # MONGOID-3365 + field :period_started_at, type: Time + has_many :consumption_periods, dependent: :destroy, validate: false + + def current_consumption + consumption_periods.find_or_create_by(started_at: period_started_at) + end +end diff --git a/spec/support/models/drug_auto.rb b/spec/support/models/drug_auto.rb new file mode 100644 index 000000000..097dba216 --- /dev/null +++ b/spec/support/models/drug_auto.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class DrugAuto + include Mongoid::Document + field :name, type: String + field :generic, type: Mongoid::Boolean + belongs_to :person, class_name: 'PersonAuto', inverse_of: nil, counter_cache: true +end diff --git a/spec/support/models/person_auto.rb b/spec/support/models/person_auto.rb new file mode 100644 index 000000000..1136d4aac --- /dev/null +++ b/spec/support/models/person_auto.rb @@ -0,0 +1,236 @@ +# frozen_string_literal: true + +class PersonAuto + include Mongoid::Document + include Mongoid::Attributes::Dynamic + attr_accessor :mode + + class_attribute :somebody_elses_important_class_options + self.somebody_elses_important_class_options = { keep_me_around: true } + + field :username, default: -> { "arthurnn#{rand(0..10)}" } + field :title + field :terms, type: Mongoid::Boolean + field :pets, type: Mongoid::Boolean, default: false + field :age, type: Integer, default: '100' + field :dob, type: Date + field :employer_id + field :lunch_time, type: Time + field :aliases, type: Array + field :map, type: Hash + field :map_with_default, type: Hash, default: {} + field :score, type: Integer + field :blood_alcohol_content, type: Float, default: -> { 0.0 } + field :last_drink_taken_at, type: Date, default: -> { 1.day.ago.in_time_zone('Alaska') } + field :ssn + field :owner_id, type: Integer + field :security_code + field :reading, type: Object + field :bson_id, type: BSON::ObjectId + field :pattern, type: Regexp + field :override_me, type: Integer + field :at, as: :aliased_timestamp, type: Time + field :t, as: :test, type: String + field :i, as: :inte, type: Integer + field :a, as: :array, type: Array + field :desc, localize: true + field :localized_translations, localize: true + field :test_array, type: Array + field :overridden_setter, type: String + field :arrays, type: Array + field :range, type: Range + field :species, type: Symbol + field :posts_count, type: Integer, default: 0 + + index age: 1 + index addresses: 1 + index dob: 1 + index name: 1 + index title: 1 + + attr_reader :rescored + + embeds_many :favorites, order: :title.desc, inverse_of: :perp, validate: false + embeds_many :videos, order: [%i[title asc]], validate: false + embeds_many :phone_numbers, class_name: 'Phone', validate: false + embeds_many :phones, store_as: :mobile_phones, validate: false + embeds_many :addresses, as: :addressable, validate: false do + def extension + 'Testing' + end + + def find_by_street(street) + where(street: street).first + end + end + + embeds_many :address_components, validate: false + embeds_many :services, cascade_callbacks: true, validate: false + embeds_many :symptoms, validate: false + embeds_many :appointments, validate: false + embeds_many :messages, validate: false + + embeds_one :passport, autobuild: true, store_as: :pass, validate: false + embeds_one :purse, store_as: 'Purse' + embeds_one :pet, class_name: 'Animal', validate: false + embeds_one :name, as: :namable, validate: false do + def extension + 'Testing' + end + + def dawkins? + first_name == 'Richard' && last_name == 'Dawkins' + end + end + embeds_one :quiz, validate: false + + # Must have dependent: :destroy + has_one :game, dependent: :destroy, validate: false do + def extension + 'Testing' + end + end + + has_many \ + :posts, + dependent: :delete_all, + validate: false do + def extension + 'Testing' + end + end + has_many :ordered_posts, order: :rating.desc, validate: false + has_and_belongs_to_many \ + :preferences, + index: true, + dependent: :nullify, + validate: false + has_and_belongs_to_many :user_accounts, validate: false + has_and_belongs_to_many :houses, validate: false + has_and_belongs_to_many :ordered_preferences, order: :value.desc, validate: false + + has_many :drugs, class_name: 'DrugAuto', validate: false + # Must not have dependent: :destroy + has_one :account, class_name: 'AccountAuto', validate: false + has_one :cat, dependent: :nullify, validate: false, primary_key: :username + has_one :book, autobuild: true, validate: false + has_one :home, dependent: :delete_all, validate: false + + has_and_belongs_to_many \ + :administrated_events, + class_name: 'Event', + inverse_of: :administrators, + dependent: :nullify, + validate: false + + belongs_to :mother, class_name: 'PersonAuto' + has_many :children, class_name: 'PersonAuto' + + accepts_nested_attributes_for :addresses + accepts_nested_attributes_for :name, update_only: true + accepts_nested_attributes_for :pet, allow_destroy: true + accepts_nested_attributes_for :game, allow_destroy: true + accepts_nested_attributes_for :favorites, allow_destroy: true, limit: 5 + accepts_nested_attributes_for :posts + accepts_nested_attributes_for :preferences + accepts_nested_attributes_for :quiz + accepts_nested_attributes_for :services, allow_destroy: true + + scope :minor, -> { where(:age.lt => 18) } + scope :without_ssn, -> { without(:ssn) } + scope :search, ->(query) { any_of({ title: query }) } + + def self.older_than(age:) + where(:age.gt => age) + end + + def score_with_rescoring=(score) + @rescored = score.to_i + 20 + self.score_without_rescoring = score + end + + alias_method :score_without_rescoring=, :score= + alias_method :score=, :score_with_rescoring= + + def update_addresses + addresses.each do |address| + address.street = 'Updated Address' + end + end + + def employer=(emp) + self.employer_id = emp.id + end + + def overridden_addresses=(addresses) + self.addresses = addresses + end + + def override_me + read_attribute(:override_me).to_s + end + + def overridden_setter=(value) + @override_called = true + super(value) + end + + class << self + def accepted + scoped.where(terms: true) + end + + def knight + scoped.where(title: 'Sir') + end + + def old + scoped.where(age: { '$gt' => 50 }) + end + end + + def reject_if_city_is_empty(attrs) + attrs[:city].blank? + end + + def reject_if_name_is_blank(attrs) + attrs[:first_name].blank? + end + + def foo + 'i_am_foo' + end + + def preference_names=(names) + names.split(',').each do |name| + preference = Preference.where(name: name).first + if preference + preferences << preference + else + preferences.build(name: name) + end + end + end + + def overridden_map_with_default=(value) + map_with_default['key'] = value + end + + def set_personal_data(ssn:, age:) + self.ssn = ssn + self.age = age + end + + reset_callbacks(:validate) + reset_callbacks(:create) + reset_callbacks(:save) + reset_callbacks(:destroy) + + private + + def secret_name + 'secret' + end +end + +require 'support/models/doctor' From 2fc5beed38b7cace90c170a817e050e205ae5cdf Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Mon, 6 May 2024 22:02:17 +0900 Subject: [PATCH 41/43] Rename PersonAuto etc --> PersonAutosave --- spec/mongoid/association/auto_save_spec.rb | 38 +++++++++---------- .../{account_auto.rb => account_autosave.rb} | 4 +- .../models/{drug_auto.rb => drug_autosave.rb} | 4 +- .../{person_auto.rb => person_autosave.rb} | 10 ++--- 4 files changed, 28 insertions(+), 28 deletions(-) rename spec/support/models/{account_auto.rb => account_autosave.rb} (93%) rename spec/support/models/{drug_auto.rb => drug_autosave.rb} (55%) rename spec/support/models/{person_auto.rb => person_autosave.rb} (95%) diff --git a/spec/mongoid/association/auto_save_spec.rb b/spec/mongoid/association/auto_save_spec.rb index e5a0e972a..570dac3e2 100644 --- a/spec/mongoid/association/auto_save_spec.rb +++ b/spec/mongoid/association/auto_save_spec.rb @@ -9,16 +9,16 @@ describe '.auto_save' do before(:all) do - PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true - PersonAuto.has_one :account, class_name: 'AccountAuto', validate: false, autosave: true + PersonAutosave.has_many :drugs, class_name: 'DrugAutosave', validate: false, autosave: true + PersonAutosave.has_one :account, class_name: 'AccountAutosave', validate: false, autosave: true end after(:all) do - PersonAuto.reset_callbacks(:save) + PersonAutosave.reset_callbacks(:save) end let(:person) do - PersonAuto.new + PersonAutosave.new end context 'when the option is not provided' do @@ -85,11 +85,11 @@ context 'when the relation has already had the autosave callback added' do before do - PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true + PersonAutosave.has_many :drugs, class_name: 'DrugAutosave', validate: false, autosave: true end let(:drug) do - DrugAuto.new(name: 'Percocet') + DrugAutosave.new(name: 'Percocet') end it 'does not add the autosave callback twice' do @@ -102,7 +102,7 @@ context 'when the relation is a references many' do let(:drug) do - DrugAuto.new(name: 'Percocet') + DrugAutosave.new(name: 'Percocet') end context 'when saving a new parent document' do @@ -110,7 +110,7 @@ context 'when persistence options are not set on the parent' do before do - PersonAuto.has_many :drugs, class_name: 'DrugAuto', validate: false, autosave: true + PersonAutosave.has_many :drugs, class_name: 'DrugAutosave', validate: false, autosave: true end before do @@ -130,8 +130,8 @@ end after do - PersonAuto.with(database: other_database, &:delete_all) - DrugAuto.with(database: other_database, &:delete_all) + PersonAutosave.with(database: other_database, &:delete_all) + DrugAutosave.with(database: other_database, &:delete_all) end before do @@ -142,7 +142,7 @@ end it 'saves the relation with the persistence options' do - DrugAuto.with(database: other_database) do |drug_class| + DrugAutosave.with(database: other_database) do |drug_class| expect(drug_class.count).to eq(1) end end @@ -165,7 +165,7 @@ context 'when not updating the document' do let(:from_db) do - PersonAuto.find person.id + PersonAutosave.find person.id end before do @@ -183,7 +183,7 @@ context 'when the relation is a references one' do let(:account) do - AccountAuto.new(name: 'Testing') + AccountAutosave.new(name: 'Testing') end context 'when saving a new parent document' do @@ -237,7 +237,7 @@ context 'when not updating the document' do let(:from_db) do - PersonAuto.find person.id + PersonAutosave.find person.id end before do @@ -291,25 +291,25 @@ context 'when it has two relations with autosaves' do let!(:person) do - PersonAuto.create!(drugs: [percocet], account: account) + PersonAutosave.create!(drugs: [percocet], account: account) end let(:from_db) do - PersonAuto.find person.id + PersonAutosave.find person.id end let(:percocet) do - DrugAuto.new(name: 'Percocet') + DrugAutosave.new(name: 'Percocet') end let(:account) do - AccountAuto.new(name: 'Testing') + AccountAutosave.new(name: 'Testing') end context 'when updating one document' do let(:placebo) do - DrugAuto.new(name: 'Placebo') + DrugAutosave.new(name: 'Placebo') end before do diff --git a/spec/support/models/account_auto.rb b/spec/support/models/account_autosave.rb similarity index 93% rename from spec/support/models/account_auto.rb rename to spec/support/models/account_autosave.rb index 781b19663..d1c2873a6 100644 --- a/spec/support/models/account_auto.rb +++ b/spec/support/models/account_autosave.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class AccountAuto +class AccountAutosave include Mongoid::Document field :_id, type: String, overwrite: true, default: -> { name.try(:parameterize) } @@ -15,7 +15,7 @@ class AccountAuto embeds_many :memberships belongs_to :creator, class_name: 'User' - belongs_to :person, class_name: 'PersonAuto' + belongs_to :person, class_name: 'PersonAutosave' has_many :alerts, autosave: false has_and_belongs_to_many :agents has_one :comment, validate: false diff --git a/spec/support/models/drug_auto.rb b/spec/support/models/drug_autosave.rb similarity index 55% rename from spec/support/models/drug_auto.rb rename to spec/support/models/drug_autosave.rb index 097dba216..5bf288ed6 100644 --- a/spec/support/models/drug_auto.rb +++ b/spec/support/models/drug_autosave.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -class DrugAuto +class DrugAutosave include Mongoid::Document field :name, type: String field :generic, type: Mongoid::Boolean - belongs_to :person, class_name: 'PersonAuto', inverse_of: nil, counter_cache: true + belongs_to :person, class_name: 'PersonAutosave', inverse_of: nil, counter_cache: true end diff --git a/spec/support/models/person_auto.rb b/spec/support/models/person_autosave.rb similarity index 95% rename from spec/support/models/person_auto.rb rename to spec/support/models/person_autosave.rb index 1136d4aac..e17306e96 100644 --- a/spec/support/models/person_auto.rb +++ b/spec/support/models/person_autosave.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PersonAuto +class PersonAutosave include Mongoid::Document include Mongoid::Attributes::Dynamic attr_accessor :mode @@ -109,9 +109,9 @@ def extension has_and_belongs_to_many :houses, validate: false has_and_belongs_to_many :ordered_preferences, order: :value.desc, validate: false - has_many :drugs, class_name: 'DrugAuto', validate: false + has_many :drugs, class_name: 'DrugAutosave', validate: false # Must not have dependent: :destroy - has_one :account, class_name: 'AccountAuto', validate: false + has_one :account, class_name: 'AccountAutosave', validate: false has_one :cat, dependent: :nullify, validate: false, primary_key: :username has_one :book, autobuild: true, validate: false has_one :home, dependent: :delete_all, validate: false @@ -123,8 +123,8 @@ def extension dependent: :nullify, validate: false - belongs_to :mother, class_name: 'PersonAuto' - has_many :children, class_name: 'PersonAuto' + belongs_to :mother, class_name: 'PersonAutosave' + has_many :children, class_name: 'PersonAutosave' accepts_nested_attributes_for :addresses accepts_nested_attributes_for :name, update_only: true From bd77b0e21b3d48c81075fa154a26815eb5629aef Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Mon, 6 May 2024 22:30:01 +0900 Subject: [PATCH 42/43] Upgrade and fix rubocop --- .rubocop_todo.yml | 32 +++++++++++++- gemfiles/standard.rb | 2 +- lib/mongoid/association.rb | 6 +-- lib/mongoid/association/accessors.rb | 10 ++--- lib/mongoid/association/builders.rb | 4 +- lib/mongoid/association/depending.rb | 2 +- .../association/referenced/counter_cache.rb | 4 +- lib/mongoid/attributes.rb | 36 ++++++++-------- lib/mongoid/attributes/dynamic.rb | 8 ++-- lib/mongoid/attributes/processing.rb | 10 ++--- lib/mongoid/changeable.rb | 4 +- lib/mongoid/config.rb | 2 +- lib/mongoid/config/options.rb | 4 +- lib/mongoid/contextual/memory.rb | 2 +- lib/mongoid/copyable.rb | 8 ++-- lib/mongoid/extensions/object.rb | 4 +- lib/mongoid/inspectable.rb | 4 +- lib/mongoid/interceptable.rb | 4 +- lib/mongoid/warnings.rb | 6 +-- .../referenced/has_many/proxy_spec.rb | 2 +- spec/mongoid/attributes/dynamic_spec.rb | 8 ++-- spec/mongoid/attributes_spec.rb | 2 +- spec/mongoid/clients_spec.rb | 16 +++---- spec/mongoid/contextual/none_spec.rb | 2 +- .../document_persistence_context_spec.rb | 18 ++++---- spec/mongoid/fields_spec.rb | 2 +- spec/mongoid/inspectable_spec.rb | 42 +++++++++---------- spec/mongoid/interceptable_spec_models.rb | 8 ++-- spec/mongoid/search_indexable_spec.rb | 6 +-- spec/mongoid/warnings_spec.rb | 8 ++-- spec/mongoid_spec.rb | 6 +-- spec/support/immutable_ids.rb | 6 +-- spec/support/macros.rb | 12 +++--- 33 files changed, 160 insertions(+), 130 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 8b95d8ee4..96194f50c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -308,7 +308,37 @@ RSpec/ExpectInHook: # Offense count: 24 # Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly. # Include: **/*_spec*rb*, **/spec/**/* -RSpec/FilePath: +RSpec/SpecFilePathFormat: + Exclude: + - 'spec/integration/atomic/modifiers_spec.rb' + - 'spec/integration/bson_regexp_raw_spec.rb' + - 'spec/integration/document_spec.rb' + - 'spec/mongoid/association/auto_save_spec.rb' + - 'spec/mongoid/association/counter_cache_spec.rb' + - 'spec/mongoid/association/eager_spec.rb' + - 'spec/mongoid/clients/transactions_spec.rb' + - 'spec/mongoid/criteria/queryable/extensions/boolean_spec.rb' + - 'spec/mongoid/criteria/queryable/extensions/numeric_spec.rb' + - 'spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb' + - 'spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb' + - 'spec/mongoid/criteria/queryable/queryable_spec.rb' + - 'spec/mongoid/extensions/binary_spec.rb' + - 'spec/mongoid/extensions/boolean_spec.rb' + - 'spec/mongoid/extensions/raw_value_spec.rb' + - 'spec/mongoid/extensions/stringified_symbol_spec.rb' + - 'spec/mongoid/loading_spec.rb' + - 'spec/mongoid/validatable/associated_spec.rb' + - 'spec/mongoid/validatable/format_spec.rb' + - 'spec/mongoid/validatable/length_spec.rb' + - 'spec/mongoid/validatable/numericality_spec.rb' + - 'spec/mongoid/validatable/presence_spec.rb' + - 'spec/mongoid/validatable/uniqueness_spec.rb' + - 'spec/mongoid/version_spec.rb' + +# Offense count: 24 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly. +# Include: **/*_spec*rb*, **/spec/**/* +RSpec/SpecFilePathSuffix: Exclude: - 'spec/integration/atomic/modifiers_spec.rb' - 'spec/integration/bson_regexp_raw_spec.rb' diff --git a/gemfiles/standard.rb b/gemfiles/standard.rb index fed0abd5f..4f364e734 100644 --- a/gemfiles/standard.rb +++ b/gemfiles/standard.rb @@ -8,7 +8,7 @@ def standard_dependencies end group :development, :test do - gem 'rubocop', '~> 1.60.0' + gem 'rubocop', '~> 1.63.4' gem 'rubocop-performance', '~> 1.16.0' gem 'rubocop-rails', '~> 2.17.4' gem 'rubocop-rake', '~> 0.6.0' diff --git a/lib/mongoid/association.rb b/lib/mongoid/association.rb index d37757c24..ad1e6b177 100644 --- a/lib/mongoid/association.rb +++ b/lib/mongoid/association.rb @@ -129,10 +129,10 @@ def referenced_one? # @return [ Hash ] The association metadata. def reload_relations relations.each_pair do |name, _meta| - next unless instance_variable_defined?("@_#{name}") && - (_parent.nil? || instance_variable_get("@_#{name}") != _parent) + next unless instance_variable_defined?(:"@_#{name}") && + (_parent.nil? || instance_variable_get(:"@_#{name}") != _parent) - remove_instance_variable("@_#{name}") + remove_instance_variable(:"@_#{name}") end end end diff --git a/lib/mongoid/association/accessors.rb b/lib/mongoid/association/accessors.rb index 309b88d36..e585730b9 100644 --- a/lib/mongoid/association/accessors.rb +++ b/lib/mongoid/association/accessors.rb @@ -67,7 +67,7 @@ def create_relation(object, association, selected_fields = nil) # # @param [ Symbol ] name The name of the association. def reset_relation_criteria(name) - return unless instance_variable_defined?("@_#{name}") + return unless instance_variable_defined?(:"@_#{name}") send(name).reset_unloaded end @@ -83,7 +83,7 @@ def reset_relation_criteria(name) # # @return [ Mongoid::Association::Proxy ] The association. def set_relation(name, relation) - instance_variable_set("@_#{name}", relation) + instance_variable_set(:"@_#{name}", relation) end # Adds the existence check for associations. @@ -129,7 +129,7 @@ def self.define_getter!(association) klass.re_define_method(name) do |reload = false| value = get_relation(name, association, nil, reload) if value.nil? && association.autobuilding? && !without_autobuild? - value = send("build_#{name}") + value = send(:"build_#{name}") end value end @@ -222,7 +222,7 @@ def self.define_builder!(association) attributes, _options = parse_args(*args) document = Factory.build(association.relation_class, attributes) _building do - child = send("#{name}=", document) + child = send(:"#{name}=", document) child.run_callbacks(:build) child end @@ -247,7 +247,7 @@ def self.define_creator!(association) attributes, _options = parse_args(*args) document = Factory.build(association.klass, attributes) doc = _assigning do - send("#{name}=", document) + send(:"#{name}=", document) end doc.save save if new_record? && association.stores_foreign_key? diff --git a/lib/mongoid/association/builders.rb b/lib/mongoid/association/builders.rb index 56354b8d2..4eab3940b 100644 --- a/lib/mongoid/association/builders.rb +++ b/lib/mongoid/association/builders.rb @@ -33,7 +33,7 @@ def self.define_builder!(association) attributes, _options = parse_args(*args) document = Factory.execute_build(association.relation_class, attributes, execute_callbacks: false) _building do - child = send("#{association.name}=", document) + child = send(:"#{association.name}=", document) child.run_pending_callbacks child.run_callbacks(:build) child @@ -57,7 +57,7 @@ def self.define_creator!(association) attributes, _options = parse_args(*args) document = Factory.execute_build(association.relation_class, attributes, execute_callbacks: false) doc = _assigning do - send("#{association.name}=", document) + send(:"#{association.name}=", document) end doc.run_pending_callbacks doc.save diff --git a/lib/mongoid/association/depending.rb b/lib/mongoid/association/depending.rb index d96dd4465..9a9b490db 100644 --- a/lib/mongoid/association/depending.rb +++ b/lib/mongoid/association/depending.rb @@ -90,7 +90,7 @@ def self.validate!(association) def apply_destroy_dependencies! self.class._all_dependents.each do |association| if (dependent = association.try(:dependent)) - send("_dependent_#{dependent}!", association) + send(:"_dependent_#{dependent}!", association) end end end diff --git a/lib/mongoid/association/referenced/counter_cache.rb b/lib/mongoid/association/referenced/counter_cache.rb index af15089df..e5deb481e 100644 --- a/lib/mongoid/association/referenced/counter_cache.rb +++ b/lib/mongoid/association/referenced/counter_cache.rb @@ -103,8 +103,8 @@ def self.define_callbacks!(association) klass.after_update do foreign_key = association.foreign_key - if send("#{foreign_key}_previously_changed?") - original, current = send("#{foreign_key}_previous_change") + if send(:"#{foreign_key}_previously_changed?") + original, current = send(:"#{foreign_key}_previous_change") unless original.nil? association.klass.with(persistence_context.for_child(association.klass)) do |k| diff --git a/lib/mongoid/attributes.rb b/lib/mongoid/attributes.rb index 6e585acce..8ca238611 100644 --- a/lib/mongoid/attributes.rb +++ b/lib/mongoid/attributes.rb @@ -321,15 +321,15 @@ def alias_attribute(name, original) aliased_fields[name.to_s] = original.to_s alias_method name, original - alias_method "#{name}=", "#{original}=" - alias_method "#{name}?", "#{original}?" - alias_method "#{name}_change", "#{original}_change" - alias_method "#{name}_changed?", "#{original}_changed?" - alias_method "reset_#{name}!", "reset_#{original}!" - alias_method "reset_#{name}_to_default!", "reset_#{original}_to_default!" - alias_method "#{name}_was", "#{original}_was" - alias_method "#{name}_will_change!", "#{original}_will_change!" - alias_method "#{name}_before_type_cast", "#{original}_before_type_cast" + alias_method :"#{name}=", :"#{original}=" + alias_method :"#{name}?", :"#{original}?" + alias_method :"#{name}_change", :"#{original}_change" + alias_method :"#{name}_changed?", :"#{original}_changed?" + alias_method :"reset_#{name}!", :"reset_#{original}!" + alias_method :"reset_#{name}_to_default!", :"reset_#{original}_to_default!" + alias_method :"#{name}_was", :"#{original}_was" + alias_method :"#{name}_will_change!", :"#{original}_will_change!" + alias_method :"#{name}_before_type_cast", :"#{original}_before_type_cast" end # Removes a field alias. @@ -341,15 +341,15 @@ def unalias_attribute(name) end remove_method name - remove_method "#{name}=" - remove_method "#{name}?" - remove_method "#{name}_change" - remove_method "#{name}_changed?" - remove_method "reset_#{name}!" - remove_method "reset_#{name}_to_default!" - remove_method "#{name}_was" - remove_method "#{name}_will_change!" - remove_method "#{name}_before_type_cast" + remove_method :"#{name}=" + remove_method :"#{name}?" + remove_method :"#{name}_change" + remove_method :"#{name}_changed?" + remove_method :"reset_#{name}!" + remove_method :"reset_#{name}_to_default!" + remove_method :"#{name}_was" + remove_method :"#{name}_will_change!" + remove_method :"#{name}_before_type_cast" end end diff --git a/lib/mongoid/attributes/dynamic.rb b/lib/mongoid/attributes/dynamic.rb index 43b6b0354..f4179c766 100644 --- a/lib/mongoid/attributes/dynamic.rb +++ b/lib/mongoid/attributes/dynamic.rb @@ -49,7 +49,7 @@ def define_dynamic_reader(name) # @param [ String ] name The name of the field. def define_dynamic_before_type_cast_reader(name) class_eval do - define_method("#{name}_before_type_cast") do + define_method(:"#{name}_before_type_cast") do attribute_will_change!(name) read_attribute_before_type_cast(name) end @@ -68,7 +68,7 @@ def define_dynamic_writer(name) return unless name.valid_method_name? class_eval do - define_method("#{name}=") do |value| + define_method(:"#{name}=") do |value| write_attribute(name, value) end end @@ -83,9 +83,9 @@ def define_dynamic_writer(name) # @param [ Symbol ] name The name of the field. # @param [ Object ] value The value of the field. def process_attribute(name, value) - responds = respond_to?("#{name}=") + responds = respond_to?(:"#{name}=") if responds - send("#{name}=", value) + send(:"#{name}=", value) else write_attribute(name, value) end diff --git a/lib/mongoid/attributes/processing.rb b/lib/mongoid/attributes/processing.rb index f936a17d3..fb4e76e0a 100644 --- a/lib/mongoid/attributes/processing.rb +++ b/lib/mongoid/attributes/processing.rb @@ -119,13 +119,13 @@ def pending_nested # @param [ Symbol ] name The name of the field. # @param [ Object ] value The value of the field. def process_attribute(name, value) - if !respond_to?("#{name}=", true) && (store_as = aliased_fields.invert[name.to_s]) + if !respond_to?(:"#{name}=", true) && (store_as = aliased_fields.invert[name.to_s]) name = store_as end - responds = respond_to?("#{name}=", true) + responds = respond_to?(:"#{name}=", true) raise Errors::UnknownAttribute.new(self.class, name) unless responds - send("#{name}=", value) + send(:"#{name}=", value) end # Process all the pending nested attributes that needed to wait until @@ -135,7 +135,7 @@ def process_attribute(name, value) # document.process_nested def process_nested pending_nested.each_pair do |name, value| - send("#{name}=", value) + send(:"#{name}=", value) end end @@ -160,7 +160,7 @@ def process_relations if value.is_a?(Hash) association.nested_builder(value, {}).build(self) else - send("#{name}=", value) + send(:"#{name}=", value) end end end diff --git a/lib/mongoid/changeable.rb b/lib/mongoid/changeable.rb index bf5bfff1e..d24b04777 100644 --- a/lib/mongoid/changeable.rb +++ b/lib/mongoid/changeable.rb @@ -336,9 +336,9 @@ def reset_attribute!(attr) def reset_attribute_to_default!(attr) attr = database_field_name(attr) if (field = fields[attr]) - __send__("#{attr}=", field.eval_default(self)) + __send__(:"#{attr}=", field.eval_default(self)) else - __send__("#{attr}=", nil) + __send__(:"#{attr}=", nil) end end diff --git a/lib/mongoid/config.rb b/lib/mongoid/config.rb index 57ada7e88..7c5c3be2f 100644 --- a/lib/mongoid/config.rb +++ b/lib/mongoid/config.rb @@ -337,7 +337,7 @@ def options=(options) Validators::AsyncQueryExecutor.validate(options) options.each_pair do |option, value| Validators::Option.validate(option) - send("#{option}=", value) + send(:"#{option}=", value) end end diff --git a/lib/mongoid/config/options.rb b/lib/mongoid/config/options.rb index befb6e108..a0a606b82 100644 --- a/lib/mongoid/config/options.rb +++ b/lib/mongoid/config/options.rb @@ -38,12 +38,12 @@ def option(name, options = {}) end end - define_method("#{name}=") do |value| + define_method(:"#{name}=") do |value| settings[name] = value options[:on_change]&.call(value) end - define_method("#{name}?") do + define_method(:"#{name}?") do !!send(name) end end diff --git a/lib/mongoid/contextual/memory.rb b/lib/mongoid/contextual/memory.rb index 18593f540..c748704b3 100644 --- a/lib/mongoid/contextual/memory.rb +++ b/lib/mongoid/contextual/memory.rb @@ -746,7 +746,7 @@ def retrieve_value_at_path(document, field_path) # If this is a localized field, and there are remaining, get the # _translations hash so that we can get the specified translation in # the remaining - document.send("#{segment}_translations") if field&.localized? + document.send(:"#{segment}_translations") if field&.localized? end meth = klass.aliased_associations[segment] || segment res.nil? ? document.try(meth) : res diff --git a/lib/mongoid/copyable.rb b/lib/mongoid/copyable.rb index 12c4b1a79..6ce3982e8 100644 --- a/lib/mongoid/copyable.rb +++ b/lib/mongoid/copyable.rb @@ -52,12 +52,12 @@ def self.clone_with_hash(klass, attrs) dynamic_attrs.each do |attr_name, value| assoc = object.embedded_relations[attr_name] if assoc&.one? && value.is_a?(Hash) - object.send("#{attr_name}=", clone_with_hash(assoc.klass, value)) + object.send(:"#{attr_name}=", clone_with_hash(assoc.klass, value)) elsif assoc&.many? && value.is_a?(Array) docs = value.map { |h| clone_with_hash(assoc.klass, h) } - object.send("#{attr_name}=", docs) - elsif object.respond_to?("#{attr_name}=") - object.send("#{attr_name}=", value) + object.send(:"#{attr_name}=", docs) + elsif object.respond_to?(:"#{attr_name}=") + object.send(:"#{attr_name}=", value) else object.attributes[attr_name] = value end diff --git a/lib/mongoid/extensions/object.rb b/lib/mongoid/extensions/object.rb index 5ae96d628..e5987306b 100644 --- a/lib/mongoid/extensions/object.rb +++ b/lib/mongoid/extensions/object.rb @@ -142,8 +142,8 @@ def numeric? # # @return [ true | false ] If the variable was defined. def remove_ivar(name) - if instance_variable_defined?("@_#{name}") - remove_instance_variable("@_#{name}") + if instance_variable_defined?(:"@_#{name}") + remove_instance_variable(:"@_#{name}") else false end diff --git a/lib/mongoid/inspectable.rb b/lib/mongoid/inspectable.rb index 84df9f316..d83cded92 100644 --- a/lib/mongoid/inspectable.rb +++ b/lib/mongoid/inspectable.rb @@ -30,7 +30,7 @@ def inspect def pretty_print(pretty_printer) keys = fields.keys | attributes.keys pretty_printer.group(1, "#<#{self.class.name}", '>') do - sep = lambda { pretty_printer.text(',') } + sep = -> { pretty_printer.text(',') } pretty_printer.seplist(keys, sep) do |key| pretty_printer.breakable field = fields[key] @@ -39,7 +39,7 @@ def pretty_print(pretty_printer) pretty_printer.text(':') pretty_printer.group(1) do pretty_printer.breakable - if key == "_id" + if key == '_id' pretty_printer.text(_id.to_s) else pretty_printer.pp(@attributes[key]) diff --git a/lib/mongoid/interceptable.rb b/lib/mongoid/interceptable.rb index d598c37bc..956a5c298 100644 --- a/lib/mongoid/interceptable.rb +++ b/lib/mongoid/interceptable.rb @@ -64,7 +64,7 @@ module Interceptable # # @return [ true | false ] If the callback can be executed. def callback_executable?(kind) - respond_to?("_#{kind}_callbacks") + respond_to?(:"_#{kind}_callbacks") end # Is the document currently in a state that could potentially require @@ -401,7 +401,7 @@ def run_targeted_callbacks(place, kind) name = "_run__#{place}__#{kind}__callbacks" unless respond_to?(name) chain = ActiveSupport::Callbacks::CallbackChain.new(name, {}) - send("_#{kind}_callbacks").each do |callback| + send(:"_#{kind}_callbacks").each do |callback| chain.append(callback) if callback.kind == place end self.class.send :define_method, name do diff --git a/lib/mongoid/warnings.rb b/lib/mongoid/warnings.rb index 67cee0cc6..8ef08d22b 100644 --- a/lib/mongoid/warnings.rb +++ b/lib/mongoid/warnings.rb @@ -18,11 +18,11 @@ class << self # @api private def warning(id, message) singleton_class.class_eval do - define_method("warn_#{id}") do - return if instance_variable_get("@#{id}") + define_method(:"warn_#{id}") do + return if instance_variable_get(:"@#{id}") Mongoid.logger.warn(message) - instance_variable_set("@#{id}", true) + instance_variable_set(:"@#{id}", true) end end end diff --git a/spec/mongoid/association/referenced/has_many/proxy_spec.rb b/spec/mongoid/association/referenced/has_many/proxy_spec.rb index b7265d873..d190bd12f 100644 --- a/spec/mongoid/association/referenced/has_many/proxy_spec.rb +++ b/spec/mongoid/association/referenced/has_many/proxy_spec.rb @@ -735,7 +735,7 @@ def initialize(*args) let(:child) { parent.children.send(method) } it 'calls #initialize' do - expect(child.name).to be == 'default' + expect(child.name).to eq 'default' end end diff --git a/spec/mongoid/attributes/dynamic_spec.rb b/spec/mongoid/attributes/dynamic_spec.rb index 2530177a3..a4d922aec 100644 --- a/spec/mongoid/attributes/dynamic_spec.rb +++ b/spec/mongoid/attributes/dynamic_spec.rb @@ -112,7 +112,7 @@ it 'cannot be written' do expect do - bar.send("#{attr_name}=", 'foo bar') + bar.send(:"#{attr_name}=", 'foo bar') bar.save! end.to raise_error(NoMethodError) end @@ -122,7 +122,7 @@ let(:bar) { Bar.new(attr_name => 'foo bar') } it 'can be written' do - bar.send("#{attr_name}=", 'new foo bar') + bar.send(:"#{attr_name}=", 'new foo bar') bar.save! bar_found = Bar.find(bar.id) @@ -161,7 +161,7 @@ end it 'responds to writer method' do - expect(bar.respond_to?("#{attr_name}=")).to be true + expect(bar.respond_to?(:"#{attr_name}=")).to be true end end @@ -173,7 +173,7 @@ end it 'does not respond to writer method' do - expect(bar.respond_to?("#{attr_name}=")).to be false + expect(bar.respond_to?(:"#{attr_name}=")).to be false end end end diff --git a/spec/mongoid/attributes_spec.rb b/spec/mongoid/attributes_spec.rb index d84a2934f..d16ab9d24 100644 --- a/spec/mongoid/attributes_spec.rb +++ b/spec/mongoid/attributes_spec.rb @@ -2650,7 +2650,7 @@ let(:doc) { NestedBook.create! } before do - doc.send("#{meth}_cover", attrs) + doc.send(:"#{meth}_cover", attrs) end it 'updates the attributes' do diff --git a/spec/mongoid/clients_spec.rb b/spec/mongoid/clients_spec.rb index a8ccd1a36..1091b7c1b 100644 --- a/spec/mongoid/clients_spec.rb +++ b/spec/mongoid/clients_spec.rb @@ -1195,10 +1195,10 @@ class StoreChild2 < StoreParent end end - context '#disconnect' do + describe '#disconnect' do let(:clients) do - Mongoid::Clients.clients.values + described_class.clients.values end before do @@ -1209,18 +1209,18 @@ class StoreChild2 < StoreParent clients.each do |client| expect(client).to receive(:close).and_call_original end - Mongoid::Clients.disconnect + described_class.disconnect end it 'returns true' do - expect(Mongoid::Clients.disconnect).to eq(true) + expect(described_class.disconnect).to be(true) end end - context '#reconnect' do + describe '#reconnect' do let(:clients) do - Mongoid::Clients.clients.values + described_class.clients.values end before do @@ -1231,11 +1231,11 @@ class StoreChild2 < StoreParent clients.each do |client| expect(client).to receive(:reconnect).and_call_original end - Mongoid::Clients.reconnect + described_class.reconnect end it 'returns true' do - expect(Mongoid::Clients.reconnect).to eq(true) + expect(described_class.reconnect).to be(true) end end end diff --git a/spec/mongoid/contextual/none_spec.rb b/spec/mongoid/contextual/none_spec.rb index ef8990de2..ef733ca39 100644 --- a/spec/mongoid/contextual/none_spec.rb +++ b/spec/mongoid/contextual/none_spec.rb @@ -188,7 +188,7 @@ describe "##{meth}!" do it 'raises an error' do expect do - context.send("#{meth}!") + context.send(:"#{meth}!") end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./) end end diff --git a/spec/mongoid/document_persistence_context_spec.rb b/spec/mongoid/document_persistence_context_spec.rb index baf2e2fe6..fcff8ee18 100644 --- a/spec/mongoid/document_persistence_context_spec.rb +++ b/spec/mongoid/document_persistence_context_spec.rb @@ -39,28 +39,28 @@ config_override :legacy_persistence_context_behavior, false it 'remembers its persistence context when created' do - expect(person.collection_name).to be == :extra_people + expect(person.collection_name).to eq :extra_people end it 'remembers its context when queried specifically' do person_by_id = Person.with(options) { Person.find(_id: person._id) } - expect(person_by_id.collection_name).to be == :extra_people + expect(person_by_id.collection_name).to eq :extra_people end it 'remembers its context when queried generally' do person # force the person to be created person_generally = Person.with(options) { Person.all[0] } - expect(person_generally.collection_name).to be == :extra_people + expect(person_generally.collection_name).to eq :extra_people end it 'can be reloaded without specifying the context' do expect { person.reload }.to_not raise_error - expect(person.collection_name).to be == :extra_people + expect(person.collection_name).to eq :extra_people end it 'can be updated without specifying the context' do person.update username: 'zyg15' - expect(Person.with(options) { Person.first.username }).to be == 'zyg15' + expect(Person.with(options) { Person.first.username }).to eq 'zyg15' end it 'an explicit context takes precedence over a remembered context when persisting' do @@ -81,18 +81,18 @@ config_override :legacy_persistence_context_behavior, true it 'does not remember its persistence context when created' do - expect(person.collection_name).to be == :people + expect(person.collection_name).to eq :people end it 'does not remember its context when queried specifically' do person_by_id = Person.with(options) { Person.find(_id: person._id) } - expect(person_by_id.collection_name).to be == :people + expect(person_by_id.collection_name).to eq :people end it 'does not remember its context when queried generally' do person # force the person to be created person_generally = Person.with(options) { Person.all[0] } - expect(person_generally.collection_name).to be == :people + expect(person_generally.collection_name).to eq :people end it 'cannot be reloaded without specifying the context' do @@ -101,7 +101,7 @@ it 'cannot be updated without specifying the context' do person.update username: 'zyg15' - expect(Person.with(options) { Person.first.username }).to be == 'zyg14' + expect(Person.with(options) { Person.first.username }).to eq 'zyg14' end end end diff --git a/spec/mongoid/fields_spec.rb b/spec/mongoid/fields_spec.rb index 5c0158ac6..07fb36b25 100644 --- a/spec/mongoid/fields_spec.rb +++ b/spec/mongoid/fields_spec.rb @@ -575,7 +575,7 @@ end it 'is declared as BSON::Decimal128' do - expect(Mop.fields['decimal128_field'].type).to be == BSON::Decimal128 + expect(Mop.fields['decimal128_field'].type).to eq BSON::Decimal128 end context 'when BSON version <= 4' do diff --git a/spec/mongoid/inspectable_spec.rb b/spec/mongoid/inspectable_spec.rb index 563246b55..1baaa8d12 100644 --- a/spec/mongoid/inspectable_spec.rb +++ b/spec/mongoid/inspectable_spec.rb @@ -84,43 +84,43 @@ end end - describe "#pretty_inspect" do + describe '#pretty_inspect' do - context "when not allowing dynamic fields" do + context 'when not allowing dynamic fields' do let(:person) do - Person.new(title: "CEO") + Person.new(title: 'CEO') end let(:pretty_inspected) do person.pretty_inspect end - it "includes the model type" do - expect(pretty_inspected).to include("#\n" + expect(shirt.pretty_inspect).to eq "#\n" end end end