mongoid 2.3.5 → 2.4.0

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.
Files changed (126) hide show
  1. data/CHANGELOG.md +34 -176
  2. data/LICENSE +1 -1
  3. data/lib/config/locales/bg.yml +6 -0
  4. data/lib/config/locales/de.yml +6 -0
  5. data/lib/config/locales/en-GB.yml +8 -0
  6. data/lib/config/locales/en.yml +8 -0
  7. data/lib/config/locales/es.yml +9 -3
  8. data/lib/config/locales/fr.yml +6 -0
  9. data/lib/config/locales/hi.yml +6 -0
  10. data/lib/config/locales/hu.yml +6 -0
  11. data/lib/config/locales/id.yml +6 -0
  12. data/lib/config/locales/it.yml +6 -0
  13. data/lib/config/locales/ja.yml +6 -0
  14. data/lib/config/locales/kr.yml +6 -0
  15. data/lib/config/locales/nl.yml +8 -0
  16. data/lib/config/locales/pl.yml +6 -0
  17. data/lib/config/locales/pt-BR.yml +6 -0
  18. data/lib/config/locales/pt.yml +8 -2
  19. data/lib/config/locales/ro.yml +6 -0
  20. data/lib/config/locales/ru.yml +6 -0
  21. data/lib/config/locales/sv.yml +6 -0
  22. data/lib/config/locales/vi.yml +14 -8
  23. data/lib/config/locales/zh-CN.yml +6 -0
  24. data/lib/mongoid/atomic.rb +62 -13
  25. data/lib/mongoid/atomic/modifiers.rb +33 -1
  26. data/lib/mongoid/attributes.rb +5 -19
  27. data/lib/mongoid/callbacks.rb +2 -1
  28. data/lib/mongoid/collection.rb +2 -2
  29. data/lib/mongoid/collections/retry.rb +18 -6
  30. data/lib/mongoid/components.rb +2 -0
  31. data/lib/mongoid/config.rb +8 -63
  32. data/lib/mongoid/config/environment.rb +41 -0
  33. data/lib/mongoid/config/options.rb +74 -0
  34. data/lib/mongoid/contexts/enumerable.rb +0 -24
  35. data/lib/mongoid/contexts/mongo.rb +33 -3
  36. data/lib/mongoid/copyable.rb +1 -1
  37. data/lib/mongoid/criteria.rb +4 -2
  38. data/lib/mongoid/criterion/inclusion.rb +1 -16
  39. data/lib/mongoid/criterion/optional.rb +37 -10
  40. data/lib/mongoid/criterion/scoping.rb +83 -0
  41. data/lib/mongoid/criterion/selector.rb +9 -6
  42. data/lib/mongoid/default_scope.rb +1 -1
  43. data/lib/mongoid/dirty.rb +163 -29
  44. data/lib/mongoid/document.rb +58 -7
  45. data/lib/mongoid/errors.rb +2 -0
  46. data/lib/mongoid/errors/no_environment.rb +19 -0
  47. data/lib/mongoid/errors/scope_overwrite.rb +21 -0
  48. data/lib/mongoid/extensions.rb +6 -0
  49. data/lib/mongoid/extensions/array/deep_copy.rb +25 -0
  50. data/lib/mongoid/extensions/hash/deep_copy.rb +25 -0
  51. data/lib/mongoid/extensions/hash/scoping.rb +1 -1
  52. data/lib/mongoid/extensions/object/deep_copy.rb +21 -0
  53. data/lib/mongoid/extensions/proc/scoping.rb +2 -2
  54. data/lib/mongoid/extensions/symbol/inflections.rb +1 -0
  55. data/lib/mongoid/fields.rb +171 -104
  56. data/lib/mongoid/fields/{serializable → internal}/array.rb +33 -1
  57. data/lib/mongoid/fields/{serializable → internal}/big_decimal.rb +16 -1
  58. data/lib/mongoid/fields/{serializable → internal}/bignum.rb +1 -1
  59. data/lib/mongoid/fields/{serializable → internal}/binary.rb +1 -1
  60. data/lib/mongoid/fields/{serializable → internal}/boolean.rb +16 -1
  61. data/lib/mongoid/fields/{serializable → internal}/date.rb +1 -1
  62. data/lib/mongoid/fields/{serializable → internal}/date_time.rb +1 -1
  63. data/lib/mongoid/fields/{serializable → internal}/fixnum.rb +1 -1
  64. data/lib/mongoid/fields/{serializable → internal}/float.rb +16 -1
  65. data/lib/mongoid/fields/internal/foreign_keys/array.rb +74 -0
  66. data/lib/mongoid/fields/{serializable → internal}/foreign_keys/object.rb +11 -2
  67. data/lib/mongoid/fields/{serializable → internal}/hash.rb +1 -1
  68. data/lib/mongoid/fields/{serializable → internal}/integer.rb +16 -1
  69. data/lib/mongoid/fields/{serializable → internal}/localized.rb +23 -2
  70. data/lib/mongoid/fields/{serializable → internal}/nil_class.rb +16 -1
  71. data/lib/mongoid/fields/{serializable → internal}/object.rb +1 -1
  72. data/lib/mongoid/fields/{serializable → internal}/object_id.rb +16 -1
  73. data/lib/mongoid/fields/{serializable → internal}/range.rb +21 -2
  74. data/lib/mongoid/fields/{serializable → internal}/set.rb +16 -1
  75. data/lib/mongoid/fields/{serializable → internal}/string.rb +16 -1
  76. data/lib/mongoid/fields/{serializable → internal}/symbol.rb +17 -1
  77. data/lib/mongoid/fields/{serializable → internal}/time.rb +1 -1
  78. data/lib/mongoid/fields/{serializable → internal}/time_with_zone.rb +1 -1
  79. data/lib/mongoid/fields/{serializable → internal}/timekeeping.rb +16 -1
  80. data/lib/mongoid/fields/mappings.rb +8 -3
  81. data/lib/mongoid/fields/serializable.rb +34 -3
  82. data/lib/mongoid/hierarchy.rb +14 -14
  83. data/lib/mongoid/identity_map.rb +3 -2
  84. data/lib/mongoid/logger.rb +1 -7
  85. data/lib/mongoid/named_scope.rb +16 -12
  86. data/lib/mongoid/observer.rb +5 -1
  87. data/lib/mongoid/paranoia.rb +1 -0
  88. data/lib/mongoid/persistence.rb +11 -4
  89. data/lib/mongoid/persistence/atomic.rb +4 -1
  90. data/lib/mongoid/persistence/atomic/add_to_set.rb +17 -1
  91. data/lib/mongoid/persistence/atomic/sets.rb +1 -1
  92. data/lib/mongoid/railties/database.rake +1 -1
  93. data/lib/mongoid/relations.rb +1 -3
  94. data/lib/mongoid/relations/auto_save.rb +1 -1
  95. data/lib/mongoid/relations/builders.rb +1 -1
  96. data/lib/mongoid/relations/builders/embedded/many.rb +2 -6
  97. data/lib/mongoid/relations/builders/nested_attributes/many.rb +1 -1
  98. data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
  99. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +1 -1
  100. data/lib/mongoid/relations/cascading/delete.rb +1 -1
  101. data/lib/mongoid/relations/cyclic.rb +10 -6
  102. data/lib/mongoid/relations/embedded/atomic.rb +3 -3
  103. data/lib/mongoid/relations/embedded/many.rb +98 -20
  104. data/lib/mongoid/relations/macros.rb +2 -0
  105. data/lib/mongoid/relations/many.rb +13 -0
  106. data/lib/mongoid/relations/metadata.rb +3 -3
  107. data/lib/mongoid/relations/nested_builder.rb +4 -3
  108. data/lib/mongoid/relations/proxy.rb +0 -1
  109. data/lib/mongoid/relations/referenced/batch.rb +3 -2
  110. data/lib/mongoid/relations/referenced/in.rb +3 -3
  111. data/lib/mongoid/relations/referenced/many.rb +89 -10
  112. data/lib/mongoid/relations/referenced/many_to_many.rb +34 -43
  113. data/lib/mongoid/relations/referenced/one.rb +8 -4
  114. data/lib/mongoid/relations/synchronization.rb +22 -5
  115. data/lib/mongoid/threaded.rb +38 -276
  116. data/lib/mongoid/threaded/lifecycle.rb +18 -18
  117. data/lib/mongoid/timestamps/updated.rb +13 -3
  118. data/lib/mongoid/validations.rb +22 -1
  119. data/lib/mongoid/validations/presence.rb +40 -0
  120. data/lib/mongoid/validations/uniqueness.rb +14 -3
  121. data/lib/mongoid/version.rb +1 -1
  122. data/lib/mongoid/versioning.rb +6 -2
  123. data/lib/rails/mongoid.rb +7 -1
  124. metadata +64 -45
  125. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +0 -42
  126. data/lib/mongoid/relations/embedded/sort.rb +0 -31
@@ -116,6 +116,19 @@ module Mongoid #:nodoc:
116
116
  target.map { |document| document.serializable_hash(options) }
117
117
  end
118
118
 
119
+ # Get a criteria for the embedded documents without the default scoping
120
+ # applied.
121
+ #
122
+ # @example Get the unscoped criteria.
123
+ # person.addresses.unscoped
124
+ #
125
+ # @return [ Criteria ] The unscoped criteria.
126
+ #
127
+ # @since 2.4.0
128
+ def unscoped
129
+ criteria.unscoped
130
+ end
131
+
119
132
  private
120
133
 
121
134
  # Find the first object given the supplied attributes or create/initialize it.
@@ -197,13 +197,13 @@ module Mongoid # :nodoc:
197
197
  # @example Get the eager loading criteria.
198
198
  # metadata.eager_load(criteria)
199
199
  #
200
- # @param [ Criteria ] criteria The criteria to load from.
200
+ # @param [ Array<Object> ] ids The ids of the returned parents.
201
201
  #
202
202
  # @return [ Criteria ] The eager loading criteria.
203
203
  #
204
204
  # @since 2.2.0
205
- def eager_load(criteria)
206
- relation.eager_load(self, criteria.clone)
205
+ def eager_load(ids)
206
+ relation.eager_load(self, ids)
207
207
  end
208
208
 
209
209
  # Will determine if the relation is an embedded one or not. Currently
@@ -60,15 +60,16 @@ module Mongoid # :nodoc:
60
60
  # @todo Durran: Move this into a common reusable place.
61
61
  #
62
62
  # @example Convert the id.
63
- # builder.convert_id("4d371b444835d98b8b000010")
63
+ # builder.convert_id(Person, "4d371b444835d98b8b000010")
64
64
  #
65
+ # @param [ Class ] klass The class we're trying to convert for.
65
66
  # @param [ String ] id The id, usually coming from the form.
66
67
  #
67
68
  # @return [ BSON::ObjectId, String, Object ] The converted id.
68
69
  #
69
70
  # @since 2.0.0.rc.6
70
- def convert_id(id)
71
- metadata.constraint.convert(id)
71
+ def convert_id(klass, id)
72
+ BSON::ObjectId.convert(klass, id)
72
73
  end
73
74
  end
74
75
  end
@@ -18,7 +18,6 @@ module Mongoid # :nodoc:
18
18
  # Backwards compatibility with Mongoid beta releases.
19
19
  delegate :klass, :to => :metadata
20
20
  delegate :bind_one, :unbind_one, :to => :binding
21
- delegate :collection_name, :to => :base
22
21
 
23
22
  # Convenience for setting the target and the metadata properties since
24
23
  # all proxies will need to do this.
@@ -38,11 +38,12 @@ module Mongoid #:nodoc:
38
38
  #
39
39
  # @since 2.0.2, batch-relational-insert
40
40
  def batched(&block)
41
- inserter = Threaded.insert ||= Insert.new
41
+ name = collection.name
42
+ inserter = Threaded.insert(name) || Threaded.set_insert(name, Insert.new)
42
43
  count_executions(&block)
43
44
  ensure
44
45
  if @executions.zero?
45
- Threaded.insert = nil
46
+ Threaded.set_insert(name, nil)
46
47
  inserter.execute(collection)
47
48
  end
48
49
  end
@@ -117,15 +117,15 @@ module Mongoid # :nodoc:
117
117
  # Proxy.eager_load(metadata, criteria)
118
118
  #
119
119
  # @param [ Metadata ] metadata The relation metadata.
120
- # @param [ Criteria ] criteria The criteria being used.
120
+ # @param [ Array<Object> ] ids The ids of the target docs.
121
121
  #
122
122
  # @return [ Criteria ] The criteria to eager load the relation.
123
123
  #
124
124
  # @since 2.2.0
125
- def eager_load(metadata, criteria)
125
+ def eager_load(metadata, ids)
126
126
  raise Errors::EagerLoad.new(metadata.name) if metadata.polymorphic?
127
127
  klass, foreign_key = metadata.klass, metadata.foreign_key
128
- klass.any_in("_id" => criteria.load_ids(foreign_key).uniq).each do |doc|
128
+ klass.any_in("_id" => ids).each do |doc|
129
129
  IdentityMap.set(doc)
130
130
  end
131
131
  end
@@ -29,16 +29,40 @@ module Mongoid #:nodoc:
29
29
  #
30
30
  # @since 2.0.0.beta.1
31
31
  def <<(*args)
32
+ docs = args.flatten
33
+ return concat(docs) if docs.size > 1
34
+ if doc = docs.first
35
+ append(doc)
36
+ doc.save if persistable? && !_assigning? && !doc.validated?
37
+ end
38
+ end
39
+ alias :push :<<
40
+
41
+ # Appends an array of documents to the relation. Performs a batch
42
+ # insert of the documents instead of persisting one at a time.
43
+ #
44
+ # @note When performing batch inserts the *after* callbacks will get
45
+ # executed before the documents have actually been persisted to the
46
+ # database due to an issue with Active Support's callback system - we
47
+ # cannot explicitly fire the after callbacks by themselves.
48
+ #
49
+ # @example Concat with other documents.
50
+ # person.posts.concat([ post_one, post_two ])
51
+ #
52
+ # @param [ Array<Document> ] documents The docs to add.
53
+ #
54
+ # @return [ Array<Document> ] The documents.
55
+ #
56
+ # @since 2.4.0
57
+ def concat(documents)
32
58
  batched do
33
- args.flatten.each do |doc|
59
+ documents.each do |doc|
34
60
  next unless doc
35
61
  append(doc)
36
- doc.save if persistable? && !doc.validated?
62
+ doc.save if persistable?
37
63
  end
38
64
  end
39
65
  end
40
- alias :concat :<<
41
- alias :push :<<
42
66
 
43
67
  # Build a new document from the attributes and append it to this
44
68
  # relation without saving.
@@ -66,6 +90,7 @@ module Mongoid #:nodoc:
66
90
  Factory.build(type || klass, attributes, options).tap do |doc|
67
91
  append(doc)
68
92
  yield(doc) if block_given?
93
+ doc.run_callbacks(:build) { doc }
69
94
  end
70
95
  end
71
96
  alias :new :build
@@ -293,13 +318,37 @@ module Mongoid #:nodoc:
293
318
  # @since 2.0.0.rc.1
294
319
  def substitute(replacement)
295
320
  tap do |proxy|
296
- if replacement != proxy.in_memory
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)
330
+ end
331
+ else
297
332
  proxy.purge
298
- proxy.push(replacement.compact.uniq) if replacement
299
333
  end
300
334
  end
301
335
  end
302
336
 
337
+ # Get a criteria for the documents without the default scoping
338
+ # applied.
339
+ #
340
+ # @example Get the unscoped criteria.
341
+ # person.posts.unscoped
342
+ #
343
+ # @return [ Criteria ] The unscoped criteria.
344
+ #
345
+ # @since 2.4.0
346
+ def unscoped
347
+ klass.unscoped.where(
348
+ metadata.foreign_key => Conversions.flag(base.id, metadata)
349
+ )
350
+ end
351
+
303
352
  private
304
353
 
305
354
  # Appends the document to the target array, updating the index on the
@@ -408,7 +457,7 @@ module Mongoid #:nodoc:
408
457
  #
409
458
  # @since 2.1.0
410
459
  def persistable?
411
- _creating? || base.persisted? && !_binding? && !_building?
460
+ !_binding? && (_creating? || base.persisted? && !_building?)
412
461
  end
413
462
 
414
463
  # Deletes all related documents from the database given the supplied
@@ -437,6 +486,36 @@ module Mongoid #:nodoc:
437
486
  end
438
487
  end
439
488
 
489
+ # Remove all the documents in the proxy that do not have the provided
490
+ # ids.
491
+ #
492
+ # @todo: Durran: Refactor 3.0. Temp for bug fix in 2.4.
493
+ #
494
+ # @example Remove all documents without the ids.
495
+ # proxy.remove_not_in([ id ])
496
+ #
497
+ # @param [ Array<Object> ] ids The ids.
498
+ #
499
+ # @since 2.4.0
500
+ def remove_not_in(ids)
501
+ removed = criteria.not_in(:_id => ids)
502
+ if metadata.destructive?
503
+ removed.delete_all
504
+ else
505
+ removed.update(metadata.foreign_key => nil)
506
+ end
507
+ in_memory.each do |doc|
508
+ if !ids.include?(doc.id)
509
+ unbind_one(doc)
510
+ added.try { |p| p.delete_one(doc) }
511
+ loaded.try { |p| p.delete_one(doc) }
512
+ if metadata.destructive?
513
+ doc.destroyed = true
514
+ end
515
+ end
516
+ end
517
+ end
518
+
440
519
  class << self
441
520
 
442
521
  # Return the builder that is responsible for generating the documents
@@ -479,14 +558,14 @@ module Mongoid #:nodoc:
479
558
  # Proxy.eager_load(metadata, criteria)
480
559
  #
481
560
  # @param [ Metadata ] metadata The relation metadata.
482
- # @param [ Criteria ] criteria The criteria being used.
561
+ # @param [ Array<Object> ] ids The ids of the base docs.
483
562
  #
484
563
  # @return [ Criteria ] The criteria to eager load the relation.
485
564
  #
486
565
  # @since 2.2.0
487
- def eager_load(metadata, criteria)
566
+ def eager_load(metadata, ids)
488
567
  klass, foreign_key = metadata.klass, metadata.foreign_key
489
- klass.any_in(foreign_key => criteria.load_ids("_id").uniq).each do |doc|
568
+ klass.any_in(foreign_key => ids).each do |doc|
490
569
  IdentityMap.set_many(doc, foreign_key => doc.send(foreign_key))
491
570
  end
492
571
  end
@@ -81,49 +81,6 @@ module Mongoid # :nodoc:
81
81
  end
82
82
  alias :new :build
83
83
 
84
- # Creates a new document on the references many relation. This will
85
- # save the document if the parent has been persisted.
86
- #
87
- # @example Create and save the new document.
88
- # person.posts.create(:text => "Testing")
89
- #
90
- # @param [ Hash ] attributes The attributes to create with.
91
- # @param [ Class ] type The optional type of document to create.
92
- #
93
- # @return [ Document ] The newly created document.
94
- #
95
- # @since 2.0.0.beta.1
96
- def create(attributes = nil, type = nil, &block)
97
- super.tap do |doc|
98
- base.send(metadata.foreign_key).delete_one(doc.id)
99
- base.push(metadata.foreign_key, doc.id)
100
- base.synced[metadata.foreign_key] = false
101
- end
102
- end
103
-
104
- # Creates a new document on the references many relation. This will
105
- # save the document if the parent has been persisted and will raise an
106
- # error if validation fails.
107
- #
108
- # @example Create and save the new document.
109
- # person.posts.create!(:text => "Testing")
110
- #
111
- # @param [ Hash ] attributes The attributes to create with.
112
- # @param [ Class ] type The optional type of document to create.
113
- #
114
- # @raise [ Errors::Validations ] If validation failed.
115
- #
116
- # @return [ Document ] The newly created document.
117
- #
118
- # @since 2.0.0.beta.1
119
- def create!(attributes = nil, type = nil, &block)
120
- super.tap do |doc|
121
- base.send(metadata.foreign_key).delete_one(doc.id)
122
- base.push(metadata.foreign_key, doc.id)
123
- base.synced[metadata.foreign_key] = false
124
- end
125
- end
126
-
127
84
  # Delete the document from the relation. This will set the foreign key
128
85
  # on the document to nil. If the dependent options on the relation are
129
86
  # :delete or :destroy the appropriate removal will occur.
@@ -171,6 +128,40 @@ module Mongoid # :nodoc:
171
128
  alias :clear :nullify
172
129
  alias :purge :nullify
173
130
 
131
+ # Substitutes the supplied target documents for the existing documents
132
+ # in the relation. If the new target is nil, perform the necessary
133
+ # deletion.
134
+ #
135
+ # @example Replace the relation.
136
+ # person.preferences.substitute([ new_post ])
137
+ #
138
+ # @param [ Array<Document> ] replacement The replacement target.
139
+ #
140
+ # @return [ Many ] The relation.
141
+ #
142
+ # @since 2.0.0.rc.1
143
+ def substitute(replacement)
144
+ tap do |proxy|
145
+ if replacement != proxy.in_memory
146
+ proxy.purge
147
+ proxy.push(replacement.compact.uniq) if replacement
148
+ end
149
+ end
150
+ end
151
+
152
+ # Get a criteria for the documents without the default scoping
153
+ # applied.
154
+ #
155
+ # @example Get the unscoped criteria.
156
+ # person.preferences.unscoped
157
+ #
158
+ # @return [ Criteria ] The unscoped criteria.
159
+ #
160
+ # @since 2.4.0
161
+ def unscoped
162
+ klass.unscoped.any_in(:_id => base.send(metadata.foreign_key))
163
+ end
164
+
174
165
  private
175
166
 
176
167
  # Appends the document to the target array, updating the index on the
@@ -53,7 +53,11 @@ module Mongoid # :nodoc:
53
53
  def substitute(replacement)
54
54
  unbind_one
55
55
  if persistable?
56
- metadata.destructive? ? send(metadata.dependent) : save
56
+ if metadata.destructive?
57
+ send(metadata.dependent)
58
+ else
59
+ save if persisted?
60
+ end
57
61
  end
58
62
  return nil unless replacement
59
63
  One.new(base, replacement, metadata)
@@ -128,14 +132,14 @@ module Mongoid # :nodoc:
128
132
  # Proxy.eager_load(metadata, criteria)
129
133
  #
130
134
  # @param [ Metadata ] metadata The relation metadata.
131
- # @param [ Criteria ] criteria The criteria being used.
135
+ # @param [ Array<Object> ] ids The ids of the base docs.
132
136
  #
133
137
  # @return [ Criteria ] The criteria to eager load the relation.
134
138
  #
135
139
  # @since 2.2.0
136
- def eager_load(metadata, criteria)
140
+ def eager_load(metadata, ids)
137
141
  klass, foreign_key = metadata.klass, metadata.foreign_key
138
- klass.any_in(foreign_key => criteria.load_ids("_id").uniq).each do |doc|
142
+ klass.any_in(foreign_key => ids).each do |doc|
139
143
  IdentityMap.set_one(doc, foreign_key => doc.send(foreign_key))
140
144
  end
141
145
  end
@@ -75,11 +75,28 @@ module Mongoid # :nodoc:
75
75
  #
76
76
  # @since 2.1.0
77
77
  def update_inverse_keys(meta)
78
- return unless changes.has_key?(meta.foreign_key)
79
- old, new = changes[meta.foreign_key]
80
- adds, subs = new - (old || []), (old || []) - new
81
- meta.criteria(adds).add_to_set(meta.inverse_foreign_key, id) unless adds.empty?
82
- meta.criteria(subs).pull(meta.inverse_foreign_key, id) unless subs.empty?
78
+ if changes.has_key?(meta.foreign_key)
79
+ old, new = changes[meta.foreign_key]
80
+ adds, subs = new - (old || []), (old || []) - new
81
+
82
+ # If we are autosaving we don't want a duplicate to get added - the
83
+ # $addToSet would run previously and then the $pushAll from the
84
+ # inverse on the autosave would cause this. We delete each id from
85
+ # what's in memory in case a mix of id addition and object addition
86
+ # had occurred.
87
+ if meta.autosave?
88
+ send(meta.name).in_memory.each do |doc|
89
+ adds.delete_one(doc.id)
90
+ end
91
+ end
92
+
93
+ unless adds.empty?
94
+ meta.criteria(adds).add_to_set(meta.inverse_foreign_key, id)
95
+ end
96
+ unless subs.empty?
97
+ meta.criteria(subs).pull(meta.inverse_foreign_key, id)
98
+ end
99
+ end
83
100
  end
84
101
 
85
102
  module ClassMethods #:nodoc:
@@ -8,76 +8,60 @@ module Mongoid #:nodoc:
8
8
  module Threaded
9
9
  extend self
10
10
 
11
- # Begins a assigning block.
11
+ # Begin entry into a named thread local stack.
12
12
  #
13
- # @example Begin the assign.
14
- # Threaded.begin_assign
13
+ # @example Begin entry into the stack.
14
+ # Threaded.begin(:create)
15
15
  #
16
- # @return [ true ] Always true.
16
+ # @param [ Symbol ] name The name of the stack
17
17
  #
18
- # @since 2.1.9
19
- def begin_assign
20
- assign_stack.push(true)
21
- end
22
-
23
- # Begins a binding block.
24
- #
25
- # @example Begin the bind.
26
- # Threaded.begin_bind
18
+ # @return [ true ] True.
27
19
  #
28
- # @return [ true ] Always true.
29
- #
30
- # @since 2.1.9
31
- def begin_bind
32
- bind_stack.push(true)
20
+ # @since 2.4.0
21
+ def begin(name)
22
+ stack(name).push(true)
33
23
  end
34
24
 
35
- # Begins a building block.
25
+ # Are in the middle of executing the named stack
36
26
  #
37
- # @example Begin the build.
38
- # Threaded.begin_build
27
+ # @example Are we in the stack execution?
28
+ # Threaded.executing?(:create)
39
29
  #
40
- # @return [ true ] Always true.
30
+ # @param [ Symbol ] name The name of the stack
41
31
  #
42
- # @since 2.1.9
43
- def begin_build
44
- build_stack.push(true)
45
- end
46
-
47
- # Begins a creating block.
48
- #
49
- # @example Begin the create.
50
- # Threaded.begin_create
32
+ # @return [ true ] If the stack is being executed.
51
33
  #
52
- # @return [ true ] Always true.
53
- #
54
- # @since 2.1.9
55
- def begin_create
56
- create_stack.push(true)
34
+ # @since 2.4.0
35
+ def executing?(name)
36
+ !stack(name).empty?
57
37
  end
58
38
 
59
- # Begins a loading block.
39
+ # Exit from a named thread local stack.
60
40
  #
61
- # @example Begin the load.
62
- # Threaded.begin_load
41
+ # @example Exit from the stack.
42
+ # Threaded.exit(:create)
63
43
  #
64
- # @return [ true ] Always true.
44
+ # @param [ Symbol ] name The name of the stack
65
45
  #
66
- # @since 2.3.2
67
- def begin_load
68
- load_stack.push(true)
46
+ # @return [ true ] True.
47
+ #
48
+ # @since 2.4.0
49
+ def exit(name)
50
+ stack(name).pop
69
51
  end
70
52
 
71
- # Begins a loading revision block.
53
+ # Get the named stack.
54
+ #
55
+ # @example Get a stack by name
56
+ # Threaded.stack(:create)
72
57
  #
73
- # @example Begin the revision load.
74
- # Threaded.begin_load_revision
58
+ # @param [ Symbol ] name The name of the stack
75
59
  #
76
- # @return [ true ] Always true.
60
+ # @return [ Array ] The stack.
77
61
  #
78
- # @since 2.3.4
79
- def begin_load_revision
80
- load_revision_stack.push(true)
62
+ # @since 2.4.0
63
+ def stack(name)
64
+ Thread.current[:"[mongoid]:#{name}-stack"] ||= []
81
65
  end
82
66
 
83
67
  # Begin validating a document on the current thread.
@@ -92,156 +76,6 @@ module Mongoid #:nodoc:
92
76
  validations_for(document.class).push(document.id)
93
77
  end
94
78
 
95
- # Is the current thread in assigning mode?
96
- #
97
- # @example Is the thread in assigning mode?
98
- # Threaded.assigning?
99
- #
100
- # @return [ true, false ] If the thread is in assigning mode?
101
- #
102
- # @since 2.1.0
103
- def assigning?
104
- !assign_stack.empty?
105
- end
106
-
107
- # Is the current thread in binding mode?
108
- #
109
- # @example Is the thread in binding mode?
110
- # Threaded.binding?
111
- #
112
- # @return [ true, false ] If the thread is in binding mode?
113
- #
114
- # @since 2.1.0
115
- def binding?
116
- !bind_stack.empty?
117
- end
118
-
119
- # Is the current thread in building mode?
120
- #
121
- # @example Is the thread in building mode?
122
- # Threaded.building?
123
- #
124
- # @return [ true, false ] If the thread is in building mode?
125
- #
126
- # @since 2.1.0
127
- def building?
128
- !build_stack.empty?
129
- end
130
-
131
- # Is the current thread in creating mode?
132
- #
133
- # @example Is the thread in creating mode?
134
- # Threaded.creating?
135
- #
136
- # @return [ true, false ] If the thread is in creating mode?
137
- #
138
- # @since 2.1.0
139
- def creating?
140
- !create_stack.empty?
141
- end
142
-
143
- # Is the current thread in loading mode?
144
- #
145
- # @example Is the thread in loading mode?
146
- # Threaded.loading?
147
- #
148
- # @return [ true, false ] If the thread is in loading mode?
149
- #
150
- # @since 2.3.2
151
- def loading?
152
- !load_stack.empty?
153
- end
154
-
155
- # Is the current thread in revision load mode?
156
- #
157
- # @example Is the thread in revision load mode?
158
- # Threaded.loading_revision?
159
- #
160
- # @return [ true, false ] If the thread is in revision load mode?
161
- #
162
- # @since 2.3.4
163
- def loading_revision?
164
- !load_revision_stack.empty?
165
- end
166
-
167
- # Get the assign stack for the current thread. Is simply an array of calls
168
- # to Mongoid's assigning method.
169
- #
170
- # @example Get the assign stack.
171
- # Threaded.assign_stack
172
- #
173
- # @return [ Array ] The array of assign calls.
174
- #
175
- # @since 2.1.9
176
- def assign_stack
177
- Thread.current[:"[mongoid]:assign-stack"] ||= []
178
- end
179
-
180
- # Get the bind stack for the current thread. Is simply an array of calls
181
- # to Mongoid's binding method.
182
- #
183
- # @example Get the bind stack.
184
- # Threaded.bind_stack
185
- #
186
- # @return [ Array ] The array of bind calls.
187
- #
188
- # @since 2.1.9
189
- def bind_stack
190
- Thread.current[:"[mongoid]:bind-stack"] ||= []
191
- end
192
-
193
- # Get the build stack for the current thread. Is simply an array of calls
194
- # to Mongoid's building method.
195
- #
196
- # @example Get the build stack.
197
- # Threaded.build_stack
198
- #
199
- # @return [ Array ] The array of build calls.
200
- #
201
- # @since 2.1.9
202
- def build_stack
203
- Thread.current[:"[mongoid]:build-stack"] ||= []
204
- end
205
-
206
- # Get the create stack for the current thread. Is simply an array of calls
207
- # to Mongoid's creating method.
208
- #
209
- # @example Get the create stack.
210
- # Threaded.create_stack
211
- #
212
- # @return [ Array ] The array of create calls.
213
- #
214
- # @since 2.1.9
215
- def create_stack
216
- Thread.current[:"[mongoid]:create-stack"] ||= []
217
- end
218
-
219
- # Get the load stack for the current thread. Is simply an array of calls
220
- # to Mongoid's loading method.
221
- #
222
- # @example Get the load stack.
223
- # Threaded.load_stack
224
- #
225
- # @return [ Array ] The array of load calls.
226
- #
227
- # @since 2.3.2
228
- def load_stack
229
- Thread.current[:"[mongoid]:load-stack"] ||= []
230
- end
231
-
232
- # Get the revision load stack for the current thread. Is simply an array
233
- # of calls to Mongoid's loading_revision method.
234
- #
235
- # @example Get the revision load stack.
236
- # Threaded.load_revision_stack
237
- #
238
- # @return [ Array ] The array of load revision calls.
239
- #
240
- # @since 2.3.4
241
- def load_revision_stack
242
- Thread.current[:"[mongoid]:load-revision-stack"] ||= []
243
- end
244
-
245
79
  # Clear out all the safety options set using the safely proxy.
246
80
  #
247
81
  # @example Clear out the options.
@@ -265,78 +99,6 @@ module Mongoid #:nodoc:
265
99
  self.timeless = false
266
100
  end
267
101
 
268
- # Exit the assigning block.
269
- #
270
- # @example Exit the assigning block.
271
- # Threaded.exit_assign
272
- #
273
- # @return [ true ] The last element in the stack.
274
- #
275
- # @since 2.1.9
276
- def exit_assign
277
- assign_stack.pop
278
- end
279
-
280
- # Exit the binding block.
281
- #
282
- # @example Exit the binding block.
283
- # Threaded.exit_bind
284
- #
285
- # @return [ true ] The last element in the stack.
286
- #
287
- # @since 2.1.9
288
- def exit_bind
289
- bind_stack.pop
290
- end
291
-
292
- # Exit the building block.
293
- #
294
- # @example Exit the building block.
295
- # Threaded.exit_build
296
- #
297
- # @return [ true ] The last element in the stack.
298
- #
299
- # @since 2.1.9
300
- def exit_build
301
- build_stack.pop
302
- end
303
-
304
- # Exit the creating block.
305
- #
306
- # @example Exit the creating block.
307
- # Threaded.exit_create
308
- #
309
- # @return [ true ] The last element in the stack.
310
- #
311
- # @since 2.1.9
312
- def exit_create
313
- create_stack.pop
314
- end
315
-
316
- # Exit the loading block.
317
- #
318
- # @example Exit the loading block.
319
- # Threaded.exit_load
320
- #
321
- # @return [ true ] The last element in the stack.
322
- #
323
- # @since 2.1.9
324
- def exit_load
325
- load_stack.pop
326
- end
327
-
328
- # Exit the revision loading block.
329
- #
330
- # @example Exit the revision loading block.
331
- # Threaded.exit_load_revision
332
- #
333
- # @return [ true ] The last element in the stack.
334
- #
335
- # @since 2.3.4
336
- def exit_load_revision
337
- load_revision_stack.pop
338
- end
339
-
340
102
  # Exit validating a document on the current thread.
341
103
  #
342
104
  # @example Exit validation.
@@ -369,8 +131,8 @@ module Mongoid #:nodoc:
369
131
  # @return [ Object ] The batch insert consumer.
370
132
  #
371
133
  # @since 2.1.0
372
- def insert
373
- Thread.current[:"[mongoid]:insert-consumer"]
134
+ def insert(name)
135
+ Thread.current[:"[mongoid][#{name}]:insert-consumer"]
374
136
  end
375
137
 
376
138
  # Set the insert consumer on the current thread.
@@ -383,8 +145,8 @@ module Mongoid #:nodoc:
383
145
  # @return [ Object ] The insert consumer.
384
146
  #
385
147
  # @since 2.1.0
386
- def insert=(consumer)
387
- Thread.current[:"[mongoid]:insert-consumer"] = consumer
148
+ def set_insert(name, consumer)
149
+ Thread.current[:"[mongoid][#{name}]:insert-consumer"] = consumer
388
150
  end
389
151
 
390
152
  # Get the safety options for the current thread.