mongoid 2.4.9 → 2.4.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,10 +3,37 @@
3
3
  For instructions on upgrading to newer versions, visit
4
4
  [mongoid.org](http://mongoid.org/docs/upgrading.html).
5
5
 
6
- ## 2.4.10 (branch: 2.4.0-stable)
6
+ ## 2.4.11 (branch: 2.4.0-stable)
7
7
 
8
8
  ### Resolved Issues
9
9
 
10
+ ## 2.4.10
11
+
12
+ ### Resolved Issues
13
+
14
+ * \#2003 Don't fail on document generation when an embedded document was
15
+ stored as nil in the database.
16
+
17
+ * \#1997 Don't delete paranoid embedded docs via nested attributes when
18
+ a before_destroy callback returns false.
19
+
20
+ * \#1994 `dependent: :delete` only hits the database once now for one to
21
+ many and many to many relations instead of once for each document.
22
+
23
+ * \#1987 Don't double-insert documents into identity map when eager loading
24
+ twice inside the unit of work.
25
+
26
+ * \#1976 Don't execute eager load queries when base query is empty.
27
+
28
+ * \#1953 Uniqueness validation now works on localized fields.
29
+
30
+ * \#1936 Allow setting n levels deep embedded documents atomically without
31
+ conflicting mods when not using nested attributes or documents themselves
32
+ in an update call from the parent.
33
+
34
+ * \#1957/\#1954 Ensure database name is set with inheritance.
35
+ (Hans Hasselberg)
36
+
10
37
  ## 2.4.9
11
38
 
12
39
  ### Resolved Issues
@@ -252,6 +252,9 @@ module Mongoid #:nodoc:
252
252
  # @example Get the atomic paths.
253
253
  # document.atomic_paths
254
254
  #
255
+ # @todo: Durran: Should probably raise error for embedded docs w/o
256
+ # metadata.
257
+ #
255
258
  # @return [ Object ] The associated path.
256
259
  #
257
260
  # @since 2.1.0
@@ -144,13 +144,15 @@ module Mongoid #:nodoc:
144
144
  #
145
145
  # @since 2.4.1
146
146
  def eager_load(docs)
147
- parent_ids = docs.map(&:id)
148
147
  criteria.inclusions.reject! do |metadata|
149
- if metadata.macro == :referenced_in
150
- child_ids = load_ids(metadata.foreign_key)
151
- metadata.eager_load(child_ids)
152
- else
153
- metadata.eager_load(parent_ids)
148
+ unless docs.empty?
149
+ parent_ids = docs.map(&:id)
150
+ if metadata.macro == :referenced_in
151
+ child_ids = load_ids(metadata.foreign_key)
152
+ metadata.eager_load(child_ids)
153
+ else
154
+ metadata.eager_load(parent_ids)
155
+ end
154
156
  end
155
157
  end
156
158
  end
@@ -15,7 +15,7 @@ module Mongoid #:nodoc:
15
15
  # @param [ Hash ] optiosn The mass assignment scoping options.
16
16
  #
17
17
  # @return [ Document ] The instantiated document.
18
- def build(klass, attributes = {}, options = {})
18
+ def build(klass, attributes = nil, options = {})
19
19
  type = (attributes || {})["_type"]
20
20
  if type && klass._types.include?(type)
21
21
  type.constantize.new(attributes, options)
@@ -34,8 +34,8 @@ module Mongoid #:nodoc:
34
34
  # @param [ Hash ] attributes The document attributes.
35
35
  #
36
36
  # @return [ Document ] The instantiated document.
37
- def from_db(klass, attributes = {})
38
- type = attributes["_type"]
37
+ def from_db(klass, attributes = nil)
38
+ type = (attributes || {})["_type"]
39
39
  if type.blank?
40
40
  klass.instantiate(attributes)
41
41
  else
@@ -4,6 +4,21 @@ module Mongoid #:nodoc:
4
4
  # Defines behaviour for the identity map in Mongoid.
5
5
  class IdentityMap < Hash
6
6
 
7
+ # Clear the many documents.
8
+ #
9
+ # @example Clear the docs.
10
+ # identity_map.clear_many(Post, selector)
11
+ #
12
+ # @param [ Class ] klass The klass to clear.
13
+ # @param [ Hash ] selector The selector to identify it.
14
+ #
15
+ # @return [ Array<Document> ] The documents.
16
+ #
17
+ # @since 2.4.10
18
+ def clear_many(klass, selector)
19
+ (documents_for(klass)[selector] ||= []).clear
20
+ end
21
+
7
22
  # Get a document from the identity map by its id.
8
23
  #
9
24
  # @example Get the document from the map.
@@ -26,6 +26,11 @@ module Mongoid #:nodoc:
26
26
  def set_database(name)
27
27
  @database = name.to_s
28
28
  end
29
+
30
+ def inherited(subclass)
31
+ super
32
+ subclass.set_database(database.dup) if database
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -27,8 +27,18 @@ module Mongoid # :nodoc:
27
27
  #
28
28
  # @example Perform the cascading delete.
29
29
  # strategy.cascade
30
+ #
31
+ # @since 2.0.0
30
32
  def cascade
31
- Array.wrap(relation).each { |doc| doc.delete } if relation
33
+ if relation
34
+ if relation.cascades.empty?
35
+ safety = Threaded.safety_options
36
+ relation.clear
37
+ Threaded.safety_options = safety
38
+ else
39
+ ::Array.wrap(relation).each { |doc| doc.delete }
40
+ end
41
+ end
32
42
  end
33
43
  end
34
44
  end
@@ -189,8 +189,12 @@ module Mongoid # :nodoc:
189
189
  target.delete_one(document).tap do |doc|
190
190
  _unscoped.delete_one(doc)
191
191
  if doc && !_binding?
192
- if _assigning? && !doc.paranoid?
193
- base.add_atomic_pull(doc)
192
+ if _assigning?
193
+ if doc.paranoid?
194
+ doc.destroy(:suppress => true)
195
+ else
196
+ base.add_atomic_pull(doc)
197
+ end
194
198
  else
195
199
  doc.delete(:suppress => true)
196
200
  end
@@ -307,7 +311,11 @@ module Mongoid # :nodoc:
307
311
  atomically(:$set) do
308
312
  base.delayed_atomic_sets.clear
309
313
  if replacement.first.is_a?(Hash)
310
- replacement = Many.builder(base, metadata, replacement).build
314
+ replacement = replacement.map do |doc|
315
+ attributes = { :metadata => metadata, :_parent => base }
316
+ attributes.merge!(doc)
317
+ Factory.build(klass, attributes)
318
+ end
311
319
  end
312
320
  docs = replacement.compact
313
321
  proxy.target = docs
@@ -319,6 +327,10 @@ module Mongoid # :nodoc:
319
327
  end
320
328
  if _assigning?
321
329
  name = _unscoped.first.atomic_path
330
+ base._children.each do |child|
331
+ child.delayed_atomic_sets.clear
332
+ end
333
+ base.instance_variable_set(:@_children, nil)
322
334
  base.delayed_atomic_sets[name] = proxy.as_document
323
335
  end
324
336
  end
@@ -6,6 +6,18 @@ module Mongoid # :nodoc:
6
6
  # behaviour or those proxies.
7
7
  class One < Proxy
8
8
 
9
+ # Clear this relation - same as calling #delete on the document.
10
+ #
11
+ # @example Clear the relation.
12
+ # relation.clear
13
+ #
14
+ # @return [ true, false ] If the delete suceeded.
15
+ #
16
+ # @since 3.0.0
17
+ def clear
18
+ target.delete
19
+ end
20
+
9
21
  # Get all the documents in the relation that are loaded into memory.
10
22
  #
11
23
  # @example Get the in memory documents.
@@ -563,9 +563,15 @@ module Mongoid #:nodoc:
563
563
  #
564
564
  # @since 2.2.0
565
565
  def eager_load(metadata, ids)
566
+ cleared = false
566
567
  klass, foreign_key = metadata.klass, metadata.foreign_key
567
568
  klass.any_in(foreign_key => ids).each do |doc|
568
- IdentityMap.set_many(doc, foreign_key => doc.send(foreign_key))
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)
569
575
  end
570
576
  end
571
577
 
@@ -141,6 +141,40 @@ module Mongoid #:nodoc:
141
141
  validates_with(UniquenessValidator, _merge_attributes(args))
142
142
  end
143
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
+
144
178
  # Validates whether or not a field is present - meaning nil or empty.
145
179
  #
146
180
  # @example
@@ -44,32 +44,36 @@ module Mongoid #:nodoc:
44
44
  attrib, val = to_validate(document, attribute, value)
45
45
  return unless validation_required?(document, attrib)
46
46
  if document.embedded?
47
- return if skip_validation?(document)
48
- relation = document._parent.send(document.metadata.name)
49
- criteria = relation.where(criterion(document, attrib, val))
50
- criteria = scope(criteria, document, attrib)
51
- if criteria.count > 1
52
- document.errors.add(
53
- attrib,
54
- :taken,
55
- options.except(:case_sensitive, :scope).merge(:val => val)
56
- )
57
- end
47
+ validate_embedded(document, attrib, val)
58
48
  else
59
- criteria = klass.unscoped.where(criterion(document, attrib, val))
60
- criteria = scope(criteria, document, attrib)
61
- if criteria.exists?
62
- document.errors.add(
63
- attrib, :taken, options.except(:case_sensitive, :scope).merge(:val => val)
64
- )
65
- end
49
+ validate_root(document, attrib, val)
66
50
  end
67
51
  end
68
52
 
69
- protected
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
70
72
 
71
73
  # Should the uniqueness validation be case sensitive?
72
74
  #
75
+ # @api private
76
+ #
73
77
  # @example Is the validation case sensitive?
74
78
  # validator.case_sensitive?
75
79
  #
@@ -80,8 +84,38 @@ module Mongoid #:nodoc:
80
84
  !(options[:case_sensitive] == false)
81
85
  end
82
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.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
+
83
115
  # Get the default criteria for checking uniqueness.
84
116
  #
117
+ # @api private
118
+ #
85
119
  # @example Get the criteria.
86
120
  # validator.criterion(person, :title, "Sir")
87
121
  #
@@ -102,6 +136,8 @@ module Mongoid #:nodoc:
102
136
 
103
137
  # Filter the value based on whether the check is case sensitive or not.
104
138
  #
139
+ # @api private
140
+ #
105
141
  # @example Filter the value.
106
142
  # validator.filter("testing")
107
143
  #
@@ -116,6 +152,8 @@ module Mongoid #:nodoc:
116
152
 
117
153
  # Scope the criteria to the scope options provided.
118
154
  #
155
+ # @api private
156
+ #
119
157
  # @example Scope the criteria.
120
158
  # validator.scope(criteria, document)
121
159
  #
@@ -134,6 +172,8 @@ module Mongoid #:nodoc:
134
172
 
135
173
  # Should validation be skipped?
136
174
  #
175
+ # @api private
176
+ #
137
177
  # @example Should the validation be skipped?
138
178
  # validator.skip_validation?(doc)
139
179
  #
@@ -148,6 +188,8 @@ module Mongoid #:nodoc:
148
188
 
149
189
  # Scope reference has changed?
150
190
  #
191
+ # @api private
192
+ #
151
193
  # @example Has scope reference changed?
152
194
  # validator.scope_value_changed?(doc)
153
195
  #
@@ -167,6 +209,8 @@ module Mongoid #:nodoc:
167
209
  # we need to send the key name and value to the db, not the relation
168
210
  # object.
169
211
  #
212
+ # @api private
213
+ #
170
214
  # @example Get the name and key to validate.
171
215
  # validator.to_validate(doc, :parent, Parent.new)
172
216
  #
@@ -186,6 +230,42 @@ module Mongoid #:nodoc:
186
230
  end
187
231
  end
188
232
 
233
+ # Validate an embedded document.
234
+ #
235
+ # @api private
236
+ #
237
+ # @example Validate the embedded document.
238
+ # validator.validate_embedded(doc, :name, "test")
239
+ #
240
+ # @param [ Document ] document The document.
241
+ # @param [ Symbol ] attribute The attribute name.
242
+ # @param [ Object ] value The value.
243
+ #
244
+ # @since 2.4.10
245
+ def validate_embedded(document, attribute, value)
246
+ return if skip_validation?(document)
247
+ relation = document._parent.send(document.metadata.name)
248
+ criteria = create_criteria(relation, document, attribute, value)
249
+ add_error(document, attribute, value) if criteria.count > 1
250
+ end
251
+
252
+ # Validate a root document.
253
+ #
254
+ # @api private
255
+ #
256
+ # @example Validate the root document.
257
+ # validator.validate_root(doc, :name, "test")
258
+ #
259
+ # @param [ Document ] document The document.
260
+ # @param [ Symbol ] attribute The attribute name.
261
+ # @param [ Object ] value The value.
262
+ #
263
+ # @since 2.4.10
264
+ def validate_root(document, attribute, value)
265
+ criteria = create_criteria(klass, document, attribute, value)
266
+ add_error(document, attribute, value) if criteria.exists?
267
+ end
268
+
189
269
  # Are we required to validate the document?
190
270
  #
191
271
  # @example Is validation needed?
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
- VERSION = "2.4.9"
3
+ VERSION = "2.4.10"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.9
4
+ version: 2.4.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-24 00:00:00.000000000 Z
12
+ date: 2012-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -461,7 +461,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
461
461
  version: '0'
462
462
  segments:
463
463
  - 0
464
- hash: 3721994237204070315
464
+ hash: 2758014138080710719
465
465
  required_rubygems_version: !ruby/object:Gem::Requirement
466
466
  none: false
467
467
  requirements:
@@ -470,7 +470,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
470
470
  version: 1.3.6
471
471
  requirements: []
472
472
  rubyforge_project: mongoid
473
- rubygems_version: 1.8.19
473
+ rubygems_version: 1.8.24
474
474
  signing_key:
475
475
  specification_version: 3
476
476
  summary: Elegant Persistance in Ruby for MongoDB.