mongoid 2.1.9 → 2.2.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 (78) hide show
  1. data/CHANGELOG.md +281 -0
  2. data/{README.rdoc → README.md} +24 -14
  3. data/Rakefile +1 -1
  4. data/lib/config/locales/bg.yml +5 -0
  5. data/lib/config/locales/de.yml +5 -0
  6. data/lib/config/locales/en-GB.yml +5 -0
  7. data/lib/config/locales/en.yml +5 -0
  8. data/lib/config/locales/es.yml +3 -0
  9. data/lib/config/locales/fr.yml +5 -0
  10. data/lib/config/locales/hi.yml +5 -0
  11. data/lib/config/locales/hu.yml +5 -0
  12. data/lib/config/locales/id.yml +5 -0
  13. data/lib/config/locales/it.yml +5 -0
  14. data/lib/config/locales/ja.yml +5 -0
  15. data/lib/config/locales/kr.yml +5 -0
  16. data/lib/config/locales/nl.yml +5 -0
  17. data/lib/config/locales/pl.yml +5 -0
  18. data/lib/config/locales/pt-BR.yml +5 -0
  19. data/lib/config/locales/pt.yml +5 -0
  20. data/lib/config/locales/ro.yml +5 -0
  21. data/lib/config/locales/ru.yml +5 -0
  22. data/lib/config/locales/sv.yml +5 -0
  23. data/lib/config/locales/vi.yml +5 -0
  24. data/lib/config/locales/zh-CN.yml +5 -0
  25. data/lib/mongoid/atomic.rb +61 -10
  26. data/lib/mongoid/atomic/modifiers.rb +156 -24
  27. data/lib/mongoid/attributes.rb +38 -10
  28. data/lib/mongoid/collection.rb +21 -3
  29. data/lib/mongoid/collections.rb +52 -6
  30. data/lib/mongoid/collections/master.rb +8 -2
  31. data/lib/mongoid/collections/operations.rb +1 -0
  32. data/lib/mongoid/config.rb +5 -2
  33. data/lib/mongoid/config/database.rb +15 -2
  34. data/lib/mongoid/contexts/mongo.rb +3 -0
  35. data/lib/mongoid/criteria.rb +27 -3
  36. data/lib/mongoid/criterion/inclusion.rb +58 -0
  37. data/lib/mongoid/dirty.rb +2 -0
  38. data/lib/mongoid/document.rb +23 -0
  39. data/lib/mongoid/errors.rb +3 -0
  40. data/lib/mongoid/errors/callback.rb +26 -0
  41. data/lib/mongoid/errors/eager_load.rb +25 -0
  42. data/lib/mongoid/errors/invalid_find.rb +19 -0
  43. data/lib/mongoid/extensions.rb +3 -1
  44. data/lib/mongoid/extensions/object_id/conversions.rb +1 -1
  45. data/lib/mongoid/extras.rb +12 -2
  46. data/lib/mongoid/fields.rb +41 -2
  47. data/lib/mongoid/fields/serializable.rb +36 -0
  48. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +13 -14
  49. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +13 -14
  50. data/lib/mongoid/finders.rb +4 -4
  51. data/lib/mongoid/identity_map.rb +33 -20
  52. data/lib/mongoid/keys.rb +24 -1
  53. data/lib/mongoid/nested_attributes.rb +16 -4
  54. data/lib/mongoid/persistence.rb +50 -6
  55. data/lib/mongoid/persistence/operations.rb +1 -4
  56. data/lib/mongoid/persistence/operations/update.rb +3 -1
  57. data/lib/mongoid/relations/builders/nested_attributes/many.rb +27 -44
  58. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  59. data/lib/mongoid/relations/builders/referenced/one.rb +1 -1
  60. data/lib/mongoid/relations/cascading.rb +12 -1
  61. data/lib/mongoid/relations/embedded/many.rb +11 -4
  62. data/lib/mongoid/relations/embedded/one.rb +6 -2
  63. data/lib/mongoid/relations/macros.rb +19 -12
  64. data/lib/mongoid/relations/metadata.rb +17 -0
  65. data/lib/mongoid/relations/polymorphic.rb +12 -1
  66. data/lib/mongoid/relations/proxy.rb +24 -0
  67. data/lib/mongoid/relations/referenced/in.rb +20 -0
  68. data/lib/mongoid/relations/referenced/many.rb +30 -6
  69. data/lib/mongoid/relations/referenced/many_to_many.rb +18 -2
  70. data/lib/mongoid/relations/referenced/one.rb +19 -0
  71. data/lib/mongoid/relations/reflections.rb +23 -3
  72. data/lib/mongoid/relations/targets/enumerable.rb +29 -1
  73. data/lib/mongoid/serialization.rb +1 -1
  74. data/lib/mongoid/sharding.rb +12 -2
  75. data/lib/mongoid/threaded.rb +98 -0
  76. data/lib/mongoid/version.rb +1 -1
  77. data/lib/mongoid/versioning.rb +12 -2
  78. metadata +25 -21
@@ -7,13 +7,36 @@ module Mongoid #:nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  attr_reader :identifier
10
- delegate :primary_key, :using_object_ids?, :to => "self.class"
11
10
 
12
11
  included do
13
12
  cattr_accessor :primary_key, :using_object_ids
14
13
  self.using_object_ids = true
15
14
  end
16
15
 
16
+ # Get the document's primary key.
17
+ #
18
+ # @note Refactored from using delegate for class load performance.
19
+ #
20
+ # @example Get the primary key.
21
+ # model.primary_key
22
+ #
23
+ # @return [ Array ] The primary key
24
+ def primary_key
25
+ self.class.primary_key
26
+ end
27
+
28
+ # Is the document using object ids?
29
+ #
30
+ # @note Refactored from using delegate for class load performance.
31
+ #
32
+ # @example Is the document using object ids?
33
+ # model.using_object_ids?
34
+ #
35
+ # @return [ true, false ] Using object ids.
36
+ def using_object_ids?
37
+ self.class.using_object_ids?
38
+ end
39
+
17
40
  private
18
41
 
19
42
  # Determines if any field that the document id is composed of has changed.
@@ -5,13 +5,23 @@ module Mongoid #:nodoc:
5
5
  module NestedAttributes
6
6
  extend ActiveSupport::Concern
7
7
 
8
- delegate :nested_attributes, :to => "self.class"
9
-
10
8
  included do
11
9
  class_attribute :nested_attributes
12
10
  self.nested_attributes = []
13
11
  end
14
12
 
13
+ # Get the nested attributes.
14
+ #
15
+ # @note Refactored from using delegate for class load performance.
16
+ #
17
+ # @example Get the nested attributes.
18
+ # model.nested_attributes
19
+ #
20
+ # @return [ Array<String> ] The nested attributes methods.
21
+ def nested_attributes
22
+ self.class.nested_attributes
23
+ end
24
+
15
25
  module ClassMethods #:nodoc:
16
26
 
17
27
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
@@ -44,8 +54,10 @@ module Mongoid #:nodoc:
44
54
  args.each do |name|
45
55
  self.nested_attributes += [ "#{name}_attributes=" ]
46
56
  define_method("#{name}_attributes=") do |attrs|
47
- relation = relations[name.to_s]
48
- relation.nested_builder(attrs, options).build(self)
57
+ assigning do
58
+ relation = relations[name.to_s]
59
+ relation.nested_builder(attrs, options).build(self)
60
+ end
49
61
  end
50
62
  end
51
63
  end
@@ -67,7 +67,11 @@ module Mongoid #:nodoc:
67
67
  #
68
68
  # @return [ true, false ] True if validation passed.
69
69
  def save!(options = {})
70
- self.class.fail_validate!(self) unless upsert(options); true
70
+ unless upsert(options)
71
+ self.class.fail_validate!(self) if errors.any?
72
+ self.class.fail_callback!(self, :save!)
73
+ end
74
+ return true
71
75
  end
72
76
 
73
77
  # Update the document in the datbase.
@@ -124,7 +128,10 @@ module Mongoid #:nodoc:
124
128
  # @return [ true, false ] True if validation passed.
125
129
  def update_attributes!(attributes = {})
126
130
  update_attributes(attributes).tap do |result|
127
- self.class.fail_validate!(self) unless result
131
+ unless result
132
+ self.class.fail_validate!(self) if errors.any?
133
+ self.class.fail_callback!(self, :update_attributes!)
134
+ end
128
135
  end
129
136
  end
130
137
 
@@ -159,7 +166,9 @@ module Mongoid #:nodoc:
159
166
  #
160
167
  # @return [ Document ] The newly created document.
161
168
  def create(attributes = {}, &block)
162
- new(attributes, &block).tap { |doc| doc.save }
169
+ creating do
170
+ new(attributes, &block).tap { |doc| doc.save }
171
+ end
163
172
  end
164
173
 
165
174
  # Create a new document. This will instantiate a new document and
@@ -174,9 +183,12 @@ module Mongoid #:nodoc:
174
183
  #
175
184
  # @return [ Document ] The newly created document.
176
185
  def create!(attributes = {}, &block)
177
- document = new(attributes, &block)
178
- fail_validate!(document) if document.insert.errors.any?
179
- document
186
+ creating do
187
+ new(attributes, &block).tap do |doc|
188
+ fail_validate!(doc) if doc.insert.errors.any?
189
+ fail_callback!(doc, :create!) if doc.new?
190
+ end
191
+ end
180
192
  end
181
193
 
182
194
  # Delete all documents given the supplied conditions. If no conditions
@@ -230,6 +242,38 @@ module Mongoid #:nodoc:
230
242
  def fail_validate!(document)
231
243
  raise Errors::Validations.new(document)
232
244
  end
245
+
246
+ # Raise an error if a callback failed.
247
+ #
248
+ # @example Raise the callback error.
249
+ # Person.fail_callback!(person, :create!)
250
+ #
251
+ # @param [ Document ] document The document to fail.
252
+ # @param [ Symbol ] method The method being called.
253
+ #
254
+ # @since 2.2.0
255
+ def fail_callback!(document, method)
256
+ raise Errors::Callback.new(document.class, method)
257
+ end
258
+
259
+ private
260
+
261
+ # Execute a block in creating mode.
262
+ #
263
+ # @example Execute in creating mode.
264
+ # creating do
265
+ # relation.push(doc)
266
+ # end
267
+ #
268
+ # @return [ Object ] The return value of the block.
269
+ #
270
+ # @since 2.1.0
271
+ def creating
272
+ Threaded.begin_create
273
+ yield
274
+ ensure
275
+ Threaded.exit_create
276
+ end
233
277
  end
234
278
  end
235
279
  end
@@ -155,10 +155,7 @@ module Mongoid #:nodoc:
155
155
  # @since 2.1.0
156
156
  def init_updates
157
157
  document.atomic_updates.tap do |updates|
158
- conflicts = updates.delete(:other)
159
- if conflicts
160
- @conflicts = { "$pushAll" => conflicts }
161
- end
158
+ @conflicts = updates.delete(:conflicts) || {}
162
159
  end
163
160
  end
164
161
 
@@ -43,7 +43,9 @@ module Mongoid #:nodoc:
43
43
  prepare do
44
44
  unless updates.empty?
45
45
  collection.update(selector, updates, options)
46
- collection.update(selector, conflicts, options) if conflicts
46
+ conflicts.each_pair do |key, value|
47
+ collection.update(selector, { key => value }, options)
48
+ end
47
49
  end
48
50
  end
49
51
  end
@@ -12,13 +12,12 @@ module Mongoid # :nodoc:
12
12
  # the existing relation, a replacement of the relation with a new
13
13
  # document, or a removal of the relation.
14
14
  #
15
- # Example:
15
+ # @example Build the nested attrs.
16
+ # many.build(person)
16
17
  #
17
- # <tt>many.build(person)</tt>
18
+ # @param [ Document ] parent The parent document of the relation.
18
19
  #
19
- # Options:
20
- #
21
- # parent: The parent document of the relation.
20
+ # @return [ Array ] The attributes.
22
21
  def build(parent)
23
22
  @existing = parent.send(metadata.name)
24
23
  if over_limit?(attributes)
@@ -36,19 +35,12 @@ module Mongoid # :nodoc:
36
35
  # Create the new builder for nested attributes on one-to-many
37
36
  # relations.
38
37
  #
39
- # Example:
40
- #
41
- # <tt>One.new(metadata, attributes, options)</tt>
42
- #
43
- # Options:
44
- #
45
- # metadata: The relation metadata
46
- # attributes: The attributes hash to attempt to set.
47
- # options: The options defined.
38
+ # @example Initialize the builder.
39
+ # One.new(metadata, attributes, options)
48
40
  #
49
- # Returns:
50
- #
51
- # A new builder.
41
+ # @param [ Metadata ] metadata The relation metadata.
42
+ # @param [ Hash ] attributes The attributes hash to attempt to set.
43
+ # @param [ Hash ] options The options defined.
52
44
  def initialize(metadata, attributes, options = {})
53
45
  if attributes.respond_to?(:with_indifferent_access)
54
46
  @attributes = attributes.with_indifferent_access.sort do |a, b|
@@ -65,17 +57,12 @@ module Mongoid # :nodoc:
65
57
 
66
58
  # Can the existing relation potentially be deleted?
67
59
  #
68
- # Example:
69
- #
70
- # <tt>destroyable?({ :_destroy => "1" })</tt>
71
- #
72
- # Options:
73
- #
74
- # attributes: The attributes to pull the flag from.
60
+ # @example Is the document destroyable?
61
+ # destroyable?({ :_destroy => "1" })
75
62
  #
76
- # Returns:
63
+ # @parma [ Hash ] attributes The attributes to pull the flag from.
77
64
  #
78
- # True if the relation can potentially be deleted.
65
+ # @return [ true, false ] If the relation can potentially be deleted.
79
66
  def destroyable?(attributes)
80
67
  destroy = attributes.delete(:_destroy)
81
68
  [ 1, "1", true, "true" ].include?(destroy) && allow_destroy?
@@ -84,17 +71,12 @@ module Mongoid # :nodoc:
84
71
  # Are the supplied attributes of greater number than the supplied
85
72
  # limit?
86
73
  #
87
- # Example:
74
+ # @example Are we over the set limit?
75
+ # builder.over_limit?({ "street" => "Bond" })
88
76
  #
89
- # <tt>builder.over_limit?({ "street" => "Bond" })</tt>
77
+ # @param [ Hash ] attributes The attributes being set.
90
78
  #
91
- # Options:
92
- #
93
- # attributes: The attributes being set.
94
- #
95
- # Returns:
96
- #
97
- # True if a limit supplied and the attributes are of greater number.
79
+ # @return [ true, false ] If the attributes exceed the limit.
98
80
  def over_limit?(attributes)
99
81
  limit = options[:limit]
100
82
  limit ? attributes.size > limit : false
@@ -103,20 +85,21 @@ module Mongoid # :nodoc:
103
85
  # Process each set of attributes one at a time for each potential
104
86
  # new, existing, or ignored document.
105
87
  #
106
- # Example:
107
- #
108
- # <tt>builder.process({ "id" => 1, "street" => "Bond" })
88
+ # @example Process the attributes
89
+ # builder.process({ "id" => 1, "street" => "Bond" })
109
90
  #
110
- # Options:
111
- #
112
- # attrs: The single document attributes to process.
91
+ # @param [ Hash ] attrs The single document attributes to process.
113
92
  def process(attrs)
114
93
  return if reject?(attrs)
115
94
  if id = attrs["id"] || attrs["_id"]
116
- document = existing.find(convert_id(id))
117
- destroyable?(attrs) ? existing.delete(document) : document.update_attributes(attrs)
95
+ doc = existing.find(convert_id(id))
96
+ if destroyable?(attrs)
97
+ existing.delete(doc)
98
+ else
99
+ metadata.embedded? ? doc.attributes = attrs : doc.update_attributes(attrs)
100
+ end
118
101
  else
119
- existing.push(Mongoid::Factory.build(metadata.klass, attrs)) unless destroyable?(attrs)
102
+ existing.push(Factory.build(metadata.klass, attrs)) unless destroyable?(attrs)
120
103
  end
121
104
  end
122
105
  end
@@ -17,7 +17,8 @@ module Mongoid # :nodoc:
17
17
  def build(type = nil)
18
18
  return object unless query?
19
19
  return [] if object.is_a?(Array)
20
- metadata.criteria(convertable(metadata, object))
20
+ crit = metadata.criteria(convertable(metadata, object))
21
+ IdentityMap.get(crit.klass, crit.selector) || crit
21
22
  end
22
23
 
23
24
  private
@@ -17,7 +17,7 @@ module Mongoid # :nodoc:
17
17
  def build(type = nil)
18
18
  return object unless query?
19
19
  criteria = metadata.criteria(object)
20
- IdentityMap.match(criteria) || criteria.first
20
+ IdentityMap.get(criteria.klass, criteria.selector) || criteria.first
21
21
  end
22
22
  end
23
23
  end
@@ -15,7 +15,6 @@ module Mongoid # :nodoc:
15
15
  included do
16
16
  class_attribute :cascades
17
17
  self.cascades = []
18
- delegate :cascades, :to => "self.class"
19
18
  end
20
19
 
21
20
  # Perform all cascading deletes, destroys, or nullifies. Will delegate to
@@ -33,6 +32,18 @@ module Mongoid # :nodoc:
33
32
  end
34
33
  end
35
34
 
35
+ # Get the cascading definitions.
36
+ #
37
+ # @note Refactored from using delegate for class load performance.
38
+ #
39
+ # @example Get the cascades.
40
+ # model.cascades
41
+ #
42
+ # @return [ Array<String> ] The cascading relation names.
43
+ def cascades
44
+ self.class.cascades
45
+ end
46
+
36
47
  module ClassMethods #:nodoc:
37
48
 
38
49
  # Attempt to add the cascading information for the document to know how
@@ -26,7 +26,7 @@ module Mongoid # :nodoc:
26
26
  args.flatten.each do |doc|
27
27
  next unless doc
28
28
  append(doc)
29
- doc.save if persistable?
29
+ doc.save if persistable? && !assigning?
30
30
  end
31
31
  end
32
32
  end
@@ -124,7 +124,11 @@ module Mongoid # :nodoc:
124
124
  def delete(document)
125
125
  target.delete_one(document).tap do |doc|
126
126
  if doc && !binding?
127
- doc.delete(:suppress => true)
127
+ if assigning?
128
+ base.add_atomic_pull(doc)
129
+ else
130
+ doc.delete(:suppress => true)
131
+ end
128
132
  unbind_one(doc)
129
133
  end
130
134
  reindex
@@ -228,6 +232,9 @@ module Mongoid # :nodoc:
228
232
  def substitute(replacement)
229
233
  tap do |proxy|
230
234
  if replacement.blank?
235
+ if assigning?
236
+ base.atomic_unsets.push(proxy.first.atomic_path)
237
+ end
231
238
  proxy.clear
232
239
  else
233
240
  atomically(:$set) do
@@ -238,7 +245,7 @@ module Mongoid # :nodoc:
238
245
  proxy.target.each_with_index do |doc, index|
239
246
  integrate(doc)
240
247
  doc._index = index
241
- doc.save if base.persisted?
248
+ doc.save if base.persisted? && !assigning?
242
249
  end
243
250
  end
244
251
  end
@@ -377,7 +384,7 @@ module Mongoid # :nodoc:
377
384
  criteria.size.tap do
378
385
  criteria.each do |doc|
379
386
  target.delete_one(doc)
380
- doc.send(method, :suppress => true)
387
+ doc.send(method, :suppress => true) unless assigning?
381
388
  unbind_one(doc)
382
389
  end
383
390
  reindex
@@ -36,7 +36,11 @@ module Mongoid # :nodoc:
36
36
  # @since 2.0.0.rc.1
37
37
  def substitute(replacement)
38
38
  tap do |proxy|
39
- proxy.delete
39
+ if assigning?
40
+ base.atomic_unsets.push(proxy.atomic_path)
41
+ else
42
+ proxy.delete
43
+ end
40
44
  proxy.unbind_one
41
45
  return nil unless replacement
42
46
  proxy.target = replacement
@@ -69,7 +73,7 @@ module Mongoid # :nodoc:
69
73
  #
70
74
  # @since 2.1.0
71
75
  def persistable?
72
- base.persisted? && !binding? && !building?
76
+ base.persisted? && !binding? && !building? && !assigning?
73
77
  end
74
78
 
75
79
  class << self
@@ -7,10 +7,6 @@ module Mongoid # :nodoc:
7
7
  module Macros
8
8
  extend ActiveSupport::Concern
9
9
 
10
- # Convenience methods for the instance to know about attributes that
11
- # are located at the class level.
12
- delegate :associations, :relations, :to => "self.class"
13
-
14
10
  included do
15
11
  cattr_accessor :embedded
16
12
  class_attribute :relations
@@ -27,6 +23,19 @@ module Mongoid # :nodoc:
27
23
  end
28
24
  end
29
25
 
26
+ # Get the metadata for all the defined relations.
27
+ #
28
+ # @note Refactored from using delegate for class load performance.
29
+ #
30
+ # @example Get the relations.
31
+ # model.relations
32
+ #
33
+ # @return [ Hash<String, Metadata> ] The relation metadata.
34
+ def relations
35
+ self.class.relations
36
+ end
37
+ alias :associations :relations
38
+
30
39
  module ClassMethods #:nodoc:
31
40
 
32
41
  # Adds the relation back to the parent document. This macro is
@@ -241,14 +250,12 @@ module Mongoid # :nodoc:
241
250
  #
242
251
  # @return [ Metadata ] The metadata for the relation.
243
252
  def characterize(name, relation, options, &block)
244
- Metadata.new(
245
- options.merge(
246
- :relation => relation,
247
- :extend => create_extension_module(name, &block),
248
- :inverse_class_name => self.name,
249
- :name => name
250
- )
251
- )
253
+ Metadata.new({
254
+ :relation => relation,
255
+ :extend => create_extension_module(name, &block),
256
+ :inverse_class_name => self.name,
257
+ :name => name
258
+ }.merge(options))
252
259
  end
253
260
 
254
261
  # Generate a named extension module suitable for marshaling