mongoid 2.1.9 → 2.2.0

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