mongoid 2.0.0.beta.20 → 2.0.0.rc.1

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 (134) hide show
  1. data/README.rdoc +8 -0
  2. data/Rakefile +51 -0
  3. data/lib/config/locales/nl.yml +39 -0
  4. data/lib/config/locales/ro.yml +1 -1
  5. data/lib/mongoid.rb +17 -17
  6. data/lib/mongoid/atomicity.rb +54 -22
  7. data/lib/mongoid/attributes.rb +145 -125
  8. data/lib/mongoid/callbacks.rb +7 -2
  9. data/lib/mongoid/collection.rb +49 -32
  10. data/lib/mongoid/collections.rb +0 -1
  11. data/lib/mongoid/components.rb +34 -29
  12. data/lib/mongoid/config.rb +207 -193
  13. data/lib/mongoid/config/database.rb +167 -0
  14. data/lib/mongoid/contexts.rb +2 -5
  15. data/lib/mongoid/contexts/enumerable.rb +30 -4
  16. data/lib/mongoid/contexts/ids.rb +2 -2
  17. data/lib/mongoid/contexts/mongo.rb +30 -5
  18. data/lib/mongoid/copyable.rb +44 -0
  19. data/lib/mongoid/criteria.rb +110 -56
  20. data/lib/mongoid/criterion/creational.rb +34 -0
  21. data/lib/mongoid/criterion/destructive.rb +37 -0
  22. data/lib/mongoid/criterion/exclusion.rb +3 -1
  23. data/lib/mongoid/criterion/inclusion.rb +59 -64
  24. data/lib/mongoid/criterion/inspection.rb +22 -0
  25. data/lib/mongoid/criterion/optional.rb +42 -54
  26. data/lib/mongoid/criterion/selector.rb +9 -0
  27. data/lib/mongoid/default_scope.rb +28 -0
  28. data/lib/mongoid/deprecation.rb +5 -5
  29. data/lib/mongoid/dirty.rb +4 -5
  30. data/lib/mongoid/document.rb +161 -114
  31. data/lib/mongoid/extensions.rb +7 -11
  32. data/lib/mongoid/extensions/array/parentization.rb +2 -2
  33. data/lib/mongoid/extensions/date/conversions.rb +1 -1
  34. data/lib/mongoid/extensions/hash/conversions.rb +0 -23
  35. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  36. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  37. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  38. data/lib/mongoid/extensions/string/conversions.rb +23 -4
  39. data/lib/mongoid/extensions/time_conversions.rb +4 -4
  40. data/lib/mongoid/field.rb +30 -19
  41. data/lib/mongoid/fields.rb +15 -5
  42. data/lib/mongoid/finders.rb +19 -11
  43. data/lib/mongoid/hierarchy.rb +34 -28
  44. data/lib/mongoid/identity.rb +62 -20
  45. data/lib/mongoid/inspection.rb +58 -0
  46. data/lib/mongoid/matchers.rb +20 -0
  47. data/lib/mongoid/multi_database.rb +11 -0
  48. data/lib/mongoid/nested_attributes.rb +41 -0
  49. data/lib/mongoid/paranoia.rb +3 -4
  50. data/lib/mongoid/paths.rb +1 -1
  51. data/lib/mongoid/persistence.rb +89 -90
  52. data/lib/mongoid/persistence/command.rb +20 -4
  53. data/lib/mongoid/persistence/insert.rb +13 -11
  54. data/lib/mongoid/persistence/insert_embedded.rb +8 -6
  55. data/lib/mongoid/persistence/remove.rb +6 -4
  56. data/lib/mongoid/persistence/remove_all.rb +6 -4
  57. data/lib/mongoid/persistence/remove_embedded.rb +8 -6
  58. data/lib/mongoid/persistence/update.rb +12 -10
  59. data/lib/mongoid/railtie.rb +2 -2
  60. data/lib/mongoid/railties/database.rake +10 -9
  61. data/lib/mongoid/relations.rb +104 -0
  62. data/lib/mongoid/relations/accessors.rb +154 -0
  63. data/lib/mongoid/relations/auto_save.rb +34 -0
  64. data/lib/mongoid/relations/binding.rb +24 -0
  65. data/lib/mongoid/relations/bindings.rb +9 -0
  66. data/lib/mongoid/relations/bindings/embedded/in.rb +77 -0
  67. data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
  68. data/lib/mongoid/relations/bindings/embedded/one.rb +65 -0
  69. data/lib/mongoid/relations/bindings/referenced/in.rb +78 -0
  70. data/lib/mongoid/relations/bindings/referenced/many.rb +93 -0
  71. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +94 -0
  72. data/lib/mongoid/relations/bindings/referenced/one.rb +63 -0
  73. data/lib/mongoid/relations/builder.rb +41 -0
  74. data/lib/mongoid/relations/builders.rb +79 -0
  75. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  76. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  77. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  78. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  79. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  80. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  81. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  82. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  83. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  84. data/lib/mongoid/relations/cascading.rb +55 -0
  85. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  86. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  87. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  88. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  89. data/lib/mongoid/relations/cyclic.rb +97 -0
  90. data/lib/mongoid/relations/embedded/in.rb +172 -0
  91. data/lib/mongoid/relations/embedded/many.rb +450 -0
  92. data/lib/mongoid/relations/embedded/one.rb +169 -0
  93. data/lib/mongoid/relations/macros.rb +302 -0
  94. data/lib/mongoid/relations/many.rb +185 -0
  95. data/lib/mongoid/relations/metadata.rb +529 -0
  96. data/lib/mongoid/relations/nested_builder.rb +52 -0
  97. data/lib/mongoid/relations/one.rb +29 -0
  98. data/lib/mongoid/relations/polymorphic.rb +54 -0
  99. data/lib/mongoid/relations/proxy.rb +122 -0
  100. data/lib/mongoid/relations/referenced/in.rb +214 -0
  101. data/lib/mongoid/relations/referenced/many.rb +358 -0
  102. data/lib/mongoid/relations/referenced/many_to_many.rb +379 -0
  103. data/lib/mongoid/relations/referenced/one.rb +204 -0
  104. data/lib/mongoid/relations/reflections.rb +45 -0
  105. data/lib/mongoid/safe.rb +11 -1
  106. data/lib/mongoid/safety.rb +122 -97
  107. data/lib/mongoid/scope.rb +14 -9
  108. data/lib/mongoid/state.rb +37 -3
  109. data/lib/mongoid/timestamps.rb +11 -0
  110. data/lib/mongoid/validations.rb +42 -3
  111. data/lib/mongoid/validations/associated.rb +8 -5
  112. data/lib/mongoid/validations/uniqueness.rb +23 -2
  113. data/lib/mongoid/version.rb +1 -1
  114. data/lib/mongoid/versioning.rb +25 -16
  115. data/lib/rails/generators/mongoid/model/templates/model.rb +3 -1
  116. metadata +95 -80
  117. data/lib/mongoid/associations.rb +0 -364
  118. data/lib/mongoid/associations/embedded_in.rb +0 -74
  119. data/lib/mongoid/associations/embeds_many.rb +0 -299
  120. data/lib/mongoid/associations/embeds_one.rb +0 -111
  121. data/lib/mongoid/associations/foreign_key.rb +0 -35
  122. data/lib/mongoid/associations/meta_data.rb +0 -38
  123. data/lib/mongoid/associations/options.rb +0 -78
  124. data/lib/mongoid/associations/proxy.rb +0 -60
  125. data/lib/mongoid/associations/referenced_in.rb +0 -70
  126. data/lib/mongoid/associations/references_many.rb +0 -254
  127. data/lib/mongoid/associations/references_many_as_array.rb +0 -128
  128. data/lib/mongoid/associations/references_one.rb +0 -104
  129. data/lib/mongoid/extensions/array/accessors.rb +0 -17
  130. data/lib/mongoid/extensions/array/assimilation.rb +0 -26
  131. data/lib/mongoid/extensions/hash/accessors.rb +0 -42
  132. data/lib/mongoid/extensions/hash/assimilation.rb +0 -40
  133. data/lib/mongoid/extensions/nil/assimilation.rb +0 -17
  134. data/lib/mongoid/memoization.rb +0 -33
@@ -1,364 +0,0 @@
1
- # encoding: utf-8
2
- require "mongoid/associations/proxy"
3
- require "mongoid/associations/embedded_in"
4
- require "mongoid/associations/embeds_many"
5
- require "mongoid/associations/embeds_one"
6
- require "mongoid/associations/foreign_key"
7
- require "mongoid/associations/references_many"
8
- require "mongoid/associations/references_many_as_array"
9
- require "mongoid/associations/references_one"
10
- require "mongoid/associations/referenced_in"
11
- require "mongoid/associations/options"
12
- require "mongoid/associations/meta_data"
13
-
14
- module Mongoid # :nodoc:
15
- module Associations #:nodoc:
16
- extend ActiveSupport::Concern
17
- included do
18
- include ForeignKey
19
-
20
- cattr_accessor :embedded
21
- self.embedded = false
22
-
23
- class_inheritable_accessor :associations, :cascades
24
- self.associations = {}
25
- self.cascades = {}
26
-
27
- delegate :embedded, :embedded?, :to => "self.class"
28
- end
29
-
30
- # Returns the associations for the +Document+.
31
- def associations
32
- self.class.associations
33
- end
34
-
35
- # are we in an embeds_many?
36
- def embedded_many?
37
- embedded? && _parent.associations[association_name].association == EmbedsMany
38
- end
39
-
40
- # are we in an embeds_one?
41
- def embedded_one?
42
- embedded? && !embedded_many?
43
- end
44
-
45
- # Update the one-to-one relational association for the name.
46
- def update_association(name)
47
- association = send(name)
48
- association.save if new_record? && !association.nil?
49
- end
50
-
51
- # Updates all the one-to-many relational associations for the name.
52
- def update_associations(name)
53
- send(name).each { |doc| doc.save } if new_record?
54
- end
55
-
56
- def update_foreign_keys
57
- associations.each do |name, association|
58
- next unless association.macro == :referenced_in
59
- foreign_key = association.options.foreign_key
60
- if send(foreign_key).nil?
61
- proxy = send(name)
62
- send("#{foreign_key}=", proxy && proxy.target ? proxy.id : nil)
63
- end
64
- end
65
- end
66
-
67
- module ClassMethods
68
- # Gets whether or not the document is embedded.
69
- #
70
- # Example:
71
- #
72
- # <tt>Person.embedded?</tt>
73
- #
74
- # Returns:
75
- #
76
- # <tt>true</tt> if embedded, <tt>false</tt> if not.
77
- def embedded?
78
- !!self.embedded
79
- end
80
-
81
- # Adds the association back to the parent document. This macro is
82
- # necessary to set the references from the child back to the parent
83
- # document. If a child does not define this association calling
84
- # persistence methods on the child object will cause a save to fail.
85
- #
86
- # Options:
87
- #
88
- # name: A +Symbol+ that matches the name of the parent class.
89
- #
90
- # Example:
91
- #
92
- # class Person
93
- # include Mongoid::Document
94
- # embeds_many :addresses
95
- # end
96
- #
97
- # class Address
98
- # include Mongoid::Document
99
- # embedded_in :person, :inverse_of => :addresses
100
- # end
101
- def embedded_in(name, options = {}, &block)
102
- opts = optionize(name, options, nil, &block)
103
- Associations::EmbeddedIn.validate_options(opts)
104
- self.embedded = true
105
- associate(Associations::EmbeddedIn, opts)
106
- end
107
-
108
- # Adds the association from a parent document to its children. The name
109
- # of the association needs to be a pluralized form of the child class
110
- # name.
111
- #
112
- # Options:
113
- #
114
- # name: A +Symbol+ that is the plural child class name.
115
- #
116
- # Example:
117
- #
118
- # class Person
119
- # include Mongoid::Document
120
- # embeds_many :addresses
121
- # end
122
- #
123
- # class Address
124
- # include Mongoid::Document
125
- # embedded_in :person, :inverse_of => :addresses
126
- # end
127
- def embeds_many(name, options = {}, &block)
128
- opts = optionize(name, options, nil, &block)
129
- Associations::EmbedsMany.validate_options(opts)
130
- associate(Associations::EmbedsMany, opts)
131
- end
132
-
133
- alias :embed_many :embeds_many
134
-
135
- # Adds the association from a parent document to its child. The name
136
- # of the association needs to be a singular form of the child class
137
- # name.
138
- #
139
- # Options:
140
- #
141
- # name: A +Symbol+ that is the plural child class name.
142
- #
143
- # Example:
144
- #
145
- # class Person
146
- # include Mongoid::Document
147
- # embeds_one :name
148
- # end
149
- #
150
- # class Name
151
- # include Mongoid::Document
152
- # embedded_in :person
153
- # end
154
- def embeds_one(name, options = {}, &block)
155
- opts = optionize(name, options, nil, &block)
156
- type = Associations::EmbedsOne
157
- type.validate_options(opts)
158
- associate(type, opts)
159
- add_builder(type, opts)
160
- add_creator(type, opts)
161
- end
162
-
163
- alias :embed_one :embeds_one
164
-
165
- # Adds a relational association from the child Document to a Document in
166
- # another database or collection.
167
- #
168
- # Options:
169
- #
170
- # name: A +Symbol+ that is the related class name.
171
- #
172
- # Example:
173
- #
174
- # class Game
175
- # include Mongoid::Document
176
- # referenced_in :person
177
- # end
178
- #
179
- def referenced_in(name, options = {}, &block)
180
- opts = optionize(name, options, constraint(name, options, :in), &block)
181
- Associations::ReferencedIn.validate_options(opts)
182
- associate(Associations::ReferencedIn, opts)
183
- field(opts.foreign_key, :inverse_class_name => opts.class_name, :identity => true)
184
- index(opts.foreign_key, :background => true) if !embedded? && opts.index
185
- set_callback(:save, :before) { |document| document.update_foreign_keys }
186
- end
187
-
188
- alias :belongs_to_related :referenced_in
189
-
190
- # Adds a relational association from the Document to many Documents in
191
- # another database or collection.
192
- #
193
- # Options:
194
- #
195
- # name: A +Symbol+ that is the related class name pluralized.
196
- # default_order: A +Criteria+ that specifies the default sort order for
197
- # this association. (e.g. :position.asc). If an explicit ordering is
198
- # specified on a +Criteria+ object, the default order will NOT be used.
199
- #
200
- # Example:
201
- #
202
- # class Person
203
- # include Mongoid::Document
204
- # references_many :posts
205
- # references_many :board_games, :default_order => :title.asc
206
- # end
207
- #
208
- def references_many(name, options = {}, &block)
209
- reference_many(name, options, &block)
210
- set_callback :save, :before do |document|
211
- document.update_associations(name)
212
- end
213
- add_cascade(name, options)
214
- end
215
-
216
- alias :has_many_related :references_many
217
-
218
- # Adds a relational association from the Document to one Document in
219
- # another database or collection.
220
- #
221
- # Options:
222
- #
223
- # name: A +Symbol+ that is the related class name pluralized.
224
- #
225
- # Example:
226
- #
227
- # class Person
228
- # include Mongoid::Document
229
- # references_one :game
230
- # end
231
- def references_one(name, options = {}, &block)
232
- opts = optionize(name, options, constraint(name, options, :one), &block)
233
- type = Associations::ReferencesOne
234
- associate(type, opts)
235
- add_builder(type, opts)
236
- add_creator(type, opts)
237
- set_callback :save, :before do |document|
238
- document.update_association(name)
239
- end
240
- add_cascade(name, options)
241
- end
242
-
243
- alias :has_one_related :references_one
244
-
245
- # Returns the association reflection object with the supplied association
246
- # name.
247
- #
248
- # Options:
249
- #
250
- # name: The association name.
251
- #
252
- # Example:
253
- #
254
- # <tt>Person.reflect_on_association(:addresses)</tt>
255
- def reflect_on_association(name)
256
- association = associations[name.to_s]
257
- end
258
-
259
- # Returns all association meta data for the provided type.
260
- #
261
- # Options:
262
- #
263
- # macro: The association macro.
264
- #
265
- # Example:
266
- #
267
- # <tt>Person.reflect_on_all_associations(:embeds_many)</tt>
268
- def reflect_on_all_associations(macro)
269
- associations.values.select { |meta| meta.macro == macro }
270
- end
271
-
272
- protected
273
- # Adds the association to the associations hash with the type as the key,
274
- # then adds the accessors for the association. The defined setters and
275
- # getters for the associations will perform the necessary memoization.
276
- #
277
- # Example:
278
- #
279
- # <tt>Person.associate(EmbedsMany, { :name => :addresses })</tt>
280
- def associate(type, options)
281
- name = options.name.to_s
282
- associations[name] = MetaData.new(type, options)
283
- define_method(name) do
284
- memoized(name) do
285
- proxy = type.new(self, options)
286
- case proxy
287
- when Associations::ReferencesOne,
288
- Associations::EmbedsOne,
289
- Associations::ReferencedIn,
290
- Associations::EmbeddedIn
291
- proxy.target ? proxy : nil
292
- else
293
- proxy
294
- end
295
- end
296
- end
297
- define_method("#{name}=") do |object|
298
- unmemoize(name)
299
- memoized(name) { type.update(object, self, options) }
300
- end
301
- end
302
-
303
- # Adds a builder for a has_one association. This comes in the form of
304
- # build_name(attributes)
305
- def add_builder(type, options)
306
- name = options.name.to_s
307
- define_method("build_#{name}") do |*params|
308
- attrs = params[0]
309
- attr_options = params[1] || {}
310
- reset(name) do
311
- proxy = type.new(self, options)
312
- proxy.build((attrs || {}).stringify_keys)
313
- proxy
314
- end unless type == Associations::EmbedsOne && attr_options[:update_only]
315
- end
316
- end
317
-
318
- # Adds a creator for a has_one association. This comes in the form of
319
- # create_name(attributes)
320
- def add_creator(type, options)
321
- name = options.name.to_s
322
- define_method("create_#{name}") do |*params|
323
- attrs = params[0]
324
- attr_options = params[1] || {}
325
- unless type == Associations::EmbedsOne && attr_options[:update_only]
326
- send("build_#{name}", attrs, attr_options).tap(&:save)
327
- end
328
- end
329
- end
330
-
331
- # Create the callbacks for dependent deletes and destroys.
332
- def add_cascade(name, options)
333
- dependent = options[:dependent]
334
- self.cascades[name] = dependent if dependent
335
- end
336
-
337
- # build the options given the params.
338
- def optionize(name, options, foreign_key, &block)
339
- Associations::Options.new(
340
- options.merge(:name => name, :foreign_key => foreign_key, :extend => block)
341
- )
342
- end
343
-
344
- def reference_many(name, options, &block)
345
- if (options[:stored_as] == :array)
346
- foreign_key = "#{name.to_s.singularize}_ids"
347
- opts = optionize(name, options, constraint(name, options, :many_as_array), &block)
348
- field(
349
- foreign_key,
350
- :type => Array,
351
- :default => [],
352
- :identity => true,
353
- :inverse_class_name => opts.class_name
354
- )
355
- index(foreign_key, :background => true) if opts.index
356
- associate(Associations::ReferencesManyAsArray, opts)
357
- else
358
- opts = optionize(name, options, constraint(name, options, :many), &block)
359
- associate(Associations::ReferencesMany, opts)
360
- end
361
- end
362
- end
363
- end
364
- end
@@ -1,74 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents an association that is embedded within another document in the
5
- # database, either as one or many.
6
- class EmbeddedIn < Proxy
7
-
8
- # Creates the new association by setting the internal
9
- # target as the passed in Document. This should be the
10
- # parent.
11
- #
12
- # All method calls on this object will then be delegated
13
- # to the internal document itself.
14
- #
15
- # Options:
16
- #
17
- # document: The child +Document+
18
- # options: The association options
19
- def initialize(document, options, target = nil)
20
- if target
21
- inverse = determine_name(target, options)
22
- document.parentize(target, inverse)
23
- document.notify
24
- target.unmemoize(inverse)
25
- end
26
- @target, @options = document._parent, options
27
- extends(options)
28
- end
29
-
30
- # Returns the parent document. The id param is present for
31
- # compatibility with rails, however this could be overwritten
32
- # in the future.
33
- def find(id)
34
- @target
35
- end
36
-
37
- protected
38
- def determine_name(target, options)
39
- inverse = options.inverse_of
40
- return inverse unless inverse.is_a?(Array)
41
- inverse.detect { |name| target.respond_to?(name) }
42
- end
43
-
44
- class << self
45
- # Returns the macro used to create the association.
46
- def macro
47
- :embedded_in
48
- end
49
-
50
- # Perform an update of the relationship of the parent and child. This
51
- # is initialized by setting a parent object as the association on the
52
- # +Document+. Will properly set an embeds_one or an embeds_many.
53
- #
54
- # Returns:
55
- #
56
- # A new +EmbeddedIn+ association proxy.
57
- def update(target, child, options)
58
- new(child, options, target)
59
- end
60
-
61
- # Validate the options passed to the embedded in macro, to encapsulate
62
- # the behavior in this class instead of the associations module.
63
- #
64
- # Options:
65
- #
66
- # options: Thank you captain obvious.
67
- def validate_options(options = {})
68
- check_dependent_not_allowed!(options)
69
- check_inverse_must_be_defined!(options)
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,299 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents embedding many documents within a parent document, which will
5
- # be an array as the underlying storage mechanism.
6
- class EmbedsMany < Proxy
7
-
8
- attr_accessor :association_name, :klass
9
-
10
- # Appends the object to the +Array+, setting its parent in
11
- # the process.
12
- def <<(*documents)
13
- documents.flatten.each do |doc|
14
- doc.parentize(@parent, @association_name)
15
- @target << doc
16
- doc._index = @target.size - 1
17
- doc.notify
18
- end
19
- end
20
-
21
- alias :concat :<<
22
- alias :push :<<
23
-
24
- # Builds a new Document and adds it to the association collection. The
25
- # document created will be of the same class as the others in the
26
- # association, and the attributes will be passed into the constructor.
27
- #
28
- # Returns:
29
- #
30
- # The newly created Document.
31
- def build(attrs = {}, type = nil)
32
- document = type ? type.instantiate : @klass.instantiate
33
- document.parentize(@parent, @association_name)
34
- document.write_attributes(attrs)
35
- @target << document
36
- document._index = @target.size - 1
37
- document
38
- end
39
-
40
- # Clears the association, and notifies the parents of the removal.
41
- def clear
42
- unless @target.empty?
43
- document = @target.first
44
- document._parent.update_child(document, true) if (document._parent)
45
- @target.clear
46
- end
47
- end
48
-
49
- # Returns a count of the number of documents in the association that have
50
- # actually been persisted to the database.
51
- #
52
- # Use #size if you want the total number of documents.
53
- #
54
- # Returns:
55
- #
56
- # The total number of persisted embedded docs, as flagged by the
57
- # #persisted? method.
58
- def count
59
- @target.select(&:persisted?).size
60
- end
61
-
62
- # Creates a new Document and adds it to the association collection. The
63
- # document created will be of the same class as the others in the
64
- # association, and the attributes will be passed into the constructor and
65
- # the new object will then be saved.
66
- #
67
- # Returns:
68
- #
69
- # The newly created Document.
70
- def create(attrs = {}, type = nil)
71
- build(attrs, type).tap(&:save)
72
- end
73
-
74
- # Creates a new Document and adds it to the association collection. The
75
- # document created will be of the same class as the others in the
76
- # association, and the attributes will be passed into the constructor and
77
- # the new object will then be saved. If validation fails an error will
78
- # get raised.
79
- #
80
- # Returns:
81
- #
82
- # The newly created Document.
83
- def create!(attrs = {}, type = nil)
84
- document = create(attrs, type)
85
- raise Errors::Validations.new(document) unless document.errors.empty?
86
- document
87
- end
88
-
89
- # Delete all the documents in the association without running callbacks.
90
- #
91
- # Example:
92
- #
93
- # <tt>addresses.delete_all</tt>
94
- #
95
- # Returns:
96
- #
97
- # The number of documents deleted.
98
- def delete_all(conditions = {})
99
- remove(:delete, conditions)
100
- end
101
-
102
- # Delete all the documents in the association and run destroy callbacks.
103
- #
104
- # Example:
105
- #
106
- # <tt>addresses.destroy_all</tt>
107
- #
108
- # Returns:
109
- #
110
- # The number of documents destroyed.
111
- def destroy_all(conditions = {})
112
- remove(:destroy, conditions)
113
- end
114
-
115
- # Finds a document in this association.
116
- #
117
- # If :all is passed, returns all the documents
118
- #
119
- # If an id is passed, will return the document for that id.
120
- #
121
- # Returns:
122
- #
123
- # Array or single Document.
124
- def find(param)
125
- return @target if param == :all
126
- criteria.id(param).first
127
- end
128
-
129
- # Creates the new association by finding the attributes in
130
- # the parent document with its name, and instantiating a
131
- # new document for each one found. These will then be put in an
132
- # internal array.
133
- #
134
- # This then delegated all methods to the array class since this is
135
- # essentially a proxy to an array itself.
136
- #
137
- # Options:
138
- #
139
- # parent: The parent document to the association.
140
- # options: The association options.
141
- def initialize(parent, options, target_array = nil)
142
- @parent, @association_name = parent, options.name
143
- @klass, @options = options.klass, options
144
- if target_array
145
- build_children_from_target_array(target_array)
146
- else
147
- build_children_from_attributes(parent.raw_attributes[@association_name])
148
- end
149
- extends(options)
150
- end
151
-
152
- # If the target array does not respond to the supplied method then try to
153
- # find a named scope or criteria on the class and send the call there.
154
- #
155
- # If the method exists on the array, use the default proxy behavior.
156
- def method_missing(name, *args, &block)
157
- if @target.respond_to?(name)
158
- super
159
- else
160
- @klass.send(:with_scope, criteria) do
161
- object = @klass.send(name, *args)
162
- end
163
- end
164
- end
165
-
166
- # Used for setting associations via a nested attributes setter from the
167
- # parent +Document+.
168
- #
169
- # Options:
170
- #
171
- # attributes: A +Hash+ of integer keys and +Hash+ values.
172
- #
173
- # Returns:
174
- #
175
- # The newly build target Document.
176
- def nested_build(attributes, options = {})
177
- @parent.instance_variable_set(:@building_nested, true)
178
- id_index, reordering = {}, false
179
- attributes.each do |index, attrs|
180
- document = if attrs["id"].present?
181
- reordering = true
182
- id_index[attrs["id"]] = index.to_i
183
- detect { |document| document.id.to_s == attrs["id"].to_s }
184
- else
185
- detect { |document| document._index == index.to_i }
186
- end
187
- if document
188
- if options && options[:allow_destroy] && Boolean.set(attrs['_destroy'])
189
- @target.delete(document)
190
- document.destroy
191
- else
192
- document.write_attributes(attrs)
193
- end
194
- else
195
- document = build(attrs)
196
- id_index[document.id.to_s] = index.to_i
197
- end
198
- end
199
- if reordering
200
- @target.sort! do |a, b|
201
- ai, bi = id_index[a.id.to_s], id_index[b.id.to_s]
202
- ai.nil? ? (bi.nil? ? 0 : 1) : (bi.nil? ? -1 : ai <=> bi)
203
- end
204
- end
205
- @target.each_with_index { |document, index| document._index = index }
206
- @parent.instance_variable_set(:@building_nested, false)
207
- self
208
- end
209
-
210
- # Paginate the association. Will create a new criteria, set the documents
211
- # on it and execute in an enumerable context.
212
- #
213
- # Options:
214
- #
215
- # options: A +Hash+ of pagination options.
216
- #
217
- # Returns:
218
- #
219
- # A +WillPaginate::Collection+.
220
- def paginate(options)
221
- criteria = Mongoid::Criteria.translate(@klass, options)
222
- criteria.documents = @target
223
- criteria.paginate(options)
224
- end
225
-
226
- protected
227
- # Initializes each of the attributes in the hash.
228
- def build_children_from_attributes(attributes)
229
- @target = []
230
- if attributes
231
- attributes.each_with_index do |attrs, index|
232
- klass = attrs.klass
233
- child = klass ? klass.instantiate(attrs) : @klass.instantiate(attrs)
234
- child.parentize(@parent, @association_name)
235
- child._index = index
236
- @target << child
237
- end
238
- end
239
- end
240
-
241
- # Initializes the target array from an existing array of documents.
242
- def build_children_from_target_array(target_array)
243
- @target = target_array
244
- @target.each_with_index do |child, index|
245
- child._index = index
246
- end
247
- end
248
-
249
- # Removes documents based on a method.
250
- def remove(method, conditions)
251
- criteria = @klass.find(conditions || {})
252
- criteria.documents = @target
253
- count = criteria.size
254
- criteria.each do |doc|
255
- @target.delete(doc); doc.send(method)
256
- end; count
257
- end
258
-
259
- # Returns the criteria object for the target class with its documents set
260
- # to @target.
261
- def criteria
262
- criteria = @klass.criteria
263
- criteria.documents = @target
264
- criteria
265
- end
266
-
267
- class << self
268
- # Returns the macro used to create the association.
269
- def macro
270
- :embeds_many
271
- end
272
-
273
- # Perform an update of the relationship of the parent and child. This
274
- # is initialized by setting the has_many to the supplied +Enumerable+
275
- # and setting up the parentization.
276
- def update(children, parent, options)
277
- parent.raw_attributes.delete(options.name)
278
- children.assimilate(parent, options)
279
- if children && children.first.is_a?(Mongoid::Document)
280
- new(parent, options, children)
281
- else
282
- new(parent, options)
283
- end
284
- end
285
-
286
- # Validate the options passed to the embeds many macro, to encapsulate
287
- # the behavior in this class instead of the associations module.
288
- #
289
- # Options:
290
- #
291
- # options: Thank you captain obvious.
292
- def validate_options(options = {})
293
- check_dependent_not_allowed!(options)
294
- check_inverse_not_allowed!(options)
295
- end
296
- end
297
- end
298
- end
299
- end