mongoid 3.0.23 → 3.1.0

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