mongoid 2.3.1 → 2.3.2

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