mongoid 2.4.0 → 2.4.12
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.
- data/CHANGELOG.md +329 -1
- data/Rakefile +0 -9
- data/lib/config/locales/bg.yml +1 -0
- data/lib/config/locales/de.yml +1 -0
- data/lib/config/locales/en-GB.yml +1 -0
- data/lib/config/locales/en.yml +1 -0
- data/lib/config/locales/es.yml +1 -0
- data/lib/config/locales/fr.yml +1 -0
- data/lib/config/locales/hi.yml +1 -0
- data/lib/config/locales/hu.yml +1 -0
- data/lib/config/locales/id.yml +1 -0
- data/lib/config/locales/it.yml +1 -0
- data/lib/config/locales/ja.yml +1 -0
- data/lib/config/locales/kr.yml +1 -0
- data/lib/config/locales/nl.yml +1 -0
- data/lib/config/locales/pl.yml +1 -0
- data/lib/config/locales/pt-BR.yml +1 -0
- data/lib/config/locales/pt.yml +1 -0
- data/lib/config/locales/ro.yml +1 -0
- data/lib/config/locales/ru.yml +1 -0
- data/lib/config/locales/sv.yml +1 -0
- data/lib/config/locales/vi.yml +1 -0
- data/lib/config/locales/zh-CN.yml +1 -0
- data/lib/mongoid/atomic/modifiers.rb +56 -15
- data/lib/mongoid/atomic.rb +14 -8
- data/lib/mongoid/attributes.rb +1 -1
- data/lib/mongoid/callbacks.rb +3 -3
- data/lib/mongoid/collection.rb +5 -1
- data/lib/mongoid/collections/retry.rb +2 -1
- data/lib/mongoid/components.rb +2 -1
- data/lib/mongoid/config/database.rb +4 -7
- data/lib/mongoid/config/environment.rb +4 -2
- data/lib/mongoid/config/replset_database.rb +3 -5
- data/lib/mongoid/config.rb +18 -0
- data/lib/mongoid/contexts/enumerable.rb +26 -1
- data/lib/mongoid/contexts/mongo.rb +62 -14
- data/lib/mongoid/copyable.rb +38 -8
- data/lib/mongoid/criteria.rb +1 -0
- data/lib/mongoid/criterion/exclusion.rb +3 -1
- data/lib/mongoid/criterion/inclusion.rb +2 -2
- data/lib/mongoid/criterion/optional.rb +2 -2
- data/lib/mongoid/criterion/selector.rb +25 -4
- data/lib/mongoid/cursor.rb +4 -2
- data/lib/mongoid/dirty.rb +61 -18
- data/lib/mongoid/document.rb +6 -2
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +3 -1
- data/lib/mongoid/extensions/string/inflections.rb +1 -1
- data/lib/mongoid/factory.rb +3 -3
- data/lib/mongoid/fields/internal/array.rb +4 -9
- data/lib/mongoid/fields/internal/big_decimal.rb +7 -1
- data/lib/mongoid/fields/internal/date_time.rb +1 -1
- data/lib/mongoid/fields/internal/false_class.rb +10 -0
- data/lib/mongoid/fields/internal/foreign_keys/array.rb +22 -8
- data/lib/mongoid/fields/internal/set.rb +1 -1
- data/lib/mongoid/fields/internal/timekeeping.rb +2 -1
- data/lib/mongoid/fields/internal/true_class.rb +10 -0
- data/lib/mongoid/fields/mappings.rb +14 -13
- data/lib/mongoid/fields/serializable.rb +41 -3
- data/lib/mongoid/fields.rb +154 -26
- data/lib/mongoid/hierarchy.rb +6 -1
- data/lib/mongoid/identity_map.rb +15 -0
- data/lib/mongoid/logger.rb +7 -1
- data/lib/mongoid/matchers/all.rb +8 -1
- data/lib/mongoid/matchers/in.rb +8 -1
- data/lib/mongoid/matchers/strategies.rb +6 -2
- data/lib/mongoid/multi_database.rb +5 -0
- data/lib/mongoid/named_scope.rb +17 -1
- data/lib/mongoid/nested_attributes.rb +1 -1
- data/lib/mongoid/observer.rb +7 -0
- data/lib/mongoid/persistence/insertion.rb +2 -1
- data/lib/mongoid/persistence/operations/update.rb +0 -9
- data/lib/mongoid/persistence.rb +5 -5
- data/lib/mongoid/railtie.rb +22 -4
- data/lib/mongoid/relations/bindings/referenced/in.rb +31 -11
- data/lib/mongoid/relations/bindings/referenced/many.rb +8 -8
- data/lib/mongoid/relations/bindings/referenced/one.rb +8 -8
- data/lib/mongoid/relations/builders/nested_attributes/many.rb +3 -1
- data/lib/mongoid/relations/cascading/delete.rb +29 -2
- data/lib/mongoid/relations/cascading/destroy.rb +18 -1
- data/lib/mongoid/relations/cascading/nullify.rb +18 -1
- data/lib/mongoid/relations/cascading.rb +0 -1
- data/lib/mongoid/relations/embedded/atomic.rb +3 -3
- data/lib/mongoid/relations/embedded/many.rb +58 -26
- data/lib/mongoid/relations/embedded/one.rb +1 -1
- data/lib/mongoid/relations/metadata.rb +33 -1
- data/lib/mongoid/relations/one.rb +12 -0
- data/lib/mongoid/relations/proxy.rb +6 -3
- data/lib/mongoid/relations/referenced/many.rb +14 -9
- data/lib/mongoid/relations/referenced/many_to_many.rb +3 -4
- data/lib/mongoid/relations/targets/enumerable.rb +9 -3
- data/lib/mongoid/relations.rb +3 -1
- data/lib/mongoid/serialization.rb +18 -6
- data/lib/mongoid/state.rb +1 -1
- data/lib/mongoid/threaded.rb +26 -0
- data/lib/mongoid/timestamps/updated.rb +1 -1
- data/lib/mongoid/validations/format.rb +20 -0
- data/lib/mongoid/validations/length.rb +20 -0
- data/lib/mongoid/validations/localizable.rb +30 -0
- data/lib/mongoid/validations/presence.rb +6 -2
- data/lib/mongoid/validations/uniqueness.rb +162 -30
- data/lib/mongoid/validations.rb +54 -2
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +12 -3
- data/lib/mongoid.rb +1 -0
- data/lib/rails/mongoid.rb +18 -12
- metadata +58 -41
- data/lib/mongoid/relations/cascading/strategy.rb +0 -26
|
@@ -5,19 +5,22 @@ module Mongoid # :nodoc:
|
|
|
5
5
|
# This class is the superclass for all relation proxy objects, and contains
|
|
6
6
|
# common behaviour for all of them.
|
|
7
7
|
class Proxy
|
|
8
|
-
|
|
8
|
+
alias :extend_proxy :extend
|
|
9
9
|
|
|
10
10
|
# We undefine most methods to get them sent through to the target.
|
|
11
11
|
instance_methods.each do |method|
|
|
12
12
|
undef_method(method) unless
|
|
13
|
-
method =~ /(^__|^send
|
|
13
|
+
method =~ /(^__|^send|^object_id|^respond_to|^tap|extend_proxy)/
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
include Threaded::Lifecycle
|
|
17
|
+
|
|
16
18
|
attr_accessor :base, :loaded, :metadata, :target
|
|
17
19
|
|
|
18
20
|
# Backwards compatibility with Mongoid beta releases.
|
|
19
21
|
delegate :klass, :to => :metadata
|
|
20
22
|
delegate :bind_one, :unbind_one, :to => :binding
|
|
23
|
+
delegate :collection_name, :to => :base
|
|
21
24
|
|
|
22
25
|
# Convenience for setting the target and the metadata properties since
|
|
23
26
|
# all proxies will need to do this.
|
|
@@ -33,7 +36,7 @@ module Mongoid # :nodoc:
|
|
|
33
36
|
def init(base, target, metadata)
|
|
34
37
|
@base, @target, @metadata = base, target, metadata
|
|
35
38
|
yield(self) if block_given?
|
|
36
|
-
|
|
39
|
+
extend_proxy(metadata.extension) if metadata.extension?
|
|
37
40
|
end
|
|
38
41
|
|
|
39
42
|
# The default substitutable object for a relation proxy is the clone of
|
|
@@ -89,6 +89,7 @@ module Mongoid #:nodoc:
|
|
|
89
89
|
|
|
90
90
|
Factory.build(type || klass, attributes, options).tap do |doc|
|
|
91
91
|
append(doc)
|
|
92
|
+
doc.apply_proc_defaults
|
|
92
93
|
yield(doc) if block_given?
|
|
93
94
|
doc.run_callbacks(:build) { doc }
|
|
94
95
|
end
|
|
@@ -319,15 +320,13 @@ module Mongoid #:nodoc:
|
|
|
319
320
|
def substitute(replacement)
|
|
320
321
|
tap do |proxy|
|
|
321
322
|
if replacement
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
docs.push(doc) if doc.send(metadata.foreign_key) != base.id
|
|
328
|
-
end
|
|
329
|
-
proxy.concat(docs)
|
|
323
|
+
new_docs, docs = replacement.compact.uniq, []
|
|
324
|
+
new_ids = new_docs.map { |doc| doc.id }
|
|
325
|
+
remove_not_in(new_ids)
|
|
326
|
+
new_docs.each do |doc|
|
|
327
|
+
docs.push(doc) if doc.send(metadata.foreign_key) != base.id
|
|
330
328
|
end
|
|
329
|
+
proxy.concat(docs)
|
|
331
330
|
else
|
|
332
331
|
proxy.purge
|
|
333
332
|
end
|
|
@@ -564,9 +563,15 @@ module Mongoid #:nodoc:
|
|
|
564
563
|
#
|
|
565
564
|
# @since 2.2.0
|
|
566
565
|
def eager_load(metadata, ids)
|
|
566
|
+
cleared = false
|
|
567
567
|
klass, foreign_key = metadata.klass, metadata.foreign_key
|
|
568
568
|
klass.any_in(foreign_key => ids).each do |doc|
|
|
569
|
-
|
|
569
|
+
base_id = doc.send(foreign_key)
|
|
570
|
+
unless cleared
|
|
571
|
+
IdentityMap.clear_many(klass, foreign_key => base_id)
|
|
572
|
+
cleared = true
|
|
573
|
+
end
|
|
574
|
+
IdentityMap.set_many(doc, foreign_key => base_id)
|
|
570
575
|
end
|
|
571
576
|
end
|
|
572
577
|
|
|
@@ -75,6 +75,7 @@ module Mongoid # :nodoc:
|
|
|
75
75
|
Factory.build(type || klass, attributes, options).tap do |doc|
|
|
76
76
|
base.send(metadata.foreign_key).push(doc.id)
|
|
77
77
|
append(doc)
|
|
78
|
+
doc.apply_proc_defaults
|
|
78
79
|
doc.synced[metadata.inverse_foreign_key] = false
|
|
79
80
|
yield(doc) if block_given?
|
|
80
81
|
end
|
|
@@ -142,10 +143,8 @@ module Mongoid # :nodoc:
|
|
|
142
143
|
# @since 2.0.0.rc.1
|
|
143
144
|
def substitute(replacement)
|
|
144
145
|
tap do |proxy|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
proxy.push(replacement.compact.uniq) if replacement
|
|
148
|
-
end
|
|
146
|
+
proxy.purge
|
|
147
|
+
proxy.push(replacement.compact.uniq) unless replacement.blank?
|
|
149
148
|
end
|
|
150
149
|
end
|
|
151
150
|
|
|
@@ -151,8 +151,9 @@ module Mongoid #:nodoc:
|
|
|
151
151
|
end
|
|
152
152
|
else
|
|
153
153
|
unloaded.each do |doc|
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
document = added.delete_one(doc) || loaded.delete_one(doc) || doc
|
|
155
|
+
yield(document)
|
|
156
|
+
loaded.push(document)
|
|
156
157
|
end
|
|
157
158
|
end
|
|
158
159
|
added.each do |doc|
|
|
@@ -315,7 +316,12 @@ module Mongoid #:nodoc:
|
|
|
315
316
|
#
|
|
316
317
|
# @since 2.1.0
|
|
317
318
|
def size
|
|
318
|
-
(unloaded ? unloaded.count : loaded.count)
|
|
319
|
+
count = (unloaded ? unloaded.count : loaded.count)
|
|
320
|
+
if count.zero?
|
|
321
|
+
count + added.count
|
|
322
|
+
else
|
|
323
|
+
count + added.count{ |d| d.new_record? }
|
|
324
|
+
end
|
|
319
325
|
end
|
|
320
326
|
alias :length :size
|
|
321
327
|
|
data/lib/mongoid/relations.rb
CHANGED
|
@@ -119,7 +119,9 @@ module Mongoid # :nodoc:
|
|
|
119
119
|
def reload_relations
|
|
120
120
|
relations.each_pair do |name, meta|
|
|
121
121
|
if instance_variable_defined?("@#{name}")
|
|
122
|
-
|
|
122
|
+
if _parent.nil? || instance_variable_get("@#{name}") != _parent
|
|
123
|
+
remove_instance_variable("@#{name}")
|
|
124
|
+
end
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
end
|
|
@@ -31,18 +31,31 @@ module Mongoid # :nodoc:
|
|
|
31
31
|
only = Array.wrap(options[:only]).map(&:to_s)
|
|
32
32
|
except = Array.wrap(options[:except]).map(&:to_s)
|
|
33
33
|
|
|
34
|
-
except |= ['_type']
|
|
34
|
+
except |= ['_type'] unless Mongoid.include_type_for_serialization
|
|
35
35
|
|
|
36
36
|
field_names = fields.keys.map { |field| field.to_s }
|
|
37
|
-
attribute_names = (
|
|
37
|
+
attribute_names = (as_document.keys + field_names).sort
|
|
38
38
|
if only.any?
|
|
39
39
|
attribute_names &= only
|
|
40
40
|
elsif except.any?
|
|
41
41
|
attribute_names -= except
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
method_names = Array.wrap(options[:methods]).map
|
|
45
|
-
|
|
44
|
+
method_names = Array.wrap(options[:methods]).map do |name|
|
|
45
|
+
name.to_s if respond_to?(name)
|
|
46
|
+
end.compact
|
|
47
|
+
|
|
48
|
+
{}.tap do |attrs|
|
|
49
|
+
(attribute_names + method_names).each do |name|
|
|
50
|
+
if relations.has_key?(name)
|
|
51
|
+
value = send(name)
|
|
52
|
+
attrs[name] = value ? value.serializable_hash(options) : nil
|
|
53
|
+
elsif attribute_names.include?(name) && !fields.has_key?(name)
|
|
54
|
+
attrs[name] = read_attribute(name)
|
|
55
|
+
else
|
|
56
|
+
attrs[name] = send(name)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
46
59
|
serialize_relations(attrs, options) if options[:include]
|
|
47
60
|
end
|
|
48
61
|
end
|
|
@@ -88,8 +101,7 @@ module Mongoid # :nodoc:
|
|
|
88
101
|
inclusions = options[:include]
|
|
89
102
|
relation_names(inclusions).each do |name|
|
|
90
103
|
metadata = relations[name.to_s]
|
|
91
|
-
relation = send(metadata.name)
|
|
92
|
-
if relation
|
|
104
|
+
if metadata && relation = send(metadata.name)
|
|
93
105
|
attributes[metadata.name.to_s] =
|
|
94
106
|
relation.serializable_hash(relation_options(inclusions, options, name))
|
|
95
107
|
end
|
data/lib/mongoid/state.rb
CHANGED
data/lib/mongoid/threaded.rb
CHANGED
|
@@ -175,6 +175,32 @@ module Mongoid #:nodoc:
|
|
|
175
175
|
Thread.current[:"[mongoid]:safety-options"] = options
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
+
# Get the field selection options from the current thread.
|
|
179
|
+
#
|
|
180
|
+
# @example Get the field selection options.
|
|
181
|
+
# Threaded.selection
|
|
182
|
+
#
|
|
183
|
+
# @return [ Hash ] The field selection.
|
|
184
|
+
#
|
|
185
|
+
# @since 2.4.4
|
|
186
|
+
def selection
|
|
187
|
+
Thread.current[:"[mongoid]:selection"]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Set the field selection on the current thread.
|
|
191
|
+
#
|
|
192
|
+
# @example Set the field selection.
|
|
193
|
+
# Threaded.selection = { field: 1 }
|
|
194
|
+
#
|
|
195
|
+
# @param [ Hash ] value The current field selection.
|
|
196
|
+
#
|
|
197
|
+
# @return [ Hash ] The field selection.
|
|
198
|
+
#
|
|
199
|
+
# @since 2.4.4
|
|
200
|
+
def selection=(value)
|
|
201
|
+
Thread.current[:"[mongoid]:selection"] = value
|
|
202
|
+
end
|
|
203
|
+
|
|
178
204
|
# Get the mongoid scope stack for chained criteria.
|
|
179
205
|
#
|
|
180
206
|
# @example Get the scope stack.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid #:nodoc:
|
|
3
|
+
module Validations #:nodoc:
|
|
4
|
+
|
|
5
|
+
# Validates that the specified attributes do or do not match a certain
|
|
6
|
+
# regular expression.
|
|
7
|
+
#
|
|
8
|
+
# @example Set up the format validator.
|
|
9
|
+
#
|
|
10
|
+
# class Person
|
|
11
|
+
# include Mongoid::Document
|
|
12
|
+
# field :website
|
|
13
|
+
#
|
|
14
|
+
# validates_format_of :website, :with => URI.regexp
|
|
15
|
+
# end
|
|
16
|
+
class FormatValidator < ActiveModel::Validations::FormatValidator
|
|
17
|
+
include Localizable
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid
|
|
3
|
+
module Validations
|
|
4
|
+
|
|
5
|
+
# Validates that the specified attributes do or do not match a certain
|
|
6
|
+
# length.
|
|
7
|
+
#
|
|
8
|
+
# @example Set up the length validator.
|
|
9
|
+
#
|
|
10
|
+
# class Person
|
|
11
|
+
# include Mongoid::Document
|
|
12
|
+
# field :website
|
|
13
|
+
#
|
|
14
|
+
# validates_length_of :website, in: 1..10
|
|
15
|
+
# end
|
|
16
|
+
class LengthValidator < ActiveModel::Validations::LengthValidator
|
|
17
|
+
include Localizable
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mongoid
|
|
3
|
+
module Validations
|
|
4
|
+
|
|
5
|
+
# Adds localization support to validations.
|
|
6
|
+
module Localizable
|
|
7
|
+
|
|
8
|
+
# Validates each for localized fields.
|
|
9
|
+
#
|
|
10
|
+
# @example Validate localized fields.
|
|
11
|
+
# validator.validate_each(model, :name, "value")
|
|
12
|
+
#
|
|
13
|
+
# @param [ Document ] document The document.
|
|
14
|
+
# @param [ Symbol, String ] attribute The attribute to validate.
|
|
15
|
+
# @param [ Object ] value The attribute value.
|
|
16
|
+
#
|
|
17
|
+
# @since 2.4.2
|
|
18
|
+
def validate_each(document, attribute, value)
|
|
19
|
+
field = document.fields[attribute.to_s]
|
|
20
|
+
if field.try(:localized?) && !value.blank?
|
|
21
|
+
value.values.each do |_value|
|
|
22
|
+
super(document, attribute, _value)
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -27,9 +27,13 @@ module Mongoid #:nodoc:
|
|
|
27
27
|
# @since 2.4.0
|
|
28
28
|
def validate_each(document, attribute, value)
|
|
29
29
|
field = document.fields[attribute.to_s]
|
|
30
|
-
if field
|
|
30
|
+
if field.try(:localized?) && !value.blank?
|
|
31
31
|
value.each_pair do |locale, value|
|
|
32
|
-
document.errors.add(attribute, :
|
|
32
|
+
document.errors.add(attribute, :blank_on_locale, options.merge(:location => locale)) if value.blank?
|
|
33
|
+
end
|
|
34
|
+
elsif document.relations.has_key?(attribute.to_s)
|
|
35
|
+
if value.blank? && document.send(attribute).blank?
|
|
36
|
+
document.errors.add(attribute, :blank, options)
|
|
33
37
|
end
|
|
34
38
|
else
|
|
35
39
|
document.errors.add(attribute, :blank, options) if value.blank?
|
|
@@ -41,38 +41,39 @@ module Mongoid #:nodoc:
|
|
|
41
41
|
#
|
|
42
42
|
# @since 1.0.0
|
|
43
43
|
def validate_each(document, attribute, value)
|
|
44
|
+
attrib, val = to_validate(document, attribute, value)
|
|
45
|
+
return unless validation_required?(document, attrib)
|
|
44
46
|
if document.embedded?
|
|
45
|
-
|
|
46
|
-
relation = document._parent.send(document.metadata.name)
|
|
47
|
-
criteria = relation.where(criterion(document, attribute, value))
|
|
48
|
-
criteria = scope(criteria, document, attribute)
|
|
49
|
-
if document.primary_key == Array.wrap(attribute)
|
|
50
|
-
if criteria.count > 1
|
|
51
|
-
document.errors.add(
|
|
52
|
-
attribute,
|
|
53
|
-
:taken, options.except(:case_sensitive, :scope).merge(:value => value)
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
else
|
|
57
|
-
if criteria.exists?
|
|
58
|
-
document.errors.add(
|
|
59
|
-
attribute,
|
|
60
|
-
:taken,
|
|
61
|
-
options.except(:case_sensitive, :scope).merge(:value => value)
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
47
|
+
validate_embedded(document, attrib, val)
|
|
65
48
|
else
|
|
66
|
-
|
|
67
|
-
criteria = scope(criteria, document, attribute)
|
|
68
|
-
document.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value)) if criteria.exists?
|
|
49
|
+
validate_root(document, attrib, val)
|
|
69
50
|
end
|
|
70
51
|
end
|
|
71
52
|
|
|
72
|
-
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Add the error to the document.
|
|
56
|
+
#
|
|
57
|
+
# @api private
|
|
58
|
+
#
|
|
59
|
+
# @example Add the error.
|
|
60
|
+
# validator.add_error(doc, :name, "test")
|
|
61
|
+
#
|
|
62
|
+
# @param [ Document ] document The document to validate.
|
|
63
|
+
# @param [ Symbol ] attribute The name of the attribute.
|
|
64
|
+
# @param [ Object ] value The value of the object.
|
|
65
|
+
#
|
|
66
|
+
# @since 2.4.10
|
|
67
|
+
def add_error(document, attribute, value)
|
|
68
|
+
document.errors.add(
|
|
69
|
+
attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value)
|
|
70
|
+
)
|
|
71
|
+
end
|
|
73
72
|
|
|
74
73
|
# Should the uniqueness validation be case sensitive?
|
|
75
74
|
#
|
|
75
|
+
# @api private
|
|
76
|
+
#
|
|
76
77
|
# @example Is the validation case sensitive?
|
|
77
78
|
# validator.case_sensitive?
|
|
78
79
|
#
|
|
@@ -83,8 +84,38 @@ module Mongoid #:nodoc:
|
|
|
83
84
|
!(options[:case_sensitive] == false)
|
|
84
85
|
end
|
|
85
86
|
|
|
87
|
+
# Create the validation criteria.
|
|
88
|
+
#
|
|
89
|
+
# @api private
|
|
90
|
+
#
|
|
91
|
+
# @example Create the criteria.
|
|
92
|
+
# validator.create_criteria(User, user, :name, "syd")
|
|
93
|
+
#
|
|
94
|
+
# @param [ Class, Proxy ] base The base to execute the criteria from.
|
|
95
|
+
# @param [ Document ] document The document to validate.
|
|
96
|
+
# @param [ Symbol ] attribute The name of the attribute.
|
|
97
|
+
# @param [ Object ] value The value of the object.
|
|
98
|
+
#
|
|
99
|
+
# @return [ Criteria ] The criteria.
|
|
100
|
+
#
|
|
101
|
+
# @since 2.4.10
|
|
102
|
+
def create_criteria(base, document, attribute, value)
|
|
103
|
+
field = document.fields[attribute.to_s]
|
|
104
|
+
criteria = base.paranoid ? base.unscoped.where(:deleted_at => nil) : base.unscoped
|
|
105
|
+
if field.try(:localized?)
|
|
106
|
+
criterion(document, attribute, value).each_pair do |key, value|
|
|
107
|
+
criteria.selector.store(key, value)
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
criteria = criteria.where(criterion(document, attribute, value))
|
|
111
|
+
end
|
|
112
|
+
scope(criteria, document, attribute)
|
|
113
|
+
end
|
|
114
|
+
|
|
86
115
|
# Get the default criteria for checking uniqueness.
|
|
87
116
|
#
|
|
117
|
+
# @api private
|
|
118
|
+
#
|
|
88
119
|
# @example Get the criteria.
|
|
89
120
|
# validator.criterion(person, :title, "Sir")
|
|
90
121
|
#
|
|
@@ -97,8 +128,7 @@ module Mongoid #:nodoc:
|
|
|
97
128
|
# @since 2.3.0
|
|
98
129
|
def criterion(document, attribute, value)
|
|
99
130
|
{ attribute => filter(value) }.tap do |selector|
|
|
100
|
-
if document.persisted?
|
|
101
|
-
(document.embedded? && (document.primary_key != Array.wrap(attribute)))
|
|
131
|
+
if document.persisted? && !document.embedded?
|
|
102
132
|
selector.merge!(:_id => { "$ne" => document.id })
|
|
103
133
|
end
|
|
104
134
|
end
|
|
@@ -106,6 +136,8 @@ module Mongoid #:nodoc:
|
|
|
106
136
|
|
|
107
137
|
# Filter the value based on whether the check is case sensitive or not.
|
|
108
138
|
#
|
|
139
|
+
# @api private
|
|
140
|
+
#
|
|
109
141
|
# @example Filter the value.
|
|
110
142
|
# validator.filter("testing")
|
|
111
143
|
#
|
|
@@ -120,6 +152,8 @@ module Mongoid #:nodoc:
|
|
|
120
152
|
|
|
121
153
|
# Scope the criteria to the scope options provided.
|
|
122
154
|
#
|
|
155
|
+
# @api private
|
|
156
|
+
#
|
|
123
157
|
# @example Scope the criteria.
|
|
124
158
|
# validator.scope(criteria, document)
|
|
125
159
|
#
|
|
@@ -130,16 +164,17 @@ module Mongoid #:nodoc:
|
|
|
130
164
|
#
|
|
131
165
|
# @since 2.3.0
|
|
132
166
|
def scope(criteria, document, attribute)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
criteria = criteria.where(item => document.attributes[item.to_s])
|
|
136
|
-
end
|
|
167
|
+
Array.wrap(options[:scope]).each do |item|
|
|
168
|
+
criteria = criteria.where(item => document.attributes[item.to_s])
|
|
137
169
|
end
|
|
170
|
+
criteria = criteria.where(:deleted_at => nil) if document.paranoid?
|
|
138
171
|
criteria
|
|
139
172
|
end
|
|
140
173
|
|
|
141
174
|
# Should validation be skipped?
|
|
142
175
|
#
|
|
176
|
+
# @api private
|
|
177
|
+
#
|
|
143
178
|
# @example Should the validation be skipped?
|
|
144
179
|
# validator.skip_validation?(doc)
|
|
145
180
|
#
|
|
@@ -151,6 +186,103 @@ module Mongoid #:nodoc:
|
|
|
151
186
|
def skip_validation?(document)
|
|
152
187
|
!document._parent || document.embedded_one?
|
|
153
188
|
end
|
|
189
|
+
|
|
190
|
+
# Scope reference has changed?
|
|
191
|
+
#
|
|
192
|
+
# @api private
|
|
193
|
+
#
|
|
194
|
+
# @example Has scope reference changed?
|
|
195
|
+
# validator.scope_value_changed?(doc)
|
|
196
|
+
#
|
|
197
|
+
# @param [ Document ] document The embedded document.
|
|
198
|
+
#
|
|
199
|
+
# @return [ true, false ] If the scope reference has changed.
|
|
200
|
+
#
|
|
201
|
+
# @since
|
|
202
|
+
def scope_value_changed?(document)
|
|
203
|
+
Array.wrap(options[:scope]).any? do |item|
|
|
204
|
+
document.send("attribute_changed?", item.to_s)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Get the name of the field and the value to validate. This is for the
|
|
209
|
+
# case when we validate a relation via the relation name and not the key,
|
|
210
|
+
# we need to send the key name and value to the db, not the relation
|
|
211
|
+
# object.
|
|
212
|
+
#
|
|
213
|
+
# @api private
|
|
214
|
+
#
|
|
215
|
+
# @example Get the name and key to validate.
|
|
216
|
+
# validator.to_validate(doc, :parent, Parent.new)
|
|
217
|
+
#
|
|
218
|
+
# @param [ Document ] document The doc getting validated.
|
|
219
|
+
# @param [ Symbol ] attribute The attribute getting validated.
|
|
220
|
+
# @param [ Object ] value The value of the attribute.
|
|
221
|
+
#
|
|
222
|
+
# @return [ Array<Object, Object> ] The field and value.
|
|
223
|
+
#
|
|
224
|
+
# @since 2.4.4
|
|
225
|
+
def to_validate(document, attribute, value)
|
|
226
|
+
metadata = document.relations[attribute.to_s]
|
|
227
|
+
if metadata && metadata.stores_foreign_key?
|
|
228
|
+
[ metadata.foreign_key, value.id ]
|
|
229
|
+
else
|
|
230
|
+
[ attribute, value ]
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Validate an embedded document.
|
|
235
|
+
#
|
|
236
|
+
# @api private
|
|
237
|
+
#
|
|
238
|
+
# @example Validate the embedded document.
|
|
239
|
+
# validator.validate_embedded(doc, :name, "test")
|
|
240
|
+
#
|
|
241
|
+
# @param [ Document ] document The document.
|
|
242
|
+
# @param [ Symbol ] attribute The attribute name.
|
|
243
|
+
# @param [ Object ] value The value.
|
|
244
|
+
#
|
|
245
|
+
# @since 2.4.10
|
|
246
|
+
def validate_embedded(document, attribute, value)
|
|
247
|
+
return if skip_validation?(document)
|
|
248
|
+
relation = document._parent.send(document.metadata.name)
|
|
249
|
+
criteria = create_criteria(relation, document, attribute, value)
|
|
250
|
+
add_error(document, attribute, value) if criteria.count > 1
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Validate a root document.
|
|
254
|
+
#
|
|
255
|
+
# @api private
|
|
256
|
+
#
|
|
257
|
+
# @example Validate the root document.
|
|
258
|
+
# validator.validate_root(doc, :name, "test")
|
|
259
|
+
#
|
|
260
|
+
# @param [ Document ] document The document.
|
|
261
|
+
# @param [ Symbol ] attribute The attribute name.
|
|
262
|
+
# @param [ Object ] value The value.
|
|
263
|
+
#
|
|
264
|
+
# @since 2.4.10
|
|
265
|
+
def validate_root(document, attribute, value)
|
|
266
|
+
criteria = create_criteria(klass, document, attribute, value)
|
|
267
|
+
add_error(document, attribute, value) if criteria.exists?
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Are we required to validate the document?
|
|
271
|
+
#
|
|
272
|
+
# @example Is validation needed?
|
|
273
|
+
# validator.validation_required?(doc, :field)
|
|
274
|
+
#
|
|
275
|
+
# @param [ Document ] document The document getting validated.
|
|
276
|
+
# @param [ Symbol ] attribute The attribute to validate.
|
|
277
|
+
#
|
|
278
|
+
# @return [ true, false ] If we need to validate.
|
|
279
|
+
#
|
|
280
|
+
# @since 2.4.4
|
|
281
|
+
def validation_required?(document, attribute)
|
|
282
|
+
document.new_record? ||
|
|
283
|
+
document.send("attribute_changed?", attribute.to_s) ||
|
|
284
|
+
scope_value_changed?(document)
|
|
285
|
+
end
|
|
154
286
|
end
|
|
155
287
|
end
|
|
156
288
|
end
|
data/lib/mongoid/validations.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
require "mongoid/validations/localizable"
|
|
2
3
|
require "mongoid/validations/associated"
|
|
3
|
-
require "mongoid/validations/
|
|
4
|
+
require "mongoid/validations/format"
|
|
5
|
+
require "mongoid/validations/length"
|
|
4
6
|
require "mongoid/validations/presence"
|
|
7
|
+
require "mongoid/validations/uniqueness"
|
|
5
8
|
|
|
6
9
|
module Mongoid #:nodoc:
|
|
7
10
|
|
|
@@ -45,7 +48,7 @@ module Mongoid #:nodoc:
|
|
|
45
48
|
# @since 2.0.0.rc.1
|
|
46
49
|
def read_attribute_for_validation(attr)
|
|
47
50
|
attribute = attr.to_s
|
|
48
|
-
if relations
|
|
51
|
+
if relations.has_key?(attribute)
|
|
49
52
|
begin_validate
|
|
50
53
|
relation = send(attr)
|
|
51
54
|
exit_validate
|
|
@@ -88,6 +91,21 @@ module Mongoid #:nodoc:
|
|
|
88
91
|
|
|
89
92
|
module ClassMethods #:nodoc:
|
|
90
93
|
|
|
94
|
+
# Validates whether or not a field matches a certain regular expression.
|
|
95
|
+
#
|
|
96
|
+
# @example
|
|
97
|
+
# class Person
|
|
98
|
+
# include Mongoid::Document
|
|
99
|
+
# field :website
|
|
100
|
+
#
|
|
101
|
+
# validates_format_of :website, :with => URI.regexp
|
|
102
|
+
# end
|
|
103
|
+
#
|
|
104
|
+
# @param [ Array ] *args The arguments to pass to the validator.
|
|
105
|
+
def validates_format_of(*args)
|
|
106
|
+
validates_with(FormatValidator, _merge_attributes(args))
|
|
107
|
+
end
|
|
108
|
+
|
|
91
109
|
# Validates whether or not an association is valid or not. Will correctly
|
|
92
110
|
# handle has one and has many associations.
|
|
93
111
|
#
|
|
@@ -123,6 +141,40 @@ module Mongoid #:nodoc:
|
|
|
123
141
|
validates_with(UniquenessValidator, _merge_attributes(args))
|
|
124
142
|
end
|
|
125
143
|
|
|
144
|
+
# Validates the format of a field.
|
|
145
|
+
#
|
|
146
|
+
# @example
|
|
147
|
+
# class Person
|
|
148
|
+
# include Mongoid::Document
|
|
149
|
+
# field :title
|
|
150
|
+
#
|
|
151
|
+
# validates_format_of :title, with: /^[a-z0-9 \-_]*$/i
|
|
152
|
+
# end
|
|
153
|
+
#
|
|
154
|
+
# @param [ Array ] args The names of the fields to validate.
|
|
155
|
+
#
|
|
156
|
+
# @since 2.4.0
|
|
157
|
+
def validates_format_of(*args)
|
|
158
|
+
validates_with(Mongoid::Validations::FormatValidator, _merge_attributes(args))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Validates the length of a field.
|
|
162
|
+
#
|
|
163
|
+
# @example
|
|
164
|
+
# class Person
|
|
165
|
+
# include Mongoid::Document
|
|
166
|
+
# field :title
|
|
167
|
+
#
|
|
168
|
+
# validates_length_of :title, minimum: 100
|
|
169
|
+
# end
|
|
170
|
+
#
|
|
171
|
+
# @param [ Array ] args The names of the fields to validate.
|
|
172
|
+
#
|
|
173
|
+
# @since 2.4.0
|
|
174
|
+
def validates_length_of(*args)
|
|
175
|
+
validates_with(Mongoid::Validations::LengthValidator, _merge_attributes(args))
|
|
176
|
+
end
|
|
177
|
+
|
|
126
178
|
# Validates whether or not a field is present - meaning nil or empty.
|
|
127
179
|
#
|
|
128
180
|
# @example
|
data/lib/mongoid/version.rb
CHANGED