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

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