mongoid 7.0.3 → 7.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +1 -0
- data/LICENSE +1 -0
- data/README.md +3 -2
- data/Rakefile +12 -0
- data/lib/mongoid.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/association/relatable.rb +23 -21
- data/lib/mongoid/atomic.rb +13 -3
- data/lib/mongoid/atomic/paths/embedded.rb +1 -1
- data/lib/mongoid/attributes.rb +28 -20
- data/lib/mongoid/attributes/dynamic.rb +15 -14
- data/lib/mongoid/config/environment.rb +21 -8
- data/lib/mongoid/copyable.rb +5 -1
- data/lib/mongoid/criteria.rb +7 -1
- data/lib/mongoid/criteria/modifiable.rb +13 -2
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
- data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
- data/lib/mongoid/criteria/queryable/key.rb +67 -8
- data/lib/mongoid/criteria/queryable/mergeable.rb +5 -4
- data/lib/mongoid/criteria/queryable/selectable.rb +3 -4
- data/lib/mongoid/criteria/queryable/selector.rb +9 -31
- data/lib/mongoid/extensions/hash.rb +4 -2
- data/lib/mongoid/extensions/regexp.rb +1 -1
- data/lib/mongoid/extensions/string.rb +5 -3
- data/lib/mongoid/fields.rb +2 -1
- data/lib/mongoid/matchable.rb +14 -15
- data/lib/mongoid/matchable/all.rb +4 -3
- data/lib/mongoid/matchable/default.rb +71 -24
- data/lib/mongoid/matchable/regexp.rb +2 -2
- data/lib/mongoid/persistable/pushable.rb +11 -2
- data/lib/mongoid/persistence_context.rb +6 -6
- data/lib/mongoid/positional.rb +1 -1
- data/lib/mongoid/query_cache.rb +3 -2
- data/lib/mongoid/validatable/macros.rb +1 -1
- data/lib/mongoid/validatable/uniqueness.rb +1 -1
- data/lib/mongoid/version.rb +2 -1
- data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
- data/spec/README.md +18 -0
- data/spec/app/models/delegating_patient.rb +16 -0
- data/spec/app/models/other_owner_object.rb +2 -0
- data/spec/integration/app_spec.rb +192 -0
- data/spec/integration/associations/embedded_spec.rb +62 -0
- data/spec/integration/criteria/time_with_zone_spec.rb +32 -0
- data/spec/integration/document_spec.rb +22 -0
- data/spec/integration/matchable_spec.rb +680 -0
- data/spec/lite_spec_helper.rb +15 -5
- data/spec/mongoid/association/embedded/embedded_in_spec.rb +58 -0
- data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
- data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
- data/spec/mongoid/association/embedded/embeds_one_dnl_models.rb +6 -0
- data/spec/mongoid/association/embedded/embeds_one_models.rb +51 -0
- data/spec/mongoid/association/embedded/embeds_one_spec.rb +46 -0
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +23 -6
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +2 -1
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
- data/spec/mongoid/association/referenced/has_one_spec.rb +12 -2
- data/spec/mongoid/attributes/dynamic_spec.rb +153 -0
- data/spec/mongoid/attributes_spec.rb +19 -7
- data/spec/mongoid/clients/factory_spec.rb +2 -2
- data/spec/mongoid/clients/options_spec.rb +4 -4
- data/spec/mongoid/clients/sessions_spec.rb +20 -7
- data/spec/mongoid/clients/transactions_spec.rb +36 -15
- data/spec/mongoid/clients_spec.rb +2 -2
- data/spec/mongoid/contextual/atomic_spec.rb +20 -10
- data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
- data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
- data/spec/mongoid/contextual/mongo_spec.rb +76 -53
- data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
- data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +54 -0
- data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
- data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/key_spec.rb +48 -6
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +762 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +5 -224
- data/spec/mongoid/criteria/queryable/selector_spec.rb +37 -0
- data/spec/mongoid/criteria_spec.rb +7 -2
- data/spec/mongoid/document_fields_spec.rb +88 -0
- data/spec/mongoid/document_persistence_context_spec.rb +33 -0
- data/spec/mongoid/extensions/string_spec.rb +35 -7
- data/spec/mongoid/indexable_spec.rb +6 -4
- data/spec/mongoid/matchable/default_spec.rb +10 -3
- data/spec/mongoid/matchable/regexp_spec.rb +2 -2
- data/spec/mongoid/matchable_spec.rb +2 -2
- data/spec/mongoid/persistable/pushable_spec.rb +55 -1
- data/spec/mongoid/query_cache_spec.rb +2 -1
- data/spec/mongoid/relations/proxy_spec.rb +1 -1
- data/spec/mongoid/scopable_spec.rb +2 -1
- data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
- data/spec/mongoid/tasks/database_spec.rb +1 -1
- data/spec/mongoid/validatable/uniqueness_spec.rb +33 -6
- data/spec/spec_helper.rb +4 -37
- data/spec/support/child_process_helper.rb +76 -0
- data/spec/support/cluster_config.rb +158 -0
- data/spec/support/constraints.rb +29 -19
- data/spec/support/expectations.rb +17 -3
- data/spec/support/spec_config.rb +12 -4
- metadata +525 -464
- metadata.gz.sig +2 -0
@@ -58,7 +58,7 @@ module Mongoid
|
|
58
58
|
#
|
59
59
|
# @since 1.0.0
|
60
60
|
def __numeric__(object)
|
61
|
-
object.to_s =~ /(
|
61
|
+
object.to_s =~ /(\A[-+]?[0-9]+\z)|(\.0+\z)|(\.\z)/ ? object.to_i : Float(object)
|
62
62
|
end
|
63
63
|
|
64
64
|
# Evolve the object to an integer.
|
@@ -10,7 +10,7 @@ module Mongoid
|
|
10
10
|
# Is the object a regexp?
|
11
11
|
#
|
12
12
|
# @example Is the object a regex?
|
13
|
-
#
|
13
|
+
# /\A[123]/.regexp?
|
14
14
|
#
|
15
15
|
# @return [ true ] Always true.
|
16
16
|
#
|
@@ -22,7 +22,7 @@ module Mongoid
|
|
22
22
|
# Evolve the object into a regex.
|
23
23
|
#
|
24
24
|
# @example Evolve the object to a regex.
|
25
|
-
# Regexp.evolve("
|
25
|
+
# Regexp.evolve("\A[123]")
|
26
26
|
#
|
27
27
|
# @param [ Regexp, String ] object The object to evolve.
|
28
28
|
#
|
@@ -53,7 +53,7 @@ module Mongoid
|
|
53
53
|
# Evolve the object into a raw bson regex.
|
54
54
|
#
|
55
55
|
# @example Evolve the object to a regex.
|
56
|
-
# BSON::Regexp::Raw.evolve("
|
56
|
+
# BSON::Regexp::Raw.evolve("\\A[123]")
|
57
57
|
#
|
58
58
|
# @param [ BSON::Regexp::Raw, String ] object The object to evolve.
|
59
59
|
#
|
@@ -3,16 +3,75 @@ module Mongoid
|
|
3
3
|
class Criteria
|
4
4
|
module Queryable
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# Key objects represent specifications for building query expressions
|
7
|
+
# utilizing MongoDB selectors.
|
8
|
+
#
|
9
|
+
# Simple key-value conditions are translated directly into expression
|
10
|
+
# hashes by Mongoid without utilizing Key objects. For example, the
|
11
|
+
# following condition:
|
12
|
+
#
|
13
|
+
# Foo.where(price: 1)
|
14
|
+
#
|
15
|
+
# ... is translated to the following simple expression:
|
16
|
+
#
|
17
|
+
# {price: 1}
|
18
|
+
#
|
19
|
+
# More complex conditions would start involving Key objects. For example:
|
20
|
+
#
|
21
|
+
# Foo.where(:price.gt => 1)
|
22
|
+
#
|
23
|
+
# ... causes a Key instance to be created thusly:
|
24
|
+
#
|
25
|
+
# Key.new(:price, :__override__, '$gt')
|
26
|
+
#
|
27
|
+
# This Key instance utilizes +operator+ but not +expanded+ nor +block+.
|
28
|
+
# The corresponding MongoDB query expression is:
|
29
|
+
#
|
30
|
+
# {price: {'$gt' => 1}}
|
31
|
+
#
|
32
|
+
# A yet more more complex example is the following condition:
|
33
|
+
#
|
34
|
+
# Foo.geo_spacial(:boundary.intersects_point => [1, 10])
|
35
|
+
#
|
36
|
+
# Processing this condition will cause a Key instance to be created as
|
37
|
+
# follows:
|
38
|
+
#
|
39
|
+
# Key.new(:location, :__override__, '$geoIntersects', '$geometry') do |value|
|
40
|
+
# { "type" => POINT, "coordinates" => value }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# ... eventually producing the following MongoDB query expression:
|
44
|
+
#
|
45
|
+
# {
|
46
|
+
# boundary: {
|
47
|
+
# '$geoIntersects' => {
|
48
|
+
# '$geometry' => {
|
49
|
+
# type: "Point" ,
|
50
|
+
# coordinates: [ 1, 10 ]
|
51
|
+
# }
|
52
|
+
# }
|
53
|
+
# }
|
54
|
+
# }
|
55
|
+
#
|
56
|
+
# Key instances can be thought of as procs that map a value to the
|
57
|
+
# MongoDB query expression required to obtain the key's condition,
|
58
|
+
# given the value.
|
8
59
|
class Key
|
9
60
|
|
10
|
-
# @
|
11
|
-
|
12
|
-
|
13
|
-
# @
|
14
|
-
|
15
|
-
|
61
|
+
# @return [ String | Symbol ] The name of the field.
|
62
|
+
attr_reader :name
|
63
|
+
|
64
|
+
# @return [ String ] The MongoDB query operator.
|
65
|
+
attr_reader :operator
|
66
|
+
|
67
|
+
# @return [ String ] The MongoDB expanded query operator.
|
68
|
+
attr_reader :expanded
|
69
|
+
|
70
|
+
# @return [ Symbol ] The name of the merge strategy.
|
71
|
+
attr_reader :strategy
|
72
|
+
|
73
|
+
# @return [ Proc ] The optional block to transform values.
|
74
|
+
attr_reader :block
|
16
75
|
|
17
76
|
# Does the key equal another object?
|
18
77
|
#
|
@@ -45,7 +45,7 @@ module Mongoid
|
|
45
45
|
use(:__union__)
|
46
46
|
end
|
47
47
|
|
48
|
-
#
|
48
|
+
# Clear the current strategy and negating flag, used after cloning.
|
49
49
|
#
|
50
50
|
# @example Reset the strategies.
|
51
51
|
# mergeable.reset_strategies!
|
@@ -54,7 +54,8 @@ module Mongoid
|
|
54
54
|
#
|
55
55
|
# @since 1.0.0
|
56
56
|
def reset_strategies!
|
57
|
-
self.strategy
|
57
|
+
self.strategy = nil
|
58
|
+
self.negating = nil
|
58
59
|
end
|
59
60
|
|
60
61
|
private
|
@@ -167,7 +168,7 @@ module Mongoid
|
|
167
168
|
# @example Add the criterion.
|
168
169
|
# mergeable.__override__([ 1, 2 ], "$in")
|
169
170
|
#
|
170
|
-
# @param [ Hash ] criterion The criteria.
|
171
|
+
# @param [ Hash | Criteria ] criterion The criteria.
|
171
172
|
# @param [ String ] operator The MongoDB operator.
|
172
173
|
#
|
173
174
|
# @return [ Mergeable ] The new mergeable.
|
@@ -225,7 +226,7 @@ module Mongoid
|
|
225
226
|
# @api private
|
226
227
|
#
|
227
228
|
# @example Add criterion with a strategy.
|
228
|
-
# mergeable.with_strategy(:__union__, [ 1, 2, 3 ], "$in")
|
229
|
+
# mergeable.with_strategy(:__union__, {field_name: [ 1, 2, 3 ]}, "$in")
|
229
230
|
#
|
230
231
|
# @param [ Symbol ] strategy The name of the strategy method.
|
231
232
|
# @param [ Object ] criterion The criterion to add.
|
@@ -24,7 +24,7 @@ module Mongoid
|
|
24
24
|
# @since 2.0.0
|
25
25
|
POLYGON = "Polygon"
|
26
26
|
|
27
|
-
# @attribute [rw] negating If the next
|
27
|
+
# @attribute [rw] negating If the next expression is negated.
|
28
28
|
# @attribute [rw] selector The query selector.
|
29
29
|
attr_accessor :negating, :selector
|
30
30
|
|
@@ -434,7 +434,7 @@ module Mongoid
|
|
434
434
|
# @since 1.0.0
|
435
435
|
def not(*criterion)
|
436
436
|
if criterion.empty?
|
437
|
-
tap { |query| query.negating = true }
|
437
|
+
dup.tap { |query| query.negating = true }
|
438
438
|
else
|
439
439
|
__override__(criterion.first, "$not")
|
440
440
|
end
|
@@ -630,8 +630,6 @@ module Mongoid
|
|
630
630
|
# Take the provided criterion and store it as a selection in the query
|
631
631
|
# selector.
|
632
632
|
#
|
633
|
-
# @api private
|
634
|
-
#
|
635
633
|
# @example Store the selection.
|
636
634
|
# selectable.selection({ field: "value" })
|
637
635
|
#
|
@@ -640,6 +638,7 @@ module Mongoid
|
|
640
638
|
# @return [ Selectable ] The cloned selectable.
|
641
639
|
#
|
642
640
|
# @since 1.0.0
|
641
|
+
# @api private
|
643
642
|
def selection(criterion = nil)
|
644
643
|
clone.tap do |query|
|
645
644
|
if criterion
|
@@ -21,9 +21,10 @@ module Mongoid
|
|
21
21
|
other.each_pair do |key, value|
|
22
22
|
if value.is_a?(Hash) && self[key.to_s].is_a?(Hash)
|
23
23
|
value = self[key.to_s].merge(value) do |_key, old_val, new_val|
|
24
|
-
|
24
|
+
case _key
|
25
|
+
when '$in'
|
25
26
|
new_val & old_val
|
26
|
-
|
27
|
+
when '$nin'
|
27
28
|
(old_val + new_val).uniq
|
28
29
|
else
|
29
30
|
new_val
|
@@ -52,10 +53,13 @@ module Mongoid
|
|
52
53
|
def store(key, value)
|
53
54
|
name, serializer = storage_pair(key)
|
54
55
|
if multi_selection?(name)
|
55
|
-
|
56
|
+
store_name = name
|
57
|
+
store_value = evolve_multi(value)
|
56
58
|
else
|
57
|
-
|
59
|
+
store_name = localized_key(name, serializer)
|
60
|
+
store_value = evolve(serializer, value)
|
58
61
|
end
|
62
|
+
super(store_name, store_value)
|
59
63
|
end
|
60
64
|
alias :[]= :store
|
61
65
|
|
@@ -178,33 +182,7 @@ module Mongoid
|
|
178
182
|
#
|
179
183
|
# @since 1.0.0
|
180
184
|
def multi_selection?(key)
|
181
|
-
|
182
|
-
end
|
183
|
-
|
184
|
-
# Determines if the selection operator takes a list. Returns true for $in and $nin.
|
185
|
-
#
|
186
|
-
# @api private
|
187
|
-
#
|
188
|
-
# @example Does the selection operator take multiple values?
|
189
|
-
# selector.multi_value?("$nin")
|
190
|
-
#
|
191
|
-
# @param [ String ] key The key to check.
|
192
|
-
#
|
193
|
-
# @return [ true, false ] If the key is $in or $nin.
|
194
|
-
#
|
195
|
-
# @since 2.1.1
|
196
|
-
def multi_value?(key)
|
197
|
-
key =~ /\$nin|\$in/
|
198
|
-
end
|
199
|
-
|
200
|
-
private
|
201
|
-
|
202
|
-
def in?(key)
|
203
|
-
key =~ /\$in/
|
204
|
-
end
|
205
|
-
|
206
|
-
def nin?(key)
|
207
|
-
key =~ /\$nin/
|
185
|
+
%w($and $or $nor).include?(key)
|
208
186
|
end
|
209
187
|
end
|
210
188
|
end
|
@@ -46,9 +46,11 @@ module Mongoid
|
|
46
46
|
value.each_pair do |_key, _value|
|
47
47
|
value[_key] = (key == "$rename") ? _value.to_s : mongoize_for(key, klass, _key, _value)
|
48
48
|
end
|
49
|
-
|
49
|
+
consolidated[key] ||= {}
|
50
|
+
consolidated[key].update(value)
|
50
51
|
else
|
51
|
-
|
52
|
+
consolidated["$set"] ||= {}
|
53
|
+
consolidated["$set"].update(key => mongoize_for(key, klass, key, value))
|
52
54
|
end
|
53
55
|
end
|
54
56
|
consolidated
|
@@ -69,7 +69,7 @@ module Mongoid
|
|
69
69
|
#
|
70
70
|
# @since 2.3.1
|
71
71
|
def mongoid_id?
|
72
|
-
self =~ /\A(|_)id
|
72
|
+
self =~ /\A(|_)id\z/
|
73
73
|
end
|
74
74
|
|
75
75
|
# Is the string a number? The literals "NaN", "Infinity", and "-Infinity"
|
@@ -82,7 +82,9 @@ module Mongoid
|
|
82
82
|
#
|
83
83
|
# @since 3.0.0
|
84
84
|
def numeric?
|
85
|
-
|
85
|
+
!!Float(self)
|
86
|
+
rescue ArgumentError
|
87
|
+
(self =~ /\A(?:NaN|-?Infinity)\z/) == 0
|
86
88
|
end
|
87
89
|
|
88
90
|
# Get the string as a getter string.
|
@@ -94,7 +96,7 @@ module Mongoid
|
|
94
96
|
#
|
95
97
|
# @since 1.0.0
|
96
98
|
def reader
|
97
|
-
delete("=").sub(/\_before\_type\_cast
|
99
|
+
delete("=").sub(/\_before\_type\_cast\z/, '')
|
98
100
|
end
|
99
101
|
|
100
102
|
# Is this string a writer?
|
data/lib/mongoid/fields.rb
CHANGED
@@ -498,7 +498,8 @@ module Mongoid
|
|
498
498
|
def create_translations_getter(name, meth)
|
499
499
|
generated_methods.module_eval do
|
500
500
|
re_define_method("#{meth}_translations") do
|
501
|
-
|
501
|
+
attributes[name] ||= {}
|
502
|
+
attributes[name].with_indifferent_access
|
502
503
|
end
|
503
504
|
alias_method :"#{meth}_t", :"#{meth}_translations"
|
504
505
|
end
|
data/lib/mongoid/matchable.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require "mongoid/matchable/default"
|
3
3
|
require "mongoid/matchable/all"
|
4
4
|
require "mongoid/matchable/and"
|
5
|
+
require "mongoid/matchable/elem_match"
|
5
6
|
require "mongoid/matchable/eq"
|
6
7
|
require "mongoid/matchable/exists"
|
7
8
|
require "mongoid/matchable/gt"
|
@@ -11,15 +12,14 @@ require "mongoid/matchable/lt"
|
|
11
12
|
require "mongoid/matchable/lte"
|
12
13
|
require "mongoid/matchable/ne"
|
13
14
|
require "mongoid/matchable/nin"
|
14
|
-
require "mongoid/matchable/or"
|
15
15
|
require "mongoid/matchable/nor"
|
16
|
-
require "mongoid/matchable/
|
17
|
-
require "mongoid/matchable/elem_match"
|
16
|
+
require "mongoid/matchable/or"
|
18
17
|
require "mongoid/matchable/regexp"
|
18
|
+
require "mongoid/matchable/size"
|
19
19
|
|
20
20
|
module Mongoid
|
21
21
|
|
22
|
-
# This module contains all the behavior for
|
22
|
+
# This module contains all the behavior for Ruby implementations of MongoDB
|
23
23
|
# selectors.
|
24
24
|
#
|
25
25
|
# @since 4.0.0
|
@@ -31,8 +31,8 @@ module Mongoid
|
|
31
31
|
# @since 1.0.0
|
32
32
|
MATCHERS = {
|
33
33
|
"$all" => All,
|
34
|
-
"$elemMatch" => ElemMatch,
|
35
34
|
"$and" => And,
|
35
|
+
"$elemMatch" => ElemMatch,
|
36
36
|
"$eq" => Eq,
|
37
37
|
"$exists" => Exists,
|
38
38
|
"$gt" => Gt,
|
@@ -42,8 +42,8 @@ module Mongoid
|
|
42
42
|
"$lte" => Lte,
|
43
43
|
"$ne" => Ne,
|
44
44
|
"$nin" => Nin,
|
45
|
-
"$or" => Or,
|
46
45
|
"$nor" => Nor,
|
46
|
+
"$or" => Or,
|
47
47
|
"$size" => Size,
|
48
48
|
}.with_indifferent_access.freeze
|
49
49
|
|
@@ -64,13 +64,13 @@ module Mongoid
|
|
64
64
|
value.each do |item|
|
65
65
|
if item[0].to_s == "$not".freeze
|
66
66
|
item = item[1]
|
67
|
-
return false if matcher(
|
67
|
+
return false if matcher(key, item)._matches?(item)
|
68
68
|
else
|
69
|
-
return false unless matcher(
|
69
|
+
return false unless matcher(key, Hash[*item])._matches?(Hash[*item])
|
70
70
|
end
|
71
71
|
end
|
72
72
|
else
|
73
|
-
return false unless matcher(
|
73
|
+
return false unless matcher(key, value)._matches?(value)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
true
|
@@ -81,20 +81,18 @@ module Mongoid
|
|
81
81
|
# Get the matcher for the supplied key and value. Will determine the class
|
82
82
|
# name from the key.
|
83
83
|
#
|
84
|
-
# @api private
|
85
|
-
#
|
86
84
|
# @example Get the matcher.
|
87
85
|
# document.matcher(:title, { "$in" => [ "test" ] })
|
88
86
|
#
|
89
|
-
# @param [ Document ] document The document to check.
|
90
87
|
# @param [ Symbol, String ] key The field name.
|
91
88
|
# @param [ Object, Hash ] value The value or selector.
|
92
89
|
#
|
93
90
|
# @return [ Matcher ] The matcher.
|
94
91
|
#
|
95
92
|
# @since 2.0.0.rc.7
|
96
|
-
|
97
|
-
|
93
|
+
# @api private
|
94
|
+
def matcher(key, value)
|
95
|
+
Matchable.matcher(self, key, value)
|
98
96
|
end
|
99
97
|
|
100
98
|
class << self
|
@@ -105,7 +103,7 @@ module Mongoid
|
|
105
103
|
# @api private
|
106
104
|
#
|
107
105
|
# @example Get the matcher.
|
108
|
-
#
|
106
|
+
# Matchable.matcher(document, :title, { "$in" => [ "test" ] })
|
109
107
|
#
|
110
108
|
# @param [ Document ] document The document to check.
|
111
109
|
# @param [ Symbol, String ] key The field name.
|
@@ -149,6 +147,7 @@ module Mongoid
|
|
149
147
|
# @return [ Object ] The value of the attribute.
|
150
148
|
#
|
151
149
|
# @since 2.2.1
|
150
|
+
# @api private
|
152
151
|
def extract_attribute(document, key)
|
153
152
|
if (key_string = key.to_s) =~ /.+\..+/
|
154
153
|
key_string.split('.').inject(document.send(:as_attributes)) do |_attribs, _key|
|
@@ -10,11 +10,12 @@ module Mongoid
|
|
10
10
|
# @example Do the values match?
|
11
11
|
# matcher._matches?({ :key => 10 })
|
12
12
|
#
|
13
|
-
# @param [ Hash ]
|
13
|
+
# @param [ Hash ] condition The condition to evaluate. This must be
|
14
|
+
# a one-element hash like {'$gt' => 1}.
|
14
15
|
#
|
15
16
|
# @return [ true, false ] If the values match.
|
16
|
-
def _matches?(
|
17
|
-
first =
|
17
|
+
def _matches?(condition)
|
18
|
+
first = condition_value(condition)
|
18
19
|
return false if first.is_a?(Array) && first.empty?
|
19
20
|
|
20
21
|
attribute_array = Array.wrap(@attribute)
|
@@ -20,52 +20,99 @@ module Mongoid
|
|
20
20
|
@attribute, @document = attribute, document
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
24
|
-
#
|
23
|
+
# Checks whether the attribute matches the value, using the default
|
24
|
+
# MongoDB matching logic (i.e., when no operator is specified in the
|
25
|
+
# criteria).
|
25
26
|
#
|
26
|
-
#
|
27
|
-
#
|
27
|
+
# If attribute and value are both of basic types like string or number,
|
28
|
+
# this method returns true if and only if the attribute equals the value.
|
28
29
|
#
|
29
|
-
#
|
30
|
+
# Value can also be of a type like Regexp or Range which defines
|
31
|
+
# more complex matching/inclusion behavior via the === operator.
|
32
|
+
# If so, and attribute is still of a basic type like string or number,
|
33
|
+
# this method returns true if and only if the value's === operator
|
34
|
+
# returns true for the attribute. For example, this method returns true
|
35
|
+
# if attribute is a string and value is a Regexp and attribute matches
|
36
|
+
# the value, of if attribute is a number and value is a Range and
|
37
|
+
# the value includes the attribute.
|
30
38
|
#
|
31
|
-
#
|
39
|
+
# If attribute is an array and value is not an array, the checks just
|
40
|
+
# described (i.e. the === operator invocation) are performed on each item
|
41
|
+
# of the attribute array. If any of the items in the attribute match
|
42
|
+
# the value according to the value type's === operator, this method
|
43
|
+
# returns true.
|
44
|
+
#
|
45
|
+
# If attribute and value are both arrays, this method returns true if and
|
46
|
+
# only if the arrays are equal (including the order of the elements).
|
47
|
+
#
|
48
|
+
# @param [ Object ] value The value to check.
|
49
|
+
#
|
50
|
+
# @return [ true, false ] True if attribute matches the value, false if not.
|
32
51
|
#
|
33
52
|
# @since 1.0.0
|
34
53
|
def _matches?(value)
|
35
|
-
attribute.is_a?(Array) && !value.is_a?(Array)
|
54
|
+
if attribute.is_a?(Array) && !value.is_a?(Array)
|
55
|
+
attribute.any? { |_attribute| value === _attribute }
|
56
|
+
else
|
57
|
+
value === attribute
|
58
|
+
end
|
36
59
|
end
|
37
60
|
|
38
61
|
protected
|
39
62
|
|
40
|
-
#
|
63
|
+
# Given a condition, which is a one-element hash consisting of an
|
64
|
+
# operator and a value like {'$gt' => 1}, return the value.
|
41
65
|
#
|
42
|
-
# @example Get the
|
43
|
-
# matcher.
|
66
|
+
# @example Get the condition value.
|
67
|
+
# matcher.condition_value({'$gt' => 1})
|
68
|
+
# # => 1
|
44
69
|
#
|
45
|
-
# @param [ Hash ]
|
70
|
+
# @param [ Hash ] condition The condition.
|
46
71
|
#
|
47
|
-
# @return [ Object ] The
|
72
|
+
# @return [ Object ] The value of the condition.
|
48
73
|
#
|
49
74
|
# @since 1.0.0
|
50
|
-
def
|
51
|
-
|
75
|
+
def condition_value(condition)
|
76
|
+
unless condition.is_a?(Hash)
|
77
|
+
raise ArgumentError, 'Condition must be a hash'
|
78
|
+
end
|
79
|
+
|
80
|
+
unless condition.length == 1
|
81
|
+
raise ArgumentError, 'Condition must have one element'
|
82
|
+
end
|
83
|
+
|
84
|
+
condition.values.first
|
52
85
|
end
|
53
86
|
|
54
|
-
#
|
87
|
+
# Determines whether the attribute value stored in this matcher
|
88
|
+
# satisfies the provided condition using the provided operator.
|
89
|
+
#
|
90
|
+
# For example, given an instance of Gt matcher with the @attribute of
|
91
|
+
# 2, the matcher is set up to answer whether the attribute is
|
92
|
+
# greater than some input value. This input value is provided in
|
93
|
+
# the condition, which could be {"$gt" => 1}, and the operator is
|
94
|
+
# provided (somewhat in a duplicate fashion) in the operator argument,
|
95
|
+
# in this case :>.
|
55
96
|
#
|
56
|
-
# @example
|
57
|
-
# matcher.
|
97
|
+
# @example
|
98
|
+
# matcher = Matchable::Gt.new(2)
|
99
|
+
# matcher.determine({'$gt' => 1}, :>)
|
100
|
+
# # => true
|
58
101
|
#
|
59
|
-
# @param [
|
60
|
-
#
|
102
|
+
# @param [ Hash ] condition The condition to evaluate. This must be
|
103
|
+
# a one-element hash; the key is ignored, and the value is passed
|
104
|
+
# as the argument to the operator.
|
105
|
+
# @param [ Symbol, String ] operator The comparison operator or method.
|
106
|
+
# The operator is invoked on the attribute stored in the matcher
|
107
|
+
# instance.
|
61
108
|
#
|
62
|
-
# @return [ true, false ]
|
109
|
+
# @return [ true, false ] Result of condition evaluation.
|
63
110
|
#
|
64
111
|
# @since 1.0.0
|
65
|
-
def determine(
|
66
|
-
attribute.__array__.any?
|
67
|
-
attr
|
68
|
-
|
112
|
+
def determine(condition, operator)
|
113
|
+
attribute.__array__.any? do |attr|
|
114
|
+
attr && attr.send(operator, condition_value(condition))
|
115
|
+
end
|
69
116
|
end
|
70
117
|
end
|
71
118
|
end
|