mongoid 2.1.9 → 2.2.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 (78) hide show
  1. data/CHANGELOG.md +281 -0
  2. data/{README.rdoc → README.md} +24 -14
  3. data/Rakefile +1 -1
  4. data/lib/config/locales/bg.yml +5 -0
  5. data/lib/config/locales/de.yml +5 -0
  6. data/lib/config/locales/en-GB.yml +5 -0
  7. data/lib/config/locales/en.yml +5 -0
  8. data/lib/config/locales/es.yml +3 -0
  9. data/lib/config/locales/fr.yml +5 -0
  10. data/lib/config/locales/hi.yml +5 -0
  11. data/lib/config/locales/hu.yml +5 -0
  12. data/lib/config/locales/id.yml +5 -0
  13. data/lib/config/locales/it.yml +5 -0
  14. data/lib/config/locales/ja.yml +5 -0
  15. data/lib/config/locales/kr.yml +5 -0
  16. data/lib/config/locales/nl.yml +5 -0
  17. data/lib/config/locales/pl.yml +5 -0
  18. data/lib/config/locales/pt-BR.yml +5 -0
  19. data/lib/config/locales/pt.yml +5 -0
  20. data/lib/config/locales/ro.yml +5 -0
  21. data/lib/config/locales/ru.yml +5 -0
  22. data/lib/config/locales/sv.yml +5 -0
  23. data/lib/config/locales/vi.yml +5 -0
  24. data/lib/config/locales/zh-CN.yml +5 -0
  25. data/lib/mongoid/atomic.rb +61 -10
  26. data/lib/mongoid/atomic/modifiers.rb +156 -24
  27. data/lib/mongoid/attributes.rb +38 -10
  28. data/lib/mongoid/collection.rb +21 -3
  29. data/lib/mongoid/collections.rb +52 -6
  30. data/lib/mongoid/collections/master.rb +8 -2
  31. data/lib/mongoid/collections/operations.rb +1 -0
  32. data/lib/mongoid/config.rb +5 -2
  33. data/lib/mongoid/config/database.rb +15 -2
  34. data/lib/mongoid/contexts/mongo.rb +3 -0
  35. data/lib/mongoid/criteria.rb +27 -3
  36. data/lib/mongoid/criterion/inclusion.rb +58 -0
  37. data/lib/mongoid/dirty.rb +2 -0
  38. data/lib/mongoid/document.rb +23 -0
  39. data/lib/mongoid/errors.rb +3 -0
  40. data/lib/mongoid/errors/callback.rb +26 -0
  41. data/lib/mongoid/errors/eager_load.rb +25 -0
  42. data/lib/mongoid/errors/invalid_find.rb +19 -0
  43. data/lib/mongoid/extensions.rb +3 -1
  44. data/lib/mongoid/extensions/object_id/conversions.rb +1 -1
  45. data/lib/mongoid/extras.rb +12 -2
  46. data/lib/mongoid/fields.rb +41 -2
  47. data/lib/mongoid/fields/serializable.rb +36 -0
  48. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +13 -14
  49. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +13 -14
  50. data/lib/mongoid/finders.rb +4 -4
  51. data/lib/mongoid/identity_map.rb +33 -20
  52. data/lib/mongoid/keys.rb +24 -1
  53. data/lib/mongoid/nested_attributes.rb +16 -4
  54. data/lib/mongoid/persistence.rb +50 -6
  55. data/lib/mongoid/persistence/operations.rb +1 -4
  56. data/lib/mongoid/persistence/operations/update.rb +3 -1
  57. data/lib/mongoid/relations/builders/nested_attributes/many.rb +27 -44
  58. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  59. data/lib/mongoid/relations/builders/referenced/one.rb +1 -1
  60. data/lib/mongoid/relations/cascading.rb +12 -1
  61. data/lib/mongoid/relations/embedded/many.rb +11 -4
  62. data/lib/mongoid/relations/embedded/one.rb +6 -2
  63. data/lib/mongoid/relations/macros.rb +19 -12
  64. data/lib/mongoid/relations/metadata.rb +17 -0
  65. data/lib/mongoid/relations/polymorphic.rb +12 -1
  66. data/lib/mongoid/relations/proxy.rb +24 -0
  67. data/lib/mongoid/relations/referenced/in.rb +20 -0
  68. data/lib/mongoid/relations/referenced/many.rb +30 -6
  69. data/lib/mongoid/relations/referenced/many_to_many.rb +18 -2
  70. data/lib/mongoid/relations/referenced/one.rb +19 -0
  71. data/lib/mongoid/relations/reflections.rb +23 -3
  72. data/lib/mongoid/relations/targets/enumerable.rb +29 -1
  73. data/lib/mongoid/serialization.rb +1 -1
  74. data/lib/mongoid/sharding.rb +12 -2
  75. data/lib/mongoid/threaded.rb +98 -0
  76. data/lib/mongoid/version.rb +1 -1
  77. data/lib/mongoid/versioning.rb +12 -2
  78. metadata +25 -21
@@ -178,6 +178,23 @@ module Mongoid # :nodoc:
178
178
  !!dependent
179
179
  end
180
180
 
181
+ # Get the criteria needed to eager load this relation.
182
+ #
183
+ # @example Get the eager loading criteria.
184
+ # metadata.eager_load(criteria)
185
+ #
186
+ # @param [ Criteria ] criteria The criteria to load from.
187
+ #
188
+ # @return [ Criteria ] The eager loading criteria.
189
+ #
190
+ # @since 2.2.0
191
+ def eager_load(criteria)
192
+ relation.eager_load(
193
+ self,
194
+ criteria.clone.tap { |crit| crit.inclusions.clear }
195
+ )
196
+ end
197
+
181
198
  # Will determine if the relation is an embedded one or not. Currently
182
199
  # only checks against embeds one and many.
183
200
  #
@@ -9,7 +9,18 @@ module Mongoid # :nodoc:
9
9
 
10
10
  included do
11
11
  class_attribute :polymorphic
12
- delegate :polymorphic?, :to => "self.class"
12
+ end
13
+
14
+ # Is the document in a polymorphic relation?
15
+ #
16
+ # @note Refactored from using delegate for class load performance.
17
+ #
18
+ # @example Is the document polymorphic?
19
+ # model.polymorphic?
20
+ #
21
+ # @return [ true, false ] If the document is in a polymorphic relation.
22
+ def polymorphic?
23
+ self.class.polymorphic?
13
24
  end
14
25
 
15
26
  module ClassMethods #:nodoc:
@@ -50,6 +50,18 @@ module Mongoid # :nodoc:
50
50
 
51
51
  protected
52
52
 
53
+ # Is the current thread in assigning mode?
54
+ #
55
+ # @example Is the current thread in assigning mode?
56
+ # proxy.assigning?
57
+ #
58
+ # @return [ true, false ] If the thread is assigning.
59
+ #
60
+ # @since 2.1.0
61
+ def assigning?
62
+ Threaded.assigning?
63
+ end
64
+
53
65
  # Is the current thread in binding mode?
54
66
  #
55
67
  # @example Is the current thread in binding mode?
@@ -74,6 +86,18 @@ module Mongoid # :nodoc:
74
86
  Threaded.building?
75
87
  end
76
88
 
89
+ # Is the current thread in creating mode?
90
+ #
91
+ # @example Is the current thread in creating mode?
92
+ # proxy.creating?
93
+ #
94
+ # @return [ true, false ] If the thread is creating.
95
+ #
96
+ # @since 2.1.0
97
+ def creating?
98
+ Threaded.creating?
99
+ end
100
+
77
101
  # Get the collection from the root of the hierarchy.
78
102
  #
79
103
  # @example Get the collection.
@@ -109,6 +109,26 @@ module Mongoid # :nodoc:
109
109
  type.where(:_id => object)
110
110
  end
111
111
 
112
+ # Get the criteria that is used to eager load a relation of this
113
+ # type.
114
+ #
115
+ # @example Get the eager load criteria.
116
+ # Proxy.eager_load(metadata, criteria)
117
+ #
118
+ # @param [ Metadata ] metadata The relation metadata.
119
+ # @param [ Criteria ] criteria The criteria being used.
120
+ #
121
+ # @return [ Criteria ] The criteria to eager load the relation.
122
+ #
123
+ # @since 2.2.0
124
+ def eager_load(metadata, criteria)
125
+ raise Errors::EagerLoad.new(metadata.name) if metadata.polymorphic?
126
+ klass, foreign_key = metadata.klass, metadata.foreign_key
127
+ klass.any_in("_id" => criteria.load_ids(foreign_key).uniq).each do |doc|
128
+ IdentityMap.set(doc)
129
+ end
130
+ end
131
+
112
132
  # Returns true if the relation is an embedded one. In this case
113
133
  # always false.
114
134
  #
@@ -245,10 +245,14 @@ module Mongoid #:nodoc:
245
245
  #
246
246
  # @since 2.0.0.beta.1
247
247
  def purge
248
- criteria.delete_all
249
- target.clear do |doc|
250
- unbind_one(doc)
251
- doc.destroyed = true
248
+ unless metadata.destructive?
249
+ nullify
250
+ else
251
+ criteria.delete_all
252
+ target.clear do |doc|
253
+ unbind_one(doc)
254
+ doc.destroyed = true
255
+ end
252
256
  end
253
257
  end
254
258
  alias :clear :purge
@@ -267,8 +271,10 @@ module Mongoid #:nodoc:
267
271
  # @since 2.0.0.rc.1
268
272
  def substitute(replacement)
269
273
  tap do |proxy|
270
- proxy.purge
271
- proxy.push(replacement.compact) if replacement
274
+ if replacement != proxy.in_memory
275
+ proxy.purge
276
+ proxy.push(replacement.compact) if replacement
277
+ end
272
278
  end
273
279
  end
274
280
 
@@ -464,6 +470,24 @@ module Mongoid #:nodoc:
464
470
  metadata.klass.where(metadata.foreign_key => object)
465
471
  end
466
472
 
473
+ # Eager load the relation based on the criteria.
474
+ #
475
+ # @example Eager load the criteria.
476
+ # Proxy.eager_load(metadata, criteria)
477
+ #
478
+ # @param [ Metadata ] metadata The relation metadata.
479
+ # @param [ Criteria ] criteria The criteria being used.
480
+ #
481
+ # @return [ Criteria ] The criteria to eager load the relation.
482
+ #
483
+ # @since 2.2.0
484
+ def eager_load(metadata, criteria)
485
+ klass, foreign_key = metadata.klass, metadata.foreign_key
486
+ klass.any_in(foreign_key => criteria.load_ids("_id").uniq).each do |doc|
487
+ IdentityMap.set_many(doc, foreign_key => doc.send(foreign_key))
488
+ end
489
+ end
490
+
467
491
  # Returns true if the relation is an embedded one. In this case
468
492
  # always false.
469
493
  #
@@ -30,14 +30,14 @@ module Mongoid # :nodoc:
30
30
  args.flatten.each do |doc|
31
31
  next unless doc
32
32
  append(doc)
33
- if persistable?
33
+ if persistable? || creating?
34
34
  ids.push(doc.id)
35
35
  doc.save
36
36
  else
37
37
  base.send(metadata.foreign_key).push(doc.id)
38
38
  end
39
39
  end
40
- if persistable?
40
+ if persistable? || creating?
41
41
  base.push_all(metadata.foreign_key, ids)
42
42
  base.synced[metadata.foreign_key] = false
43
43
  end
@@ -232,6 +232,22 @@ module Mongoid # :nodoc:
232
232
  metadata.klass.any_in(:_id => object)
233
233
  end
234
234
 
235
+ # Get the criteria that is used to eager load a relation of this
236
+ # type.
237
+ #
238
+ # @example Get the eager load criteria.
239
+ # Proxy.eager_load(metadata, criteria)
240
+ #
241
+ # @param [ Metadata ] metadata The relation metadata.
242
+ # @param [ Criteria ] criteria The criteria being used.
243
+ #
244
+ # @return [ Criteria ] The criteria to eager load the relation.
245
+ #
246
+ # @since 2.2.0
247
+ def eager_load(metadata, criteria)
248
+ raise Errors::EagerLoad.new(metadata.name)
249
+ end
250
+
235
251
  # Returns true if the relation is an embedded one. In this case
236
252
  # always false.
237
253
  #
@@ -122,6 +122,25 @@ module Mongoid # :nodoc:
122
122
  metadata.klass.where(metadata.foreign_key => object)
123
123
  end
124
124
 
125
+ # Get the criteria that is used to eager load a relation of this
126
+ # type.
127
+ #
128
+ # @example Get the eager load criteria.
129
+ # Proxy.eager_load(metadata, criteria)
130
+ #
131
+ # @param [ Metadata ] metadata The relation metadata.
132
+ # @param [ Criteria ] criteria The criteria being used.
133
+ #
134
+ # @return [ Criteria ] The criteria to eager load the relation.
135
+ #
136
+ # @since 2.2.0
137
+ def eager_load(metadata, criteria)
138
+ klass, foreign_key = metadata.klass, metadata.foreign_key
139
+ klass.any_in(foreign_key => criteria.load_ids("_id").uniq).each do |doc|
140
+ IdentityMap.set_one(doc, foreign_key => doc.send(foreign_key))
141
+ end
142
+ end
143
+
125
144
  # Returns true if the relation is an embedded one. In this case
126
145
  # always false.
127
146
  #
@@ -7,9 +7,29 @@ module Mongoid # :nodoc:
7
7
  module Reflections
8
8
  extend ActiveSupport::Concern
9
9
 
10
- delegate \
11
- :reflect_on_association,
12
- :reflect_on_all_associations, :to => "self.class"
10
+ # Returns the relation metadata for the supplied name.
11
+ #
12
+ # @example Find relation metadata by name.
13
+ # person.reflect_on_association(:addresses)
14
+ #
15
+ # @param [ String, Symbol ] name The name of the relation to find.
16
+ #
17
+ # @return [ Metadata ] The matching relation metadata.
18
+ def reflect_on_association(name)
19
+ self.class.reflect_on_association(name)
20
+ end
21
+
22
+ # Returns all relation metadata for the supplied macros.
23
+ #
24
+ # @example Find multiple relation metadata by macro.
25
+ # person.reflect_on_all_associations(:embeds_many)
26
+ #
27
+ # @param [ Array<String, Symbol> ] *macros The relation macros.
28
+ #
29
+ # @return [ Array<Metadata> ] The matching relation metadata.
30
+ def reflect_on_all_associations(*macros)
31
+ self.class.reflect_on_all_associations(*macros)
32
+ end
13
33
 
14
34
  module ClassMethods #:nodoc
15
35
 
@@ -189,7 +189,7 @@ module Mongoid #:nodoc:
189
189
  #
190
190
  # @since 2.1.0
191
191
  def first
192
- (loaded? ? loaded.first : unloaded.first) || added.first
192
+ added.first || (loaded? ? loaded.first : unloaded.first)
193
193
  end
194
194
 
195
195
  # Initialize the new enumerable either with a criteria or an array.
@@ -320,6 +320,34 @@ module Mongoid #:nodoc:
320
320
  end
321
321
  alias :length :size
322
322
 
323
+ # Send #to_json to the entries.
324
+ #
325
+ # @example Get the enumerable as json.
326
+ # enumerable.to_json
327
+ #
328
+ # @param [ Hash ] options Optional parameters.
329
+ #
330
+ # @return [ String ] The entries all loaded as a string.
331
+ #
332
+ # @since 2.2.0
333
+ def to_json(options = {})
334
+ entries.to_json(options)
335
+ end
336
+
337
+ # Send #as_json to the entries, without encoding.
338
+ #
339
+ # @example Get the enumerable as json.
340
+ # enumerable.as_json
341
+ #
342
+ # @param [ Hash ] options Optional parameters.
343
+ #
344
+ # @return [ Hash ] The entries all loaded as a hash.
345
+ #
346
+ # @since 2.2.0
347
+ def as_json(options = {})
348
+ entries.as_json(options)
349
+ end
350
+
323
351
  # Return all the unique documents in the enumerable.
324
352
  #
325
353
  # @note This operation loads all documents from the database.
@@ -74,7 +74,7 @@ module Mongoid # :nodoc:
74
74
  inclusions = options[:include]
75
75
  relation_names(inclusions).each do |name|
76
76
  metadata = relations[name.to_s]
77
- relation = send(metadata.name, false, :eager => true)
77
+ relation = send(metadata.name)
78
78
  if relation
79
79
  attributes[metadata.name.to_s] =
80
80
  relation.serializable_hash(relation_options(inclusions, options, name))
@@ -5,13 +5,23 @@ module Mongoid #:nodoc
5
5
  module Sharding
6
6
  extend ActiveSupport::Concern
7
7
 
8
- delegate :shard_key_fields, :to => "self.class"
9
-
10
8
  included do
11
9
  cattr_accessor :shard_key_fields
12
10
  self.shard_key_fields = []
13
11
  end
14
12
 
13
+ # Get the shard key fields.
14
+ #
15
+ # @note Refactored from using delegate for class load performance.
16
+ #
17
+ # @example Get the shard key fields.
18
+ # model.shard_key_fields
19
+ #
20
+ # @return [ Array<String> ] The shard key field names.
21
+ def shard_key_fields
22
+ self.class.shard_key_fields
23
+ end
24
+
15
25
  # Get the document selector with the defined shard keys.
16
26
  #
17
27
  # @example Get the selector for the shard keys.
@@ -6,6 +6,18 @@ module Mongoid #:nodoc:
6
6
  module Threaded
7
7
  extend self
8
8
 
9
+ # Begins a assigning block.
10
+ #
11
+ # @example Begin the assign.
12
+ # Threaded.begin_assign
13
+ #
14
+ # @return [ true ] Always true.
15
+ #
16
+ # @since 2.1.9
17
+ def begin_assign
18
+ assign_stack.push(true)
19
+ end
20
+
9
21
  # Begins a binding block.
10
22
  #
11
23
  # @example Begin the bind.
@@ -30,6 +42,18 @@ module Mongoid #:nodoc:
30
42
  build_stack.push(true)
31
43
  end
32
44
 
45
+ # Begins a creating block.
46
+ #
47
+ # @example Begin the create.
48
+ # Threaded.begin_create
49
+ #
50
+ # @return [ true ] Always true.
51
+ #
52
+ # @since 2.1.9
53
+ def begin_create
54
+ create_stack.push(true)
55
+ end
56
+
33
57
  # Begin validating a document on the current thread.
34
58
  #
35
59
  # @example Begin validation.
@@ -42,6 +66,18 @@ module Mongoid #:nodoc:
42
66
  validations_for(document.class).push(document.id)
43
67
  end
44
68
 
69
+ # Is the current thread in assigning mode?
70
+ #
71
+ # @example Is the thread in assigning mode?
72
+ # Threaded.assigning?
73
+ #
74
+ # @return [ true, false ] If the thread is in assigning mode?
75
+ #
76
+ # @since 2.1.0
77
+ def assigning?
78
+ !assign_stack.empty?
79
+ end
80
+
45
81
  # Is the current thread in binding mode?
46
82
  #
47
83
  # @example Is the thread in binding mode?
@@ -66,6 +102,31 @@ module Mongoid #:nodoc:
66
102
  !build_stack.empty?
67
103
  end
68
104
 
105
+ # Is the current thread in creating mode?
106
+ #
107
+ # @example Is the thread in creating mode?
108
+ # Threaded.creating?
109
+ #
110
+ # @return [ true, false ] If the thread is in creating mode?
111
+ #
112
+ # @since 2.1.0
113
+ def creating?
114
+ !create_stack.empty?
115
+ end
116
+
117
+ # Get the assign stack for the current thread. Is simply an array of calls
118
+ # to Mongoid's assigning method.
119
+ #
120
+ # @example Get the assign stack.
121
+ # Threaded.assign_stack
122
+ #
123
+ # @return [ Array ] The array of assign calls.
124
+ #
125
+ # @since 2.1.9
126
+ def assign_stack
127
+ Thread.current[:"[mongoid]:assign-stack"] ||= []
128
+ end
129
+
69
130
  # Get the bind stack for the current thread. Is simply an array of calls
70
131
  # to Mongoid's binding method.
71
132
  #
@@ -92,6 +153,19 @@ module Mongoid #:nodoc:
92
153
  Thread.current[:"[mongoid]:build-stack"] ||= []
93
154
  end
94
155
 
156
+ # Get the create stack for the current thread. Is simply an array of calls
157
+ # to Mongoid's creating method.
158
+ #
159
+ # @example Get the create stack.
160
+ # Threaded.create_stack
161
+ #
162
+ # @return [ Array ] The array of create calls.
163
+ #
164
+ # @since 2.1.9
165
+ def create_stack
166
+ Thread.current[:"[mongoid]:create-stack"] ||= []
167
+ end
168
+
95
169
  # Clear out all the safety options set using the safely proxy.
96
170
  #
97
171
  # @example Clear out the options.
@@ -104,6 +178,18 @@ module Mongoid #:nodoc:
104
178
  Thread.current[:"[mongoid]:safety-options"] = nil
105
179
  end
106
180
 
181
+ # Exit the assigning block.
182
+ #
183
+ # @example Exit the assigning block.
184
+ # Threaded.exit_assign
185
+ #
186
+ # @return [ true ] The last element in the stack.
187
+ #
188
+ # @since 2.1.9
189
+ def exit_assign
190
+ assign_stack.pop
191
+ end
192
+
107
193
  # Exit the binding block.
108
194
  #
109
195
  # @example Exit the binding block.
@@ -128,6 +214,18 @@ module Mongoid #:nodoc:
128
214
  build_stack.pop
129
215
  end
130
216
 
217
+ # Exit the creating block.
218
+ #
219
+ # @example Exit the creating block.
220
+ # Threaded.exit_create
221
+ #
222
+ # @return [ true ] The last element in the stack.
223
+ #
224
+ # @since 2.1.9
225
+ def exit_create
226
+ create_stack.pop
227
+ end
228
+
131
229
  # Exit validating a document on the current thread.
132
230
  #
133
231
  # @example Exit validation.