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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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 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 25/25] 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