mongoid 2.3.1 → 2.3.2

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 (47) hide show
  1. data/CHANGELOG.md +34 -1
  2. data/lib/mongoid.rb +3 -2
  3. data/lib/mongoid/atomic.rb +23 -2
  4. data/lib/mongoid/attributes.rb +0 -21
  5. data/lib/mongoid/callbacks.rb +9 -1
  6. data/lib/mongoid/components.rb +2 -0
  7. data/lib/mongoid/config.rb +4 -2
  8. data/lib/mongoid/contexts/mongo.rb +19 -8
  9. data/lib/mongoid/criterion/inclusion.rb +6 -4
  10. data/lib/mongoid/dirty.rb +1 -0
  11. data/lib/mongoid/document.rb +1 -25
  12. data/lib/mongoid/extensions/hash/criteria_helpers.rb +13 -0
  13. data/lib/mongoid/extensions/object/yoda.rb +1 -0
  14. data/lib/mongoid/matchers/strategies.rb +6 -1
  15. data/lib/mongoid/persistence.rb +2 -21
  16. data/lib/mongoid/railtie.rb +8 -1
  17. data/lib/mongoid/relations/accessors.rb +7 -5
  18. data/lib/mongoid/relations/binding.rb +2 -29
  19. data/lib/mongoid/relations/bindings/embedded/in.rb +4 -4
  20. data/lib/mongoid/relations/bindings/embedded/many.rb +4 -4
  21. data/lib/mongoid/relations/bindings/embedded/one.rb +4 -4
  22. data/lib/mongoid/relations/bindings/referenced/in.rb +4 -4
  23. data/lib/mongoid/relations/bindings/referenced/many.rb +4 -4
  24. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  25. data/lib/mongoid/relations/bindings/referenced/one.rb +4 -4
  26. data/lib/mongoid/relations/builder.rb +18 -4
  27. data/lib/mongoid/relations/builders.rb +0 -17
  28. data/lib/mongoid/relations/builders/embedded/in.rb +3 -3
  29. data/lib/mongoid/relations/builders/embedded/many.rb +4 -4
  30. data/lib/mongoid/relations/builders/embedded/one.rb +3 -3
  31. data/lib/mongoid/relations/builders/nested_attributes/many.rb +1 -1
  32. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  33. data/lib/mongoid/relations/embedded/in.rb +4 -3
  34. data/lib/mongoid/relations/embedded/many.rb +6 -5
  35. data/lib/mongoid/relations/embedded/one.rb +4 -3
  36. data/lib/mongoid/relations/metadata.rb +20 -5
  37. data/lib/mongoid/relations/proxy.rb +1 -48
  38. data/lib/mongoid/relations/referenced/in.rb +4 -3
  39. data/lib/mongoid/relations/referenced/many.rb +4 -3
  40. data/lib/mongoid/relations/referenced/many_to_many.rb +5 -4
  41. data/lib/mongoid/relations/referenced/one.rb +4 -3
  42. data/lib/mongoid/reloading.rb +91 -0
  43. data/lib/mongoid/state.rb +15 -22
  44. data/lib/mongoid/threaded.rb +51 -0
  45. data/lib/mongoid/threaded/lifecycle.rb +163 -0
  46. data/lib/mongoid/version.rb +1 -1
  47. metadata +21 -19
@@ -8,7 +8,40 @@ For instructions on upgrading to newer versions, visit
8
8
  * Ranges can now be passed to #where criteria to create a $gte/$lte query under the
9
9
  covers. `Person.where(dob: start_date...end_date)`
10
10
 
11
- ## 2.3.1 \[ In Development \] \[ Branch: 2.3.0-stable \]
11
+ ## 2.3.2 \[ In Development \] \[ Branch: 2.3.0-stable \]
12
+
13
+ * \#1347 Fix embedded matchers when provided a hash value that does not have a
14
+ modifier as a key.
15
+
16
+ * \#1346 Dup default sorting criteria when calling first/last on a criteria.
17
+
18
+ * \#1343 When passing no arguments to `Criteria#all_of` return all documents.
19
+ (Chris Leishman)
20
+
21
+ * \#1339 Ensure destroy callbacks are run on cascadable children when deleting via
22
+ nested attributes.
23
+
24
+ * \#1324 Setting `inverse_of: nil` on a many-to-many referencing the same class
25
+ returns nil for the inverse foreign key.
26
+
27
+ * \#1323 Allow both strings and symbols as ids in the attributes array for
28
+ nested attributes. (Michael Wood)
29
+
30
+ * \#1312 Setting a logger on the config now accepts anything that quacks like a
31
+ logger.
32
+
33
+ * \#1297 Don't hit the database when accessing relations if the base is new.
34
+
35
+ * \#1239 Allow appending of referenced relations in create blocks, post default set.
36
+
37
+ * \#1236 Ensure all models are loaded in rake tasks, so even in threadsafe mode
38
+ all indexes can be created.
39
+
40
+ * \#736 Calling #reload on embedded documents now works properly.
41
+
42
+ ### Resolved Issues
43
+
44
+ ## 2.3.1
12
45
 
13
46
  ### Resolved Issues
14
47
 
@@ -28,10 +28,10 @@ require "active_support/inflector"
28
28
  require "active_support/time_with_zone"
29
29
  require "active_model"
30
30
  require "mongo"
31
- require "mongoid/errors"
32
31
  require "mongoid/extensions"
33
- require "mongoid/relations"
32
+ require "mongoid/errors"
34
33
  require "mongoid/threaded"
34
+ require "mongoid/relations"
35
35
  require "mongoid/atomic"
36
36
  require "mongoid/attributes"
37
37
  require "mongoid/callbacks"
@@ -64,6 +64,7 @@ require "mongoid/named_scope"
64
64
  require "mongoid/nested_attributes"
65
65
  require "mongoid/observer"
66
66
  require "mongoid/persistence"
67
+ require "mongoid/reloading"
67
68
  require "mongoid/safety"
68
69
  require "mongoid/scope"
69
70
  require "mongoid/serialization"
@@ -101,7 +101,15 @@ module Mongoid #:nodoc:
101
101
  #
102
102
  # @since 2.2.0
103
103
  def atomic_pulls
104
- @atomic_pulls ||= {}
104
+ delayed_atomic_pulls.inject({}) do |pulls, (name, docs)|
105
+ pulls.tap do |pull|
106
+ docs.each do |doc|
107
+ (pull[doc.atomic_path] ||= []).push(doc.as_document)
108
+ doc.destroyed = true
109
+ doc.flagged_for_destroy = false
110
+ end
111
+ end
112
+ end
105
113
  end
106
114
 
107
115
  # Add the document as an atomic pull.
@@ -113,7 +121,8 @@ module Mongoid #:nodoc:
113
121
  #
114
122
  # @since 2.2.0
115
123
  def add_atomic_pull(document)
116
- (atomic_pulls[document.atomic_path] ||= []).push(document.as_document)
124
+ document.flagged_for_destroy = true
125
+ (delayed_atomic_pulls[document.metadata.name.to_s] ||= []).push(document)
117
126
  end
118
127
 
119
128
  # Get all the push attributes that need to occur.
@@ -175,6 +184,18 @@ module Mongoid #:nodoc:
175
184
  @delayed_atomic_sets ||= {}
176
185
  end
177
186
 
187
+ # Get a hash of atomic pulls that are pending.
188
+ #
189
+ # @example Get the atomic pulls.
190
+ # document.delayed_atomic_pulls
191
+ #
192
+ # @return [ Hash ] name/document pairs.
193
+ #
194
+ # @since 2.3.2
195
+ def delayed_atomic_pulls
196
+ @delayed_atomic_pulls ||= {}
197
+ end
198
+
178
199
  private
179
200
 
180
201
  # Get the atomic paths utility for this document.
@@ -175,27 +175,6 @@ module Mongoid #:nodoc:
175
175
  end
176
176
  end
177
177
 
178
- # Begin the assignment of attributes. While in this block embedded
179
- # documents will not autosave themselves in order to allow the document to
180
- # be in a valid state.
181
- #
182
- # @example Execute the assignment.
183
- # _assigning do
184
- # person.attributes = { :addresses => [ address ] }
185
- # end
186
- #
187
- # @return [ Object ] The yielded value.
188
- #
189
- # @since 2.2.0
190
- def _assigning
191
- begin
192
- Threaded.begin_assign
193
- yield
194
- ensure
195
- Threaded.exit_assign
196
- end
197
- end
198
-
199
178
  # Used for allowing accessor methods for dynamic attributes.
200
179
  #
201
180
  # @param [ String, Symbol ] name The name of the method.
@@ -82,6 +82,8 @@ module Mongoid #:nodoc:
82
82
  [].tap do |children|
83
83
  relations.each_pair do |name, metadata|
84
84
  next unless metadata.cascading_callbacks?
85
+ delayed_pulls = delayed_atomic_pulls[name]
86
+ children.concat(delayed_pulls) if delayed_pulls
85
87
  child = send(name)
86
88
  Array.wrap(child).each do |doc|
87
89
  children.push(doc) if cascadable_child?(kind, doc)
@@ -121,7 +123,13 @@ module Mongoid #:nodoc:
121
123
  #
122
124
  # @since 2.3.0
123
125
  def child_callback_type(kind, child)
124
- kind == :update && child.new_record? ? :create : kind
126
+ if kind == :update
127
+ return :create if child.new_record?
128
+ return :destroy if child.flagged_for_destroy?
129
+ kind
130
+ else
131
+ kind
132
+ end
125
133
  end
126
134
  end
127
135
  end
@@ -34,10 +34,12 @@ module Mongoid #:nodoc
34
34
  include Mongoid::NestedAttributes
35
35
  include Mongoid::Persistence
36
36
  include Mongoid::Relations
37
+ include Mongoid::Reloading
37
38
  include Mongoid::Safety
38
39
  include Mongoid::Serialization
39
40
  include Mongoid::Sharding
40
41
  include Mongoid::State
42
+ include Mongoid::Threaded::Lifecycle
41
43
  include Mongoid::Timestamps::Timeless
42
44
  include Mongoid::Validations
43
45
  include Mongoid::Callbacks
@@ -166,8 +166,10 @@ module Mongoid #:nodoc
166
166
  # @return [ Logger ] The newly set logger.
167
167
  def logger=(logger)
168
168
  case logger
169
- when Logger then @logger = logger
170
- when false, nil then @logger = nil
169
+ when false, nil then @logger = nil
170
+ when true then @logger = default_logger
171
+ else
172
+ @logger = logger if logger.respond_to?(:info)
171
173
  end
172
174
  end
173
175
 
@@ -158,10 +158,7 @@ module Mongoid #:nodoc:
158
158
  #
159
159
  # @return [ Document ] The first document in the collection.
160
160
  def first
161
- opts = process_options
162
- sorting = opts[:sort] ||= []
163
- sorting << [:_id, :asc]
164
- attributes = klass.collection.find_one(selector, opts)
161
+ attributes = klass.collection.find_one(selector, options_with_default_sorting)
165
162
  attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
166
163
  end
167
164
  alias :one :first
@@ -225,10 +222,8 @@ module Mongoid #:nodoc:
225
222
  #
226
223
  # @return [ Document ] The last document in the collection.
227
224
  def last
228
- opts = process_options
229
- sorting = opts[:sort] ||= []
230
- sorting << [:_id, :asc]
231
- opts[:sort] = sorting.map{ |option| [ option[0], option[1].invert ] }.uniq
225
+ opts = options_with_default_sorting
226
+ opts[:sort] = opts[:sort].map{ |option| [ option[0], option[1].invert ] }.uniq
232
227
  attributes = klass.collection.find_one(selector, opts)
233
228
  attributes ? Mongoid::Factory.from_db(klass, attributes) : nil
234
229
  end
@@ -374,6 +369,22 @@ module Mongoid #:nodoc:
374
369
  value && value.do_or_do_not(:nan?) ? nil : value
375
370
  end
376
371
 
372
+ # Get the options hash with the default sorting options provided.
373
+ #
374
+ # @example Get the options.
375
+ # criteria.options_with_default_sorting
376
+ #
377
+ # @return [ Hash ] The options.
378
+ #
379
+ # @since 2.3.2
380
+ def options_with_default_sorting
381
+ process_options.tap do |opts|
382
+ sorting = opts[:sort] ? opts[:sort].dup : []
383
+ sorting << [:_id, :asc]
384
+ opts[:sort] = sorting
385
+ end
386
+ end
387
+
377
388
  # Filters the field list. If no fields have been supplied, then it will be
378
389
  # empty. If fields have been defined then _type will be included as well.
379
390
  #
@@ -34,10 +34,12 @@ module Mongoid #:nodoc:
34
34
  # @since 2.3.0
35
35
  def all_of(*args)
36
36
  clone.tap do |crit|
37
- criterion = @selector["$and"] || []
38
- converted = BSON::ObjectId.convert(klass, args.flatten)
39
- expanded = converted.collect { |hash| hash.expand_complex_criteria }
40
- crit.selector["$and"] = criterion.concat(expanded)
37
+ unless args.empty?
38
+ criterion = @selector["$and"] || []
39
+ converted = BSON::ObjectId.convert(klass, args.flatten)
40
+ expanded = converted.collect { |hash| hash.expand_complex_criteria }
41
+ crit.selector["$and"] = criterion.concat(expanded)
42
+ end
41
43
  end
42
44
  end
43
45
 
@@ -19,6 +19,7 @@ module Mongoid #:nodoc:
19
19
  atomic_pulls.clear
20
20
  atomic_unsets.clear
21
21
  delayed_atomic_sets.clear
22
+ delayed_atomic_pulls.clear
22
23
  changed_attributes.clear
23
24
  end
24
25
 
@@ -129,38 +129,14 @@ module Mongoid #:nodoc:
129
129
  @attributes ||= {}
130
130
  options ||= {}
131
131
  process(attrs, options[:as] || :default, !options[:without_protection]) do
132
- yield self if block_given?
133
132
  identify
134
133
  apply_defaults
134
+ yield(self) if block_given?
135
135
  end
136
136
  run_callbacks(:initialize) { self }
137
137
  end
138
138
  end
139
139
 
140
- # Reloads the +Document+ attributes from the database. If the document has
141
- # not been saved then an error will get raised if the configuration option
142
- # was set.
143
- #
144
- # @example Reload the document.
145
- # person.reload
146
- #
147
- # @raise [ Errors::DocumentNotFound ] If the document was deleted.
148
- #
149
- # @return [ Document ] The document, reloaded.
150
- def reload
151
- reloaded = collection.find_one(:_id => id)
152
- if Mongoid.raise_not_found_error
153
- raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
154
- end
155
- @attributes = {}.merge(reloaded || {})
156
- changed_attributes.clear
157
- apply_defaults
158
- tap do
159
- reload_relations
160
- run_callbacks(:initialize)
161
- end
162
- end
163
-
164
140
  # Return an array with this +Document+ only in it.
165
141
  #
166
142
  # @example Return the document in an array.
@@ -26,6 +26,19 @@ module Mongoid #:nodoc:
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ # Get the id attribute from this hash, whether it's prefixed with an
31
+ # underscore or is a symbol.
32
+ #
33
+ # @example Extract the id.
34
+ # { :_id => 1 }.extract_id
35
+ #
36
+ # @return [ Object ] The value of the id.
37
+ #
38
+ # @since 2.3.2
39
+ def extract_id
40
+ self["id"] || self["_id"] || self[:id] || self[:_id]
41
+ end
29
42
  end
30
43
  end
31
44
  end
@@ -19,6 +19,7 @@ module Mongoid #:nodoc:
19
19
  #
20
20
  # @since 2.0.0.rc.1
21
21
  def do_or_do_not(name, *args)
22
+ return nil unless name
22
23
  respond_to?(name) ? send(name, *args) : nil
23
24
  end
24
25
 
@@ -51,7 +51,12 @@ module Mongoid #:nodoc:
51
51
  # @since 2.0.0.rc.7
52
52
  def matcher(document, key, value)
53
53
  if value.is_a?(Hash)
54
- MATCHERS[value.keys.first].new(extract_attribute(document, key))
54
+ matcher = MATCHERS[value.keys.first]
55
+ if matcher
56
+ matcher.new(extract_attribute(document, key))
57
+ else
58
+ Default.new(extract_attribute(document, key))
59
+ end
55
60
  else
56
61
  case key
57
62
  when "$or" then Matchers::Or.new(value, document)
@@ -168,7 +168,7 @@ module Mongoid #:nodoc:
168
168
  #
169
169
  # @return [ Document ] The newly created document.
170
170
  def create(attributes = {}, options = {}, &block)
171
- creating do
171
+ _creating do
172
172
  new(attributes, options, &block).tap { |doc| doc.save }
173
173
  end
174
174
  end
@@ -187,7 +187,7 @@ module Mongoid #:nodoc:
187
187
  #
188
188
  # @return [ Document ] The newly created document.
189
189
  def create!(attributes = {}, options = {}, &block)
190
- creating do
190
+ _creating do
191
191
  new(attributes, options, &block).tap do |doc|
192
192
  fail_validate!(doc) if doc.insert.errors.any?
193
193
  fail_callback!(doc, :create!) if doc.new?
@@ -259,25 +259,6 @@ module Mongoid #:nodoc:
259
259
  def fail_callback!(document, method)
260
260
  raise Errors::Callback.new(document.class, method)
261
261
  end
262
-
263
- private
264
-
265
- # Execute a block in creating mode.
266
- #
267
- # @example Execute in creating mode.
268
- # creating do
269
- # relation.push(doc)
270
- # end
271
- #
272
- # @return [ Object ] The return value of the block.
273
- #
274
- # @since 2.1.0
275
- def creating
276
- Threaded.begin_create
277
- yield
278
- ensure
279
- Threaded.exit_create
280
- end
281
262
  end
282
263
  end
283
264
  end
@@ -95,7 +95,14 @@ module Rails #:nodoc:
95
95
  # environments.
96
96
  initializer "preload all application models" do |app|
97
97
  config.to_prepare do
98
- ::Rails::Mongoid.preload_models(app) unless $rails_rake_task
98
+ if $rails_rake_task
99
+ # We previously got rid of this, however in the case where
100
+ # threadsafe! is enabled we must load all models so things like
101
+ # creating indexes works properly.
102
+ ::Rails::Mongoid.load_models(app)
103
+ else
104
+ ::Rails::Mongoid.preload_models(app)
105
+ end
99
106
  end
100
107
  end
101
108
 
@@ -22,8 +22,8 @@ module Mongoid # :nodoc:
22
22
  # @return [ Proxy ] The relation.
23
23
  #
24
24
  # @since 2.0.0.rc.1
25
- def build(name, object, metadata, options = {})
26
- relation = create_relation(object, metadata, options[:loading])
25
+ def build(name, object, metadata)
26
+ relation = create_relation(object, metadata)
27
27
  set_relation(name, relation)
28
28
  end
29
29
 
@@ -38,9 +38,9 @@ module Mongoid # :nodoc:
38
38
  # @return [ Proxy ] The relation.
39
39
  #
40
40
  # @since 2.0.0.rc.1
41
- def create_relation(object, metadata, loading = false)
41
+ def create_relation(object, metadata)
42
42
  type = @attributes[metadata.inverse_type]
43
- target = metadata.builder(object, loading).build(type)
43
+ target = metadata.builder(self, object).build(type)
44
44
  target ? metadata.relation.new(self, target, metadata) : nil
45
45
  end
46
46
 
@@ -97,7 +97,9 @@ module Mongoid # :nodoc:
97
97
  instance_variable_get(variable)
98
98
  else
99
99
  _building do
100
- build(name, attributes[metadata.key], metadata, :loading => true)
100
+ _loading do
101
+ build(name, attributes[metadata.key], metadata)
102
+ end
101
103
  end
102
104
  end
103
105
  end
@@ -4,36 +4,9 @@ module Mongoid # :nodoc:
4
4
 
5
5
  # Superclass for all objects that bind relations together.
6
6
  class Binding
7
- attr_reader :base, :target, :metadata
8
-
9
- # Execute a block in binding mode.
10
- #
11
- # @example Execute in binding mode.
12
- # binding do
13
- # relation.push(doc)
14
- # end
15
- #
16
- # @return [ Object ] The return value of the block.
17
- #
18
- # @since 2.1.0
19
- def binding
20
- Threaded.begin_bind
21
- yield
22
- ensure
23
- Threaded.exit_bind
24
- end
7
+ include Threaded::Lifecycle
25
8
 
26
- # Is the current thread in binding mode?
27
- #
28
- # @example Is the thread in binding mode?
29
- # binding.binding?
30
- #
31
- # @return [ true, false ] If the thread is binding.
32
- #
33
- # @since 2.1.0
34
- def binding?
35
- Threaded.binding?
36
- end
9
+ attr_reader :base, :target, :metadata
37
10
 
38
11
  # Create the new binding.
39
12
  #