mongoid 2.0.0.rc.7 → 2.0.0.rc.8

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 (90) hide show
  1. data/lib/config/locales/en.yml +3 -0
  2. data/lib/config/locales/id.yml +46 -0
  3. data/lib/config/locales/ja.yml +40 -0
  4. data/lib/config/locales/vi.yml +45 -0
  5. data/lib/mongoid.rb +5 -3
  6. data/lib/mongoid/attributes.rb +24 -63
  7. data/lib/mongoid/attributes/processing.rb +5 -2
  8. data/lib/mongoid/callbacks.rb +10 -0
  9. data/lib/mongoid/collection.rb +24 -0
  10. data/lib/mongoid/collections/master.rb +14 -6
  11. data/lib/mongoid/collections/operations.rb +1 -1
  12. data/lib/mongoid/collections/retry.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +26 -10
  14. data/lib/mongoid/components.rb +4 -4
  15. data/lib/mongoid/config.rb +6 -3
  16. data/lib/mongoid/contexts.rb +0 -1
  17. data/lib/mongoid/contexts/enumerable.rb +19 -7
  18. data/lib/mongoid/contexts/mongo.rb +9 -5
  19. data/lib/mongoid/copyable.rb +10 -8
  20. data/lib/mongoid/criteria.rb +83 -61
  21. data/lib/mongoid/criterion/builder.rb +34 -0
  22. data/lib/mongoid/criterion/creational.rb +2 -2
  23. data/lib/mongoid/criterion/exclusion.rb +58 -32
  24. data/lib/mongoid/criterion/inclusion.rb +49 -10
  25. data/lib/mongoid/criterion/optional.rb +1 -1
  26. data/lib/mongoid/criterion/selector.rb +80 -11
  27. data/lib/mongoid/cursor.rb +6 -1
  28. data/lib/mongoid/default_scope.rb +27 -19
  29. data/lib/mongoid/document.rb +26 -1
  30. data/lib/mongoid/errors.rb +1 -0
  31. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  32. data/lib/mongoid/extensions/object_id/conversions.rb +7 -4
  33. data/lib/mongoid/factory.rb +1 -1
  34. data/lib/mongoid/field.rb +47 -30
  35. data/lib/mongoid/fields.rb +9 -2
  36. data/lib/mongoid/finders.rb +15 -49
  37. data/lib/mongoid/identity.rb +6 -4
  38. data/lib/mongoid/keys.rb +85 -31
  39. data/lib/mongoid/multi_parameter_attributes.rb +2 -2
  40. data/lib/mongoid/named_scope.rb +129 -28
  41. data/lib/mongoid/observer.rb +36 -0
  42. data/lib/mongoid/paranoia.rb +3 -3
  43. data/lib/mongoid/paths.rb +1 -1
  44. data/lib/mongoid/persistence.rb +2 -0
  45. data/lib/mongoid/persistence/atomic.rb +88 -0
  46. data/lib/mongoid/persistence/atomic/add_to_set.rb +30 -0
  47. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  48. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  49. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  50. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  51. data/lib/mongoid/railtie.rb +13 -1
  52. data/lib/mongoid/relations.rb +1 -0
  53. data/lib/mongoid/relations/accessors.rb +20 -2
  54. data/lib/mongoid/relations/builders/embedded/one.rb +1 -0
  55. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  56. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  57. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  58. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  59. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  60. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  61. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  62. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  63. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  64. data/lib/mongoid/relations/embedded/many.rb +57 -25
  65. data/lib/mongoid/relations/macros.rb +6 -4
  66. data/lib/mongoid/relations/many.rb +51 -10
  67. data/lib/mongoid/relations/metadata.rb +4 -2
  68. data/lib/mongoid/relations/proxy.rb +39 -24
  69. data/lib/mongoid/relations/referenced/many.rb +47 -26
  70. data/lib/mongoid/relations/referenced/many_to_many.rb +47 -14
  71. data/lib/mongoid/relations/referenced/one.rb +14 -0
  72. data/lib/mongoid/sharding.rb +51 -0
  73. data/lib/mongoid/state.rb +3 -2
  74. data/lib/mongoid/timestamps.rb +5 -29
  75. data/lib/mongoid/timestamps/created.rb +31 -0
  76. data/lib/mongoid/timestamps/updated.rb +33 -0
  77. data/lib/mongoid/validations.rb +10 -3
  78. data/lib/mongoid/validations/referenced.rb +58 -0
  79. data/lib/mongoid/version.rb +1 -1
  80. data/lib/mongoid/versioning.rb +67 -5
  81. data/lib/rails/generators/mongoid/model/templates/model.rb +2 -0
  82. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  83. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  84. data/lib/rails/generators/mongoid_generator.rb +10 -1
  85. data/lib/rails/mongoid.rb +1 -0
  86. metadata +29 -8
  87. data/lib/mongoid/contexts/ids.rb +0 -25
  88. data/lib/mongoid/modifiers.rb +0 -24
  89. data/lib/mongoid/modifiers/command.rb +0 -18
  90. data/lib/mongoid/modifiers/inc.rb +0 -24
@@ -208,7 +208,8 @@ module Mongoid # :nodoc:
208
208
  # @since 2.0.0.rc.1
209
209
  def inverse_foreign_key
210
210
  @inverse_foreign_key ||=
211
- (inverse_class_name.underscore << relation.foreign_key_suffix)
211
+ ( inverse_of ? inverse_of.to_s.singularize : inverse_class_name.underscore ) <<
212
+ relation.foreign_key_suffix
212
213
  end
213
214
 
214
215
  # Returns the inverse class of the proxied relation.
@@ -402,8 +403,9 @@ module Mongoid # :nodoc:
402
403
  #
403
404
  # @since 2.0.0.rc.1
404
405
  def determine_cyclic_inverse
406
+ underscored = class_name.underscore
405
407
  klass.relations.each_pair do |key, meta|
406
- if key =~ /#{inverse_klass.name.underscore}/ &&
408
+ if key =~ /#{underscored.singularize}|#{underscored.pluralize}/ &&
407
409
  meta.relation != relation
408
410
  return key.to_sym
409
411
  end
@@ -14,7 +14,7 @@ module Mongoid # :nodoc:
14
14
 
15
15
  attr_accessor :base, :loaded, :metadata, :target
16
16
 
17
- # Backwards compatibiloty with Mongoid beta releases.
17
+ # Backwards compatibility with Mongoid beta releases.
18
18
  delegate :klass, :to => :metadata
19
19
 
20
20
  # Convenience for setting the target and the metadata properties since
@@ -29,37 +29,24 @@ module Mongoid # :nodoc:
29
29
  #
30
30
  # @since 2.0.0.rc.1
31
31
  def init(base, target, metadata, &block)
32
- @base, @building, @target, @metadata = base, false, target, metadata
33
- yield block if block_given?
32
+ @base, @target, @metadata = base, target, metadata
33
+ block.call if block
34
34
  extend Module.new(&metadata.extension) if metadata.extension?
35
35
  end
36
36
 
37
37
  protected
38
38
 
39
- # Yields to the block to allow the building flag to get set and unset for
40
- # the supplied code.
39
+ # Get the collection from the root of the hierarchy.
41
40
  #
42
- # @example Set the building status.
43
- # person.building { @target << Post.new }
41
+ # @example Get the collection.
42
+ # relation.collection
44
43
  #
45
- # @since 2.0.0.rc.1
46
- def building(&block)
47
- @building = true
48
- yield block if block_given?
49
- @building = false
50
- end
51
-
52
- # Convenience method for determining if we are building an association.
53
- # We never want to save in this case.
54
- #
55
- # @example Are we currently building?
56
- # person.posts.building?
57
- #
58
- # @return [ true, false ] True if currently building, false if not.
44
+ # @return [ Collection ] The root's collection.
59
45
  #
60
- # @since 2.0.0.rc.1
61
- def building?
62
- !!@building
46
+ # @since 2.0.0
47
+ def collection
48
+ root = base._root
49
+ root.collection unless root.embedded?
63
50
  end
64
51
 
65
52
  # Return a new document for the type of class we want to instantiate.
@@ -123,6 +110,34 @@ module Mongoid # :nodoc:
123
110
  def method_missing(name, *args, &block)
124
111
  target.send(name, *args, &block)
125
112
  end
113
+
114
+ # When the base document illegally references an embedded document this
115
+ # error will get raised.
116
+ #
117
+ # @example Raise the error.
118
+ # relation.raise_mixed
119
+ #
120
+ # @raise [ Errors::MixedRelations ] The error.
121
+ #
122
+ # @since 2.0.0
123
+ def raise_mixed
124
+ raise Errors::MixedRelations.new(base.class, metadata.klass)
125
+ end
126
+
127
+ # When the base is not yet saved and the user calls create or create!
128
+ # on the relation, this error will get raised.
129
+ #
130
+ # @example Raise the error.
131
+ # relation.raise_unsaved(post)
132
+ #
133
+ # @param [ Document ] doc The child document getting created.
134
+ #
135
+ # @raise [ Errors::UnsavedDocument ] The error.
136
+ #
137
+ # @since 2.0.0.rc.6
138
+ def raise_unsaved(doc)
139
+ raise Errors::UnsavedDocument.new(base, doc)
140
+ end
126
141
  end
127
142
  end
128
143
  end
@@ -29,6 +29,9 @@ module Mongoid #:nodoc:
29
29
  target.map(&:save) if base.persisted? && !options[:binding]
30
30
  end
31
31
 
32
+ alias :concat :<<
33
+ alias :push :<<
34
+
32
35
  # Clear the relation. Will delete the documents from the db if they are
33
36
  # already persisted.
34
37
  #
@@ -66,8 +69,8 @@ module Mongoid #:nodoc:
66
69
  # @param [ Class ] type The optional type of document to create.
67
70
  #
68
71
  # @return [ Document ] The newly created document.
69
- def create(attributes = nil, type = nil)
70
- build(attributes, type).tap do |doc|
72
+ def create(attributes = nil, type = nil, &block)
73
+ build(attributes, type, &block).tap do |doc|
71
74
  base.persisted? ? doc.save : raise_unsaved(doc)
72
75
  end
73
76
  end
@@ -85,8 +88,8 @@ module Mongoid #:nodoc:
85
88
  # @raise [ Errors::Validations ] If validation failed.
86
89
  #
87
90
  # @return [ Document ] The newly created document.
88
- def create!(attributes = nil, type = nil)
89
- build(attributes, type).tap do |doc|
91
+ def create!(attributes = nil, type = nil, &block)
92
+ build(attributes, type, &block).tap do |doc|
90
93
  base.persisted? ? doc.save! : raise_unsaved(doc)
91
94
  end
92
95
  end
@@ -104,10 +107,11 @@ module Mongoid #:nodoc:
104
107
  #
105
108
  # @return [ Integer ] The number of documents deleted.
106
109
  def delete_all(conditions = nil)
110
+ raise_mixed if klass.embedded?
107
111
  selector = (conditions || {})[:conditions] || {}
108
112
  target.delete_if { |doc| doc.matches?(selector) }
109
113
  metadata.klass.delete_all(
110
- :conditions => selector.merge(metadata.foreign_key => base.id)
114
+ :conditions => criteria.selector.merge(selector)
111
115
  )
112
116
  end
113
117
 
@@ -124,10 +128,11 @@ module Mongoid #:nodoc:
124
128
  #
125
129
  # @return [ Integer ] The number of documents destroyd.
126
130
  def destroy_all(conditions = nil)
131
+ raise_mixed if klass.embedded?
127
132
  selector = (conditions || {})[:conditions] || {}
128
133
  target.delete_if { |doc| doc.matches?(selector) }
129
134
  metadata.klass.destroy_all(
130
- :conditions => selector.merge(metadata.foreign_key => base.id)
135
+ :conditions => criteria.selector.merge(selector)
131
136
  )
132
137
  end
133
138
 
@@ -154,9 +159,8 @@ module Mongoid #:nodoc:
154
159
  # @param [ Hash ] options The options to search with.
155
160
  #
156
161
  # @return [ Document, Criteria ] The matching document(s).
157
- def find(arg, options = {})
158
- return criteria.id_criteria(arg) unless arg.is_a?(Symbol)
159
- criteria.find(arg, :conditions => options[:conditions] || {})
162
+ def find(*args)
163
+ criteria.find(*args)
160
164
  end
161
165
 
162
166
  # Instantiate a new references_many relation. Will set the foreign key
@@ -182,6 +186,7 @@ module Mongoid #:nodoc:
182
186
  #
183
187
  # @since 2.0.0.rc.5
184
188
  def load!(options = {})
189
+ raise_mixed if klass.embedded?
185
190
  tap do |relation|
186
191
  unless relation.loaded?
187
192
  relation.target = target.entries
@@ -242,7 +247,7 @@ module Mongoid #:nodoc:
242
247
  # @since 2.0.0.rc.1
243
248
  def unbind(options = {})
244
249
  binding.unbind(options)
245
- if base.persisted?
250
+ unless base.new_record?
246
251
  target.each(&:delete) unless options[:binding]
247
252
  target.each(&:save) if options[:nullify]
248
253
  end
@@ -261,7 +266,8 @@ module Mongoid #:nodoc:
261
266
  #
262
267
  # @since 2.0.0.rc.1
263
268
  def append(document, options = {})
264
- load!(options) and target.push(document)
269
+ init_target if !initialized? && !loaded?
270
+ target.push(document)
265
271
  characterize_one(document)
266
272
  binding.bind_one(document, options)
267
273
  end
@@ -288,9 +294,39 @@ module Mongoid #:nodoc:
288
294
  #
289
295
  # @return [ Criteria ] A new criteria.
290
296
  def criteria
297
+ raise_mixed if klass.embedded?
291
298
  metadata.klass.where(metadata.foreign_key => base.id)
292
299
  end
293
300
 
301
+ # Tells if the target array been initialized.
302
+ #
303
+ # @example Is the target initialized?
304
+ # relation.initialized?
305
+ #
306
+ # @return [ true, false ] If the target is an array.
307
+ #
308
+ # @since 2.0.0
309
+ def initialized?
310
+ !!@initialized
311
+ end
312
+
313
+ # Initializes the target of the proxy as an empty array instead of
314
+ # hitting the database.
315
+ #
316
+ # @example Initialize the target.
317
+ # relation.init_target
318
+ #
319
+ # @raise [ Errors::MixedRelations ] If the class is embedded.
320
+ #
321
+ # @return [ true ] Always true.
322
+ #
323
+ # @since 2.0.0
324
+ def init_target
325
+ raise_mixed if klass.embedded?
326
+ @target = []
327
+ @initialized = true
328
+ end
329
+
294
330
  # If the target array does not respond to the supplied method then try to
295
331
  # find a named scope or criteria on the class and send the call there.
296
332
  #
@@ -309,21 +345,6 @@ module Mongoid #:nodoc:
309
345
  end
310
346
  end
311
347
 
312
- # When the base is not yet saved and the user calls create or create!
313
- # on the relation, this error will get raised.
314
- #
315
- # @example Raise the error.
316
- # relation.raise_unsaved(post)
317
- #
318
- # @param [ Document ] doc The child document getting created.
319
- #
320
- # @raise [ Errors::UnsavedDocument ] The error.
321
- #
322
- # @since 2.0.0.rc.6
323
- def raise_unsaved(doc)
324
- raise Errors::UnsavedDocument.new(base, doc)
325
- end
326
-
327
348
  class << self
328
349
 
329
350
  # Return the builder that is responsible for generating the documents
@@ -17,14 +17,22 @@ module Mongoid # :nodoc:
17
17
  # person.addresses.push(address)
18
18
  #
19
19
  # @example Concat with other documents.
20
- # perosn.addresses.concat([ address_one, address_two ])
20
+ # person.addresses.concat([ address_one, address_two ])
21
21
  #
22
22
  # @param [ Document, Array<Document> ] *args Any number of documents.
23
23
  def <<(*args)
24
24
  options = default_options(args)
25
- super(args)
26
- base.save if base.persisted? && !options[:binding]
25
+ args.flatten.each do |doc|
26
+ return doc unless doc
27
+ append(doc, options)
28
+ if base.persisted? && !options[:binding]
29
+ doc.save
30
+ base.add_to_set(metadata.foreign_key, doc.id)
31
+ end
32
+ end
27
33
  end
34
+ alias :concat :<<
35
+ alias :push :<<
28
36
 
29
37
  # Creates a new document on the references many relation. This will
30
38
  # save the document if the parent has been persisted.
@@ -38,7 +46,10 @@ module Mongoid # :nodoc:
38
46
  # @return [ Document ] The newly created document.
39
47
  def create(attributes = nil, type = nil)
40
48
  build(attributes, type).tap do |doc|
41
- doc.save and base.save if base.persisted?
49
+ if base.persisted?
50
+ doc.save
51
+ base.add_to_set(metadata.foreign_key, doc.id)
52
+ end
42
53
  end
43
54
  end
44
55
 
@@ -57,7 +68,10 @@ module Mongoid # :nodoc:
57
68
  # @return [ Document ] The newly created document.
58
69
  def create!(attributes = nil, type = nil)
59
70
  build(attributes, type).tap do |doc|
60
- doc.save! and base.save! if base.persisted?
71
+ if base.persisted?
72
+ doc.save!
73
+ base.add_to_set(metadata.foreign_key, doc.id)
74
+ end
61
75
  end
62
76
  end
63
77
 
@@ -88,10 +102,7 @@ module Mongoid # :nodoc:
88
102
  #
89
103
  # @return [ Integer ] The number of documents deleted.
90
104
  def delete_all(conditions = nil)
91
- selector = (conditions || {})[:conditions] || {}
92
- target.delete_if { |doc| doc.matches?(selector) }
93
- scoping = { :_id => { "$in" => base.send(metadata.foreign_key) } }
94
- metadata.klass.delete_all(:conditions => selector.merge(scoping))
105
+ remove_all(conditions, :delete_all)
95
106
  end
96
107
 
97
108
  # Destroys all related documents from the database given the supplied
@@ -107,10 +118,7 @@ module Mongoid # :nodoc:
107
118
  #
108
119
  # @return [ Integer ] The number of documents destroyd.
109
120
  def destroy_all(conditions = nil)
110
- selector = (conditions || {})[:conditions] || {}
111
- target.delete_if { |doc| doc.matches?(selector) }
112
- scoping = { :_id => { "$in" => base.send(metadata.foreign_key) } }
113
- metadata.klass.destroy_all(:conditions => selector.merge(scoping))
121
+ remove_all(conditions, :destroy_all)
114
122
  end
115
123
 
116
124
  # Removes all associations between the base document and the target
@@ -204,7 +212,11 @@ module Mongoid # :nodoc:
204
212
  #
205
213
  # @return [ Criteria ] A new criteria.
206
214
  def criteria
207
- metadata.klass.any_in(metadata.inverse_foreign_key => [ base.id ])
215
+ if metadata.inverse
216
+ metadata.klass.any_in(metadata.inverse_foreign_key => [ base.id ])
217
+ else
218
+ metadata.klass.where(:_id => { "$in" => base.send(metadata.foreign_key) })
219
+ end
208
220
  end
209
221
 
210
222
  # Dereferences the supplied document from the base of the relation.
@@ -219,6 +231,27 @@ module Mongoid # :nodoc:
219
231
  document.save
220
232
  end
221
233
 
234
+ # Remove all documents from the relation, either with a delete or a
235
+ # destroy depending on what this was called through.
236
+ #
237
+ # @example Destroy documents from the relation.
238
+ # relation.remove_all(:conditions => { :num => 1 }, true)
239
+ #
240
+ # @param [ Hash ] conditions Conditions to filter by.
241
+ # @param [ true, false ] destroy If true then destroy, else delete.
242
+ #
243
+ # @return [ Integer ] The number of documents removed.
244
+ def remove_all(conditions = {}, method = :destroy)
245
+ cond = conditions || {}
246
+ target.delete_if do |doc|
247
+ doc.matches?(cond[:conditions] || {})
248
+ end
249
+ ids = criteria.merge(cond).only(:_id).map(&:_id)
250
+ criteria.merge(cond).send(method).tap do
251
+ base.pull_all(metadata.foreign_key, ids)
252
+ end
253
+ end
254
+
222
255
  class << self
223
256
 
224
257
  # Return the builder that is responsible for generating the documents
@@ -44,6 +44,20 @@ module Mongoid # :nodoc:
44
44
  end
45
45
  end
46
46
 
47
+ # Will load the target into an array if the target had not already been
48
+ # loaded.
49
+ #
50
+ # @example Load the relation into memory.
51
+ # relation.load!
52
+ #
53
+ # @return [ One ] The relation.
54
+ #
55
+ # @since 2.0.0.rc.5
56
+ def load!(options = {})
57
+ raise_mixed if klass.embedded?
58
+ super(options)
59
+ end
60
+
47
61
  # Removes the association between the base document and the target
48
62
  # document by deleting the foreign key and the reference, orphaning
49
63
  # the target document in the process.
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+
4
+ # This module contains behaviour for adding shard key fields to updates.
5
+ module Sharding
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ cattr_accessor :shard_key_fields
10
+ self.shard_key_fields = []
11
+
12
+ delegate :shard_key_fields, :to => "self.class"
13
+ end
14
+
15
+ # Get the document selector with the defined shard keys.
16
+ #
17
+ # @example Get the selector for the shard keys.
18
+ # person.shard_key_selector
19
+ #
20
+ # @return [ Hash ] The shard key selector.
21
+ #
22
+ # @since 2.0.0
23
+ def shard_key_selector
24
+ {}.tap do |selector|
25
+ shard_key_fields.each do |field|
26
+ selector[field.to_s] = send(field)
27
+ end
28
+ end
29
+ end
30
+
31
+ module ClassMethods #:nodoc
32
+
33
+ # Specifies a shard key with the field(s) specified.
34
+ #
35
+ # @example Specify the shard key.
36
+ #
37
+ # class Person
38
+ # include Mongoid::Document
39
+ # field :first_name, :type => String
40
+ # field :last_name, :type => String
41
+ #
42
+ # shard_key :first_name, :last_name
43
+ # end
44
+ #
45
+ # @since 2.0.0
46
+ def shard_key(*names)
47
+ self.shard_key_fields = names
48
+ end
49
+ end
50
+ end
51
+ end