mongoid 2.4.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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