mongoid 2.4.0 → 2.4.1

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.
@@ -3,7 +3,55 @@
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.0 \[ In Development \] \[ Branch: master \]
6
+ ## 2.4.1 \[ In Development \] \[ Branch: 2.4.0-stable \]
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#1593 Arrays on embedded documents now properly atomically update when
11
+ modified from original version.
12
+
13
+ * \#1592 Don't swallow exceptions from index generation in the create_indexes
14
+ rake task.
15
+
16
+ * \#1589 Allow assignment of empty array to HABTM when no documents are yet
17
+ loaded into memory.
18
+
19
+ * \#1587 When a previous value for an array field was an explicit nil, it can
20
+ now be reset atomically with new values.
21
+
22
+ * \#1585 `Model#respond_to?` returns true now for the setter when allowing
23
+ dynamic fields.
24
+
25
+ * \#1582 Allow nil values to be set in arrays.
26
+
27
+ * \#1580 Allow arrays to be set to nil post save, and not just empty.
28
+
29
+ * \#1579 Don't call #to_a on individual set field elements in criterion.
30
+
31
+ * \#1576 Don't hit database on uniqueness validation if the field getting
32
+ validated has not changed.
33
+
34
+ * \#1571 Aliased fields get all the dirty attribute methods and all getters and
35
+ setters for both the original name and the alias. (Hans Hasselberg)
36
+
37
+ * \#1568 Fallback to development environment with warning when no env configured.
38
+
39
+ * \#1565 For fields and foreign keys with non-standard Ruby or database names,
40
+ use define_method instead of class_eval for creating the accessors and
41
+ dirty methods.
42
+
43
+ * \#1557 Internal strategy class no longer conflicts with models.
44
+
45
+ * \#1551 Parent documents now return `true` for `Model#changed?` if only child
46
+ (embedded) documents have changed.
47
+
48
+ * \#1547 Resetting persisted children from a parent save when new waits until post
49
+ callbacks, mirroring update functionality.
50
+
51
+ * \#1536 Eager loading now happens when calling `first` or `last` on a
52
+ criteria if inclusions are specified.
53
+
54
+ ## 2.4.0
7
55
 
8
56
  ### New Features
9
57
 
@@ -77,7 +77,7 @@ module Mongoid #:nodoc:
77
77
  def respond_to?(*args)
78
78
  (Mongoid.allow_dynamic_fields &&
79
79
  attributes &&
80
- attributes.has_key?(args.first.to_s)
80
+ attributes.has_key?(args.first.to_s.reader)
81
81
  ) || super
82
82
  end
83
83
 
@@ -19,7 +19,7 @@ module Mongoid #:nodoc
19
19
  def env_name
20
20
  return Rails.env if defined?(Rails)
21
21
  return Sinatra::Base.environment.to_s if defined?(Sinatra)
22
- ENV["RACK_ENV"] || raise(Errors::NoEnvironment.new)
22
+ ENV["RACK_ENV"] || ENV["MONGOID_ENV"] || raise(Errors::NoEnvironment.new)
23
23
  end
24
24
 
25
25
  # Load the yaml from the provided path and return the settings for the
@@ -135,6 +135,26 @@ module Mongoid #:nodoc:
135
135
  klass.collection.distinct(field, selector)
136
136
  end
137
137
 
138
+ # Eager load the inclusions for the provided documents.
139
+ #
140
+ # @example Eager load the inclusions.
141
+ # context.eager_load(docs)
142
+ #
143
+ # @param [ Array<Document> ] docs The docs returning from the db.
144
+ #
145
+ # @since 2.4.1
146
+ def eager_load(docs)
147
+ parent_ids = docs.map(&:id)
148
+ 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)
154
+ end
155
+ end
156
+ end
157
+
138
158
  # Execute the context. This will take the selector and options
139
159
  # and pass them on to the Ruby driver's +find()+ method on the collection. The
140
160
  # collection itself will be retrieved from the class provided, and once the
@@ -148,15 +168,7 @@ module Mongoid #:nodoc:
148
168
  collection, options = klass.collection, process_options
149
169
  if criteria.inclusions.any?
150
170
  collection.find(selector, options).entries.tap do |docs|
151
- parent_ids = docs.map(&:id)
152
- criteria.inclusions.reject! do |metadata|
153
- if metadata.macro == :referenced_in
154
- child_ids = load_ids(metadata.foreign_key)
155
- metadata.eager_load(child_ids)
156
- else
157
- metadata.eager_load(parent_ids)
158
- end
159
- end
171
+ eager_load(docs)
160
172
  end
161
173
  else
162
174
  collection.find(selector, options)
@@ -189,7 +201,10 @@ module Mongoid #:nodoc:
189
201
  # @return [ Document ] The first document in the collection.
190
202
  def first
191
203
  attributes = klass.collection.find_one(selector, options_with_default_sorting)
192
- attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
204
+ return nil unless attributes
205
+ Mongoid::Factory.from_db(klass, attributes).tap do |doc|
206
+ eager_load([ doc ]) if criteria.inclusions.any?
207
+ end
193
208
  end
194
209
  alias :one :first
195
210
 
@@ -255,7 +270,10 @@ module Mongoid #:nodoc:
255
270
  opts = options_with_default_sorting
256
271
  opts[:sort] = opts[:sort].map{ |option| [ option[0], option[1].invert ] }.uniq
257
272
  attributes = klass.collection.find_one(selector, opts)
258
- attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
273
+ return nil unless attributes
274
+ Mongoid::Factory.from_db(klass, attributes).tap do |doc|
275
+ eager_load([ doc ]) if criteria.inclusions.any?
276
+ end
259
277
  end
260
278
 
261
279
  # Return the max value for a field.
@@ -85,11 +85,29 @@ module Mongoid #:nodoc:
85
85
  # @since 1.0.0
86
86
  def try_to_typecast(key, value)
87
87
  access = key.to_s
88
- if !fields.has_key?(access) && !aliased_fields.has_key?(access)
89
- return value
88
+ if field = fields[key.to_s] || fields[aliased_fields[key.to_s]]
89
+ typecast_value_for(field, value)
90
+ elsif proper_and_or_value?(key, value)
91
+ handle_and_or_value(value)
92
+ else
93
+ value
94
+ end
95
+ end
96
+
97
+ def proper_and_or_value?(key, value)
98
+ ["$and", "$or"].include?(key) &&
99
+ value.is_a?(Array) &&
100
+ value.all?{ |e| e.is_a?(Hash) }
101
+ end
102
+
103
+ def handle_and_or_value(values)
104
+ [].tap do |result|
105
+ values.map do |value|
106
+ value.each do |key, value|
107
+ result.push key => try_to_typecast(key, value)
108
+ end
109
+ end
90
110
  end
91
- field = fields[access] || fields[aliased_fields[access]]
92
- typecast_value_for(field, value)
93
111
  end
94
112
 
95
113
  # Get the typecast value for the defined field.
@@ -24,7 +24,21 @@ module Mongoid #:nodoc:
24
24
  #
25
25
  # @since 2.4.0
26
26
  def changed?
27
- changed_attributes.any?
27
+ changed_attributes.any? || children_changed?
28
+ end
29
+
30
+ # Have any children (embedded documents) of this document changed?
31
+ #
32
+ # @example Have any children changed?
33
+ # model.children_changed?
34
+ #
35
+ # @return [ true, false ] If any children have changed.
36
+ #
37
+ # @since 2.4.1
38
+ def children_changed?
39
+ _children.any? do |child|
40
+ child.changed?
41
+ end
28
42
  end
29
43
 
30
44
  # Get the attribute changes.
@@ -117,8 +131,14 @@ module Mongoid #:nodoc:
117
131
  field = fields[name]
118
132
  key = embedded? ? "#{atomic_position}.#{name}" : name
119
133
  if field && field.resizable?
120
- pushes, pulls = new - (old || []), (old || []) - new
121
- field.add_atomic_changes(self, key, modifications, pushes, pulls)
134
+ field.add_atomic_changes(
135
+ self,
136
+ name,
137
+ key,
138
+ modifications,
139
+ new,
140
+ old
141
+ )
122
142
  else
123
143
  modifications[key] = new
124
144
  end
@@ -207,36 +227,59 @@ module Mongoid #:nodoc:
207
227
  # Generate all the dirty methods needed for the attribute.
208
228
  #
209
229
  # @example Generate the dirty methods.
210
- # Model.create_dirty_methods("name")
230
+ # Model.create_dirty_methods("name", "name")
211
231
  #
212
- # @param [ String ] name The name of the attribute.
232
+ # @param [ String ] name The name of the field.
233
+ # @param [ String ] name The name of the accessor.
213
234
  #
214
235
  # @return [ Module ] The fields module.
215
236
  #
216
237
  # @since 2.4.0
217
- def create_dirty_methods(name)
238
+ def create_dirty_methods(name, meth)
218
239
  generated_methods.module_eval do
219
- class_eval <<-EOM
220
- def #{name}_change
221
- attribute_change(#{name.inspect})
240
+ if meth =~ /\W/
241
+ define_method("#{meth}_change") do
242
+ attribute_change(name)
222
243
  end
223
244
 
224
- def #{name}_changed?
225
- attribute_changed?(#{name.inspect})
245
+ define_method("#{meth}_changed?") do
246
+ attribute_changed?(name)
226
247
  end
227
248
 
228
- def #{name}_was
229
- attribute_was(#{name.inspect})
249
+ define_method("#{meth}_was") do
250
+ attribute_was(name)
230
251
  end
231
252
 
232
- def #{name}_will_change!
233
- attribute_will_change!(#{name.inspect})
253
+ define_method("#{meth}_will_change!") do
254
+ attribute_will_change!(name)
234
255
  end
235
256
 
236
- def reset_#{name}!
237
- reset_attribute!(#{name.inspect})
257
+ define_method("reset_#{meth}!") do
258
+ reset_attribute!(name)
238
259
  end
239
- EOM
260
+ else
261
+ class_eval <<-EOM
262
+ def #{meth}_change
263
+ attribute_change(#{name.inspect})
264
+ end
265
+
266
+ def #{meth}_changed?
267
+ attribute_changed?(#{name.inspect})
268
+ end
269
+
270
+ def #{meth}_was
271
+ attribute_was(#{name.inspect})
272
+ end
273
+
274
+ def #{meth}_will_change!
275
+ attribute_will_change!(#{name.inspect})
276
+ end
277
+
278
+ def reset_#{meth}!
279
+ reset_attribute!(#{name.inspect})
280
+ end
281
+ EOM
282
+ end
240
283
  end
241
284
  end
242
285
  end
@@ -100,7 +100,7 @@ module Mongoid #:nodoc:
100
100
  #
101
101
  # @return [ String ] The string stripped of "=".
102
102
  def reader
103
- writer? ? gsub("=", "") : self
103
+ delete("=")
104
104
  end
105
105
 
106
106
  # Is this string a writer?
@@ -276,14 +276,15 @@ module Mongoid #:nodoc
276
276
  def add_field(name, options = {})
277
277
  aliased = options[:as]
278
278
  aliased_fields[aliased.to_s] = name if aliased
279
- meth = aliased || name
280
279
  type = options[:localize] ? Fields::Internal::Localized : options[:type]
281
280
  Mappings.for(type, options[:identity]).instantiate(name, options).tap do |field|
282
281
  fields[name] = field
283
282
  add_defaults(field)
284
- create_accessors(name, meth, options)
283
+ create_accessors(name, name, options)
284
+ create_accessors(name, aliased, options) if aliased
285
285
  process_options(field)
286
- create_dirty_methods(name)
286
+ create_dirty_methods(name, name)
287
+ create_dirty_methods(name, aliased) if aliased
287
288
  end
288
289
  end
289
290
 
@@ -337,43 +338,164 @@ module Mongoid #:nodoc
337
338
  # @param [ Symbol ] name The name of the field.
338
339
  # @param [ Symbol ] meth The name of the accessor.
339
340
  # @param [ Hash ] options The options.
341
+ #
342
+ # @since 2.0.0
340
343
  def create_accessors(name, meth, options = {})
341
344
  field = fields[name]
345
+
346
+ create_field_getter(name, meth, field)
347
+ create_field_setter(name, meth)
348
+ create_field_check(name, meth)
349
+
350
+ if options[:localize]
351
+ create_translations_getter(name, meth)
352
+ create_translations_setter(name, meth)
353
+ end
354
+ end
355
+
356
+ # Create the getter method for the provided field.
357
+ #
358
+ # @example Create the getter.
359
+ # Model.create_field_getter("name", "name", field)
360
+ #
361
+ # @param [ String ] name The name of the attribute.
362
+ # @param [ String ] meth The name of the method.
363
+ # @param [ Field ] field The field.
364
+ #
365
+ # @since 2.4.0
366
+ def create_field_getter(name, meth, field)
342
367
  generated_methods.module_eval do
343
- if field.cast_on_read?
344
- class_eval <<-EOM
345
- def #{meth}
346
- fields[#{name.inspect}].deserialize(read_attribute(#{name.inspect}))
368
+ if meth =~ /\W/
369
+ if field.cast_on_read?
370
+ define_method(meth) do
371
+ fields[name].deserialize(read_attribute(name))
347
372
  end
348
- EOM
349
- else
350
- class_eval <<-EOM
351
- def #{meth}
352
- read_attribute(#{name.inspect}).tap do |value|
373
+ else
374
+ define_method(meth) do
375
+ read_attribute(name).tap do |value|
353
376
  if value.is_a?(Array) || value.is_a?(Hash)
354
- attribute_will_change!(#{name.inspect})
377
+ attribute_will_change!(name)
355
378
  end
356
379
  end
357
380
  end
358
- EOM
381
+ end
382
+ else
383
+ if field.cast_on_read?
384
+ class_eval <<-EOM
385
+ def #{meth}
386
+ fields[#{name.inspect}].deserialize(read_attribute(#{name.inspect}))
387
+ end
388
+ EOM
389
+ else
390
+ class_eval <<-EOM
391
+ def #{meth}
392
+ read_attribute(#{name.inspect}).tap do |value|
393
+ if value.is_a?(Array) || value.is_a?(Hash)
394
+ attribute_will_change!(#{name.inspect})
395
+ end
396
+ end
397
+ end
398
+ EOM
399
+ end
359
400
  end
360
- class_eval <<-EOM
361
- def #{meth}=(value)
362
- write_attribute(#{name.inspect}, value)
401
+ end
402
+ end
403
+
404
+ # Create the setter method for the provided field.
405
+ #
406
+ # @example Create the setter.
407
+ # Model.create_field_setter("name", "name")
408
+ #
409
+ # @param [ String ] name The name of the attribute.
410
+ # @param [ String ] meth The name of the method.
411
+ #
412
+ # @since 2.4.0
413
+ def create_field_setter(name, meth)
414
+ generated_methods.module_eval do
415
+ if meth =~ /\W/
416
+ define_method(meth) do |value|
417
+ write_attribute(name, value)
363
418
  end
419
+ else
420
+ class_eval <<-EOM
421
+ def #{meth}=(value)
422
+ write_attribute(#{name.inspect}, value)
423
+ end
424
+ EOM
425
+ end
426
+ end
427
+ end
364
428
 
365
- def #{meth}?
366
- attr = read_attribute(#{name.inspect})
429
+ # Create the check method for the provided field.
430
+ #
431
+ # @example Create the check.
432
+ # Model.create_field_check("name", "name")
433
+ #
434
+ # @param [ String ] name The name of the attribute.
435
+ # @param [ String ] meth The name of the method.
436
+ #
437
+ # @since 2.4.0
438
+ def create_field_check(name, meth)
439
+ generated_methods.module_eval do
440
+ if meth =~ /\W/
441
+ define_method("#{meth}?") do
442
+ attr = read_attribute(name)
367
443
  attr == true || attr.present?
368
444
  end
369
- EOM
445
+ else
446
+ class_eval <<-EOM
447
+ def #{meth}?
448
+ attr = read_attribute(#{name.inspect})
449
+ attr == true || attr.present?
450
+ end
451
+ EOM
452
+ end
453
+ end
454
+ end
370
455
 
371
- if options[:localize]
456
+ # Create the translation getter method for the provided field.
457
+ #
458
+ # @example Create the translation getter.
459
+ # Model.create_translations_getter("name", "name")
460
+ #
461
+ # @param [ String ] name The name of the attribute.
462
+ # @param [ String ] meth The name of the method.
463
+ #
464
+ # @since 2.4.0
465
+ def create_translations_getter(name, meth)
466
+ generated_methods.module_eval do
467
+ if meth =~ /\W/
468
+ define_method("#{meth}_translations") do
469
+ attributes[name]
470
+ end
471
+ else
372
472
  class_eval <<-EOM
373
473
  def #{meth}_translations
374
474
  attributes[#{name.inspect}]
375
475
  end
476
+ EOM
477
+ end
478
+ end
479
+ end
376
480
 
481
+ # Create the translation setter method for the provided field.
482
+ #
483
+ # @example Create the translation setter.
484
+ # Model.create_translations_setter("name", "name")
485
+ #
486
+ # @param [ String ] name The name of the attribute.
487
+ # @param [ String ] meth The name of the method.
488
+ #
489
+ # @since 2.4.0
490
+ def create_translations_setter(name, meth)
491
+ generated_methods.module_eval do
492
+ if meth =~ /\W/
493
+ define_method("#{meth}_translations=") do |value|
494
+ attribute_will_change!(name)
495
+ attributes[name] = value
496
+ end
497
+ else
498
+ class_eval <<-EOM
377
499
  def #{meth}_translations=(value)
378
500
  attribute_will_change!(#{name.inspect})
379
501
  attributes[#{name.inspect}] = value
@@ -387,11 +509,13 @@ module Mongoid #:nodoc
387
509
  #
388
510
  # @example Include the fields.
389
511
  # Person.generated_methods
512
+ #
513
+ # @return [ Module ] The module of generated methods.
514
+ #
515
+ # @since 2.0.0
390
516
  def generated_methods
391
517
  @generated_methods ||= begin
392
- Module.new.tap do |mod|
393
- include mod
394
- end
518
+ Module.new.tap { |mod| include(mod) }
395
519
  end
396
520
  end
397
521
  end
@@ -13,19 +13,24 @@ module Mongoid #:nodoc:
13
13
  # field.add_atomic_changes(doc, "key", {}, [], [])
14
14
  #
15
15
  # @param [ Document ] document The document to add to.
16
- # @param [ String ] key The name of the field.
16
+ # @param [ String ] name The name of the field.
17
+ # @param [ String ] key The atomic location of the field.
17
18
  # @param [ Hash ] mods The current modifications.
18
19
  # @param [ Array ] new The new elements to add.
19
20
  # @param [ Array ] old The old elements getting removed.
20
21
  #
21
22
  # @since 2.4.0
22
- def add_atomic_changes(document, key, mods, new, old)
23
- if new.any? && old.any?
24
- mods[key] = document.attributes[key]
25
- elsif new.any?
26
- document.atomic_array_pushes[key] = new
27
- elsif old.any?
28
- document.atomic_array_pulls[key] = old
23
+ def add_atomic_changes(document, name, key, mods, new, old)
24
+ pushes = (new || []) - (old || [])
25
+ pulls = (old || []) - (new || [])
26
+ if old.nil?
27
+ mods[key] = pushes
28
+ elsif !pushes.empty? && !pulls.empty?
29
+ mods[key] = document.attributes[name]
30
+ elsif !pushes.empty?
31
+ document.atomic_array_pushes[key] = pushes
32
+ elsif !pulls.empty?
33
+ document.atomic_array_pulls[key] = pulls
29
34
  end
30
35
  end
31
36
 
@@ -14,19 +14,24 @@ module Mongoid #:nodoc:
14
14
  # field.add_atomic_changes(doc, "key", {}, [], [])
15
15
  #
16
16
  # @param [ Document ] document The document to add to.
17
- # @param [ String ] key The name of the field.
17
+ # @param [ String ] name The name of the field.
18
+ # @param [ String ] key The atomic location of the field.
18
19
  # @param [ Hash ] mods The current modifications.
19
20
  # @param [ Array ] new The new elements to add.
20
21
  # @param [ Array ] old The old elements getting removed.
21
22
  #
22
23
  # @since 2.4.0
23
- def add_atomic_changes(document, key, mods, new, old)
24
- if new.any? && old.any?
25
- mods[key] = document.attributes[key]
26
- elsif new.any?
27
- document.atomic_array_add_to_sets[key] = new
28
- elsif old.any?
29
- document.atomic_array_pulls[key] = old
24
+ def add_atomic_changes(document, name, key, mods, new, old)
25
+ pushes = (new || []) - (old || [])
26
+ pulls = (old || []) - (new || [])
27
+ if old.nil?
28
+ mods[key] = pushes
29
+ elsif !pushes.empty? && !pulls.empty?
30
+ mods[key] = document.attributes[name]
31
+ elsif !pushes.empty?
32
+ document.atomic_array_add_to_sets[key] = pushes
33
+ elsif !pulls.empty?
34
+ document.atomic_array_pulls[key] = pulls
30
35
  end
31
36
  end
32
37
 
@@ -33,7 +33,7 @@ module Mongoid #:nodoc:
33
33
  #
34
34
  # @since 2.4.0
35
35
  def selection(object)
36
- return object if object.is_a?(::Hash)
36
+ return object unless object.is_a?(::Set)
37
37
  serialize(object)
38
38
  end
39
39
 
@@ -25,11 +25,12 @@ module Mongoid #:nodoc:
25
25
  doc.run_callbacks(:create) do
26
26
  yield(doc)
27
27
  doc.new_record = false
28
- doc.reset_persisted_children and true
28
+ true
29
29
  end
30
30
  end
31
31
 
32
32
  unless result == false
33
+ doc.reset_persisted_children
33
34
  doc.move_changes
34
35
  Threaded.clear_options!
35
36
  end
@@ -42,15 +42,6 @@ module Mongoid #:nodoc:
42
42
  def persist
43
43
  prepare do
44
44
  unless updates.empty?
45
- # @todo Durran: This is a temporary fix for #791 until we rewrite
46
- # the dirty tracking to properly flag a document as changed if
47
- # only embedded documents have changed.
48
- if document.respond_to?(:updated_at)
49
- if document.timestamping? && !document.updated_at_changed?
50
- document.updated_at = Time.now
51
- end
52
- end
53
-
54
45
  collection.update(selector, updates, options)
55
46
  conflicts.each_pair do |key, value|
56
47
  collection.update(selector, { key => value }, options)
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require "mongoid/relations/cascading/strategy"
3
2
  require "mongoid/relations/cascading/delete"
4
3
  require "mongoid/relations/cascading/destroy"
5
4
  require "mongoid/relations/cascading/nullify"
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Delete < Strategy
5
+ class Delete
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # Execute the cascading deletion for the relation if it already exists.
8
25
  # This should be optimized in the future potentially not to load all
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Destroy < Strategy
5
+ class Destroy
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # Execute the cascading deletion for the relation if it already exists.
8
25
  # This should be optimized in the future potentially not to load all
@@ -2,7 +2,24 @@
2
2
  module Mongoid # :nodoc:
3
3
  module Relations #:nodoc:
4
4
  module Cascading #:nodoc:
5
- class Nullify < Strategy
5
+ class Nullify
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
6
23
 
7
24
  # This cascade does not delete the referenced relations, but instead
8
25
  # sets the foreign key values to nil.
@@ -319,15 +319,13 @@ module Mongoid #:nodoc:
319
319
  def substitute(replacement)
320
320
  tap do |proxy|
321
321
  if replacement
322
- if replacement != proxy.in_memory
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
328
- end
329
- proxy.concat(docs)
322
+ new_docs, docs = replacement.compact.uniq, []
323
+ new_ids = new_docs.map { |doc| doc.id }
324
+ remove_not_in(new_ids)
325
+ new_docs.each do |doc|
326
+ docs.push(doc) if doc.send(metadata.foreign_key) != base.id
330
327
  end
328
+ proxy.concat(docs)
331
329
  else
332
330
  proxy.purge
333
331
  end
@@ -142,10 +142,8 @@ module Mongoid # :nodoc:
142
142
  # @since 2.0.0.rc.1
143
143
  def substitute(replacement)
144
144
  tap do |proxy|
145
- if replacement != proxy.in_memory
146
- proxy.purge
147
- proxy.push(replacement.compact.uniq) if replacement
148
- end
145
+ proxy.purge
146
+ proxy.push(replacement.compact.uniq) unless replacement.blank?
149
147
  end
150
148
  end
151
149
 
@@ -41,6 +41,7 @@ module Mongoid #:nodoc:
41
41
  #
42
42
  # @since 1.0.0
43
43
  def validate_each(document, attribute, value)
44
+ return unless document.send("#{attribute}_changed?")
44
45
  if document.embedded?
45
46
  return if skip_validation?(document)
46
47
  relation = document._parent.send(document.metadata.name)
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
- VERSION = "2.4.0"
3
+ VERSION = "2.4.1"
4
4
  end
@@ -15,22 +15,21 @@ module Rails #:nodoc:
15
15
  #
16
16
  # @since 2.1.0
17
17
  def create_indexes(pattern)
18
- logger = Logger.new($stdout)
19
18
  Dir.glob(pattern).each do |file|
20
19
  logger = Logger.new($stdout)
21
20
  begin
22
21
  model = determine_model(file)
23
- if model
24
- model.create_indexes
25
- logger.info("Generated indexes for #{model}")
26
- else
27
- logger.info("Not a Mongoid parent model: #{file}")
28
- end
29
22
  rescue => e
30
- logger.error %Q{Failed to create indexes for #{model}:
23
+ logger.error(%Q{Failed to determine model from #{file}:
31
24
  #{e.class}:#{e.message}
32
25
  #{e.backtrace.join("\n")}
33
- }
26
+ })
27
+ end
28
+ if model
29
+ model.create_indexes
30
+ logger.info("Generated indexes for #{model}")
31
+ else
32
+ logger.info("Not a Mongoid parent model: #{file}")
34
33
  end
35
34
  end
36
35
  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.0
4
+ version: 2.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-05 00:00:00.000000000 Z
12
+ date: 2012-01-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
16
- requirement: &70168048476500 !ruby/object:Gem::Requirement
16
+ requirement: &70092776306560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70168048476500
24
+ version_requirements: *70092776306560
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: tzinfo
27
- requirement: &70168048475740 !ruby/object:Gem::Requirement
27
+ requirement: &70092776305140 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.3.22
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70168048475740
35
+ version_requirements: *70092776305140
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mongo
38
- requirement: &70168048474680 !ruby/object:Gem::Requirement
38
+ requirement: &70092776302420 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '1.3'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70168048474680
46
+ version_requirements: *70092776302420
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdoc
49
- requirement: &70168048473620 !ruby/object:Gem::Requirement
49
+ requirement: &70092776301540 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 3.5.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70168048473620
57
+ version_requirements: *70092776301540
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bson_ext
60
- requirement: &70168048473140 !ruby/object:Gem::Requirement
60
+ requirement: &70092776300860 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '1.3'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70168048473140
68
+ version_requirements: *70092776300860
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &70168048472640 !ruby/object:Gem::Requirement
71
+ requirement: &70092776296740 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.9.12
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70168048472640
79
+ version_requirements: *70092776296740
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70168048472160 !ruby/object:Gem::Requirement
82
+ requirement: &70092776295300 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '2.6'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70168048472160
90
+ version_requirements: *70092776295300
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: watchr
93
- requirement: &70168048471300 !ruby/object:Gem::Requirement
93
+ requirement: &70092776293380 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0.6'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70168048471300
101
+ version_requirements: *70092776293380
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: ammeter
104
- requirement: &70168048470800 !ruby/object:Gem::Requirement
104
+ requirement: &70092776292500 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: 0.1.3
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70168048470800
112
+ version_requirements: *70092776292500
113
113
  description: Mongoid is an ODM (Object Document Mapper) Framework for MongoDB, written
114
114
  in Ruby.
115
115
  email:
@@ -332,7 +332,6 @@ files:
332
332
  - lib/mongoid/relations/cascading/delete.rb
333
333
  - lib/mongoid/relations/cascading/destroy.rb
334
334
  - lib/mongoid/relations/cascading/nullify.rb
335
- - lib/mongoid/relations/cascading/strategy.rb
336
335
  - lib/mongoid/relations/cascading.rb
337
336
  - lib/mongoid/relations/constraint.rb
338
337
  - lib/mongoid/relations/conversions.rb
@@ -412,7 +411,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
412
411
  version: '0'
413
412
  segments:
414
413
  - 0
415
- hash: -3374480126099241138
414
+ hash: 1067878638816238851
416
415
  required_rubygems_version: !ruby/object:Gem::Requirement
417
416
  none: false
418
417
  requirements:
@@ -1,26 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid # :nodoc:
3
- module Relations #:nodoc:
4
- module Cascading #:nodoc:
5
- class Strategy
6
-
7
- attr_accessor :document, :relation, :metadata
8
-
9
- # Initialize the new cascade strategy, which will set up the relation
10
- # and the metadata.
11
- #
12
- # @example Instantiate the strategy
13
- # Strategy.new(document, metadata)
14
- #
15
- # @param [ Document ] document The document to cascade from.
16
- # @param [ Metadata ] metadata The relation's metadata.
17
- #
18
- # @return [ Strategy ] The new strategy.
19
- def initialize(document, metadata)
20
- @document, @metadata = document, metadata
21
- @relation = document.send(metadata.name)
22
- end
23
- end
24
- end
25
- end
26
- end