mongoid 9.0.0 → 9.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/Rakefile +44 -21
  4. data/lib/config/locales/en.yml +20 -0
  5. data/lib/mongoid/association/accessors.rb +7 -2
  6. data/lib/mongoid/association/nested/one.rb +14 -1
  7. data/lib/mongoid/association/referenced/belongs_to/binding.rb +7 -1
  8. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  9. data/lib/mongoid/association/referenced/belongs_to.rb +15 -0
  10. data/lib/mongoid/association/referenced/has_many.rb +9 -8
  11. data/lib/mongoid/association/referenced/has_one/buildable.rb +3 -8
  12. data/lib/mongoid/association/referenced/with_polymorphic_criteria.rb +41 -0
  13. data/lib/mongoid/atomic_update_preparer.rb +7 -6
  14. data/lib/mongoid/attributes/nested.rb +2 -1
  15. data/lib/mongoid/clients/sessions.rb +12 -15
  16. data/lib/mongoid/composable.rb +2 -0
  17. data/lib/mongoid/config.rb +9 -0
  18. data/lib/mongoid/contextual/aggregable/memory.rb +3 -2
  19. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -2
  20. data/lib/mongoid/criteria/findable.rb +2 -2
  21. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +15 -1
  22. data/lib/mongoid/document.rb +2 -0
  23. data/lib/mongoid/errors/invalid_around_callback.rb +16 -0
  24. data/lib/mongoid/errors/unrecognized_model_alias.rb +53 -0
  25. data/lib/mongoid/errors/unrecognized_resolver.rb +27 -0
  26. data/lib/mongoid/errors/unregistered_class.rb +47 -0
  27. data/lib/mongoid/errors.rb +4 -0
  28. data/lib/mongoid/fields.rb +13 -7
  29. data/lib/mongoid/identifiable.rb +28 -0
  30. data/lib/mongoid/interceptable.rb +18 -13
  31. data/lib/mongoid/model_resolver.rb +154 -0
  32. data/lib/mongoid/persistence_context.rb +2 -1
  33. data/lib/mongoid/scopable.rb +7 -1
  34. data/lib/mongoid/touchable.rb +1 -7
  35. data/lib/mongoid/traversable.rb +5 -0
  36. data/lib/mongoid/version.rb +1 -1
  37. data/spec/integration/associations/belongs_to_spec.rb +129 -0
  38. data/spec/integration/persistence/collection_options_spec.rb +36 -0
  39. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +4 -0
  40. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +1 -0
  41. data/spec/mongoid/association/referenced/belongs_to_spec.rb +58 -21
  42. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +4 -0
  43. data/spec/mongoid/association_spec.rb +14 -0
  44. data/spec/mongoid/attributes/nested_spec.rb +1 -0
  45. data/spec/mongoid/attributes_spec.rb +16 -0
  46. data/spec/mongoid/clients/transactions_spec.rb +2 -2
  47. data/spec/mongoid/contextual/aggregable/memory_spec.rb +11 -0
  48. data/spec/mongoid/contextual/aggregable/mongo_spec.rb +11 -0
  49. data/spec/mongoid/contextual/mongo_spec.rb +72 -3
  50. data/spec/mongoid/fields_spec.rb +2 -2
  51. data/spec/mongoid/interceptable_spec.rb +31 -0
  52. data/spec/mongoid/model_resolver_spec.rb +167 -0
  53. data/spec/mongoid/monkey_patches_spec.rb +1 -1
  54. data/spec/mongoid/persistence_context_spec.rb +17 -4
  55. data/spec/mongoid/scopable_spec.rb +88 -85
  56. data/spec/mongoid/serializable_spec.rb +16 -9
  57. data/spec/mongoid/touchable_spec.rb +75 -0
  58. data/spec/mongoid/touchable_spec_models.rb +16 -0
  59. data/spec/support/models/band.rb +1 -0
  60. data/spec/support/models/lat_lng.rb +6 -0
  61. metadata +20 -82
  62. checksums.yaml.gz.sig +0 -1
  63. data/spec/shared/LICENSE +0 -20
  64. data/spec/shared/bin/get-mongodb-download-url +0 -17
  65. data/spec/shared/bin/s3-copy +0 -45
  66. data/spec/shared/bin/s3-upload +0 -69
  67. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  68. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  69. data/spec/shared/lib/mrss/constraints.rb +0 -378
  70. data/spec/shared/lib/mrss/docker_runner.rb +0 -298
  71. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  72. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  73. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  74. data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
  75. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  76. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  77. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  78. data/spec/shared/lib/mrss/utils.rb +0 -37
  79. data/spec/shared/share/Dockerfile.erb +0 -281
  80. data/spec/shared/share/haproxy-1.conf +0 -16
  81. data/spec/shared/share/haproxy-2.conf +0 -17
  82. data/spec/shared/shlib/config.sh +0 -27
  83. data/spec/shared/shlib/distro.sh +0 -74
  84. data/spec/shared/shlib/server.sh +0 -417
  85. data/spec/shared/shlib/set_env.sh +0 -146
  86. data.tar.gz.sig +0 -0
  87. metadata.gz.sig +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38b7f297f31cb9dbf9e703769a789969ef5de77ba9f7b0ebf998c0aec1c76524
4
- data.tar.gz: 0aeb2382db008ef20fe7635546da3c13df040933b31005c859747d8cd16247f9
3
+ metadata.gz: 558b30b0a39cd36cd9a3e54695f4617a9fcc0d67300637d1299b487b7d911bda
4
+ data.tar.gz: eccf9c55bdde17fd38592427e0dd6b5545d3e605b94a4308a422ba332e20c183
5
5
  SHA512:
6
- metadata.gz: de6b4d0dbf469baaa5900cbd968a82d74d3eaf1f2b52cc002ddbb37f1531a343c0b5e48a54b47e554a81a1389c7daf43653aa9338653fa7eb7c171d309dd2990
7
- data.tar.gz: 1202e0a37c944780c9b9b7ffca7a89736df81d7dd8757bfd0c2c2ec6723e7ee1ebc088feeca7479a1103fc3290ff679282bc5241cf67335505e56e08a0fee1d6
6
+ metadata.gz: 420ac9d23abeecb04c19794df124fc80d4f99883e1156d4e3be42e79b4b2e5e5a558c6b650746784024f0477a800def3a2ded537b848d3e9b97fb5e5e220b1f0
7
+ data.tar.gz: 40bfdcaf862e3ae7d3d046d84d5f9f5eea4752b5559c869eda16e1bd00cbc9aaae3eef05a7e4bec497915619fb162a248ac8f4be3525068e81f28ae876af1a29
data/README.md CHANGED
@@ -14,6 +14,8 @@ Mongoid has [extensive user documentation](https://www.mongodb.com/docs/mongoid/
14
14
  Mongoid is built on top of the MongoDB Ruby driver which has
15
15
  [its own user documentation](https://www.mongodb.com/docs/ruby-driver/current/).
16
16
 
17
+ High-level Mongoid documentation including tutorials and the reference that were in the docs folder can now be found at the docs-mongoid repository, [here](https://github.com/mongodb/docs-mongoid).
18
+
17
19
  Compatibility
18
20
  -------------
19
21
 
data/Rakefile CHANGED
@@ -2,7 +2,6 @@
2
2
  # rubocop:todo all
3
3
 
4
4
  require "bundler"
5
- require "bundler/gem_tasks"
6
5
  Bundler.setup
7
6
 
8
7
  ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
@@ -11,25 +10,53 @@ $: << File.join(ROOT, 'spec/shared/lib')
11
10
 
12
11
  require "rake"
13
12
  require "rspec/core/rake_task"
14
- require 'mrss/spec_organizer'
15
13
 
16
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
17
- require "mongoid/version"
18
-
19
- tasks = Rake.application.instance_variable_get('@tasks')
20
- tasks['release:do'] = tasks.delete('release')
21
-
22
- task :gem => :build
14
+ # stands in for the Bundler-provided `build` task, which builds the
15
+ # gem for this project. Our release process builds the gems in a
16
+ # particular way, in a GitHub action. This task is just to help remind
17
+ # developers of that fact.
23
18
  task :build do
24
- system "gem build mongoid.gemspec"
19
+ abort <<~WARNING
20
+ `rake build` does nothing in this project. The gem must be built via
21
+ the `Mongoid Release` action on GitHub, which is triggered manually when
22
+ a new release is ready.
23
+ WARNING
25
24
  end
26
25
 
27
- task :install => :build do
28
- system "sudo gem install mongoid-#{Mongoid::VERSION}.gem"
26
+ # `rake version` is used by the deployment system so get the release version
27
+ # of the product beng deployed. It must do nothing more than just print the
28
+ # product version number.
29
+ #
30
+ # See the mongodb-labs/driver-github-tools/ruby/publish Github action.
31
+ desc "Print the current value of Mongoid::VERSION"
32
+ task :version do
33
+ require 'mongoid/version'
34
+
35
+ puts Mongoid::VERSION
29
36
  end
30
37
 
38
+ # overrides the default Bundler-provided `release` task, which also
39
+ # builds the gem. Our release process assumes the gem has already
40
+ # been built (and signed via GPG), so we just need `rake release` to
41
+ # push the gem to rubygems.
31
42
  task :release do
32
- raise "Please use ./release.sh to release"
43
+ require 'mongoid/version'
44
+
45
+ if ENV['GITHUB_ACTION'].nil?
46
+ abort <<~WARNING
47
+ `rake release` must be invoked from the `Mongoid Release` GitHub action,
48
+ and must not be invoked locally. This ensures the gem is properly signed
49
+ and distributed by the appropriate user.
50
+
51
+ Note that it is the `rubygems/release-gem@v1` step in the `Mongoid Release`
52
+ action that invokes this task. Do not rename or remove this task, or the
53
+ release-gem step will fail. Reimplement this task with caution.
54
+
55
+ mongoid-#{Mongoid::VERSION}.gem was NOT pushed to RubyGems.
56
+ WARNING
57
+ end
58
+
59
+ system 'gem', 'push', "mongoid-#{Mongoid::VERSION}.gem"
33
60
  end
34
61
 
35
62
  RSpec::Core::RakeTask.new("spec") do |spec|
@@ -96,6 +123,8 @@ RUN_PRIORITY = %i(
96
123
  )
97
124
 
98
125
  def spec_organizer
126
+ require 'mrss/spec_organizer'
127
+
99
128
  Mrss::SpecOrganizer.new(
100
129
  root: ROOT,
101
130
  classifiers: CLASSIFIERS,
@@ -131,16 +160,10 @@ task :docs => 'docs:yard'
131
160
  namespace :docs do
132
161
  desc "Generate yard documentation"
133
162
  task :yard do
163
+ require "mongoid/version"
164
+
134
165
  out = File.join('yard-docs', Mongoid::VERSION)
135
166
  FileUtils.rm_rf(out)
136
167
  system "yardoc -o #{out} --title mongoid-#{Mongoid::VERSION}"
137
168
  end
138
169
  end
139
-
140
- namespace :release do
141
- task :check_private_key do
142
- unless File.exist?('gem-private_key.pem')
143
- raise "No private key present, cannot release"
144
- end
145
- end
146
- end
@@ -140,6 +140,10 @@ en:
140
140
  A collation option is only supported if the query is executed on a MongoDB server
141
141
  with version >= 3.4."
142
142
  resolution: "Remove the collation option from the query."
143
+ invalid_around_callback:
144
+ message: "An around callback must contain a yield in its definition."
145
+ summary: "The block needs to be yielded to for around callbacks to function as intended."
146
+ resolution: "Ensure there is a yield statement inside the body of the around callback."
143
147
  invalid_async_query_executor:
144
148
  message: "Invalid async_query_executor option: %{executor}."
145
149
  summary: "A invalid async query executor was specified.
@@ -676,6 +680,22 @@ en:
676
680
  resolution: "The _type field is a reserved one used by Mongoid to determine the
677
681
  class for instantiating an object. Please don't save data in this field or ensure
678
682
  that any values in this field correspond to valid models."
683
+ unrecognized_model_alias:
684
+ message: "Cannot find any model with type %{model_alias}"
685
+ summary: "A document is trying to load a polymorphic association, but the data refers to a type of object that can't be resolved (%{model_alias}). It might be that you've renamed the target class."
686
+ resolution: "Register the old name as an alias on the refactored target object, using `identify_as`. This will allow Mongoid to find the target type even if the name no longer matches what was stored in the database."
687
+ unrecognized_resolver:
688
+ message: "The model resolver %{resolver} was referenced, but never registered."
689
+ summary: "A polymorphic association has been configured to use a resolver
690
+ named %{resolver}, but that resolver has not yet been registered. This
691
+ might be a typo. Currently registered resolvers are: %{resolvers}."
692
+ resolution: "Register custom resolvers with
693
+ `Mongoid::ModelResolver.register_resolver` before attempting to query
694
+ a polymorphic association."
695
+ unregistered_class:
696
+ message: "The class %{klass} is not registered with the resolver %{resolver}."
697
+ summary: "A polymorphic association using the resolver %{resolver} has tried to link to a model of type %{klass}, but the resolver has no knowledge of any such model. This can happen if the association is configured to use a different resolver than the target mode."
698
+ resolution: "Make sure the target model is registered with the same resolver as the polymorphic association, using `identify_as`."
679
699
  unsaved_document:
680
700
  message: "Attempted to save %{document} before the parent %{base}."
681
701
  summary: "You cannot call create or create! through the
@@ -42,7 +42,8 @@ module Mongoid
42
42
  #
43
43
  # @return [ Proxy ] The association.
44
44
  def create_relation(object, association, selected_fields = nil)
45
- type = @attributes[association.inverse_type]
45
+ key = @attributes[association.inverse_type]
46
+ type = key ? association.resolver.model_for(key) : nil
46
47
  target = if t = association.build(self, object, type, selected_fields)
47
48
  association.create_relation(self, t)
48
49
  else
@@ -116,7 +117,11 @@ module Mongoid
116
117
  # during binding or when cascading callbacks. Whenever we retrieve
117
118
  # associations within the codebase, we use without_autobuild.
118
119
  if !without_autobuild? && association.embedded? && attribute_missing?(field_name)
119
- raise Mongoid::Errors::AttributeNotLoaded.new(self.class, field_name)
120
+ # We always allow accessing the parent document of an embedded one.
121
+ try_get_parent = association.is_a?(
122
+ Mongoid::Association::Embedded::EmbeddedIn
123
+ ) && field_name == association.key
124
+ raise Mongoid::Errors::AttributeNotLoaded.new(self.class, field_name) unless try_get_parent
120
125
  end
121
126
 
122
127
  if !reload && (value = ivar(name)) != false
@@ -53,12 +53,25 @@ module Mongoid
53
53
  @attributes = attributes.with_indifferent_access
54
54
  @association = association
55
55
  @options = options
56
- @class_name = options[:class_name] ? options[:class_name].constantize : association.klass
56
+ @class_name = class_from(options[:class_name])
57
57
  @destroy = @attributes.delete(:_destroy)
58
58
  end
59
59
 
60
60
  private
61
61
 
62
+ # Coerces the argument into a class, or defaults to the association's class.
63
+ #
64
+ # @param [ String | Mongoid::Document | nil ] name_or_class the value to coerce
65
+ #
66
+ # @return [ Mongoid::Document ] the resulting class
67
+ def class_from(name_or_class)
68
+ case name_or_class
69
+ when nil, false then association.klass
70
+ when String then name_or_class.constantize
71
+ else name_or_class
72
+ end
73
+ end
74
+
62
75
  # Extracts and converts the id to the expected type.
63
76
  #
64
77
  # @return [ BSON::ObjectId | String | Object | nil ] The converted id,
@@ -23,7 +23,13 @@ module Mongoid
23
23
  binding do
24
24
  check_polymorphic_inverses!(_target)
25
25
  bind_foreign_key(_base, record_id(_target))
26
- bind_polymorphic_inverse_type(_base, _target.class.name)
26
+
27
+ # set the inverse type (e.g. "#{name}_type") for new polymorphic associations
28
+ if _association.inverse_type && !_base.frozen?
29
+ key = _association.resolver.default_key_for(_target)
30
+ bind_polymorphic_inverse_type(_base, key)
31
+ end
32
+
27
33
  if inverse = _association.inverse(_target)
28
34
  if set_base_association
29
35
  if _base.referenced_many?
@@ -33,7 +33,7 @@ module Mongoid
33
33
  end
34
34
 
35
35
  def query_criteria(object, type)
36
- cls = type ? type.constantize : relation_class
36
+ cls = type ? (type.is_a?(String) ? type.constantize : type) : relation_class
37
37
  crit = cls.criteria
38
38
  crit = crit.apply_scope(scope)
39
39
  crit.where(primary_key => object)
@@ -103,6 +103,21 @@ module Mongoid
103
103
  @polymorphic ||= !!@options[:polymorphic]
104
104
  end
105
105
 
106
+ # Returns the object responsible for converting polymorphic type references into
107
+ # class objects, and vice versa. This is obtained via the `:polymorphic` option
108
+ # that was given when the association was defined.
109
+ #
110
+ # See Mongoid::ModelResolver.resolver for how the `:polymorphic` option is
111
+ # interpreted here.
112
+ #
113
+ # @raise KeyError if no such resolver has been registered under the given
114
+ # identifier.
115
+ #
116
+ # @return [ nil | Mongoid::ModelResolver ] the resolver to use
117
+ def resolver
118
+ @resolver ||= Mongoid::ModelResolver.resolver(@options[:polymorphic])
119
+ end
120
+
106
121
  # The name of the field used to store the type of polymorphic association.
107
122
  #
108
123
  # @return [ String ] The field used to store the type of polymorphic association.
@@ -6,6 +6,7 @@ require 'mongoid/association/referenced/has_many/buildable'
6
6
  require 'mongoid/association/referenced/has_many/proxy'
7
7
  require 'mongoid/association/referenced/has_many/enumerable'
8
8
  require 'mongoid/association/referenced/has_many/eager'
9
+ require 'mongoid/association/referenced/with_polymorphic_criteria'
9
10
 
10
11
  module Mongoid
11
12
  module Association
@@ -15,6 +16,7 @@ module Mongoid
15
16
  class HasMany
16
17
  include Relatable
17
18
  include Buildable
19
+ include WithPolymorphicCriteria
18
20
 
19
21
  # The options available for this type of association, in addition to the
20
22
  # common ones.
@@ -131,6 +133,12 @@ module Mongoid
131
133
  # @param [ Class ] object_class The object class.
132
134
  #
133
135
  # @return [ Mongoid::Criteria ] The criteria object.
136
+ #
137
+ # @deprecated in 9.0.x
138
+ #
139
+ # It appears as if this method is an artifact left over from a refactoring that renamed it
140
+ # `with_polymorphic_criterion`, and made it private. Regardless, this method isn't referenced
141
+ # anywhere else, and is unlikely to be useful to external clients. We should remove it.
134
142
  def add_polymorphic_criterion(criteria, object_class)
135
143
  if polymorphic?
136
144
  criteria.where(type => object_class.name)
@@ -138,6 +146,7 @@ module Mongoid
138
146
  criteria
139
147
  end
140
148
  end
149
+ Mongoid.deprecate(self, :add_polymorphic_criterion)
141
150
 
142
151
  # Is this association polymorphic?
143
152
  #
@@ -222,14 +231,6 @@ module Mongoid
222
231
  with_ordering(crit)
223
232
  end
224
233
 
225
- def with_polymorphic_criterion(criteria, base)
226
- if polymorphic?
227
- criteria.where(type => base.class.name)
228
- else
229
- criteria
230
- end
231
- end
232
-
233
234
  def with_ordering(criteria)
234
235
  if order
235
236
  criteria.order_by(order)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  # rubocop:todo all
3
3
 
4
+ require 'mongoid/association/referenced/with_polymorphic_criteria'
5
+
4
6
  module Mongoid
5
7
  module Association
6
8
  module Referenced
@@ -8,6 +10,7 @@ module Mongoid
8
10
 
9
11
  # The Builder behavior for has_one associations.
10
12
  module Buildable
13
+ include WithPolymorphicCriteria
11
14
 
12
15
  # This method either takes an _id or an object and queries for the
13
16
  # inverse side using the id or sets the object after clearing the
@@ -57,14 +60,6 @@ module Mongoid
57
60
  query_criteria(object, base).take
58
61
  end
59
62
 
60
- def with_polymorphic_criterion(criteria, base)
61
- if polymorphic?
62
- criteria.where(type => base.class.name)
63
- else
64
- criteria
65
- end
66
- end
67
-
68
63
  def query?(object)
69
64
  object && !object.is_a?(Mongoid::Document)
70
65
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Association
5
+ module Referenced
6
+ # Implements the `with_polymorphic_criteria` shared behavior.
7
+ #
8
+ # @api private
9
+ module WithPolymorphicCriteria
10
+ # If the receiver represents a polymorphic association, applies
11
+ # the polymorphic search criteria to the given `criteria` object.
12
+ #
13
+ # @param [ Mongoid::Criteria ] criteria the criteria to append to
14
+ # if receiver is polymorphic.
15
+ # @param [ Mongoid::Document ] base the document to use when resolving
16
+ # the polymorphic type keys.
17
+ #
18
+ # @return [ Mongoid::Criteria] the resulting criteria, which may be
19
+ # the same as the input.
20
+ def with_polymorphic_criterion(criteria, base)
21
+ if polymorphic?
22
+ # 1. get the resolver for the inverse association
23
+ resolver = klass.reflect_on_association(as).resolver
24
+
25
+ # 2. look up the list of keys from the resolver, given base
26
+ keys = resolver.keys_for(base)
27
+
28
+ # 3. use equality if there is just one key, `in` if there are multiple
29
+ if keys.many?
30
+ criteria.where(type => { :$in => keys })
31
+ else
32
+ criteria.where(type => keys.first)
33
+ end
34
+ else
35
+ criteria
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -25,7 +25,7 @@ module Mongoid
25
25
  if key.to_s.start_with?('$')
26
26
  (atomic_updates[key] ||= {}).update(prepare_operation(klass, key, value))
27
27
  else
28
- (atomic_updates['$set'] ||= {})[key] = mongoize_for(key, klass, key, value)
28
+ (atomic_updates['$set'] ||= {})[key] = mongoize_for('$set', klass, key, value)
29
29
  end
30
30
  end
31
31
  end
@@ -43,24 +43,25 @@ module Mongoid
43
43
  def prepare_operation(klass, key, value)
44
44
  value.each_with_object({}) do |(key2, value2), hash|
45
45
  key2 = klass.database_field_name(key2)
46
- hash[key2] = value_for(key, klass, value2)
46
+ hash[key2] = value_for(key, klass, key2, value2)
47
47
  end
48
48
  end
49
49
 
50
50
  # Get the value for the provided operator, klass, key and value.
51
51
  #
52
- # This is necessary for special cases like $rename, $addToSet and $push.
52
+ # This is necessary for special cases like $rename, $addToSet, $push, $pull and $pop.
53
53
  #
54
54
  # @param [ String ] operator The operator.
55
55
  # @param [ Class ] klass The model class.
56
+ # @param [ String | Symbol ] key The field key.
56
57
  # @param [ Object ] value The original value.
57
58
  #
58
59
  # @return [ Object ] Value prepared for the provided operator.
59
- def value_for(operator, klass, value)
60
+ def value_for(operator, klass, key, value)
60
61
  case operator
61
62
  when '$rename' then value.to_s
62
- when '$addToSet', '$push' then value.mongoize
63
- else mongoize_for(operator, klass, operator, value)
63
+ when '$addToSet', '$push', '$pull', '$pop' then value.mongoize
64
+ else mongoize_for(operator, klass, key, value)
64
65
  end
65
66
  end
66
67
 
@@ -60,7 +60,8 @@ module Mongoid
60
60
  re_define_method(meth) do |attrs|
61
61
  _assigning do
62
62
  if association.polymorphic? and association.inverse_type
63
- options = options.merge!(:class_name => self.send(association.inverse_type))
63
+ klass = association.resolver.model_for(send(association.inverse_type))
64
+ options = options.merge!(:class_name => klass)
64
65
  end
65
66
  association.nested_builder(attrs, options).build(self)
66
67
  end
@@ -89,21 +89,22 @@ module Mongoid
89
89
  def transaction(options = {}, session_options: {})
90
90
  with_session(session_options) do |session|
91
91
  begin
92
- session.start_transaction(options)
93
- yield
94
- commit_transaction(session)
92
+ session.with_transaction(options) do
93
+ yield
94
+ end
95
+ run_commit_callbacks(session)
95
96
  rescue *transactions_not_supported_exceptions
96
97
  raise Mongoid::Errors::TransactionsNotSupported
97
98
  rescue Mongoid::Errors::Rollback
98
- abort_transaction(session)
99
+ run_abort_callbacks(session)
99
100
  rescue Mongoid::Errors::InvalidSessionNesting
100
101
  # Session should be ended here.
101
102
  raise Mongoid::Errors::InvalidTransactionNesting.new
102
103
  rescue Mongo::Error::InvalidSession, Mongo::Error::InvalidTransactionOperation => e
103
- abort_transaction(session)
104
- raise Mongoid::Errors::TransactionError(e)
104
+ run_abort_callbacks(session)
105
+ raise Mongoid::Errors::TransactionError.new(e)
105
106
  rescue StandardError => e
106
- abort_transaction(session)
107
+ run_abort_callbacks(session)
107
108
  raise e
108
109
  end
109
110
  end
@@ -189,25 +190,21 @@ module Mongoid
189
190
  _session&.in_transaction? || false
190
191
  end
191
192
 
192
- # Commits the active transaction on the session, and calls
193
- # after_commit callbacks on modified documents.
193
+ # Runs after_commit callbacks on modified documents.
194
194
  #
195
195
  # @param [ Mongo::Session ] session Session on which
196
196
  # a transaction is started.
197
- def commit_transaction(session)
198
- session.commit_transaction
197
+ def run_commit_callbacks(session)
199
198
  Threaded.clear_modified_documents(session).each do |doc|
200
199
  doc.run_after_callbacks(:commit)
201
200
  end
202
201
  end
203
202
 
204
- # Aborts the active transaction on the session, and calls
205
- # after_rollback callbacks on modified documents.
203
+ # Runs after_rollback callbacks on modified documents.
206
204
  #
207
205
  # @param [ Mongo::Session ] session Session on which
208
206
  # a transaction is started.
209
- def abort_transaction(session)
210
- session.abort_transaction
207
+ def run_abort_callbacks(session)
211
208
  Threaded.clear_modified_documents(session).each do |doc|
212
209
  doc.run_after_callbacks(:rollback)
213
210
  end
@@ -5,6 +5,7 @@ require "mongoid/changeable"
5
5
  require "mongoid/collection_configurable"
6
6
  require "mongoid/encryptable"
7
7
  require "mongoid/findable"
8
+ require 'mongoid/identifiable'
8
9
  require "mongoid/indexable"
9
10
  require "mongoid/inspectable"
10
11
  require "mongoid/interceptable"
@@ -44,6 +45,7 @@ module Mongoid
44
45
  include Attributes
45
46
  include Evolvable
46
47
  include Fields
48
+ include Identifiable
47
49
  include Indexable
48
50
  include Inspectable
49
51
  include Matchable
@@ -171,6 +171,15 @@ module Mongoid
171
171
  # See https://jira.mongodb.org/browse/MONGOID-5658 for more details.
172
172
  option :around_callbacks_for_embeds, default: false
173
173
 
174
+ # When this flag is false, named scopes cannot unset a default scope.
175
+ # This is the traditional (and default) behavior in Mongoid 9 and earlier.
176
+ #
177
+ # Setting this flag to true will allow named scopes to unset the default
178
+ # scope. This will be the default in Mongoid 10.
179
+ #
180
+ # See https://jira.mongodb.org/browse/MONGOID-5785 for more details.
181
+ option :allow_scopes_to_unset_default_scope, default: false
182
+
174
183
  # Returns the Config singleton, for use in the configure DSL.
175
184
  #
176
185
  # @return [ self ] The Config singleton.
@@ -90,11 +90,12 @@ module Mongoid
90
90
  # @example Get the sum for the provided block.
91
91
  # aggregable.sum(&:likes)
92
92
  #
93
- # @param [ Symbol ] field The field to sum.
93
+ # @param [ Symbol | Numeric ] field The field to sum, or the intial
94
+ # value of the sum when a block is given.
94
95
  #
95
96
  # @return [ Numeric ] The sum value.
96
97
  def sum(field = nil)
97
- return super() if block_given?
98
+ return super(field || 0) if block_given?
98
99
 
99
100
  aggregate_by(field, :sum) || 0
100
101
  end
@@ -96,11 +96,14 @@ module Mongoid
96
96
  # @example Get the sum for the provided block.
97
97
  # aggregable.sum(&:likes)
98
98
  #
99
- # @param [ Symbol ] field The field to sum.
99
+ # @param [ Symbol | Numeric ] field The field to sum, or the initial
100
+ # value of the sum when a block is given.
100
101
  #
101
102
  # @return [ Float ] The sum value.
102
103
  def sum(field = nil)
103
- block_given? ? super() : aggregates(field)["sum"] || 0
104
+ return super(field || 0) if block_given?
105
+
106
+ aggregates(field)["sum"] || 0
104
107
  end
105
108
 
106
109
  private
@@ -169,12 +169,12 @@ module Mongoid
169
169
  args.size > 1 || !args.first.is_a?(Hash) && args.first.resizable?
170
170
  end
171
171
 
172
- # Convenience method of raising an invalid options error.
172
+ # Convenience method of raising an invalid find error.
173
173
  #
174
174
  # @example Raise the error.
175
175
  # criteria.raise_invalid
176
176
  #
177
- # @raise [ Errors::InvalidOptions ] The error.
177
+ # @raise [ Errors::InvalidFind ] The error.
178
178
  def raise_invalid
179
179
  raise Errors::InvalidFind.new
180
180
  end
@@ -44,7 +44,21 @@ module Mongoid
44
44
  #
45
45
  # @return [ Object ] The converted number.
46
46
  def __numeric__(object)
47
- object.to_s.match?(/\A[-+]?[0-9]*[0-9.]0*\z/) ? object.to_i : Float(object)
47
+ str = object.to_s
48
+ raise ArgumentError if str.empty?
49
+
50
+ # These requirements seem a bit odd, but they're explicitly specified in the tests,
51
+ # so we're obligated to keep them, for now. (This code was rewritten from a one-line
52
+ # regex, due to security concerns with a polynomial regex being used on uncontrolled
53
+ # data).
54
+
55
+ str = str.chop if str.end_with?('.')
56
+ return 0 if str.empty?
57
+
58
+ result = Integer(str) rescue Float(object)
59
+
60
+ integer = result.to_i
61
+ integer == result ? integer : result
48
62
  end
49
63
 
50
64
  # Evolve the object to an integer.
@@ -17,6 +17,7 @@ require 'mongoid/timestamps'
17
17
  require 'mongoid/association'
18
18
  require 'mongoid/composable'
19
19
  require 'mongoid/touchable'
20
+ require 'mongoid/model_resolver'
20
21
 
21
22
  module Mongoid
22
23
  # This is the base module for all domain objects that need to be persisted to
@@ -31,6 +32,7 @@ module Mongoid
31
32
 
32
33
  included do
33
34
  Mongoid.register_model(self)
35
+ Mongoid::ModelResolver.register(self)
34
36
  end
35
37
 
36
38
  # Regex for matching illegal BSON keys.
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Errors
5
+ # This error is raised when an around callback is
6
+ # defined by the user without a yield
7
+ class InvalidAroundCallback < MongoidError
8
+ # Create the new error.
9
+ #
10
+ # @api private
11
+ def initialize
12
+ super(compose_message('invalid_around_callback'))
13
+ end
14
+ end
15
+ end
16
+ end