stonegao-mongoid 2.0.0.rc.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +50 -0
  3. data/Rakefile +51 -0
  4. data/lib/config/locales/bg.yml +44 -0
  5. data/lib/config/locales/de.yml +44 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +44 -0
  8. data/lib/config/locales/fr.yml +45 -0
  9. data/lib/config/locales/hu.yml +47 -0
  10. data/lib/config/locales/it.yml +42 -0
  11. data/lib/config/locales/kr.yml +68 -0
  12. data/lib/config/locales/nl.yml +42 -0
  13. data/lib/config/locales/pl.yml +42 -0
  14. data/lib/config/locales/pt-br.yml +43 -0
  15. data/lib/config/locales/pt.yml +43 -0
  16. data/lib/config/locales/ro.yml +49 -0
  17. data/lib/config/locales/sv.yml +43 -0
  18. data/lib/config/locales/zh-CN.yml +34 -0
  19. data/lib/mongoid/atomicity.rb +111 -0
  20. data/lib/mongoid/attributes.rb +251 -0
  21. data/lib/mongoid/callbacks.rb +13 -0
  22. data/lib/mongoid/collection.rb +137 -0
  23. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/mongoid/collections/master.rb +29 -0
  25. data/lib/mongoid/collections/operations.rb +42 -0
  26. data/lib/mongoid/collections/slaves.rb +45 -0
  27. data/lib/mongoid/collections.rb +70 -0
  28. data/lib/mongoid/components.rb +45 -0
  29. data/lib/mongoid/config/database.rb +167 -0
  30. data/lib/mongoid/config/replset_database.rb +48 -0
  31. data/lib/mongoid/config.rb +343 -0
  32. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  33. data/lib/mongoid/contexts/enumerable.rb +226 -0
  34. data/lib/mongoid/contexts/ids.rb +25 -0
  35. data/lib/mongoid/contexts/mongo.rb +345 -0
  36. data/lib/mongoid/contexts/paging.rb +50 -0
  37. data/lib/mongoid/contexts.rb +21 -0
  38. data/lib/mongoid/copyable.rb +44 -0
  39. data/lib/mongoid/criteria.rb +325 -0
  40. data/lib/mongoid/criterion/complex.rb +34 -0
  41. data/lib/mongoid/criterion/creational.rb +34 -0
  42. data/lib/mongoid/criterion/exclusion.rb +67 -0
  43. data/lib/mongoid/criterion/inclusion.rb +134 -0
  44. data/lib/mongoid/criterion/inspection.rb +20 -0
  45. data/lib/mongoid/criterion/optional.rb +213 -0
  46. data/lib/mongoid/criterion/selector.rb +74 -0
  47. data/lib/mongoid/cursor.rb +81 -0
  48. data/lib/mongoid/default_scope.rb +28 -0
  49. data/lib/mongoid/dirty.rb +251 -0
  50. data/lib/mongoid/document.rb +256 -0
  51. data/lib/mongoid/errors/document_not_found.rb +29 -0
  52. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  53. data/lib/mongoid/errors/invalid_database.rb +20 -0
  54. data/lib/mongoid/errors/invalid_field.rb +19 -0
  55. data/lib/mongoid/errors/invalid_options.rb +16 -0
  56. data/lib/mongoid/errors/invalid_type.rb +26 -0
  57. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  58. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  59. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  60. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  61. data/lib/mongoid/errors/validations.rb +24 -0
  62. data/lib/mongoid/errors.rb +12 -0
  63. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  64. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  65. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  66. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  67. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  68. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  69. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  70. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  71. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  72. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  74. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  75. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  76. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  77. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  78. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  79. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  80. data/lib/mongoid/extensions/object_id/conversions.rb +57 -0
  81. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  84. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  85. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  86. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  87. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  88. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  89. data/lib/mongoid/extensions.rb +116 -0
  90. data/lib/mongoid/extras.rb +61 -0
  91. data/lib/mongoid/factory.rb +20 -0
  92. data/lib/mongoid/field.rb +95 -0
  93. data/lib/mongoid/fields.rb +138 -0
  94. data/lib/mongoid/finders.rb +173 -0
  95. data/lib/mongoid/hierarchy.rb +85 -0
  96. data/lib/mongoid/identity.rb +89 -0
  97. data/lib/mongoid/indexes.rb +38 -0
  98. data/lib/mongoid/inspection.rb +58 -0
  99. data/lib/mongoid/javascript/functions.yml +37 -0
  100. data/lib/mongoid/javascript.rb +21 -0
  101. data/lib/mongoid/json.rb +16 -0
  102. data/lib/mongoid/keys.rb +77 -0
  103. data/lib/mongoid/logger.rb +18 -0
  104. data/lib/mongoid/matchers/all.rb +11 -0
  105. data/lib/mongoid/matchers/default.rb +27 -0
  106. data/lib/mongoid/matchers/exists.rb +13 -0
  107. data/lib/mongoid/matchers/gt.rb +11 -0
  108. data/lib/mongoid/matchers/gte.rb +11 -0
  109. data/lib/mongoid/matchers/in.rb +11 -0
  110. data/lib/mongoid/matchers/lt.rb +11 -0
  111. data/lib/mongoid/matchers/lte.rb +11 -0
  112. data/lib/mongoid/matchers/ne.rb +11 -0
  113. data/lib/mongoid/matchers/nin.rb +11 -0
  114. data/lib/mongoid/matchers/size.rb +11 -0
  115. data/lib/mongoid/matchers.rb +55 -0
  116. data/lib/mongoid/modifiers/command.rb +18 -0
  117. data/lib/mongoid/modifiers/inc.rb +24 -0
  118. data/lib/mongoid/modifiers.rb +24 -0
  119. data/lib/mongoid/multi_database.rb +11 -0
  120. data/lib/mongoid/multi_parameter_attributes.rb +80 -0
  121. data/lib/mongoid/named_scope.rb +36 -0
  122. data/lib/mongoid/nested_attributes.rb +43 -0
  123. data/lib/mongoid/paranoia.rb +103 -0
  124. data/lib/mongoid/paths.rb +61 -0
  125. data/lib/mongoid/persistence/command.rb +59 -0
  126. data/lib/mongoid/persistence/insert.rb +53 -0
  127. data/lib/mongoid/persistence/insert_embedded.rb +42 -0
  128. data/lib/mongoid/persistence/remove.rb +44 -0
  129. data/lib/mongoid/persistence/remove_all.rb +40 -0
  130. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  131. data/lib/mongoid/persistence/update.rb +76 -0
  132. data/lib/mongoid/persistence.rb +237 -0
  133. data/lib/mongoid/railtie.rb +129 -0
  134. data/lib/mongoid/railties/database.rake +171 -0
  135. data/lib/mongoid/railties/document.rb +12 -0
  136. data/lib/mongoid/relations/accessors.rb +157 -0
  137. data/lib/mongoid/relations/auto_save.rb +34 -0
  138. data/lib/mongoid/relations/binding.rb +26 -0
  139. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  140. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  141. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  142. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  143. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  144. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +99 -0
  145. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  146. data/lib/mongoid/relations/bindings.rb +9 -0
  147. data/lib/mongoid/relations/builder.rb +42 -0
  148. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  149. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  150. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  151. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  152. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  153. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  154. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  155. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  156. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  157. data/lib/mongoid/relations/builders.rb +79 -0
  158. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  159. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  160. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  161. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  162. data/lib/mongoid/relations/cascading.rb +55 -0
  163. data/lib/mongoid/relations/constraint.rb +45 -0
  164. data/lib/mongoid/relations/cyclic.rb +97 -0
  165. data/lib/mongoid/relations/embedded/in.rb +173 -0
  166. data/lib/mongoid/relations/embedded/many.rb +483 -0
  167. data/lib/mongoid/relations/embedded/one.rb +170 -0
  168. data/lib/mongoid/relations/macros.rb +306 -0
  169. data/lib/mongoid/relations/many.rb +171 -0
  170. data/lib/mongoid/relations/metadata.rb +533 -0
  171. data/lib/mongoid/relations/nested_builder.rb +68 -0
  172. data/lib/mongoid/relations/one.rb +47 -0
  173. data/lib/mongoid/relations/polymorphic.rb +54 -0
  174. data/lib/mongoid/relations/proxy.rb +128 -0
  175. data/lib/mongoid/relations/referenced/in.rb +216 -0
  176. data/lib/mongoid/relations/referenced/many.rb +443 -0
  177. data/lib/mongoid/relations/referenced/many_to_many.rb +344 -0
  178. data/lib/mongoid/relations/referenced/one.rb +206 -0
  179. data/lib/mongoid/relations/reflections.rb +45 -0
  180. data/lib/mongoid/relations.rb +105 -0
  181. data/lib/mongoid/safe.rb +23 -0
  182. data/lib/mongoid/safety.rb +207 -0
  183. data/lib/mongoid/scope.rb +31 -0
  184. data/lib/mongoid/serialization.rb +99 -0
  185. data/lib/mongoid/state.rb +66 -0
  186. data/lib/mongoid/timestamps.rb +38 -0
  187. data/lib/mongoid/validations/associated.rb +42 -0
  188. data/lib/mongoid/validations/uniqueness.rb +85 -0
  189. data/lib/mongoid/validations.rb +117 -0
  190. data/lib/mongoid/version.rb +4 -0
  191. data/lib/mongoid/versioning.rb +51 -0
  192. data/lib/mongoid.rb +139 -0
  193. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  194. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +23 -0
  195. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  196. data/lib/rails/generators/mongoid/model/templates/model.rb +17 -0
  197. data/lib/rails/generators/mongoid_generator.rb +61 -0
  198. data/lib/rails/mongoid.rb +57 -0
  199. metadata +380 -0
@@ -0,0 +1,533 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # The "Grand Poobah" of information about any relation is this class. It
6
+ # contains everything you could ever possible want to know.
7
+ class Metadata < Hash
8
+
9
+ delegate :foreign_key_default, :stores_foreign_key?, :to => :relation
10
+
11
+ # Gets a relation builder associated with the relation this metadata is
12
+ # for.
13
+ #
14
+ # @example Get the builder.
15
+ # metadata.builder(document)
16
+ #
17
+ # @param [ Object ] object A document or attributes to give the builder.
18
+ #
19
+ # @return [ Builder ] The builder for the relation.
20
+ #
21
+ # @since 2.0.0.rc.1
22
+ def builder(object)
23
+ relation.builder(self, object)
24
+ end
25
+
26
+ # Returns the name of the strategy used for handling dependent relations.
27
+ #
28
+ # @example Get the strategy.
29
+ # metadata.cascade_strategy
30
+ #
31
+ # @return [ Object ] The cascading strategy to use.
32
+ #
33
+ # @since 2.0.0.rc.1
34
+ def cascade_strategy
35
+ if dependent?
36
+ strategy =
37
+ %{Mongoid::Relations::Cascading::#{dependent.to_s.classify}}
38
+ strategy.constantize
39
+ else
40
+ return nil
41
+ end
42
+ end
43
+
44
+ # Returns the name of the class that this relation contains. If the
45
+ # class_name was provided as an option this will return that, otherwise
46
+ # it will determine the name from the name property.
47
+ #
48
+ # @example Get the class name.
49
+ # metadata.class_name
50
+ #
51
+ # @return [ String ] The name of the relation's proxied class.
52
+ #
53
+ # @since 2.0.0.rc.1
54
+ def class_name
55
+ @class_name ||= (self[:class_name] || classify)
56
+ end
57
+
58
+ def constraint
59
+ @constraint ||= Constraint.new(self)
60
+ end
61
+
62
+ # Will determine if the relation is an embedded one or not. Currently
63
+ # only checks against embeds one and many.
64
+ #
65
+ # @example Is the document embedded.
66
+ # metadata.embedded?
67
+ #
68
+ # @return [ true, false ] True if embedded, false if not.
69
+ #
70
+ # @since 2.0.0.rc.1
71
+ def embedded?
72
+ @embedded ||= (macro == :embeds_one || macro == :embeds_many)
73
+ end
74
+
75
+ # Returns the extension of the relation. This can be a proc or module.
76
+ #
77
+ # @example Get the relation extension.
78
+ # metadata.extension
79
+ #
80
+ # @return [ Proc ] The extension or nil.
81
+ #
82
+ # @since 2.0.0.rc.1
83
+ def extension
84
+ self[:extend]
85
+ end
86
+
87
+ # Tells whether an extension definition exist for this relation.
88
+ #
89
+ # @example Is an extension defined?
90
+ # metadata.extension?
91
+ #
92
+ # @return [ true, false ] True if an extension exists, false if not.
93
+ #
94
+ # @since 2.0.0.rc.1
95
+ def extension?
96
+ !!extension
97
+ end
98
+
99
+ # Handles all the logic for figuring out what the foreign_key is for each
100
+ # relations query. The logic is as follows:
101
+ #
102
+ # 1. If the developer defined a custom key, use that.
103
+ # 2. If the relation stores a foreign key,
104
+ # use the class_name_id strategy.
105
+ # 3. If the relation does not store the key,
106
+ # use the inverse_class_name_id strategy.
107
+ #
108
+ # @example Get the foreign key.
109
+ # metadata.foreign_key
110
+ #
111
+ # @return [ String ] The foreign key for the relation.
112
+ #
113
+ # @since 2.0.0.rc.1
114
+ def foreign_key
115
+ @foreign_key ||= determine_foreign_key
116
+ end
117
+
118
+ # Returns the name of the method used to set the foreign key on a
119
+ # document.
120
+ #
121
+ # @example Get the setter for the foreign key.
122
+ # metadata.foreign_key_setter
123
+ #
124
+ # @return [ String ] The foreign_key plus =.
125
+ #
126
+ # @since 2.0.0.rc.1
127
+ def foreign_key_setter
128
+ @foreign_key_setter ||= "#{foreign_key}="
129
+ end
130
+
131
+ # Tells whether a foreign key index exists on the relation.
132
+ #
133
+ # @example Is the key indexed?
134
+ # metadata.indexed?
135
+ #
136
+ # @return [ true, false ] True if an index exists, false if not.
137
+ #
138
+ # @since 2.0.0.rc.1
139
+ def indexed?
140
+ !!self[:index]
141
+ end
142
+
143
+ # Instantiate new metadata for a relation.
144
+ #
145
+ # @example Create the new metadata.
146
+ # Metadata.new(:name => :addresses)
147
+ #
148
+ # @param [ Hash ] properties The relation options.
149
+ #
150
+ # @since 2.0.0.rc.1
151
+ def initialize(properties = {})
152
+ merge!(properties)
153
+ end
154
+
155
+ # Since a lot of the information from the metadata is inferred and not
156
+ # explicitly stored in the hash, the inspection needs to be much more
157
+ # detailed.
158
+ #
159
+ # @example Inspect the metadata.
160
+ # metadata.inspect
161
+ #
162
+ # @return [ String ] Oodles of information in a nice format.
163
+ #
164
+ # @since 2.0.0.rc.1
165
+ def inspect
166
+ "#<Mongoid::Relations::Metadata\n" <<
167
+ " class_name: #{class_name},\n" <<
168
+ " cyclic: #{cyclic || "No"},\n" <<
169
+ " dependent: #{dependent || "None"},\n" <<
170
+ " inverse_of: #{inverse_of || "N/A"},\n" <<
171
+ " inverse_setter: #{inverse_setter},\n" <<
172
+ " inverse_type: #{inverse_type || "N/A"},\n" <<
173
+ " inverse_type_setter: #{inverse_type_setter || "N/A"},\n" <<
174
+ " key: #{key},\n" <<
175
+ " macro: #{macro},\n" <<
176
+ " name: #{name},\n" <<
177
+ " polymorphic: #{polymorphic? ? "Yes" : "No"},\n" <<
178
+ " relation: #{relation},\n" <<
179
+ " setter: #{setter}>\n"
180
+ end
181
+
182
+ # Get the name of the inverse relation if it exists. If this is a
183
+ # polymorphic relation then just return the :as option that was defined.
184
+ #
185
+ # @example Get the name of the inverse.
186
+ # metadata.inverse
187
+ #
188
+ # @param [ Document ] other The document to aid in the discovery.
189
+ #
190
+ # @return [ Symbol ] The inverse name.
191
+ #
192
+ # @since 2.0.0.rc.1
193
+ def inverse(other = nil)
194
+ return self[:inverse_of] if inverse_of?
195
+ return self[:as] || lookup_inverse(other) if polymorphic?
196
+ @inverse ||= (cyclic? ? cyclic_inverse : inverse_relation)
197
+ end
198
+
199
+ # Used for relational many to many only. This determines the name of the
200
+ # foreign key field on the inverse side of the relation, since in this
201
+ # case there are keys on both sides.
202
+ #
203
+ # @example Find the inverse foreign key
204
+ # metadata.inverse_foreign_key
205
+ #
206
+ # @return [ String ] The foreign key on the inverse.
207
+ #
208
+ # @since 2.0.0.rc.1
209
+ def inverse_foreign_key
210
+ @inverse_foreign_key ||=
211
+ (inverse_class_name.underscore << relation.foreign_key_suffix)
212
+ end
213
+
214
+ # Returns the inverse class of the proxied relation.
215
+ #
216
+ # @example Get the inverse class.
217
+ # metadata.inverse_klass
218
+ #
219
+ # @return [ Class ] The class of the inverse of the relation.
220
+ #
221
+ # @since 2.0.0.rc.1
222
+ def inverse_klass
223
+ @inverse_klass ||= inverse_class_name.constantize
224
+ end
225
+
226
+ # Returns the setter for the inverse side of the relation.
227
+ #
228
+ # @example Get the inverse setter.
229
+ # metadata.inverse_setter
230
+ #
231
+ # @param [ Document ] other A document to aid in the discovery.
232
+ #
233
+ # @return [ String ] The inverse setter name.
234
+ #
235
+ # @since 2.0.0.rc.1
236
+ def inverse_setter(other = nil)
237
+ inverse(other).to_s << "="
238
+ end
239
+
240
+ # Returns the name of the field in which to store the name of the class
241
+ # for the polymorphic relation.
242
+ #
243
+ # @example Get the name of the field.
244
+ # metadata.inverse_type
245
+ #
246
+ # @return [ String ] The name of the field for storing the type.
247
+ #
248
+ # @since 2.0.0.rc.1
249
+ def inverse_type
250
+ if relation.stores_foreign_key? && polymorphic?
251
+ (polymorphic? ? name.to_s : class_name.underscore) << "_type"
252
+ else
253
+ return nil
254
+ end
255
+ end
256
+
257
+ # Gets the setter for the field that sets the type of document on a
258
+ # polymorphic relation.
259
+ #
260
+ # @example Get the inverse type setter.
261
+ # metadata.inverse_type_setter
262
+ #
263
+ # @return [ String ] The name of the setter.
264
+ #
265
+ # @since 2.0.0.rc.1
266
+ def inverse_type_setter
267
+ inverse_type ? inverse_type << "=" : nil
268
+ end
269
+
270
+ # This returns the key that is to be used to grab the attributes for the
271
+ # relation or the foreign key or id that a referenced relation will use
272
+ # to query for the object.
273
+ #
274
+ # @example Get the lookup key.
275
+ # metadata.key
276
+ #
277
+ # @return [ String ] The association name, foreign key name, or _id.
278
+ #
279
+ # @since 2.0.0.rc.1
280
+ def key
281
+ @key ||= determine_key
282
+ end
283
+
284
+ # Returns the class of the proxied relation.
285
+ #
286
+ # @example Get the class.
287
+ # metadata.klass
288
+ #
289
+ # @return [ Class ] The class of the relation.
290
+ #
291
+ # @since 2.0.0.rc.1
292
+ def klass
293
+ @klass ||= class_name.constantize
294
+ end
295
+
296
+ # Returns the macro for the relation of this metadata.
297
+ #
298
+ # @example Get the macro.
299
+ # metadata.macro
300
+ #
301
+ # @return [ Symbol ] The macro.
302
+ #
303
+ # @since 2.0.0.rc.1
304
+ def macro
305
+ relation.macro
306
+ end
307
+
308
+ # Gets a relation nested builder associated with the relation this metadata
309
+ # is for. Nested builders are used in conjunction with nested attributes.
310
+ #
311
+ # @example Get the nested builder.
312
+ # metadata.nested_builder(attributes, options)
313
+ #
314
+ # @param [ Hash ] attributes The attributes to build the relation with.
315
+ # @param [ Hash ] options Options for the nested builder.
316
+ #
317
+ # @return [ NestedBuilder ] The nested builder for the relation.
318
+ #
319
+ # @since 2.0.0.rc.1
320
+ def nested_builder(attributes, options)
321
+ relation.nested_builder(self, attributes, options)
322
+ end
323
+
324
+ # Returns true if the relation is polymorphic.
325
+ #
326
+ # @example Is the relation polymorphic?
327
+ # metadata.polymorphic?
328
+ #
329
+ # @return [ true, false ] True if the relation is polymorphic, false if not.
330
+ #
331
+ # @since 2.0.0.rc.1
332
+ def polymorphic?
333
+ @polymorphic ||= (!!self[:as] || !!self[:polymorphic])
334
+ end
335
+
336
+ # Gets the method name used to set this relation.
337
+ #
338
+ # @example Get the setter.
339
+ # metadata = Metadata.new(:name => :person)
340
+ # metadata.setter # => "person="
341
+ #
342
+ # @return [ String ] The name plus "=".
343
+ #
344
+ # @since 2.0.0.rc.1
345
+ def setter
346
+ @setter ||= "#{name.to_s}="
347
+ end
348
+
349
+ # Are we validating this relation automatically?
350
+ #
351
+ # @example Is automatic validation on?
352
+ # metadata.validate?
353
+ #
354
+ # @return [ true, false ] True unless explictly set to false.
355
+ #
356
+ # @since 2.0.0.rc.1
357
+ def validate?
358
+ self[:validate] != false
359
+ end
360
+
361
+ private
362
+
363
+ # Returns the class name for the relation.
364
+ #
365
+ # @example Get the class name.
366
+ # metadata.classify
367
+ #
368
+ # @return [ String ] If embedded_in, the camelized, else classified.
369
+ #
370
+ # @since 2.0.0.rc.1
371
+ def classify
372
+ macro == :embedded_in ? name.to_s.camelize : name.to_s.classify
373
+ end
374
+
375
+ # Get the name of the inverse relation in a cyclic relation.
376
+ #
377
+ # @example Get the cyclic inverse name.
378
+ #
379
+ # class Role
380
+ # include Mongoid::Document
381
+ # embedded_in :parent_role, :cyclic => true
382
+ # embeds_many :child_roles, :cyclic => true
383
+ # end
384
+ #
385
+ # metadata = Metadata.new(:name => :parent_role)
386
+ # metadata.cyclic_inverse # => "child_roles"
387
+ #
388
+ # @return [ String ] The cyclic inverse name.
389
+ #
390
+ # @since 2.0.0.rc.1
391
+ def cyclic_inverse
392
+ @cyclic_inverse ||= determine_cyclic_inverse
393
+ end
394
+
395
+ # Determine the cyclic inverse. Performance improvement with the
396
+ # memoization.
397
+ #
398
+ # @example Determine the inverse.
399
+ # metadata.determine_cyclic_inverse
400
+ #
401
+ # @return [ String ] The cyclic inverse name.
402
+ #
403
+ # @since 2.0.0.rc.1
404
+ def determine_cyclic_inverse
405
+ klass.relations.each_pair do |key, meta|
406
+ if key =~ /#{inverse_klass.name.underscore}/ &&
407
+ meta.relation != relation
408
+ return key.to_sym
409
+ end
410
+ end
411
+ end
412
+
413
+ # Determine the value for the relation's foreign key. Performance
414
+ # improvement.
415
+ #
416
+ # @example Determine the foreign key.
417
+ # metadata.determine_foreign_key
418
+ #
419
+ # @return [ String ] The foreign key.
420
+ #
421
+ # @since 2.0.0.rc.1
422
+ def determine_foreign_key
423
+ return self[:foreign_key].to_s if self[:foreign_key]
424
+ suffix = relation.foreign_key_suffix
425
+ if relation.stores_foreign_key?
426
+ if relation.macro == :references_and_referenced_in_many
427
+ name.to_s.singularize << suffix
428
+ else
429
+ name.to_s << suffix
430
+ end
431
+ else
432
+ polymorphic? ? "#{self[:as]}#{suffix}" : inverse_class_name.foreign_key
433
+ end
434
+ end
435
+
436
+ # Determine the inverse relation. Memoizing #inverse_relation and adding
437
+ # this method dropped 5 seconds off the test suite as a performance
438
+ # improvement.
439
+ #
440
+ # @example Determine the inverse.
441
+ # metadata.determine_inverse_relation
442
+ #
443
+ # @return [ Symbol ] The name of the inverse.
444
+ #
445
+ # @since 2.0.0.rc.1
446
+ def determine_inverse_relation
447
+ klass.relations.each_pair do |key, meta|
448
+ if key == inverse_klass.name.underscore ||
449
+ meta.class_name == inverse_class_name
450
+ return key.to_sym
451
+ end
452
+ end
453
+ return nil
454
+ end
455
+
456
+ # Determine the key for the relation in the attributes.
457
+ #
458
+ # @example Get the key.
459
+ # metadata.determine_key
460
+ #
461
+ # @return [ String ] The key in the attributes.
462
+ #
463
+ # @since 2.0.0.rc.1
464
+ def determine_key
465
+ return name.to_s if relation.embedded?
466
+ relation.stores_foreign_key? ? foreign_key : "_id"
467
+ end
468
+
469
+ # Determine the name of the inverse relation.
470
+ #
471
+ # @example Get the inverse name.
472
+ # metadata.inverse_relation
473
+ #
474
+ # @return [ Symbol ] The name of the inverse relation.
475
+ #
476
+ # @since 2.0.0.rc.1
477
+ def inverse_relation
478
+ @inverse_relation ||= determine_inverse_relation
479
+ end
480
+
481
+ # Infer the name of the inverse relation from the class.
482
+ #
483
+ # @example Get the inverse name
484
+ # metadata.inverse_name
485
+ #
486
+ # @return [ String ] The inverse class name underscored.
487
+ #
488
+ # @since 2.0.0.rc.1
489
+ def inverse_name
490
+ @inverse_name ||= inverse_klass.name.underscore
491
+ end
492
+
493
+ # For polymorphic children, we need to figure out the inverse from the
494
+ # actual instance on the other side, since we cannot know the exact class
495
+ # name to infer it from at load time.
496
+ #
497
+ # @example Find the inverse.
498
+ # metadata.lookup_inverse(other)
499
+ #
500
+ # @param [ Document ] : The inverse document.
501
+ #
502
+ # @return [ String ] The inverse name.
503
+ #
504
+ # @since 2.0.0.rc.1
505
+ def lookup_inverse(other)
506
+ return nil unless other
507
+ other.to_a.first.relations.each_pair do |key, meta|
508
+ return meta.name if meta.as == name
509
+ end
510
+ end
511
+
512
+ # Handles two different cases - the first is a convenience for JSON like
513
+ # access to the hash instead of having to call []. The second is a
514
+ # delegation of the "*?" methods to has_key? as a convenience to check
515
+ # for existence of a value.
516
+ #
517
+ # @example Extras provided by this method.
518
+ # metadata.name
519
+ # metadata.name?
520
+ #
521
+ # @param [ Symbol ] name The name of the method.
522
+ # @param [ Array ] args The arguments passed to the method.
523
+ #
524
+ # @return [ Object ] Either the value or a boolen.
525
+ #
526
+ # @since 2.0.0.rc.1
527
+ def method_missing(name, *args)
528
+ method = name.to_s
529
+ method =~ /\?/ ? has_key?(method.sub('?', '').to_sym) : self[name]
530
+ end
531
+ end
532
+ end
533
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This is the superclass for builders that are in charge of handling
6
+ # creation, deletion, and updates of documents through that ever so lovely
7
+ # #accepts_nested_attributes_for.
8
+ class NestedBuilder
9
+ attr_accessor :attributes, :existing, :metadata, :options
10
+
11
+ # Determines if destroys are allowed for this document.
12
+ #
13
+ # @example Do we allow a destroy?
14
+ # builder.allow_destroy?
15
+ #
16
+ # @return [ true, false ] True if the allow destroy option was set.
17
+ #
18
+ # @since 2.0.0.rc.1
19
+ def allow_destroy?
20
+ options[:allow_destroy] || false
21
+ end
22
+
23
+ # Returns the reject if option defined with the macro.
24
+ #
25
+ # @example Is there a reject proc?
26
+ # builder.reject?
27
+ #
28
+ # @param [ Hash ] attrs The attributes to check for rejection.
29
+ #
30
+ # @return [ true, false ] True and call proc if rejectable, false if not.
31
+ #
32
+ # @since 2.0.0.rc.1
33
+ def reject?(attrs)
34
+ criteria = options[:reject_if]
35
+ criteria ? criteria.call(attrs) : false
36
+ end
37
+
38
+ # Determines if only updates can occur. Only valid for one-to-one
39
+ # relations.
40
+ #
41
+ # @example Is this update only?
42
+ # builder.update_only?
43
+ #
44
+ # @return [ true, false ] True if the update_only option was set.
45
+ #
46
+ # @since 2.0.0.rc.1
47
+ def update_only?
48
+ options[:update_only] || false
49
+ end
50
+
51
+ # Convert an id to its appropriate type.
52
+ #
53
+ # @todo Durran: Move this into a common reusable place.
54
+ #
55
+ # @example Convert the id.
56
+ # builder.convert_id("4d371b444835d98b8b000010")
57
+ #
58
+ # @param [ String ] id The id, usually coming from the form.
59
+ #
60
+ # @return [ BSON::ObjectId, String, Object ] The converted id.
61
+ #
62
+ # @since 2.0.0.rc.6
63
+ def convert_id(id)
64
+ metadata.constraint.convert(id)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This is the superclass for one to one relations and defines the common
6
+ # behaviour or those proxies.
7
+ class One < Proxy
8
+
9
+ # Will load the target into an array if the target had not already been
10
+ # loaded.
11
+ #
12
+ # @example Load the relation into memory.
13
+ # relation.load!
14
+ #
15
+ # @return [ One ] The relation.
16
+ #
17
+ # @since 2.0.0.rc.5
18
+ def load!(options = {})
19
+ tap do |relation|
20
+ unless relation.loaded?
21
+ relation.bind(options)
22
+ relation.loaded = true
23
+ end
24
+ end
25
+ end
26
+
27
+ # Substitutes the supplied target documents for the existing document
28
+ # in the relation.
29
+ #
30
+ # @example Substitute the new document.
31
+ # person.name.substitute(new_name)
32
+ #
33
+ # @param [ Document ] other A document to replace the target.
34
+ #
35
+ # @return [ Document, nil ] The relation or nil.
36
+ #
37
+ # @since 2.0.0.rc.1
38
+ def substitute(new_target, options = {})
39
+ old_target = target
40
+ tap do |relation|
41
+ relation.target = new_target
42
+ new_target ? bind(options) : (unbind(old_target, options) and return nil)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This module contains the behaviour for handling polymorphic relational
6
+ # associations.
7
+ module Polymorphic
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :polymorphic
12
+ delegate :polymorphic?, :to => "self.class"
13
+ end
14
+
15
+ module ClassMethods #:nodoc:
16
+
17
+ # Attempts to set up the information needed to handle a polymorphic
18
+ # relation, if the metadata checks out.
19
+ #
20
+ # @example Set up the polymorphic information.
21
+ # Movie.polymorph(metadata)
22
+ #
23
+ # @param [ Metadata ] metadata The relation metadata.
24
+ #
25
+ # @return [ Class ] The class being set up.
26
+ #
27
+ # @since 2.0.0.rc.1
28
+ def polymorph(metadata)
29
+ tap do |klass|
30
+ if metadata.polymorphic?
31
+ klass.polymorphic = true
32
+ if metadata.relation.stores_foreign_key?
33
+ field(metadata.inverse_type, :type => String)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ # Determines if the class is in a polymorphic relations, and thus must
40
+ # store the _type field in the database.
41
+ #
42
+ # @example Check if the class is polymorphic.
43
+ # Movie.polymorphic?
44
+ #
45
+ # @return [ true, false ] True if polymorphic, false if not.
46
+ #
47
+ # @since 2.0.0.rc.1
48
+ def polymorphic?
49
+ !!polymorphic
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end