mongoid 9.0.0 → 9.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/Rakefile +44 -21
- data/lib/config/locales/en.yml +20 -0
- data/lib/mongoid/association/accessors.rb +7 -2
- data/lib/mongoid/association/nested/one.rb +14 -1
- data/lib/mongoid/association/referenced/belongs_to/binding.rb +7 -1
- data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
- data/lib/mongoid/association/referenced/belongs_to.rb +15 -0
- data/lib/mongoid/association/referenced/has_many.rb +9 -8
- data/lib/mongoid/association/referenced/has_one/buildable.rb +3 -8
- data/lib/mongoid/association/referenced/with_polymorphic_criteria.rb +41 -0
- data/lib/mongoid/atomic_update_preparer.rb +7 -6
- data/lib/mongoid/attributes/nested.rb +2 -1
- data/lib/mongoid/clients/sessions.rb +12 -15
- data/lib/mongoid/composable.rb +2 -0
- data/lib/mongoid/config.rb +9 -0
- data/lib/mongoid/contextual/aggregable/memory.rb +3 -2
- data/lib/mongoid/contextual/aggregable/mongo.rb +5 -2
- data/lib/mongoid/criteria/findable.rb +2 -2
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +15 -1
- data/lib/mongoid/document.rb +2 -0
- data/lib/mongoid/errors/invalid_around_callback.rb +16 -0
- data/lib/mongoid/errors/unrecognized_model_alias.rb +53 -0
- data/lib/mongoid/errors/unrecognized_resolver.rb +27 -0
- data/lib/mongoid/errors/unregistered_class.rb +47 -0
- data/lib/mongoid/errors.rb +4 -0
- data/lib/mongoid/fields.rb +13 -7
- data/lib/mongoid/identifiable.rb +28 -0
- data/lib/mongoid/interceptable.rb +18 -13
- data/lib/mongoid/model_resolver.rb +154 -0
- data/lib/mongoid/persistence_context.rb +2 -1
- data/lib/mongoid/scopable.rb +7 -1
- data/lib/mongoid/touchable.rb +1 -7
- data/lib/mongoid/traversable.rb +5 -0
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/associations/belongs_to_spec.rb +129 -0
- data/spec/integration/persistence/collection_options_spec.rb +36 -0
- data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +4 -0
- data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +1 -0
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +58 -21
- data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +4 -0
- data/spec/mongoid/association_spec.rb +14 -0
- data/spec/mongoid/attributes/nested_spec.rb +1 -0
- data/spec/mongoid/attributes_spec.rb +16 -0
- data/spec/mongoid/clients/transactions_spec.rb +2 -2
- data/spec/mongoid/contextual/aggregable/memory_spec.rb +11 -0
- data/spec/mongoid/contextual/aggregable/mongo_spec.rb +11 -0
- data/spec/mongoid/contextual/mongo_spec.rb +72 -3
- data/spec/mongoid/fields_spec.rb +2 -2
- data/spec/mongoid/interceptable_spec.rb +31 -0
- data/spec/mongoid/model_resolver_spec.rb +167 -0
- data/spec/mongoid/monkey_patches_spec.rb +1 -1
- data/spec/mongoid/persistence_context_spec.rb +17 -4
- data/spec/mongoid/scopable_spec.rb +88 -85
- data/spec/mongoid/serializable_spec.rb +16 -9
- data/spec/mongoid/touchable_spec.rb +75 -0
- data/spec/mongoid/touchable_spec_models.rb +16 -0
- data/spec/support/models/band.rb +1 -0
- data/spec/support/models/lat_lng.rb +6 -0
- metadata +20 -82
- checksums.yaml.gz.sig +0 -1
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/bin/s3-copy +0 -45
- data/spec/shared/bin/s3-upload +0 -69
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -231
- data/spec/shared/lib/mrss/constraints.rb +0 -378
- data/spec/shared/lib/mrss/docker_runner.rb +0 -298
- data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
- data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
- data/spec/shared/lib/mrss/session_registry.rb +0 -69
- data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
- data/spec/shared/lib/mrss/utils.rb +0 -37
- data/spec/shared/share/Dockerfile.erb +0 -281
- data/spec/shared/share/haproxy-1.conf +0 -16
- data/spec/shared/share/haproxy-2.conf +0 -17
- data/spec/shared/shlib/config.sh +0 -27
- data/spec/shared/shlib/distro.sh +0 -74
- data/spec/shared/shlib/server.sh +0 -417
- data/spec/shared/shlib/set_env.sh +0 -146
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -1
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Errors
|
5
|
+
# Raised when a polymorphic association is queried, but the type of the
|
6
|
+
# association cannot be resolved. This usually happens when the data in
|
7
|
+
# the database references a type that no longer exists.
|
8
|
+
#
|
9
|
+
# For example, consider the following model:
|
10
|
+
#
|
11
|
+
# class Manager
|
12
|
+
# include Mongoid::Document
|
13
|
+
# belongs_to :unit, polymorphic: true
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Imagine there is a document in the `managers` collection that looks
|
17
|
+
# something like this:
|
18
|
+
#
|
19
|
+
# { _id: ..., unit_id: ..., unit_type: 'Department::Engineering' }
|
20
|
+
#
|
21
|
+
# If, at some point in your refactoring, you rename the `Department::Engineering`
|
22
|
+
# model to something else, Mongoid will no longer be able to resolve the
|
23
|
+
# type of this association, and asking for `manager.unit` will raise this
|
24
|
+
# exception.
|
25
|
+
#
|
26
|
+
# To fix this exception, you can add an alias to the model class so that it
|
27
|
+
# can still be found, even after renaming it:
|
28
|
+
#
|
29
|
+
# module Engineering
|
30
|
+
# class Department
|
31
|
+
# include Mongoid::Document
|
32
|
+
#
|
33
|
+
# identify_as 'Department::Engineering'
|
34
|
+
#
|
35
|
+
# # ...
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Better practice would be to use unique strings instead of class names to
|
40
|
+
# identify these polymorphic types in the database (e.g. 'dept' instead of
|
41
|
+
# 'Department::Engineering').
|
42
|
+
class UnrecognizedModelAlias < MongoidError
|
43
|
+
def initialize(model_alias)
|
44
|
+
super(
|
45
|
+
compose_message(
|
46
|
+
'unrecognized_model_alias',
|
47
|
+
model_alias: model_alias.inspect
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Errors
|
5
|
+
# Raised when a model resolver is referenced, but not registered.
|
6
|
+
#
|
7
|
+
# class Manager
|
8
|
+
# include Mongoid::Document
|
9
|
+
# belongs_to :unit, polymorphic: :org
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# If `:org` has not previously been registered as a model resolver,
|
13
|
+
# Mongoid will raise UnrecognizedResolver when it tries to resolve
|
14
|
+
# a manager's unit.
|
15
|
+
class UnrecognizedResolver < MongoidError
|
16
|
+
def initialize(resolver)
|
17
|
+
super(
|
18
|
+
compose_message(
|
19
|
+
'unrecognized_resolver',
|
20
|
+
resolver: resolver.inspect,
|
21
|
+
resolvers: [ :default, *Mongoid::ModelResolver.resolvers.keys ].inspect
|
22
|
+
)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Errors
|
5
|
+
# Raised when Mongoid tries to query the identifier to use for a given
|
6
|
+
# class in a polymorphic association, but the class has not previously
|
7
|
+
# been registered by resolver that was used for the query.
|
8
|
+
#
|
9
|
+
# Here's an exammple:
|
10
|
+
#
|
11
|
+
# class Department
|
12
|
+
# include Mongoid::Document
|
13
|
+
# has_many :managers, as: :unit
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# class Manager
|
17
|
+
# include Mongoid::Document
|
18
|
+
# belongs_to :unit, polymorphic: :org
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# The Manager class is configured to use a custom resolver named `:org`
|
22
|
+
# when resolving the polymorphic `unit` association. However, the `Department`
|
23
|
+
# class is not registered with that resolver. When the program tries to
|
24
|
+
# associate a manager record with a department, it will not be able to find
|
25
|
+
# the required key in the `:org` resolver, and will fail with this exception.
|
26
|
+
#
|
27
|
+
# The solution is to make sure the `Department` class is properly registered
|
28
|
+
# with the `:org` resolver:
|
29
|
+
#
|
30
|
+
# class Department
|
31
|
+
# include Mongoid::Document
|
32
|
+
# identify_as resolver: :org
|
33
|
+
# has_many :managers, as: :unit
|
34
|
+
# end
|
35
|
+
class UnregisteredClass < MongoidError
|
36
|
+
def initialize(klass, resolver)
|
37
|
+
super(
|
38
|
+
compose_message(
|
39
|
+
'unregistered_class',
|
40
|
+
klass: klass,
|
41
|
+
resolver: resolver.inspect
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/mongoid/errors.rb
CHANGED
@@ -13,6 +13,7 @@ require "mongoid/errors/empty_config_file"
|
|
13
13
|
require "mongoid/errors/immutable_attribute"
|
14
14
|
require "mongoid/errors/in_memory_collation_not_supported"
|
15
15
|
require "mongoid/errors/invalid_auto_encryption_configuration"
|
16
|
+
require "mongoid/errors/invalid_around_callback"
|
16
17
|
require "mongoid/errors/invalid_async_query_executor"
|
17
18
|
require "mongoid/errors/invalid_collection"
|
18
19
|
require "mongoid/errors/invalid_config_file"
|
@@ -68,6 +69,9 @@ require "mongoid/errors/transaction_error"
|
|
68
69
|
require "mongoid/errors/transactions_not_supported"
|
69
70
|
require "mongoid/errors/unknown_attribute"
|
70
71
|
require "mongoid/errors/unknown_model"
|
72
|
+
require 'mongoid/errors/unrecognized_model_alias'
|
73
|
+
require 'mongoid/errors/unrecognized_resolver'
|
74
|
+
require 'mongoid/errors/unregistered_class'
|
71
75
|
require "mongoid/errors/unsaved_document"
|
72
76
|
require "mongoid/errors/unsupported_javascript"
|
73
77
|
require "mongoid/errors/validations"
|
data/lib/mongoid/fields.rb
CHANGED
@@ -49,6 +49,11 @@ module Mongoid
|
|
49
49
|
# @api private
|
50
50
|
INVALID_BSON_CLASSES = [ BSON::Decimal128, BSON::Int32, BSON::Int64 ].freeze
|
51
51
|
|
52
|
+
# The suffix for generated translated fields.
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
TRANSLATIONS_SFX = '_translations'
|
56
|
+
|
52
57
|
module ClassMethods
|
53
58
|
# Returns the list of id fields for this model class, as both strings
|
54
59
|
# and symbols.
|
@@ -101,8 +106,8 @@ module Mongoid
|
|
101
106
|
ar.each_with_index do |fn, i|
|
102
107
|
key = fn
|
103
108
|
unless klass.fields.key?(fn) || klass.relations.key?(fn)
|
104
|
-
if
|
105
|
-
key =
|
109
|
+
if fn.end_with?(TRANSLATIONS_SFX)
|
110
|
+
key = fn.delete_suffix(TRANSLATIONS_SFX)
|
106
111
|
else
|
107
112
|
key = fn
|
108
113
|
end
|
@@ -408,7 +413,8 @@ module Mongoid
|
|
408
413
|
#
|
409
414
|
# @api private
|
410
415
|
def database_field_name(name, relations, aliased_fields, aliased_associations)
|
411
|
-
return
|
416
|
+
return "" unless name.present?
|
417
|
+
|
412
418
|
key = name.to_s
|
413
419
|
segment, remaining = key.split('.', 2)
|
414
420
|
|
@@ -725,11 +731,11 @@ module Mongoid
|
|
725
731
|
# @api private
|
726
732
|
def create_translations_getter(name, meth)
|
727
733
|
generated_methods.module_eval do
|
728
|
-
re_define_method("#{meth}
|
734
|
+
re_define_method("#{meth}#{TRANSLATIONS_SFX}") do
|
729
735
|
attributes[name] ||= {}
|
730
736
|
attributes[name].with_indifferent_access
|
731
737
|
end
|
732
|
-
alias_method :"#{meth}_t", :"#{meth}
|
738
|
+
alias_method :"#{meth}_t", :"#{meth}#{TRANSLATIONS_SFX}"
|
733
739
|
end
|
734
740
|
end
|
735
741
|
|
@@ -745,14 +751,14 @@ module Mongoid
|
|
745
751
|
# @api private
|
746
752
|
def create_translations_setter(name, meth, field)
|
747
753
|
generated_methods.module_eval do
|
748
|
-
re_define_method("#{meth}
|
754
|
+
re_define_method("#{meth}#{TRANSLATIONS_SFX}=") do |value|
|
749
755
|
attribute_will_change!(name)
|
750
756
|
value&.transform_values! do |_value|
|
751
757
|
field.type.mongoize(_value)
|
752
758
|
end
|
753
759
|
attributes[name] = value
|
754
760
|
end
|
755
|
-
alias_method :"#{meth}_t=", :"#{meth}
|
761
|
+
alias_method :"#{meth}_t=", :"#{meth}#{TRANSLATIONS_SFX}="
|
756
762
|
end
|
757
763
|
end
|
758
764
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mongoid/model_resolver'
|
4
|
+
|
5
|
+
module Mongoid
|
6
|
+
# Implements the "identify_as" interface (for specifying type aliases
|
7
|
+
# for document classes).
|
8
|
+
module Identifiable
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
# Specifies aliases that may be used to identify this document
|
13
|
+
# class in polymorphic situations. By default, classes are identified
|
14
|
+
# by their class names, but alternative aliases may be used instead,
|
15
|
+
# if desired.
|
16
|
+
#
|
17
|
+
# @param [ Array<String | Symbol> ] aliases the list of aliases to
|
18
|
+
# assign to this class.
|
19
|
+
# @param [ Mongoid::ModelResolver::Interface | Symbol | :default ] resolver the
|
20
|
+
# resolver instance to use when registering the type. If :default, the default
|
21
|
+
# `ModelResolver` instance will be used. If any other symbol, it must identify a
|
22
|
+
# previously registered ModelResolver instance.
|
23
|
+
def identify_as(*aliases, resolver: :default)
|
24
|
+
Mongoid::ModelResolver.resolver(resolver).register(self, *aliases)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -161,11 +161,6 @@ module Mongoid
|
|
161
161
|
# Execute the callbacks of given kind for embedded documents including
|
162
162
|
# around callbacks.
|
163
163
|
#
|
164
|
-
# @note This method is prone to stack overflow errors if the document
|
165
|
-
# has a large number of embedded documents. It is recommended to avoid
|
166
|
-
# using around callbacks for embedded documents until a proper solution
|
167
|
-
# is implemented.
|
168
|
-
#
|
169
164
|
# @param [ Symbol ] kind The type of callback to execute.
|
170
165
|
# @param [ Array<Document> ] children Children to execute callbacks on. If
|
171
166
|
# nil, callbacks will be executed on all cascadable children of
|
@@ -173,17 +168,27 @@ module Mongoid
|
|
173
168
|
#
|
174
169
|
# @api private
|
175
170
|
def _mongoid_run_child_callbacks_with_around(kind, children: nil, &block)
|
176
|
-
|
171
|
+
children = (children || cascadable_children(kind))
|
177
172
|
with_children = !Mongoid::Config.prevent_multiple_calls_of_embedded_callbacks
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
173
|
+
|
174
|
+
return block&.call if children.empty?
|
175
|
+
|
176
|
+
fibers = children.map do |child|
|
177
|
+
Fiber.new do
|
178
|
+
child.run_callbacks(child_callback_type(kind, child), with_children: with_children) do
|
179
|
+
Fiber.yield
|
180
|
+
end
|
185
181
|
end
|
186
182
|
end
|
183
|
+
|
184
|
+
fibers.each do |fiber|
|
185
|
+
fiber.resume
|
186
|
+
raise Mongoid::Errors::InvalidAroundCallback unless fiber.alive?
|
187
|
+
end
|
188
|
+
|
189
|
+
block&.call
|
190
|
+
|
191
|
+
fibers.reverse.each(&:resume)
|
187
192
|
end
|
188
193
|
|
189
194
|
# Execute the callbacks of given kind for embedded documents without
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Mongoid
|
6
|
+
# The default class for resolving model classes based on discriminant keys.
|
7
|
+
# Given a key, it will return the corresponding model class, if any. By
|
8
|
+
# default, it looks for classes with names that match the given keys, but
|
9
|
+
# additional mappings may be provided.
|
10
|
+
#
|
11
|
+
# It is also possible to instantiate multiple resolvers---and even implement
|
12
|
+
# your own---so that different sets of classes can use independent resolution
|
13
|
+
# mechanics.
|
14
|
+
class ModelResolver
|
15
|
+
# The mutex instance used to make the `.instance` method thread-safe.
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
INSTANCE_MUTEX = Mutex.new
|
19
|
+
|
20
|
+
class << self
|
21
|
+
extend Forwardable
|
22
|
+
def_delegators :instance, :register
|
23
|
+
|
24
|
+
# Returns the default instance of the ModelResolver.
|
25
|
+
#
|
26
|
+
# @return [ Mongoid::ModelResolver ] the default ModelResolver instance.
|
27
|
+
def instance
|
28
|
+
@instance ||= INSTANCE_MUTEX.synchronize { @instance ||= new }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the map of registered resolvers. The default resolver is not
|
32
|
+
# included here.
|
33
|
+
#
|
34
|
+
# @return [ Hash<Symbol => Mongoid::ModelResolver::Interface> ] the hash of
|
35
|
+
# resolver instances, mapped by symbol identifier.
|
36
|
+
def resolvers
|
37
|
+
@resolvers ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the resolver instance that corresponds to the argument.
|
41
|
+
#
|
42
|
+
# @param [ nil | true | false Symbol | String | Mongoid::ModelResolver::Interface ] identifier_or_object
|
43
|
+
# When nil or false, returns nil. When true or :default, corresponds to the default resolver.
|
44
|
+
# When any other symbol or string, corresponds to the registered resolver with that identifier.
|
45
|
+
# Otherwise, it must be a resolver instance itself.
|
46
|
+
#
|
47
|
+
# @raise Mongoid::Errors::UnrecognizedResolver if the given identifier is a
|
48
|
+
# symbol or string and it does not match any registered resolver.
|
49
|
+
#
|
50
|
+
# @return [ Mongoid::ModelResolver::Interface ] the resolver instance corresponding to the
|
51
|
+
# given argument.
|
52
|
+
def resolver(identifier_or_object = :default)
|
53
|
+
case identifier_or_object
|
54
|
+
when nil, false then nil
|
55
|
+
when true, :default then instance
|
56
|
+
when String, Symbol
|
57
|
+
resolvers.fetch(identifier_or_object.to_sym) do |key|
|
58
|
+
raise Mongoid::Errors::UnrecognizedResolver, key
|
59
|
+
end
|
60
|
+
else identifier_or_object
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Register the given resolver under the given name.
|
65
|
+
#
|
66
|
+
# @param [ Mongoid::ModelResolver::Interface ] resolver the resolver to register.
|
67
|
+
# @param [ String | Symbol ] name the identifier to use to register the resolver.
|
68
|
+
def register_resolver(resolver, name)
|
69
|
+
resolvers[name.to_sym] = resolver
|
70
|
+
self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Instantiates a new ModelResolver instance.
|
75
|
+
def initialize
|
76
|
+
@key_to_model = {}
|
77
|
+
@model_to_keys = {}
|
78
|
+
end
|
79
|
+
|
80
|
+
# Registers the given model class with the given keys. In addition to the given keys, the
|
81
|
+
# class name itself will be included as a key to identify the class. Keys are given in priority
|
82
|
+
# order, with highest priority keys first and lowest last. The class name, if not given explicitly,
|
83
|
+
# is always given lowest priority.
|
84
|
+
#
|
85
|
+
# If called more than once, newer keys have higher priority than older keys. All duplicate keys will
|
86
|
+
# be removed.
|
87
|
+
#
|
88
|
+
# @param [ Mongoid::Document ] klass the document class to register
|
89
|
+
# @param [ Array<String> ] *keys the list of keys to use as an alias (optional)
|
90
|
+
def register(klass, *keys)
|
91
|
+
default_key = klass.name
|
92
|
+
|
93
|
+
@model_to_keys[klass] = [ *keys, *@model_to_keys[klass], default_key ].uniq
|
94
|
+
@key_to_model[default_key] = klass
|
95
|
+
|
96
|
+
keys.each do |key|
|
97
|
+
@key_to_model[key] = klass
|
98
|
+
end
|
99
|
+
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# The `Interface` concern represents the interface that custom resolvers
|
104
|
+
# must implement.
|
105
|
+
concerning :Interface do
|
106
|
+
# Returns the default (highest priority) key for the given record. This is typically
|
107
|
+
# the key that will be used when saving a new polymorphic association.
|
108
|
+
#
|
109
|
+
# @param [ Mongoid::Document ] record the record instance for which to query the default key.
|
110
|
+
#
|
111
|
+
# @raise Mongoid::Errors::UnregisteredClass if the record's class has not been registered with this resolver.
|
112
|
+
#
|
113
|
+
# @return [ String ] the default key for the record's class.
|
114
|
+
def default_key_for(record)
|
115
|
+
keys_for(record).first
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the list of all keys for the given record's class, in priority order (with highest
|
119
|
+
# priority keys first).
|
120
|
+
#
|
121
|
+
# @param [ Mongoid::Document] record the record instance for which to query the registered keys.
|
122
|
+
#
|
123
|
+
# @raise Mongoid::Errors::UnregisteredClass if the record's class has not been registered with this resolver.
|
124
|
+
#
|
125
|
+
# @return [ Array<String> ] the list of keys that have been registered for the given class.
|
126
|
+
def keys_for(record)
|
127
|
+
@model_to_keys.fetch(record.class) do |klass|
|
128
|
+
# figure out which resolver this is
|
129
|
+
resolver = if self == Mongoid::ModelResolver.instance
|
130
|
+
:default
|
131
|
+
else
|
132
|
+
Mongoid::ModelResolver.resolvers.keys.detect { |k| Mongoid::ModelResolver.resolvers[k] == self }
|
133
|
+
end
|
134
|
+
resolver ||= self # if it hasn't been registered, we'll show it the best we can
|
135
|
+
raise Mongoid::Errors::UnregisteredClass.new(klass, resolver)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the document class that has been registered by the given key.
|
140
|
+
#
|
141
|
+
# @param [ String ] key the key by which to query the corresponding class.
|
142
|
+
#
|
143
|
+
# @raise Mongoid::Errors::UnrecognizedModelAlias if the given key has not
|
144
|
+
# been registered with this resolver.
|
145
|
+
#
|
146
|
+
# @return [ Class ] the document class that has been registered with the given key.
|
147
|
+
def model_for(key)
|
148
|
+
@key_to_model.fetch(key) do
|
149
|
+
raise Mongoid::Errors::UnrecognizedModelAlias, key
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -25,7 +25,8 @@ module Mongoid
|
|
25
25
|
# @return [ Array<Symbol> ] The list of extra options besides client options
|
26
26
|
# that determine the persistence context.
|
27
27
|
EXTRA_OPTIONS = [ :client,
|
28
|
-
:collection
|
28
|
+
:collection,
|
29
|
+
:collection_options
|
29
30
|
].freeze
|
30
31
|
|
31
32
|
# The full list of valid persistence context options.
|
data/lib/mongoid/scopable.rb
CHANGED
@@ -294,7 +294,13 @@ module Mongoid
|
|
294
294
|
scope = instance_exec(*args, **kwargs, &scoping[:scope])
|
295
295
|
extension = scoping[:extension]
|
296
296
|
to_merge = scope || queryable
|
297
|
-
|
297
|
+
|
298
|
+
criteria = if Mongoid.allow_scopes_to_unset_default_scope
|
299
|
+
to_merge
|
300
|
+
else
|
301
|
+
to_merge.empty_and_chainable? ? to_merge : with_default_scope.merge(to_merge)
|
302
|
+
end
|
303
|
+
|
298
304
|
criteria.extend(extension)
|
299
305
|
criteria
|
300
306
|
end
|
data/lib/mongoid/touchable.rb
CHANGED
@@ -78,7 +78,7 @@ module Mongoid
|
|
78
78
|
field = database_field_name(field)
|
79
79
|
|
80
80
|
write_attribute(:updated_at, now) if respond_to?("updated_at=")
|
81
|
-
write_attribute(field, now) if field
|
81
|
+
write_attribute(field, now) if field.present?
|
82
82
|
|
83
83
|
touches = _extract_touches_from_atomic_sets(field) || {}
|
84
84
|
touches.merge!(_parent._gather_touch_updates(now) || {}) if _touchable_parent?
|
@@ -212,12 +212,6 @@ module Mongoid
|
|
212
212
|
#
|
213
213
|
# @return [ Symbol ] The method name.
|
214
214
|
def define_relation_touch_method(name, association)
|
215
|
-
relation_classes = if association.polymorphic?
|
216
|
-
association.send(:inverse_association_classes)
|
217
|
-
else
|
218
|
-
[ association.relation_class ]
|
219
|
-
end
|
220
|
-
|
221
215
|
method_name = "touch_#{name}_after_create_or_destroy"
|
222
216
|
association.inverse_class.class_eval do
|
223
217
|
define_method(method_name) do
|
data/lib/mongoid/traversable.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'mongoid/fields/validators/macro'
|
4
|
+
require 'mongoid/model_resolver'
|
4
5
|
|
5
6
|
module Mongoid
|
6
7
|
# Mixin module included in Mongoid::Document to provide behavior
|
@@ -32,6 +33,10 @@ module Mongoid
|
|
32
33
|
# rubocop:disable Metrics/AbcSize
|
33
34
|
def inherited(subclass)
|
34
35
|
super
|
36
|
+
|
37
|
+
# Register the new subclass with the resolver subsystem
|
38
|
+
Mongoid::ModelResolver.register(subclass)
|
39
|
+
|
35
40
|
@_type = nil
|
36
41
|
subclass.aliased_fields = aliased_fields.dup
|
37
42
|
subclass.localized_fields = localized_fields.dup
|
data/lib/mongoid/version.rb
CHANGED
@@ -2,8 +2,40 @@
|
|
2
2
|
# rubocop:todo all
|
3
3
|
|
4
4
|
require 'spec_helper'
|
5
|
+
require 'support/feature_sandbox'
|
6
|
+
|
5
7
|
require_relative '../../mongoid/association/referenced/has_one_models'
|
6
8
|
|
9
|
+
def quarantine(context, polymorphic:, dept_aliases:, team_aliases:)
|
10
|
+
state = {}
|
11
|
+
|
12
|
+
context.before(:context) do
|
13
|
+
state[:quarantine] = FeatureSandbox.start_quarantine
|
14
|
+
|
15
|
+
# Have to eval this, because otherwise we get syntax errors when defining a class
|
16
|
+
# inside a method.
|
17
|
+
#
|
18
|
+
# I know the scissors are sharp! But I want to run with them anwyay!
|
19
|
+
Object.class_eval <<-RUBY
|
20
|
+
class SandboxManager; include Mongoid::Document; end
|
21
|
+
class SandboxDepartment; include Mongoid::Document; end
|
22
|
+
class SandboxTeam; include Mongoid::Document; end
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
SandboxManager.belongs_to :unit, polymorphic: polymorphic
|
26
|
+
|
27
|
+
SandboxDepartment.identify_as *dept_aliases, resolver: polymorphic
|
28
|
+
SandboxDepartment.has_many :sandbox_managers, as: :unit
|
29
|
+
|
30
|
+
SandboxTeam.identify_as *team_aliases, resolver: polymorphic
|
31
|
+
SandboxTeam.has_one :sandbox_manager, as: :unit
|
32
|
+
end
|
33
|
+
|
34
|
+
context.after(:context) do
|
35
|
+
FeatureSandbox.end_quarantine(state[:quarantine])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
7
39
|
describe 'belongs_to associations' do
|
8
40
|
context 'referencing top level classes when source class is namespaced' do
|
9
41
|
let(:college) { HomCollege.create! }
|
@@ -31,4 +63,101 @@ describe 'belongs_to associations' do
|
|
31
63
|
expect(instance.movie).to eq movie
|
32
64
|
end
|
33
65
|
end
|
66
|
+
|
67
|
+
context 'when the association is polymorphic' do
|
68
|
+
let(:dept_manager) { SandboxManager.create(unit: department) }
|
69
|
+
let(:team_manager) { SandboxManager.create(unit: team) }
|
70
|
+
let(:department) { SandboxDepartment.create }
|
71
|
+
let(:team) { SandboxTeam.create }
|
72
|
+
|
73
|
+
shared_context 'it finds the associated records' do
|
74
|
+
it 'successfully finds the manager\'s unit' do
|
75
|
+
expect(dept_manager.reload.unit).to be == department
|
76
|
+
expect(team_manager.reload.unit).to be == team
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'successfully finds the unit\'s manager' do
|
80
|
+
dept_manager; team_manager # make sure these are created first...
|
81
|
+
|
82
|
+
expect(department.reload.sandbox_managers).to be == [ dept_manager ]
|
83
|
+
expect(team.reload.sandbox_manager).to be == team_manager
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
shared_context 'it searches for alternative aliases' do
|
88
|
+
it 'successfully finds the corresponding unit when unit_type is a different alias' do
|
89
|
+
dept_manager.update unit_type: 'sandbox_dept'
|
90
|
+
dept_manager.reload
|
91
|
+
|
92
|
+
team_manager.update unit_type: 'group'
|
93
|
+
team_manager.reload
|
94
|
+
|
95
|
+
expect(dept_manager.reload.unit_type).to be == 'sandbox_dept'
|
96
|
+
expect(dept_manager.unit).to be == department
|
97
|
+
|
98
|
+
expect(team_manager.reload.unit_type).to be == 'group'
|
99
|
+
expect(team_manager.unit).to be == team
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when the association uses the default resolver' do
|
104
|
+
context 'when there are no aliases given' do
|
105
|
+
quarantine(self, polymorphic: true, dept_aliases: [], team_aliases: [])
|
106
|
+
|
107
|
+
it 'populates the unit_type with the class name' do
|
108
|
+
expect(dept_manager.unit_type).to be == 'SandboxDepartment'
|
109
|
+
expect(team_manager.unit_type).to be == 'SandboxTeam'
|
110
|
+
end
|
111
|
+
|
112
|
+
it_behaves_like 'it finds the associated records'
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when there are multiple aliases given' do
|
116
|
+
quarantine(self, polymorphic: true, dept_aliases: %w[ dept sandbox_dept ], team_aliases: %w[ team group ])
|
117
|
+
|
118
|
+
it 'populates the unit_type with the first alias' do
|
119
|
+
expect(dept_manager.unit_type).to be == 'dept'
|
120
|
+
expect(team_manager.unit_type).to be == 'team'
|
121
|
+
end
|
122
|
+
|
123
|
+
it_behaves_like 'it finds the associated records'
|
124
|
+
it_behaves_like 'it searches for alternative aliases'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when the association uses a registered resolver' do
|
129
|
+
before(:context) { Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :sandbox }
|
130
|
+
quarantine(self, polymorphic: :sandbox, dept_aliases: %w[ dept sandbox_dept ], team_aliases: %w[ team group ])
|
131
|
+
|
132
|
+
it 'does not include the aliases in the default resolver' do
|
133
|
+
expect(Mongoid::ModelResolver.instance.keys_for(SandboxDepartment.new)).not_to include('dept')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'populates the unit_type with the first alias' do
|
137
|
+
expect(dept_manager.unit_type).to be == 'dept'
|
138
|
+
expect(team_manager.unit_type).to be == 'team'
|
139
|
+
end
|
140
|
+
|
141
|
+
it_behaves_like 'it finds the associated records'
|
142
|
+
it_behaves_like 'it searches for alternative aliases'
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when the association uses an unregistered resolver' do
|
146
|
+
quarantine(self, polymorphic: Mongoid::ModelResolver.new,
|
147
|
+
dept_aliases: %w[ dept sandbox_dept ],
|
148
|
+
team_aliases: %w[ team group ])
|
149
|
+
|
150
|
+
it 'does not include the aliases in the default resolver' do
|
151
|
+
expect(Mongoid::ModelResolver.instance.keys_for(SandboxDepartment.new)).not_to include('dept')
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'populates the unit_type with the first alias' do
|
155
|
+
expect(dept_manager.unit_type).to be == 'dept'
|
156
|
+
expect(team_manager.unit_type).to be == 'team'
|
157
|
+
end
|
158
|
+
|
159
|
+
it_behaves_like 'it finds the associated records'
|
160
|
+
it_behaves_like 'it searches for alternative aliases'
|
161
|
+
end
|
162
|
+
end
|
34
163
|
end
|