diff --git a/README.md b/README.md index f2394f4e02..0eaa465a95 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ Compatibility Mongoid supports and is tested against: -- MRI 2.7 - 3.1 +- MRI 2.7 - 3.2 - JRuby 9.4 -- MongoDB server 3.6 - 6.0 +- MongoDB server 3.6 - 7.0 Issues ------ diff --git a/docs/reference/configuration.txt b/docs/reference/configuration.txt index 93e2581773..2d82faf24a 100644 --- a/docs/reference/configuration.txt +++ b/docs/reference/configuration.txt @@ -863,58 +863,57 @@ as the following example shows: Usage with Forking Servers ========================== -When using Mongoid with a forking web server such as Puma, Unicorn or -Passenger, it is recommended to not perform any operations on Mongoid models -in the parent process prior to the fork. - -When a process forks, Ruby threads are not transferred to the child processes -and the Ruby driver Client objects lose their background monitoring. The -application will typically seem to work just fine until the deployment -state changes (for example due to network errors, a maintenance event) at -which point the application is likely to start getting ``NoServerAvailable`` -exception when performing MongoDB operations. - -If the parent process needs to perform operations on the MongoDB database, -reset all clients in the workers after they forked. How to do so depends -on the web server being used. - -If the parent process does not need to perform operations on the MongoDB -database after child processes are forked, close the clients in the parent -prior to forking children. If the parent process performs operations on a Mongo -client and does not close it, the parent process will continue consuming a -connection slot in the cluster and will continue monitoring the cluster for -as long as the parent remains alive. - -.. note:: - - The close/reconnect pattern described here should be used with Ruby driver - version 2.6.2 or higher. Previous driver versions did not recreate - monitoring threads when reconnecting. +When using Mongoid with a forking web server such as Puma, or any application +that otherwise forks to spawn child processes, special considerations apply. + +If possible, we recommend to not perform any MongoDB operations in the parent +process prior to forking, which will avoid any forking-related pitfalls. + +A detailed technical explanation of how the Mongo Ruby Driver handles forking +is given in the `driver's "Usage with Forking Servers" documentation +`. +In a nutshell, to avoid various connection errors such as ``Mongo::Error::SocketError`` +and ``Mongo::Error::NoServerAvailable``, you must do the following: + +1. Disconnect MongoDB clients in the parent Ruby process immediately *before* + forking using ``Mongoid.disconnect_clients``. This ensures the parent and child + process do not accidentally reuse the same sockets and have I/O conflicts. + Note that ``Mongoid.disconnect_clients`` does not disrupt any in-flight + MongoDB operations, and will automatically reconnect when you perform new + operations. +2. Reconnect your MongoDB clients in the child Ruby process immediately *after* + forking using ``Mongoid.reconnect_clients``. This is required to respawn + the driver's monitoring threads in the child process. + +Most web servers provide hooks that can be used by applications to +perform actions when the worker processes are forked. The following +are configuration examples for several common Ruby web servers. Puma ---- Use the ``on_worker_boot`` hook to reconnect clients in the workers and -the ``before_fork`` hook to close clients in the parent process -(`Puma documentation `_): +the ``before_fork`` and ``on_refork`` hooks to close clients in the +parent process (`Puma documentation `_). .. code-block:: ruby - on_worker_boot do - if defined?(Mongoid) - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - else - raise "Mongoid is not loaded. You may have forgotten to enable app preloading." - end - end + # config/puma.rb + # Runs in the Puma master process before it forks a child worker. before_fork do - if defined?(Mongoid) - Mongoid.disconnect_clients - end + Mongoid.disconnect_clients + end + + # Required when using Puma's fork_worker option. Runs in the + # child worker 0 process before it forks grandchild workers. + on_refork do + Mongoid.disconnect_clients + end + + # Runs in each Puma child process after it forks from its parent. + on_worker_boot do + Mongoid.reconnect_clients end Unicorn @@ -926,21 +925,14 @@ the ``before_fork`` hook to close clients in the parent process .. code-block:: ruby - after_fork do |server, worker| - if defined?(Mongoid) - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - else - raise "Mongoid is not loaded. You may have forgotten to enable app preloading." - end + # config/unicorn.rb + + before_fork do |_server, _worker| + Mongoid.disconnect_clients end - before_fork do |server, worker| - if defined?(Mongoid) - Mongoid.disconnect_clients - end + after_fork do |_server, _worker| + Mongoid.reconnect_clients end Passenger @@ -956,12 +948,7 @@ before the workers are forked. if defined?(PhusionPassenger) PhusionPassenger.on_event(:starting_worker_process) do |forked| - if forked - Mongoid::Clients.clients.each do |name, client| - client.close - client.reconnect - end - end + Mongoid.reconnect_clients if forked end end diff --git a/lib/mongoid.rb b/lib/mongoid.rb index 7bc8ac8bfb..4d305f178b 100644 --- a/lib/mongoid.rb +++ b/lib/mongoid.rb @@ -100,6 +100,16 @@ def disconnect_clients Clients.disconnect end + # Reconnect all active clients. + # + # @example Reconnect all active clients. + # Mongoid.reconnect_clients + # + # @return [ true ] True. + def reconnect_clients + Clients.reconnect + end + # Convenience method for getting a named client. # # @example Get a named client. diff --git a/lib/mongoid/clients.rb b/lib/mongoid/clients.rb index feaa889df8..25e22afa88 100644 --- a/lib/mongoid/clients.rb +++ b/lib/mongoid/clients.rb @@ -47,9 +47,19 @@ def default # # @return [ true ] True. def disconnect - clients.values.each do |client| - client.close - end + clients.each_value(&:close) + true + end + + # Reconnect all active clients. + # + # @example Reconnect all active clients. + # Mongoid::Clients.reconnect + # + # @return [ true ] True. + def reconnect + clients.each_value(&:reconnect) + true end # Get a stored client with the provided name. If no client exists diff --git a/lib/mongoid/config.rb b/lib/mongoid/config.rb index 352f36bc12..978948f07b 100644 --- a/lib/mongoid/config.rb +++ b/lib/mongoid/config.rb @@ -376,10 +376,14 @@ def time_zone # config.running_with_passenger? # # @return [ true | false ] If the app is deployed on Passenger. + # + # @deprecated def running_with_passenger? @running_with_passenger ||= defined?(PhusionPassenger) end + Mongoid.deprecate(self, :running_with_passenger?) + private def set_log_levels diff --git a/lib/mongoid/config/defaults.rb b/lib/mongoid/config/defaults.rb index b1981646be..eaa00499f6 100644 --- a/lib/mongoid/config/defaults.rb +++ b/lib/mongoid/config/defaults.rb @@ -25,6 +25,8 @@ def load_defaults(version) when "8.1" self.immutable_ids = false self.legacy_persistence_context_behavior = true + self.around_callbacks_for_embeds = true + self.prevent_multiple_calls_of_embedded_callbacks = false load_defaults "9.0" when "9.0" diff --git a/lib/mongoid/contextual/geo_near.rb b/lib/mongoid/contextual/geo_near.rb deleted file mode 100644 index f0bda793c1..0000000000 --- a/lib/mongoid/contextual/geo_near.rb +++ /dev/null @@ -1,239 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -module Mongoid - module Contextual - - # Represents a $geoNear database command instruction. - class GeoNear - extend Forwardable - include Enumerable - include Command - - def_delegator :results, :[] - def_delegators :entries, :==, :empty? - - # Get the average distance for all documents from the point in the - # command. - # - # @example Get the average distance. - # geo_near.average_distance - # - # @return [ Float | nil ] The average distance. - def average_distance - average = stats["avgDistance"] - (average.nil? || average.nan?) ? nil : average - end - - # Iterates over each of the documents in the $geoNear, excluding the - # extra information that was passed back from the database. - # - # @example Iterate over the results. - # geo_near.each do |doc| - # p doc - # end - # - # @return [ Enumerator ] The enumerator. - def each - if block_given? - documents.each do |doc| - yield doc - end - else - to_enum - end - end - - # Provide a distance multiplier to be used for each returned distance. - # - # @example Provide the distance multiplier. - # geo_near.distance_multiplier(13113.1) - # - # @param [ Integer | Float ] value The distance multiplier. - # - # @return [ GeoNear ] The GeoNear wrapper. - def distance_multiplier(value) - command[:distanceMultiplier] = value - self - end - - # Initialize the new map/reduce directive. - # - # @example Initialize the new map/reduce. - # MapReduce.new(criteria, map, reduce) - # - # @param [ Mongo::Collection ] collection The collection to run the - # operation on. - # @param [ Criteria ] criteria The Mongoid criteria. - # @param [ String ] near - def initialize(collection, criteria, near) - @collection, @criteria = collection, criteria - command[:geoNear] = collection.name.to_s - command[:near] = near - apply_criteria_options - end - - # Get a pretty string representation of the command. - # - # @example Inspect the geoNear. - # geo_near.inspect - # - # @return [ String ] The inspection string. - def inspect -%Q{# -} - end - - # Specify the maximum distance to find documents for, or get the value of - # the document with the furthest distance. - # - # @example Set the max distance. - # geo_near.max_distance(0.5) - # - # @example Get the max distance. - # geo_near.max_distance - # - # @param [ Integer | Float ] value The maximum distance. - # - # @return [ GeoNear | Float ] The GeoNear command or the value. - def max_distance(value = nil) - if value - command[:maxDistance] = value - self - else - stats["maxDistance"] - end - end - - # Specify the minimum distance to find documents for. - # - # @example Set the min distance. - # geo_near.min_distance(0.5) - # - # @param [ Integer | Float ] value The minimum distance. - # - # @return [ GeoNear ] The GeoNear command. - def min_distance(value) - command[:minDistance] = value - self - end - - # Tell the command to calculate based on spherical distances. - # - # @example Add the spherical flag. - # geo_near.spherical - # - # @return [ GeoNear ] The command. - def spherical - command[:spherical] = true - self - end - - # Tell the command whether or not the returned results should be unique. - # - # @example Set the unique flag. - # geo_near.unique(false) - # - # @param [ true | false ] value Whether to return unique documents. - # - # @return [ GeoNear ] The command. - def unique(value = true) - command[:unique] = value - self - end - - # Execute the $geoNear, returning the raw output. - # - # @example Run the $geoNear - # geo_near.execute - # - # @return [ Hash ] The raw output - def execute - results - end - - # Get the stats for the command run. - # - # @example Get the stats. - # geo_near.stats - # - # @return [ Hash ] The stats from the command run. - def stats - results["stats"] - end - - # Get the execution time of the command. - # - # @example Get the execution time. - # geo_near.time - # - # @return [ Float ] The execution time. - def time - stats["time"] - end - - # Is this context's criteria considered empty? - # - # @example Is this context's criteria considered empty? - # geo_near.empty_and_chainable? - # - # @return [ true ] Always true. - def empty_and_chainable? - true - end - - private - - # Apply criteria specific options - query, limit. - # - # @api private - # - # @example Apply the criteria options - # geo_near.apply_criteria_options - # - # @return [ nil ] Nothing. - def apply_criteria_options - command[:query] = criteria.selector - if limit = criteria.options[:limit] - command[:num] = limit - end - end - - # Get the result documents from the $geoNear. - # - # @api private - # - # @example Get the documents. - # geo_near.documents - # - # @return [ Array | Cursor ] The documents. - def documents - results["results"].map do |attributes| - doc = Factory.from_db(criteria.klass, attributes["obj"], criteria) - doc.attributes["geo_near_distance"] = attributes["dis"] - doc - end - end - - # Execute the $geoNear command and get the results. - # - # @api private - # - # @example Get the results. - # geo_near.results - # - # @return [ Hash ] The results of the command. - def results - @results ||= client.command(command).first - end - end - end -end diff --git a/lib/mongoid/contextual/mongo.rb b/lib/mongoid/contextual/mongo.rb index 3fceba9dfc..b93fa32d5e 100644 --- a/lib/mongoid/contextual/mongo.rb +++ b/lib/mongoid/contextual/mongo.rb @@ -6,7 +6,6 @@ require "mongoid/contextual/atomic" require "mongoid/contextual/aggregable/mongo" require "mongoid/contextual/command" -require "mongoid/contextual/geo_near" require "mongoid/contextual/map_reduce" require "mongoid/association/eager_loadable" @@ -24,8 +23,6 @@ class Mongo include Association::EagerLoadable include Queryable - Mongoid.deprecate(self, :geo_near) - # Options constant. OPTIONS = [ :hint, :limit, @@ -263,29 +260,6 @@ def find_first end end - # Execute a $geoNear command against the database. - # - # @example Find documents close to 10, 10. - # context.geo_near([ 10, 10 ]) - # - # @example Find with spherical distance. - # context.geo_near([ 10, 10 ]).spherical - # - # @example Find with a max distance. - # context.geo_near([ 10, 10 ]).max_distance(0.5) - # - # @example Provide a distance multiplier. - # context.geo_near([ 10, 10 ]).distance_multiplier(1133) - # - # @param [ Array ] coordinates The coordinates. - # - # @return [ GeoNear ] The GeoNear command. - # - # @deprecated - def geo_near(coordinates) - GeoNear.new(collection, criteria, coordinates) - end - # Create the new Mongo context. This delegates operations to the # underlying driver. # diff --git a/lib/mongoid/extensions/date.rb b/lib/mongoid/extensions/date.rb index d28e181d16..231fbf03d9 100644 --- a/lib/mongoid/extensions/date.rb +++ b/lib/mongoid/extensions/date.rb @@ -71,8 +71,10 @@ def mongoize(object) if object.is_a?(String) # https://jira.mongodb.org/browse/MONGOID-4460 time = ::Time.parse(object) - else + elsif object.respond_to?(:__mongoize_time__) time = object.__mongoize_time__ + else + nil end rescue ArgumentError nil diff --git a/lib/mongoid/extensions/object.rb b/lib/mongoid/extensions/object.rb index 879602f39e..67b91bc987 100644 --- a/lib/mongoid/extensions/object.rb +++ b/lib/mongoid/extensions/object.rb @@ -32,22 +32,6 @@ def __find_args__ end Mongoid.deprecate(self, :__find_args__) - # Mongoize a plain object into a time. - # - # @note This method should not be used, because it does not - # return correct results for non-Time objects. Override - # __mongoize_time__ in classes that are time-like to return an - # instance of Time or ActiveSupport::TimeWithZone. - # - # @example Mongoize the object. - # object.__mongoize_time__ - # - # @return [ Object ] self. - # @deprecated - def __mongoize_time__ - self - end - # Try to form a setter from this object. # # @example Try to form a setter. diff --git a/lib/mongoid/extensions/time.rb b/lib/mongoid/extensions/time.rb index a2689d664a..464a8d60ae 100644 --- a/lib/mongoid/extensions/time.rb +++ b/lib/mongoid/extensions/time.rb @@ -71,7 +71,7 @@ def demongoize(object) def mongoize(object) return if object.blank? begin - time = object.__mongoize_time__ + time = object.respond_to?(:__mongoize_time__) ? object.__mongoize_time__ : nil rescue ArgumentError return end diff --git a/lib/mongoid/findable.rb b/lib/mongoid/findable.rb index fccfe2b1cd..b533d425b1 100644 --- a/lib/mongoid/findable.rb +++ b/lib/mongoid/findable.rb @@ -38,7 +38,6 @@ module Findable :for_js, :fourth, :fourth!, - :geo_near, :includes, :last!, :map_reduce, diff --git a/lib/mongoid/inspectable.rb b/lib/mongoid/inspectable.rb index f432ab32a9..90678eac1e 100644 --- a/lib/mongoid/inspectable.rb +++ b/lib/mongoid/inspectable.rb @@ -19,6 +19,37 @@ def inspect "#<#{self.class.name} _id: #{_id}, #{inspection * ', '}>" end + # This pretty prints the same information as the inspect method. This is + # meant to be called by the standard 'pp' library. + # + # @param [ PP ] pretty_printer The pretty printer. + # + # @example Pretty print the document. + # person.pretty_inspect + # + # @api private + def pretty_print(pretty_printer) + keys = fields.keys | attributes.keys + pretty_printer.group(1, "#<#{self.class.name}", '>') do + sep = lambda { pretty_printer.text(',') } + pretty_printer.seplist(keys, sep) do |key| + pretty_printer.breakable + field = fields[key] + as = "(#{field.options[:as]})" if field && field.options[:as] + pretty_printer.text("#{key}#{as}") + pretty_printer.text(':') + pretty_printer.group(1) do + pretty_printer.breakable + if key == "_id" + pretty_printer.text(_id.to_s) + else + pretty_printer.pp(@attributes[key]) + end + end + end + end + end + private # Get an array of inspected fields for the document. diff --git a/lib/mongoid/scopable.rb b/lib/mongoid/scopable.rb index 35635acdc3..dac877ad9a 100644 --- a/lib/mongoid/scopable.rb +++ b/lib/mongoid/scopable.rb @@ -289,17 +289,15 @@ def check_scope_validity(value) # @return [ Method ] The defined method. def define_scope_method(name) singleton_class.class_eval do - ruby2_keywords( - define_method(name) do |*args, **kwargs| - scoping = _declared_scopes[name] - scope = instance_exec(*args, **kwargs, &scoping[:scope]) - extension = scoping[:extension] - to_merge = scope || queryable - criteria = to_merge.empty_and_chainable? ? to_merge : with_default_scope.merge(to_merge) - criteria.extend(extension) - criteria - end - ) + define_method(name) do |*args, **kwargs| + scoping = _declared_scopes[name] + scope = instance_exec(*args, **kwargs, &scoping[:scope]) + extension = scoping[:extension] + to_merge = scope || queryable + criteria = to_merge.empty_and_chainable? ? to_merge : with_default_scope.merge(to_merge) + criteria.extend(extension) + criteria + end end end diff --git a/lib/mongoid/version.rb b/lib/mongoid/version.rb index bd850fa5ab..a757d0eef8 100644 --- a/lib/mongoid/version.rb +++ b/lib/mongoid/version.rb @@ -2,5 +2,5 @@ # rubocop:todo all module Mongoid - VERSION = "9.0.0.alpha" + VERSION = "9.0.0" end diff --git a/spec/mongoid/clients_spec.rb b/spec/mongoid/clients_spec.rb index 055500cf8e..0e923ee447 100644 --- a/spec/mongoid/clients_spec.rb +++ b/spec/mongoid/clients_spec.rb @@ -1216,4 +1216,48 @@ class StoreChild2 < StoreParent end end end + + context "#disconnect" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "disconnects from all active clients" do + clients.each do |client| + expect(client).to receive(:close).and_call_original + end + Mongoid::Clients.disconnect + end + + it "returns true" do + expect(Mongoid::Clients.disconnect).to eq(true) + end + end + + context "#reconnect" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "reconnects all active clients" do + clients.each do |client| + expect(client).to receive(:reconnect).and_call_original + end + Mongoid::Clients.reconnect + end + + it "returns true" do + expect(Mongoid::Clients.reconnect).to eq(true) + end + end end diff --git a/spec/mongoid/config/defaults_spec.rb b/spec/mongoid/config/defaults_spec.rb index ddff45b451..cbf8904e42 100644 --- a/spec/mongoid/config/defaults_spec.rb +++ b/spec/mongoid/config/defaults_spec.rb @@ -27,6 +27,8 @@ it "uses settings for 8.1" do expect(Mongoid.immutable_ids).to be false expect(Mongoid.legacy_persistence_context_behavior).to be true + expect(Mongoid.around_callbacks_for_embeds).to be true + expect(Mongoid.prevent_multiple_calls_of_embedded_callbacks).to be false end end @@ -34,6 +36,8 @@ it "does not use settings for 8.1" do expect(Mongoid.immutable_ids).to be true expect(Mongoid.legacy_persistence_context_behavior).to be false + expect(Mongoid.around_callbacks_for_embeds).to be false + expect(Mongoid.prevent_multiple_calls_of_embedded_callbacks).to be true end end diff --git a/spec/mongoid/contextual/geo_near_spec.rb b/spec/mongoid/contextual/geo_near_spec.rb deleted file mode 100644 index 53f90e1a07..0000000000 --- a/spec/mongoid/contextual/geo_near_spec.rb +++ /dev/null @@ -1,472 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -require "spec_helper" - -describe Mongoid::Contextual::GeoNear do - max_server_version '4.0' - - describe "#average_distance" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the average distance" do - expect(geo_near.average_distance).to_not be_nil - end - end - - context "when no results are returned" do - - let(:criteria) do - Bar.where(name: "Green Door") - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:expected_value) do - if ClusterConfig.instance.fcv_ish == '4.0' && ClusterConfig.instance.topology == :sharded - # https://jira.mongodb.org/browse/SERVER-50074 - 0.0 - else - nil - end - end - - it "is nil except for 4.0 sharded when it is 0" do - expect(geo_near.average_distance).to eq expected_value - end - end - end - - describe "#each" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when no options are provided" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.entries - end - - it "returns all the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - - context "when the criteria has a limit" do - - let(:criteria) do - Bar.limit(1) - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.entries - end - - it "returns the limited documents" do - expect(results).to eq([ bar_one ]) - end - end - - context "when providing a max distance" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.max_distance(0.40).entries - end - - it "returns the limited documents" do - expect(results).to eq([ bar_one ]) - end - end - - context "when specifying spherical" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.spherical.entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - - context "when providing a distance multiplier" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.distance_multiplier(6378.1).entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - - it "multiplies the distance factor" do - expect(results.first.geo_near_distance.to_i).to eq(2490) - end - end - - context "when unique is false" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:results) do - geo_near.unique(false).entries - end - - it "returns the documents" do - expect(results).to eq([ bar_one, bar_two ]) - end - end - end - - describe "#empty?" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - context "when the $geoNear has results" do - - let(:criteria) do - Bar.all - end - - it "returns false" do - expect(geo_near).to_not be_empty - end - end - - context "when the map/reduce has no results" do - - let(:criteria) do - Bar.where(name: "Halo") - end - - it "returns true" do - expect(geo_near).to be_empty - end - end - end - - describe "#execute" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - let(:execution_results) do - geo_near.execute - end - - it "returns a hash" do - expect(execution_results).to be_a_kind_of(Hash) - end - end - - describe "#max_distance" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the max distance" do - expect(geo_near.max_distance).to_not be_nil - end - end - - context "when no results are returned" do - - let(:criteria) do - Bar.where(name: "Green Door") - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns 0.0" do - expect(geo_near.max_distance).to eq(0.0) - end - end - end - - describe "#min_distance" do - - let!(:collection) do - Pub.collection - end - - before do - Pub.create_indexes - end - - let!(:bar_one) do - Pub.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Pub.create!(location: [ 52.30, 13.35 ]) - end - - context "when results are returned" do - - let(:criteria) do - Pub.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]).spherical.min_distance(0.0) - end - - it "sets the min distance" do - expect(geo_near.to_a).to include(bar_one) - expect(geo_near.to_a).to include(bar_two) - end - end - end - - describe "#inspect" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "contains the selector" do - expect(geo_near.inspect).to include("selector") - end - - it "contains the class" do - expect(geo_near.inspect).to include("class") - end - - it "contains the near" do - expect(geo_near.inspect).to include("near") - end - - it "contains the multiplier" do - expect(geo_near.inspect).to include("multiplier") - end - - it "contains the max" do - expect(geo_near.inspect).to include("max") - end - - it "contains the unique" do - expect(geo_near.inspect).to include("unique") - end - - it "contains the spherical" do - expect(geo_near.inspect).to include("spherical") - end - end - - describe "#time" do - - let!(:collection) do - Bar.collection - end - - before do - Bar.create_indexes - end - - let!(:bar_one) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let!(:bar_two) do - Bar.create!(location: [ 52.30, 13.35 ]) - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns the execution time" do - expect(geo_near.time).to_not be_nil - end - end - - describe "#empty_and_chainable" do - - let!(:collection) do - Bar.collection - end - - let(:criteria) do - Bar.all - end - - let(:geo_near) do - described_class.new(collection, criteria, [ 52, 13 ]) - end - - it "returns true" do - expect(geo_near.empty_and_chainable?).to be(true) - end - end -end diff --git a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb index b312b50769..4ac7c4f73b 100644 --- a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +++ b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb @@ -1663,7 +1663,7 @@ # Date instance is converted to a Time instance in local time, # because we are querying on a Time field and dates are interpreted # in local time when assigning to Time fields - {'published' => {'$gt' => Time.local(2020, 2, 3)}}, + {'published' => {'$gt' => Time.zone.local(2020, 2, 3)}}, ]} end end @@ -2427,7 +2427,7 @@ context 'when a criterion has an aliased field' do let(:selection) { query.none_of({ id: 1 }) } - + it 'adds the $nor selector and aliases the field' do expect(selection.selector).to eq('$nor' => [{ '_id' => 1 }]) end @@ -2514,7 +2514,7 @@ # Date instance is converted to a Time instance in local time, # because we are querying on a Time field and dates are interpreted # in local time when assigning to Time fields - {'published' => {'$gt' => Time.local(2020, 2, 3) } }, + {'published' => {'$gt' => Time.zone.local(2020, 2, 3) } }, ] } end end diff --git a/spec/mongoid/criteria_spec.rb b/spec/mongoid/criteria_spec.rb index 65d1ca8092..c4ef5ddf39 100644 --- a/spec/mongoid/criteria_spec.rb +++ b/spec/mongoid/criteria_spec.rb @@ -1030,26 +1030,6 @@ end end - describe "#geo_near" do - max_server_version '4.0' - - before do - Bar.create_indexes - end - - let!(:match) do - Bar.create!(location: [ 52.30, 13.25 ]) - end - - let(:criteria) do - Bar.geo_near([ 52, 13 ]).max_distance(10).spherical - end - - it "returns the matching documents" do - expect(criteria).to eq([ match ]) - end - end - describe "#eq" do let!(:match) do diff --git a/spec/mongoid/extensions/object_spec.rb b/spec/mongoid/extensions/object_spec.rb index 9d5aae2d2c..ada6e0ce79 100644 --- a/spec/mongoid/extensions/object_spec.rb +++ b/spec/mongoid/extensions/object_spec.rb @@ -23,13 +23,6 @@ end end - describe "#__mongoize_time__" do - - it "returns self" do - expect(object.__mongoize_time__).to eq(object) - end - end - describe ".demongoize" do let(:object) do diff --git a/spec/mongoid/extensions/time_spec.rb b/spec/mongoid/extensions/time_spec.rb index a68bf1547c..3b06c0b7c3 100644 --- a/spec/mongoid/extensions/time_spec.rb +++ b/spec/mongoid/extensions/time_spec.rb @@ -8,7 +8,7 @@ describe ".demongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when the time zone is not defined" do @@ -22,7 +22,7 @@ it "returns the local time" do expect(Time.demongoize(time).utc_offset).to eq( - Time.local(2010, 11, 19).utc_offset + Time.zone.local(2010, 11, 19).utc_offset ) end end @@ -41,7 +41,7 @@ context "when we have a time close to midnight" do let(:time) do - Time.local(2010, 11, 19, 0, 30).utc + Time.zone.local(2010, 11, 19, 0, 30).utc end it "changes it back to the equivalent local time" do @@ -322,7 +322,7 @@ describe ".mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when given nil" do @@ -608,7 +608,7 @@ end it "converts to a utc time" do - expect(Time.mongoize(date)).to eq(Time.local(date.year, date.month, date.day)) + expect(Time.mongoize(date)).to eq(Time.zone.local(date.year, date.month, date.day)) end it "has a zero utc offset" do @@ -635,7 +635,7 @@ end it "returns a time" do - expect(Time.mongoize(array)).to eq(Time.local(*array)) + expect(Time.mongoize(array)).to eq(Time.zone.local(*array)) end context "when setting ActiveSupport time zone" do @@ -653,11 +653,11 @@ describe "#mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end let!(:eom_time) do - Time.local(2012, 11, 30, 23, 59, 59, 999999.999) + Time.zone.local(2012, 11, 30, 23, 59, 59, 999999.999) end let!(:eom_time_mongoized) do diff --git a/spec/mongoid/extensions/time_with_zone_spec.rb b/spec/mongoid/extensions/time_with_zone_spec.rb index f35897d941..027fd4025c 100644 --- a/spec/mongoid/extensions/time_with_zone_spec.rb +++ b/spec/mongoid/extensions/time_with_zone_spec.rb @@ -8,7 +8,7 @@ describe ".demongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when the time zone is not defined" do @@ -22,7 +22,7 @@ it "returns the local time" do expect(ActiveSupport::TimeWithZone.demongoize(time).utc_offset).to eq( - Time.local(2010, 11, 19).utc_offset + Time.zone.local(2010, 11, 19).utc_offset ) end end @@ -41,7 +41,7 @@ context "when we have a time close to midnight" do let(:time) do - Time.local(2010, 11, 19, 0, 30).utc + Time.zone.local(2010, 11, 19, 0, 30).utc end it "changes it back to the equivalent local time" do @@ -136,7 +136,7 @@ describe ".mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end context "when given nil" do @@ -171,7 +171,7 @@ it "returns a local date from the string" do expect(ActiveSupport::TimeWithZone.mongoize(time.to_s)).to eq( - Time.local(time.year, time.month, time.day, time.hour, time.min, time.sec) + Time.zone.local(time.year, time.month, time.day, time.hour, time.min, time.sec) ) end end @@ -275,7 +275,7 @@ end it "converts to a utc time" do - expect(ActiveSupport::TimeWithZone.mongoize(date)).to eq(Time.local(date.year, date.month, date.day)) + expect(ActiveSupport::TimeWithZone.mongoize(date)).to eq(Time.zone.local(date.year, date.month, date.day)) end it "has a zero utc offset" do @@ -302,7 +302,7 @@ end it "returns a time" do - expect(ActiveSupport::TimeWithZone.mongoize(array)).to eq(Time.local(*array)) + expect(ActiveSupport::TimeWithZone.mongoize(array)).to eq(Time.zone.local(*array)) end context "when setting ActiveSupport time zone" do @@ -320,7 +320,7 @@ describe "#mongoize" do let!(:time) do - Time.local(2010, 11, 19) + Time.zone.local(2010, 11, 19) end it "converts to a utc time" do diff --git a/spec/mongoid/inspectable_spec.rb b/spec/mongoid/inspectable_spec.rb index 203fd5ea1f..a471fa5a8b 100644 --- a/spec/mongoid/inspectable_spec.rb +++ b/spec/mongoid/inspectable_spec.rb @@ -84,4 +84,84 @@ end end end + + describe "#pretty_inspect" do + + context "when not allowing dynamic fields" do + + let(:person) do + Person.new(title: "CEO") + end + + let(:pretty_inspected) do + person.pretty_inspect + end + + it "includes the model type" do + expect(pretty_inspected).to include("#\n" + end + end + end end diff --git a/spec/mongoid/monkey_patches_spec.rb b/spec/mongoid/monkey_patches_spec.rb index 8fdded9afd..90086d4d9c 100644 --- a/spec/mongoid/monkey_patches_spec.rb +++ b/spec/mongoid/monkey_patches_spec.rb @@ -45,7 +45,6 @@ __intersect_from_array__ __intersect_from_object__ __mongoize_object_id__ - __mongoize_time__ __union__ __union_from_object__ ivar @@ -58,6 +57,7 @@ Array => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ __sort_option__ __sort_pair__ delete_one @@ -65,15 +65,18 @@ Date => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], DateTime => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], FalseClass => %i[is_a?], Float => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], Hash => %i[ __sort_option__ @@ -81,6 +84,7 @@ Integer => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], Module => %i[ re_define_method @@ -102,6 +106,7 @@ __evolve_time__ __expr_part__ __mongo_expression__ + __mongoize_time__ __sort_option__ before_type_cast? collectionize @@ -150,10 +155,12 @@ Time => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ ], ActiveSupport::TimeWithZone => %i[ __evolve_date__ __evolve_time__ + __mongoize_time__ _bson_to_i ], BSON::Decimal128 => %i[ diff --git a/spec/mongoid/scopable_spec.rb b/spec/mongoid/scopable_spec.rb index 4f971c07f9..93502c62de 100644 --- a/spec/mongoid/scopable_spec.rb +++ b/spec/mongoid/scopable_spec.rb @@ -354,25 +354,6 @@ def self.default_scope end end - context "when the lambda includes a geo_near query" do - - before do - Bar.scope(:near_by, lambda{ |location| geo_near(location) }) - end - - after do - class << Bar - undef_method :near_by - end - Bar._declared_scopes.clear - end - - it "allows the scope to be defined" do - expect(Bar.near_by([ 51.545099, -0.0106 ])).to be_a(Mongoid::Contextual::GeoNear) - end - - end - context "when a block is provided" do before do diff --git a/spec/mongoid_spec.rb b/spec/mongoid_spec.rb index c3a1be773a..038f8f9b9c 100644 --- a/spec/mongoid_spec.rb +++ b/spec/mongoid_spec.rb @@ -81,6 +81,32 @@ end Mongoid.disconnect_clients end + + it "returns true" do + expect(Mongoid.disconnect_clients).to eq(true) + end + end + + describe ".reconnect_clients" do + + let(:clients) do + Mongoid::Clients.clients.values + end + + before do + Band.all.entries + end + + it "reconnects all active clients" do + clients.each do |client| + expect(client).to receive(:reconnect).and_call_original + end + Mongoid.reconnect_clients + end + + it "returns true" do + expect(Mongoid.reconnect_clients).to eq(true) + end end describe ".client" do