mongoid 2.0.0.beta.20 → 2.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|