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.
- data/README.rdoc +8 -0
- data/Rakefile +51 -0
- data/lib/config/locales/nl.yml +39 -0
- data/lib/config/locales/ro.yml +1 -1
- data/lib/mongoid.rb +17 -17
- data/lib/mongoid/atomicity.rb +54 -22
- data/lib/mongoid/attributes.rb +145 -125
- data/lib/mongoid/callbacks.rb +7 -2
- data/lib/mongoid/collection.rb +49 -32
- data/lib/mongoid/collections.rb +0 -1
- data/lib/mongoid/components.rb +34 -29
- data/lib/mongoid/config.rb +207 -193
- data/lib/mongoid/config/database.rb +167 -0
- data/lib/mongoid/contexts.rb +2 -5
- data/lib/mongoid/contexts/enumerable.rb +30 -4
- data/lib/mongoid/contexts/ids.rb +2 -2
- data/lib/mongoid/contexts/mongo.rb +30 -5
- data/lib/mongoid/copyable.rb +44 -0
- data/lib/mongoid/criteria.rb +110 -56
- data/lib/mongoid/criterion/creational.rb +34 -0
- data/lib/mongoid/criterion/destructive.rb +37 -0
- data/lib/mongoid/criterion/exclusion.rb +3 -1
- data/lib/mongoid/criterion/inclusion.rb +59 -64
- data/lib/mongoid/criterion/inspection.rb +22 -0
- data/lib/mongoid/criterion/optional.rb +42 -54
- data/lib/mongoid/criterion/selector.rb +9 -0
- data/lib/mongoid/default_scope.rb +28 -0
- data/lib/mongoid/deprecation.rb +5 -5
- data/lib/mongoid/dirty.rb +4 -5
- data/lib/mongoid/document.rb +161 -114
- data/lib/mongoid/extensions.rb +7 -11
- data/lib/mongoid/extensions/array/parentization.rb +2 -2
- data/lib/mongoid/extensions/date/conversions.rb +1 -1
- data/lib/mongoid/extensions/hash/conversions.rb +0 -23
- data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
- data/lib/mongoid/extensions/object/reflections.rb +17 -0
- data/lib/mongoid/extensions/object/yoda.rb +27 -0
- data/lib/mongoid/extensions/string/conversions.rb +23 -4
- data/lib/mongoid/extensions/time_conversions.rb +4 -4
- data/lib/mongoid/field.rb +30 -19
- data/lib/mongoid/fields.rb +15 -5
- data/lib/mongoid/finders.rb +19 -11
- data/lib/mongoid/hierarchy.rb +34 -28
- data/lib/mongoid/identity.rb +62 -20
- data/lib/mongoid/inspection.rb +58 -0
- data/lib/mongoid/matchers.rb +20 -0
- data/lib/mongoid/multi_database.rb +11 -0
- data/lib/mongoid/nested_attributes.rb +41 -0
- data/lib/mongoid/paranoia.rb +3 -4
- data/lib/mongoid/paths.rb +1 -1
- data/lib/mongoid/persistence.rb +89 -90
- data/lib/mongoid/persistence/command.rb +20 -4
- data/lib/mongoid/persistence/insert.rb +13 -11
- data/lib/mongoid/persistence/insert_embedded.rb +8 -6
- data/lib/mongoid/persistence/remove.rb +6 -4
- data/lib/mongoid/persistence/remove_all.rb +6 -4
- data/lib/mongoid/persistence/remove_embedded.rb +8 -6
- data/lib/mongoid/persistence/update.rb +12 -10
- data/lib/mongoid/railtie.rb +2 -2
- data/lib/mongoid/railties/database.rake +10 -9
- data/lib/mongoid/relations.rb +104 -0
- data/lib/mongoid/relations/accessors.rb +154 -0
- data/lib/mongoid/relations/auto_save.rb +34 -0
- data/lib/mongoid/relations/binding.rb +24 -0
- data/lib/mongoid/relations/bindings.rb +9 -0
- data/lib/mongoid/relations/bindings/embedded/in.rb +77 -0
- data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
- data/lib/mongoid/relations/bindings/embedded/one.rb +65 -0
- data/lib/mongoid/relations/bindings/referenced/in.rb +78 -0
- data/lib/mongoid/relations/bindings/referenced/many.rb +93 -0
- data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +94 -0
- data/lib/mongoid/relations/bindings/referenced/one.rb +63 -0
- data/lib/mongoid/relations/builder.rb +41 -0
- data/lib/mongoid/relations/builders.rb +79 -0
- data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
- data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
- data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
- data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
- data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
- data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
- data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
- data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
- data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
- data/lib/mongoid/relations/cascading.rb +55 -0
- data/lib/mongoid/relations/cascading/delete.rb +19 -0
- data/lib/mongoid/relations/cascading/destroy.rb +19 -0
- data/lib/mongoid/relations/cascading/nullify.rb +18 -0
- data/lib/mongoid/relations/cascading/strategy.rb +26 -0
- data/lib/mongoid/relations/cyclic.rb +97 -0
- data/lib/mongoid/relations/embedded/in.rb +172 -0
- data/lib/mongoid/relations/embedded/many.rb +450 -0
- data/lib/mongoid/relations/embedded/one.rb +169 -0
- data/lib/mongoid/relations/macros.rb +302 -0
- data/lib/mongoid/relations/many.rb +185 -0
- data/lib/mongoid/relations/metadata.rb +529 -0
- data/lib/mongoid/relations/nested_builder.rb +52 -0
- data/lib/mongoid/relations/one.rb +29 -0
- data/lib/mongoid/relations/polymorphic.rb +54 -0
- data/lib/mongoid/relations/proxy.rb +122 -0
- data/lib/mongoid/relations/referenced/in.rb +214 -0
- data/lib/mongoid/relations/referenced/many.rb +358 -0
- data/lib/mongoid/relations/referenced/many_to_many.rb +379 -0
- data/lib/mongoid/relations/referenced/one.rb +204 -0
- data/lib/mongoid/relations/reflections.rb +45 -0
- data/lib/mongoid/safe.rb +11 -1
- data/lib/mongoid/safety.rb +122 -97
- data/lib/mongoid/scope.rb +14 -9
- data/lib/mongoid/state.rb +37 -3
- data/lib/mongoid/timestamps.rb +11 -0
- data/lib/mongoid/validations.rb +42 -3
- data/lib/mongoid/validations/associated.rb +8 -5
- data/lib/mongoid/validations/uniqueness.rb +23 -2
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +25 -16
- data/lib/rails/generators/mongoid/model/templates/model.rb +3 -1
- metadata +95 -80
- data/lib/mongoid/associations.rb +0 -364
- data/lib/mongoid/associations/embedded_in.rb +0 -74
- data/lib/mongoid/associations/embeds_many.rb +0 -299
- data/lib/mongoid/associations/embeds_one.rb +0 -111
- data/lib/mongoid/associations/foreign_key.rb +0 -35
- data/lib/mongoid/associations/meta_data.rb +0 -38
- data/lib/mongoid/associations/options.rb +0 -78
- data/lib/mongoid/associations/proxy.rb +0 -60
- data/lib/mongoid/associations/referenced_in.rb +0 -70
- data/lib/mongoid/associations/references_many.rb +0 -254
- data/lib/mongoid/associations/references_many_as_array.rb +0 -128
- data/lib/mongoid/associations/references_one.rb +0 -104
- data/lib/mongoid/extensions/array/accessors.rb +0 -17
- data/lib/mongoid/extensions/array/assimilation.rb +0 -26
- data/lib/mongoid/extensions/hash/accessors.rb +0 -42
- data/lib/mongoid/extensions/hash/assimilation.rb +0 -40
- data/lib/mongoid/extensions/nil/assimilation.rb +0 -17
- data/lib/mongoid/memoization.rb +0 -33
data/lib/mongoid/associations.rb
DELETED
@@ -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
|