mongoid 3.0.23 → 3.1.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 (95) hide show
  1. data/CHANGELOG.md +253 -9
  2. data/LICENSE +1 -1
  3. data/README.md +4 -1
  4. data/lib/config/locales/en.yml +7 -6
  5. data/lib/mongoid.rb +18 -1
  6. data/lib/mongoid/atomic.rb +22 -20
  7. data/lib/mongoid/atomic/paths/embedded.rb +19 -5
  8. data/lib/mongoid/atomic/paths/root.rb +1 -1
  9. data/lib/mongoid/atomic/positionable.rb +73 -0
  10. data/lib/mongoid/attributes.rb +63 -1
  11. data/lib/mongoid/callbacks.rb +58 -4
  12. data/lib/mongoid/components.rb +8 -3
  13. data/lib/mongoid/config.rb +71 -23
  14. data/lib/mongoid/contextual.rb +2 -1
  15. data/lib/mongoid/contextual/aggregable/mongo.rb +27 -63
  16. data/lib/mongoid/contextual/atomic.rb +4 -3
  17. data/lib/mongoid/contextual/find_and_modify.rb +1 -1
  18. data/lib/mongoid/contextual/geo_near.rb +238 -0
  19. data/lib/mongoid/contextual/map_reduce.rb +12 -1
  20. data/lib/mongoid/contextual/memory.rb +36 -31
  21. data/lib/mongoid/contextual/mongo.rb +147 -91
  22. data/lib/mongoid/contextual/queryable.rb +25 -0
  23. data/lib/mongoid/copyable.rb +4 -1
  24. data/lib/mongoid/criteria.rb +23 -275
  25. data/lib/mongoid/criterion/findable.rb +179 -0
  26. data/lib/mongoid/criterion/modifiable.rb +191 -0
  27. data/lib/mongoid/criterion/scoping.rb +11 -6
  28. data/lib/mongoid/document.rb +7 -56
  29. data/lib/mongoid/equality.rb +66 -0
  30. data/lib/mongoid/errors/mongoid_error.rb +7 -3
  31. data/lib/mongoid/extensions/array.rb +13 -1
  32. data/lib/mongoid/extensions/date.rb +9 -2
  33. data/lib/mongoid/extensions/hash.rb +38 -2
  34. data/lib/mongoid/extensions/nil_class.rb +12 -0
  35. data/lib/mongoid/extensions/object.rb +24 -0
  36. data/lib/mongoid/extensions/string.rb +14 -2
  37. data/lib/mongoid/extensions/time.rb +4 -1
  38. data/lib/mongoid/fields.rb +49 -5
  39. data/lib/mongoid/fields/foreign_key.rb +12 -0
  40. data/lib/mongoid/fields/standard.rb +12 -0
  41. data/lib/mongoid/finders.rb +8 -0
  42. data/lib/mongoid/hierarchy.rb +19 -1
  43. data/lib/mongoid/indexes.rb +30 -4
  44. data/lib/mongoid/indexes/validators/options.rb +12 -2
  45. data/lib/mongoid/inspection.rb +2 -1
  46. data/lib/mongoid/matchers/strategies.rb +5 -5
  47. data/lib/mongoid/observer.rb +27 -36
  48. data/lib/mongoid/persistence.rb +42 -17
  49. data/lib/mongoid/persistence/atomic.rb +10 -5
  50. data/lib/mongoid/persistence/atomic/operation.rb +26 -9
  51. data/lib/mongoid/persistence/atomic/unset.rb +1 -1
  52. data/lib/mongoid/persistence/operations/embedded/insert.rb +5 -2
  53. data/lib/mongoid/persistence/operations/embedded/remove.rb +5 -2
  54. data/lib/mongoid/persistence/operations/update.rb +7 -3
  55. data/lib/mongoid/railties/database.rake +12 -19
  56. data/lib/mongoid/relations.rb +2 -0
  57. data/lib/mongoid/relations/accessors.rb +30 -8
  58. data/lib/mongoid/relations/binding.rb +5 -1
  59. data/lib/mongoid/relations/bindings/referenced/in.rb +1 -1
  60. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +3 -3
  61. data/lib/mongoid/relations/counter_cache.rb +107 -0
  62. data/lib/mongoid/relations/embedded/batchable.rb +13 -4
  63. data/lib/mongoid/relations/embedded/many.rb +30 -1
  64. data/lib/mongoid/relations/macros.rb +2 -0
  65. data/lib/mongoid/relations/marshalable.rb +0 -1
  66. data/lib/mongoid/relations/metadata.rb +63 -11
  67. data/lib/mongoid/relations/options.rb +1 -0
  68. data/lib/mongoid/relations/proxy.rb +45 -2
  69. data/lib/mongoid/relations/referenced/in.rb +11 -2
  70. data/lib/mongoid/relations/referenced/many.rb +31 -3
  71. data/lib/mongoid/relations/referenced/many_to_many.rb +31 -3
  72. data/lib/mongoid/relations/referenced/one.rb +1 -1
  73. data/lib/mongoid/relations/targets/enumerable.rb +5 -1
  74. data/lib/mongoid/relations/touchable.rb +35 -6
  75. data/lib/mongoid/reloading.rb +5 -3
  76. data/lib/mongoid/scoping.rb +2 -2
  77. data/lib/mongoid/sessions.rb +57 -7
  78. data/lib/mongoid/sessions/factory.rb +22 -1
  79. data/lib/mongoid/threaded.rb +4 -30
  80. data/lib/mongoid/threaded/lifecycle.rb +12 -12
  81. data/lib/mongoid/timestamps.rb +1 -0
  82. data/lib/mongoid/timestamps/created.rb +2 -0
  83. data/lib/mongoid/timestamps/created/short.rb +19 -0
  84. data/lib/mongoid/timestamps/short.rb +10 -0
  85. data/lib/mongoid/timestamps/updated.rb +2 -0
  86. data/lib/mongoid/timestamps/updated/short.rb +19 -0
  87. data/lib/mongoid/validations.rb +2 -0
  88. data/lib/mongoid/validations/queryable.rb +2 -2
  89. data/lib/mongoid/validations/uniqueness.rb +1 -18
  90. data/lib/mongoid/version.rb +1 -1
  91. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -0
  92. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +3 -0
  93. data/lib/rails/mongoid.rb +53 -29
  94. data/lib/support/ruby_version.rb +26 -0
  95. metadata +18 -7
@@ -58,6 +58,7 @@ module Mongoid
58
58
  self.embedded = true
59
59
  relate(name, meta)
60
60
  builder(name, meta).creator(name, meta)
61
+ add_counter_cache_callbacks(meta) if meta.counter_cached?
61
62
  meta
62
63
  end
63
64
 
@@ -140,6 +141,7 @@ module Mongoid
140
141
  meta = reference_one_to_one(name, options, Referenced::In, &block)
141
142
  aliased_fields[name.to_s] = meta.foreign_key
142
143
  touchable(meta)
144
+ add_counter_cache_callbacks(meta) if meta.counter_cached?
143
145
  meta
144
146
  end
145
147
 
@@ -25,7 +25,6 @@ module Mongoid
25
25
  # @since 3.0.15
26
26
  def marshal_load(data)
27
27
  @base, @target, @metadata = data
28
- extend_proxy(metadata.extension) if metadata.extension?
29
28
  end
30
29
  end
31
30
  end
@@ -138,6 +138,34 @@ module Mongoid
138
138
  @constraint ||= Constraint.new(self)
139
139
  end
140
140
 
141
+ # Does the metadata have a counter cache?
142
+ #
143
+ # @example Is the metadata counter_cached?
144
+ # metadata.counter_cached?
145
+ #
146
+ # @return [ true, false ] If the metadata has counter_cache
147
+ #
148
+ # @since 3.1.0
149
+ def counter_cached?
150
+ !!self[:counter_cache]
151
+ end
152
+
153
+ # Returns the counter cache column name
154
+ #
155
+ # @example Get the counter cache column.
156
+ # metadata.counter_cache_column_name
157
+ #
158
+ # @return [ String ] The counter cache column
159
+ #
160
+ # @since 3.1.0
161
+ def counter_cache_column_name
162
+ if self[:counter_cache] == true
163
+ "#{inverse_class_name.demodulize.underscore.pluralize}_count"
164
+ else
165
+ self[:counter_cache].to_s
166
+ end
167
+ end
168
+
141
169
  # Get the criteria that is used to query for this metadata's relation.
142
170
  #
143
171
  # @example Get the criteria.
@@ -361,6 +389,7 @@ module Mongoid
361
389
  autobuild: #{autobuilding?}
362
390
  class_name: #{class_name}
363
391
  cyclic: #{cyclic.inspect}
392
+ counter_cache:#{counter_cached?}
364
393
  dependent: #{dependent.inspect}
365
394
  inverse_of: #{inverse_of.inspect}
366
395
  key: #{key}
@@ -477,13 +506,13 @@ module Mongoid
477
506
  # @example Get the inverse metadata.
478
507
  # metadata.inverse_metadata(doc)
479
508
  #
480
- # @param [ Document ] document The document to check.
509
+ # @param [ Document, Class ] object The document or class.
481
510
  #
482
511
  # @return [ Metadata ] The inverse metadata.
483
512
  #
484
513
  # @since 2.1.0
485
- def inverse_metadata(document)
486
- document.reflect_on_association(inverse(document))
514
+ def inverse_metadata(object)
515
+ object.reflect_on_association(inverse(object))
487
516
  end
488
517
 
489
518
  # Returns the inverse_of option of the relation.
@@ -521,8 +550,7 @@ module Mongoid
521
550
  #
522
551
  # @since 2.0.0.rc.1
523
552
  def inverse_setter(other = nil)
524
- inv = inverse(other)
525
- inv ? "#{inv}=" : nil
553
+ inverse(other).__setter__
526
554
  end
527
555
 
528
556
  # Returns the name of the field in which to store the name of the class
@@ -548,7 +576,7 @@ module Mongoid
548
576
  #
549
577
  # @since 2.0.0.rc.1
550
578
  def inverse_type_setter
551
- @inverse_type_setter ||= inverse_type ? "#{inverse_type}=" : nil
579
+ @inverse_type_setter ||= inverse_type.__setter__
552
580
  end
553
581
 
554
582
  # Returns the name of the field in which to store the name of the inverse
@@ -573,7 +601,7 @@ module Mongoid
573
601
  #
574
602
  # @return [ String ] The name of the setter.
575
603
  def inverse_of_field_setter
576
- @inverse_of_field_setter ||= inverse_of_field ? "#{inverse_of_field}=" : nil
604
+ @inverse_of_field_setter ||= inverse_of_field.__setter__
577
605
  end
578
606
 
579
607
  # This returns the key that is to be used to grab the attributes for the
@@ -705,6 +733,18 @@ module Mongoid
705
733
  @polymorphic ||= (!!self[:as] || !!self[:polymorphic])
706
734
  end
707
735
 
736
+ # Get the primary key field for finding the related document.
737
+ #
738
+ # @example Get the primary key.
739
+ # metadata.primary_key
740
+ #
741
+ # @return [ String ] The primary key field.
742
+ #
743
+ # @since 3.1.0
744
+ def primary_key
745
+ @primary_key ||= (self[:primary_key] || "_id").to_s
746
+ end
747
+
708
748
  # Get the relation associated with this metadata.
709
749
  #
710
750
  # @example Get the relation.
@@ -727,7 +767,7 @@ module Mongoid
727
767
  #
728
768
  # @since 2.0.0.rc.1
729
769
  def setter
730
- @setter ||= "#{name.to_s}="
770
+ @setter ||= "#{name}="
731
771
  end
732
772
 
733
773
  # Returns the name of the field in which to store the name of the class
@@ -740,7 +780,7 @@ module Mongoid
740
780
  #
741
781
  # @since 2.0.0.rc.1
742
782
  def type
743
- @type ||= polymorphic? ? "#{as.to_s}_type" : nil
783
+ @type ||= polymorphic? ? "#{as}_type" : nil
744
784
  end
745
785
 
746
786
  # Gets the setter for the field that sets the type of document on a
@@ -753,7 +793,7 @@ module Mongoid
753
793
  #
754
794
  # @since 2.0.0.rc.1
755
795
  def type_setter
756
- @type_setter ||= type ? "#{type}=" : nil
796
+ @type_setter ||= type.__setter__
757
797
  end
758
798
 
759
799
 
@@ -844,6 +884,18 @@ module Mongoid
844
884
  !!self[:touch]
845
885
  end
846
886
 
887
+ # Returns the metadata class types.
888
+ #
889
+ # @example Get the relation class types.
890
+ # metadata.type_relation
891
+ #
892
+ # @return [ Hash ] The hash with relation class types.
893
+ #
894
+ # @since 3.1.0
895
+ def type_relation
896
+ { _type: { "$in" => klass._types }}
897
+ end
898
+
847
899
  private
848
900
 
849
901
  # Returns the class name for the relation.
@@ -1089,7 +1141,7 @@ module Mongoid
1089
1141
  # @since 2.0.0.rc.1
1090
1142
  def determine_key
1091
1143
  return store_as.to_s if relation.embedded?
1092
- relation.stores_foreign_key? ? foreign_key : "_id"
1144
+ relation.stores_foreign_key? ? foreign_key : primary_key
1093
1145
  end
1094
1146
 
1095
1147
  # Determine the name of the inverse relation.
@@ -10,6 +10,7 @@ module Mongoid
10
10
  # These options are available to all relations.
11
11
  COMMON = [
12
12
  :class_name,
13
+ :counter_cache,
13
14
  :extend,
14
15
  :inverse_class_name,
15
16
  :inverse_of,
@@ -171,6 +171,49 @@ module Mongoid
171
171
  raise Errors::UnsavedDocument.new(base, doc)
172
172
  end
173
173
 
174
+ # Return the name of defined callback method
175
+ #
176
+ # @example returns the before_add callback method name
177
+ # callback_method(:before_add)
178
+ #
179
+ # @param [ Symbol ] which callback
180
+ #
181
+ # @return [ Array ] with callback methods to be executed, the array may have symbols and Procs
182
+ #
183
+ # @since 3.1.0
184
+ def callback_method(callback_name)
185
+ methods = []
186
+ if metadata[callback_name]
187
+ if metadata[callback_name].is_a? Array
188
+ methods.concat(metadata[callback_name])
189
+ else
190
+ methods << metadata[callback_name]
191
+ end
192
+ end
193
+ methods
194
+ end
195
+
196
+ # Executes a callback method
197
+ #
198
+ # @example execute the before add callback
199
+ # execute_callback(:before_add)
200
+ #
201
+ # @param [ Symbol ] callback to be executed
202
+ #
203
+ # @since 3.1.0
204
+ def execute_callback(callback, doc)
205
+ callback_method = callback_method(callback)
206
+ if callback_method
207
+ callback_method.each do |c|
208
+ if c.is_a? Proc
209
+ c.call(base, doc)
210
+ else
211
+ base.send c, doc
212
+ end
213
+ end
214
+ end
215
+ end
216
+
174
217
  class << self
175
218
 
176
219
  # Apply ordering to the criteria if it was defined on the relation.
@@ -204,11 +247,11 @@ module Mongoid
204
247
  klass, foreign_key = metadata.klass, metadata.foreign_key
205
248
  eager_loaded = klass.any_in(foreign_key => ids).entries
206
249
  ids.each do |id|
207
- IdentityMap.clear_many(klass, { foreign_key => id })
250
+ IdentityMap.clear_many(klass, metadata.type_relation.merge!(foreign_key => id))
208
251
  end
209
252
  eager_loaded.each do |doc|
210
253
  base_id = doc.__send__(foreign_key)
211
- yield(doc, { foreign_key => base_id })
254
+ yield(doc, metadata.type_relation.merge!(foreign_key => base_id))
212
255
  end
213
256
  end
214
257
  end
@@ -119,7 +119,7 @@ module Mongoid
119
119
  #
120
120
  # @since 2.1.0
121
121
  def criteria(metadata, object, type = nil)
122
- type.where(_id: object)
122
+ type.where(metadata.primary_key => object)
123
123
  end
124
124
 
125
125
  # Get the criteria that is used to eager load a relation of this
@@ -266,7 +266,16 @@ module Mongoid
266
266
  #
267
267
  # @since 2.1.0
268
268
  def valid_options
269
- [ :autobuild, :autosave, :dependent, :foreign_key, :index, :polymorphic, :touch ]
269
+ [
270
+ :autobuild,
271
+ :autosave,
272
+ :dependent,
273
+ :foreign_key,
274
+ :index,
275
+ :polymorphic,
276
+ :primary_key,
277
+ :touch
278
+ ]
270
279
  end
271
280
 
272
281
  # Get the default validation setting for the relation. Determines if
@@ -105,11 +105,13 @@ module Mongoid
105
105
  #
106
106
  # @since 2.1.0
107
107
  def delete(document)
108
+ execute_callback :before_remove, document
108
109
  target.delete(document) do |doc|
109
110
  if doc
110
111
  unbind_one(doc)
111
112
  cascade!(doc) if !_assigning?
112
113
  end
114
+ execute_callback :after_remove, doc
113
115
  end
114
116
  end
115
117
 
@@ -250,11 +252,20 @@ module Mongoid
250
252
  unless metadata.destructive?
251
253
  nullify
252
254
  else
255
+ after_remove_error = nil
253
256
  criteria.delete_all
254
- target.clear do |doc|
257
+ many = target.clear do |doc|
258
+ execute_callback :before_remove, doc
255
259
  unbind_one(doc)
256
260
  doc.destroyed = true
261
+ begin
262
+ execute_callback :after_remove, doc
263
+ rescue => e
264
+ after_remove_error = e
265
+ end
257
266
  end
267
+ raise after_remove_error if after_remove_error
268
+ many
258
269
  end
259
270
  end
260
271
  alias :clear :purge
@@ -313,9 +324,11 @@ module Mongoid
313
324
  #
314
325
  # @since 2.0.0.rc.1
315
326
  def append(document)
327
+ execute_callback :before_add, document
316
328
  target.push(document)
317
329
  characterize_one(document)
318
330
  bind_one(document)
331
+ execute_callback :after_add, document
319
332
  end
320
333
 
321
334
  # Instantiate the binding associated with this relation.
@@ -354,7 +367,11 @@ module Mongoid
354
367
  #
355
368
  # @since 2.0.0.beta.1
356
369
  def criteria
357
- Many.criteria(metadata, Conversions.flag(base.id, metadata), base.class)
370
+ Many.criteria(
371
+ metadata,
372
+ Conversions.flag(base.send(metadata.primary_key), metadata),
373
+ base.class
374
+ )
358
375
  end
359
376
 
360
377
  # Perform the necessary cascade operations for documents that just got
@@ -693,7 +710,18 @@ module Mongoid
693
710
  #
694
711
  # @since 2.1.0
695
712
  def valid_options
696
- [ :as, :autosave, :dependent, :foreign_key, :order ]
713
+ [
714
+ :after_add,
715
+ :after_remove,
716
+ :as,
717
+ :autosave,
718
+ :before_add,
719
+ :before_remove,
720
+ :dependent,
721
+ :foreign_key,
722
+ :order,
723
+ :primary_key
724
+ ]
697
725
  end
698
726
 
699
727
  # Get the default validation setting for the relation. Determines if
@@ -136,6 +136,9 @@ module Mongoid
136
136
  #
137
137
  # @since 2.0.0.rc.1
138
138
  def nullify
139
+ target.each do |doc|
140
+ execute_callback :before_remove, doc
141
+ end
139
142
  unless metadata.forced_nil_inverse?
140
143
  criteria.pull(inverse_foreign_key, base.id)
141
144
  end
@@ -145,12 +148,20 @@ module Mongoid
145
148
  base.send(foreign_key).clear
146
149
  )
147
150
  end
148
- target.clear do |doc|
151
+ after_remove_error = nil
152
+ many_to_many = target.clear do |doc|
149
153
  unbind_one(doc)
150
154
  unless metadata.forced_nil_inverse?
151
155
  doc.changed_attributes.delete(inverse_foreign_key)
152
156
  end
157
+ begin
158
+ execute_callback :after_remove, doc
159
+ rescue => e
160
+ after_remove_error = e
161
+ end
153
162
  end
163
+ raise after_remove_error if after_remove_error
164
+ many_to_many
154
165
  end
155
166
  alias :nullify_all :nullify
156
167
  alias :clear :nullify
@@ -203,9 +214,11 @@ module Mongoid
203
214
  #
204
215
  # @since 2.0.0.rc.1
205
216
  def append(document)
217
+ execute_callback :before_add, document
206
218
  target.push(document)
207
219
  characterize_one(document)
208
220
  bind_one(document)
221
+ execute_callback :after_add, document
209
222
  end
210
223
 
211
224
  # Instantiate the binding associated with this relation.
@@ -300,7 +313,11 @@ module Mongoid
300
313
  #
301
314
  # @since 2.1.0
302
315
  def criteria(metadata, object, type = nil)
303
- apply_ordering(metadata.klass.all_of(_id: { "$in" => object || [] }), metadata)
316
+ apply_ordering(
317
+ metadata.klass.all_of(
318
+ metadata.primary_key => { "$in" => object || [] }
319
+ ), metadata
320
+ )
304
321
  end
305
322
 
306
323
  # Get the criteria that is used to eager load a relation of this
@@ -445,7 +462,18 @@ module Mongoid
445
462
  #
446
463
  # @since 2.1.0
447
464
  def valid_options
448
- [ :autosave, :dependent, :foreign_key, :index, :order ]
465
+ [
466
+ :after_add,
467
+ :after_remove,
468
+ :autosave,
469
+ :before_add,
470
+ :before_remove,
471
+ :dependent,
472
+ :foreign_key,
473
+ :index,
474
+ :order,
475
+ :primary_key
476
+ ]
449
477
  end
450
478
 
451
479
  # Get the default validation setting for the relation. Determines if
@@ -275,7 +275,7 @@ module Mongoid
275
275
  #
276
276
  # @since 2.1.0
277
277
  def valid_options
278
- [ :as, :autobuild, :autosave, :dependent, :foreign_key ]
278
+ [ :as, :autobuild, :autosave, :dependent, :foreign_key, :primary_key ]
279
279
  end
280
280
 
281
281
  # Get the default validation setting for the relation. Determines if
@@ -163,7 +163,7 @@ module Mongoid
163
163
  yield(doc)
164
164
  end
165
165
  else
166
- _unloaded.each do |doc|
166
+ unloaded_documents.each do |doc|
167
167
  document = _added.delete(doc.id) || _loaded.delete(doc.id) || doc
168
168
  _loaded[document.id] = document
169
169
  yield(document)
@@ -448,6 +448,10 @@ module Mongoid
448
448
  ul ||
449
449
  _added.values.try(location)
450
450
  end
451
+
452
+ def unloaded_documents
453
+ _unloaded.selector.values.any?(&:blank_criteria?) ? [] : _unloaded
454
+ end
451
455
  end
452
456
  end
453
457
  end