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
@@ -20,7 +20,7 @@ module Mongoid
20
20
  #
21
21
  # @since 2.1.0
22
22
  def path
23
- position.sub(/\.\d+$/, "")
23
+ @path ||= position.sub(/\.\d+$/, "")
24
24
  end
25
25
 
26
26
  # Get the selector to use for the root document when performing atomic
@@ -33,10 +33,24 @@ module Mongoid
33
33
  #
34
34
  # @since 2.1.0
35
35
  def selector
36
- parent.atomic_selector
37
- # @todo: Durran: Bring this back once MongoDB, if ever, goes to fix
38
- # this issue: https://jira.mongodb.org/browse/SERVER-831
39
- # merge!({ "#{path}._id" => document._id }).merge!(document.shard_key_selector)
36
+ @selector ||= generate_selector
37
+ end
38
+
39
+ private
40
+
41
+ def generate_selector
42
+ if only_root_selector?
43
+ parent.atomic_selector
44
+ else
45
+ parent.
46
+ atomic_selector.
47
+ merge("#{path}._id" => document._id).
48
+ merge(document.shard_key_selector)
49
+ end
50
+ end
51
+
52
+ def only_root_selector?
53
+ document.persisted? && document._id_changed?
40
54
  end
41
55
  end
42
56
  end
@@ -44,7 +44,7 @@ module Mongoid
44
44
  #
45
45
  # @since 2.1.0
46
46
  def selector
47
- { "_id" => document._id }.merge!(document.shard_key_selector)
47
+ @selector ||= { "_id" => document._id }.merge!(document.shard_key_selector)
48
48
  end
49
49
  end
50
50
  end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Atomic
4
+
5
+ # This module is responsible for taking update selectors and switching out
6
+ # the indexes for the $ positional operator where appropriate.
7
+ #
8
+ # @since 3.1.0
9
+ module Positionable
10
+
11
+ # Takes the provided selector and atomic operations and replaces the
12
+ # indexes of the embedded documents with the positional operator when
13
+ # needed.
14
+ #
15
+ # @note The only time we can accurately know when to use the positional
16
+ # operator is at the exact time we are going to persist something. So
17
+ # we can tell by the selector that we are sending if it is actually
18
+ # possible to use the positional operator at all. For example, if the
19
+ # selector is: { "_id" => 1 }, then we could not use the positional
20
+ # operator for updating embedded documents since there would never be a
21
+ # match - we base whether we can based on the number of levels deep the
22
+ # selector goes, and if the id values are not nil.
23
+ #
24
+ # @example Process the operations.
25
+ # positionally(
26
+ # { "_id" => 1, "addresses._id" => 2 },
27
+ # { "$set" => { "addresses.0.street" => "hobrecht" }}
28
+ # )
29
+ #
30
+ # @param [ Hash ] selector The selector.
31
+ # @param [ Hash ] operations The update operations.
32
+ # @param [ Hash ] processed The processed update operations.
33
+ #
34
+ # @return [ Hash ] The new operations.
35
+ #
36
+ # @since 3.1.0
37
+ def positionally(selector, operations, processed = {})
38
+ if selector.size == 1 || selector.values.any? { |val| val.nil? }
39
+ return operations
40
+ end
41
+ keys = selector.keys.map{ |m| m.sub('._id','') } - ['_id']
42
+ keys = keys.sort_by { |s| s.length*-1 }
43
+ process_operations(keys, operations, processed)
44
+ end
45
+
46
+ private
47
+
48
+ def process_operations(keys, operations, processed)
49
+ operations.each_pair do |operation, update|
50
+ processed[operation] = process_updates(keys, update)
51
+ end
52
+ processed
53
+ end
54
+
55
+ def process_updates(keys, update, updates = {})
56
+ update.each_pair do |position, value|
57
+ updates[replace_index(keys, position)] = value
58
+ end
59
+ updates
60
+ end
61
+
62
+ def replace_index(keys, position)
63
+ # replace to $ only if that key is on the selector
64
+ keys.each do |kk|
65
+ if position =~ /^#{kk}\.\d+\.(.*)/
66
+ return "#{kk}.$.#{$1}"
67
+ end
68
+ end
69
+ position
70
+ end
71
+ end
72
+ end
73
+ end
@@ -12,6 +12,7 @@ module Mongoid
12
12
  include Readonly
13
13
 
14
14
  attr_reader :attributes
15
+ attr_reader :attributes_before_type_cast
15
16
  alias :raw_attributes :attributes
16
17
 
17
18
  # Determine if an attribute is present.
@@ -43,6 +44,22 @@ module Mongoid
43
44
  attributes.has_key?(name.to_s)
44
45
  end
45
46
 
47
+ # Does the document have the provided attribute before it was assigned
48
+ # and type cast?
49
+ #
50
+ # @example Does the document have the attribute before it was assigned?
51
+ # model.has_attribute_before_type_cast?(:name)
52
+ #
53
+ # @param [ String, Symbol ] name The name of the attribute.
54
+ #
55
+ # @return [ true, false ] If the key is present in the
56
+ # attributes_before_type_cast.
57
+ #
58
+ # @since 3.1.0
59
+ def has_attribute_before_type_cast?(name)
60
+ attributes_before_type_cast.has_key?(name.to_s)
61
+ end
62
+
46
63
  # Read a value from the document attributes. If the value does not exist
47
64
  # it will return nil.
48
65
  #
@@ -67,6 +84,28 @@ module Mongoid
67
84
  end
68
85
  alias :[] :read_attribute
69
86
 
87
+ # Read a value from the attributes before type cast. If the value has not
88
+ # yet been assigned then this will return the attribute's existing value
89
+ # using read_attribute.
90
+ #
91
+ # @example Read an attribute before type cast.
92
+ # person.read_attribute_before_type_cast(:price)
93
+ #
94
+ # @param [ String, Symbol ] name The name of the attribute to get.
95
+ #
96
+ # @return [ Object ] The value of the attribute before type cast, if
97
+ # available. Otherwise, the value of the attribute.
98
+ #
99
+ # @since 3.1.0
100
+ def read_attribute_before_type_cast(name)
101
+ attr = name.to_s
102
+ if attributes_before_type_cast.has_key?(attr)
103
+ attributes_before_type_cast[attr]
104
+ else
105
+ read_attribute(attr)
106
+ end
107
+ end
108
+
70
109
  # Remove a value from the +Document+ attributes. If the value does not exist
71
110
  # it will fail gracefully.
72
111
  #
@@ -129,6 +168,7 @@ module Mongoid
129
168
  if attribute_writable?(access)
130
169
  _assigning do
131
170
  localized = fields[access].try(:localized?)
171
+ attributes_before_type_cast[name.to_s] = value
132
172
  typed_value = typed_value_for(access, value)
133
173
  unless attributes[access] == typed_value || attribute_changed?(access)
134
174
  attribute_will_change!(access)
@@ -206,6 +246,24 @@ module Mongoid
206
246
  READER
207
247
  end
208
248
 
249
+ # Define a reader method for a dynamic attribute before type cast.
250
+ #
251
+ # @api private
252
+ #
253
+ # @example Define a reader method for an attribute.
254
+ # model.define_dynamic_before_type_cast_reader(:field)
255
+ #
256
+ # @param [ String ] name The name of the field.
257
+ #
258
+ # @since 3.1.0
259
+ def define_dynamic_before_type_cast_reader(name)
260
+ class_eval <<-READER
261
+ def #{name}_before_type_cast
262
+ read_attribute_before_type_cast(#{name.inspect})
263
+ end
264
+ READER
265
+ end
266
+
209
267
  # Define a writer method for a dynamic attribute.
210
268
  #
211
269
  # @api private
@@ -251,6 +309,9 @@ module Mongoid
251
309
  getter = attr.reader
252
310
  define_dynamic_writer(getter)
253
311
  write_attribute(getter, args.first)
312
+ elsif attr.before_type_cast?
313
+ define_dynamic_before_type_cast_reader(attr.reader)
314
+ read_attribute_before_type_cast(attr.reader)
254
315
  else
255
316
  getter = attr.reader
256
317
  define_dynamic_reader(getter)
@@ -270,7 +331,7 @@ module Mongoid
270
331
  #
271
332
  # @since 1.0.0
272
333
  def typed_value_for(key, value)
273
- fields.has_key?(key) ? fields[key].mongoize(value) : value.mongoize
334
+ fields.has_key?(key) ? fields[key].mongoize(value) : value
274
335
  end
275
336
 
276
337
  module ClassMethods
@@ -300,6 +361,7 @@ module Mongoid
300
361
  alias reset_#{name}! reset_#{original}!
301
362
  alias #{name}_was #{original}_was
302
363
  alias #{name}_will_change! #{original}_will_change!
364
+ alias #{name}_before_type_cast #{original}_before_type_cast
303
365
  RUBY
304
366
  end
305
367
  end
@@ -9,8 +9,10 @@ module Mongoid
9
9
  :after_build,
10
10
  :after_create,
11
11
  :after_destroy,
12
+ :after_find,
12
13
  :after_initialize,
13
14
  :after_save,
15
+ :after_touch,
14
16
  :after_update,
15
17
  :after_upsert,
16
18
  :after_validation,
@@ -25,14 +27,13 @@ module Mongoid
25
27
  :before_update,
26
28
  :before_upsert,
27
29
  :before_validation
28
- ]
30
+ ].freeze
29
31
 
30
32
  included do
31
33
  extend ActiveModel::Callbacks
32
34
  include ActiveModel::Validations::Callbacks
33
35
 
34
- define_model_callbacks :initialize, only: :after
35
- define_model_callbacks :build, only: :after
36
+ define_model_callbacks :build, :find, :initialize, :touch, only: :after
36
37
  define_model_callbacks :create, :destroy, :save, :update, :upsert
37
38
 
38
39
  attr_accessor :before_callback_halted
@@ -187,7 +188,9 @@ module Mongoid
187
188
  #
188
189
  # @since 2.3.0
189
190
  def cascadable_child?(kind, child)
190
- return false if kind == :initialize
191
+ if kind == :initialize || kind == :find
192
+ return false
193
+ end
191
194
  child.callback_executable?(kind) ? child.in_callback_state?(kind) : false
192
195
  end
193
196
 
@@ -256,5 +259,56 @@ module Mongoid
256
259
  end
257
260
  send(name)
258
261
  end
262
+
263
+ class << self
264
+
265
+ # Get all callbacks that can be observed.
266
+ #
267
+ # @example Get the observables.
268
+ # Callbacks.observables
269
+ #
270
+ # @return [ Array<Symbol> ] The names of the observables.
271
+ #
272
+ # @since 3.1.0
273
+ def observables
274
+ CALLBACKS + registered_observables
275
+ end
276
+
277
+ # Get all registered callbacks that can be observed, not included in
278
+ # Mongoid's defaults.
279
+ #
280
+ # @example Get the observables.
281
+ # Callbacks.registered_observables
282
+ #
283
+ # @return [ Array<Symbol> ] The names of the registered observables.
284
+ #
285
+ # @since 3.1.0
286
+ def registered_observables
287
+ @registered_observables ||= []
288
+ end
289
+ end
290
+
291
+ module ClassMethods
292
+
293
+ # Set a custom callback as able to be observed.
294
+ #
295
+ # @example Set a custom callback as observable.
296
+ # class Band
297
+ # include Mongoid::Document
298
+ #
299
+ # define_model_callbacks :notification
300
+ # observable :notification
301
+ # end
302
+ #
303
+ # @param [ Array<Symbol> ] args The names of the observable callbacks.
304
+ #
305
+ # @since 3.0.1
306
+ def observable(*args)
307
+ observables = args.flat_map do |name|
308
+ [ :"before_#{name}", :"after_#{name}", :"around_#{name}" ]
309
+ end
310
+ Callbacks.registered_observables.concat(observables).uniq
311
+ end
312
+ end
259
313
  end
260
314
  end
@@ -42,29 +42,34 @@ module Mongoid
42
42
  include Mongoid::Validations
43
43
  include Mongoid::Callbacks
44
44
  include Mongoid::Copyable
45
+ include Mongoid::Equality
45
46
 
46
47
  MODULES = [
47
48
  Mongoid::Atomic,
48
49
  Mongoid::Attributes,
50
+ Mongoid::Callbacks,
49
51
  Mongoid::Copyable,
50
52
  Mongoid::Dirty,
53
+ Mongoid::Evolvable,
51
54
  Mongoid::Fields,
52
55
  Mongoid::Hierarchy,
53
56
  Mongoid::Indexes,
54
57
  Mongoid::Inspection,
55
58
  Mongoid::JSON,
56
- Mongoid::Loggable,
57
59
  Mongoid::Matchers,
58
60
  Mongoid::NestedAttributes,
59
61
  Mongoid::Persistence,
60
62
  Mongoid::Relations,
61
- Mongoid::Relations::Proxy,
63
+ Mongoid::Reloading,
62
64
  Mongoid::Scoping,
63
65
  Mongoid::Serialization,
66
+ Mongoid::Sessions,
64
67
  Mongoid::Sharding,
65
68
  Mongoid::State,
69
+ Mongoid::Threaded::Lifecycle,
70
+ Mongoid::Timestamps::Timeless,
66
71
  Mongoid::Validations,
67
- Mongoid::Callbacks
72
+ Mongoid::Equality
68
73
  ]
69
74
 
70
75
  class << self
@@ -13,6 +13,11 @@ module Mongoid
13
13
  extend Options
14
14
  include ActiveModel::Observing
15
15
 
16
+ delegate :logger=, to: ::Mongoid
17
+ delegate :logger, to: ::Mongoid
18
+
19
+ LOCK = Mutex.new
20
+
16
21
  option :allow_dynamic_fields, default: true
17
22
  option :identity_map_enabled, default: false
18
23
  option :include_root_in_json, default: false
@@ -86,6 +91,47 @@ module Mongoid
86
91
  settings
87
92
  end
88
93
 
94
+ # Get all the models in the application - this is everything that includes
95
+ # Mongoid::Document.
96
+ #
97
+ # @example Get all the models.
98
+ # config.models
99
+ #
100
+ # @return [ Array<Class> ] All the models in the application.
101
+ #
102
+ # @since 3.1.0
103
+ def models
104
+ @models ||= []
105
+ end
106
+
107
+ # Register a model in the application with Mongoid.
108
+ #
109
+ # @example Register a model.
110
+ # config.register_model(Band)
111
+ #
112
+ # @param [ Class ] klass The model to register.
113
+ #
114
+ # @since 3.1.0
115
+ def register_model(klass)
116
+ LOCK.synchronize do
117
+ models.push(klass) unless models.include?(klass)
118
+ end
119
+ end
120
+
121
+ # From a hash of settings, load all the configuration.
122
+ #
123
+ # @example Load the configuration.
124
+ # config.load_configuration(settings)
125
+ #
126
+ # @param [ Hash ] settings The configuration settings.
127
+ #
128
+ # @since 3.1.0
129
+ def load_configuration(settings)
130
+ configuration = settings.with_indifferent_access
131
+ self.options = configuration[:options]
132
+ self.sessions = configuration[:sessions]
133
+ end
134
+
89
135
  # Override the database to use globally.
90
136
  #
91
137
  # @example Override the database globally.
@@ -119,6 +165,8 @@ module Mongoid
119
165
  # @example Purge all data.
120
166
  # Mongoid::Config.purge!
121
167
  #
168
+ # @note This is the fastest way to drop all data.
169
+ #
122
170
  # @return [ true ] true.
123
171
  #
124
172
  # @since 2.0.2
@@ -128,6 +176,22 @@ module Mongoid
128
176
  end and true
129
177
  end
130
178
 
179
+ # Truncate all data in all collections, but not the indexes.
180
+ #
181
+ # @example Truncate all collection data.
182
+ # Mongoid::Config.truncate!
183
+ #
184
+ # @note This will be slower than purge!
185
+ #
186
+ # @return [ true ] true.
187
+ #
188
+ # @since 2.0.2
189
+ def truncate!
190
+ Sessions.default.collections.each do |collection|
191
+ collection.find.remove_all
192
+ end and true
193
+ end
194
+
131
195
  # Set the configuration options. Will validate each one individually.
132
196
  #
133
197
  # @example Set the options.
@@ -145,18 +209,6 @@ module Mongoid
145
209
  end
146
210
  end
147
211
 
148
- # Is the application running under passenger?
149
- #
150
- # @example Is the application using passenger?
151
- # config.running_with_passenger?
152
- #
153
- # @return [ true, false ] If the app is deployed on Passenger.
154
- #
155
- # @since 3.0.11
156
- def running_with_passenger?
157
- @running_with_passenger ||= defined?(PhusionPassenger)
158
- end
159
-
160
212
  # Get the session configuration or an empty hash.
161
213
  #
162
214
  # @example Get the sessions configuration.
@@ -197,20 +249,16 @@ module Mongoid
197
249
  use_utc? ? "UTC" : ::Time.zone
198
250
  end
199
251
 
200
- private
201
-
202
- # From a hash of settings, load all the configuration.
252
+ # Is the application running under passenger?
203
253
  #
204
- # @example Load the configuration.
205
- # config.load_configuration(settings)
254
+ # @example Is the application using passenger?
255
+ # config.running_with_passenger?
206
256
  #
207
- # @param [ Hash ] settings The configuration settings.
257
+ # @return [ true, false ] If the app is deployed on Passenger.
208
258
  #
209
- # @since 3.0.0
210
- def load_configuration(settings)
211
- configuration = settings.with_indifferent_access
212
- self.options = configuration[:options]
213
- self.sessions = configuration[:sessions]
259
+ # @since 3.0.11
260
+ def running_with_passenger?
261
+ @running_with_passenger ||= defined?(PhusionPassenger)
214
262
  end
215
263
  end
216
264
  end