stonegao-mongoid 2.0.0.rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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