mongoid-locomotive 2.0.0.beta9

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 (274) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +47 -0
  3. data/lib/mongoid.rb +141 -0
  4. data/lib/mongoid/associations.rb +306 -0
  5. data/lib/mongoid/associations/embedded_in.rb +74 -0
  6. data/lib/mongoid/associations/embeds_many.rb +280 -0
  7. data/lib/mongoid/associations/embeds_one.rb +97 -0
  8. data/lib/mongoid/associations/foreign_key.rb +35 -0
  9. data/lib/mongoid/associations/meta_data.rb +38 -0
  10. data/lib/mongoid/associations/options.rb +62 -0
  11. data/lib/mongoid/associations/proxy.rb +33 -0
  12. data/lib/mongoid/associations/referenced_in.rb +59 -0
  13. data/lib/mongoid/associations/references_many.rb +245 -0
  14. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  15. data/lib/mongoid/associations/references_one.rb +99 -0
  16. data/lib/mongoid/atomicity.rb +55 -0
  17. data/lib/mongoid/attributes.rb +242 -0
  18. data/lib/mongoid/callbacks.rb +21 -0
  19. data/lib/mongoid/collection.rb +120 -0
  20. data/lib/mongoid/collections.rb +71 -0
  21. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  22. data/lib/mongoid/collections/master.rb +29 -0
  23. data/lib/mongoid/collections/operations.rb +41 -0
  24. data/lib/mongoid/collections/slaves.rb +45 -0
  25. data/lib/mongoid/components.rb +34 -0
  26. data/lib/mongoid/config.rb +263 -0
  27. data/lib/mongoid/contexts.rb +24 -0
  28. data/lib/mongoid/contexts/enumerable.rb +156 -0
  29. data/lib/mongoid/contexts/ids.rb +25 -0
  30. data/lib/mongoid/contexts/mongo.rb +285 -0
  31. data/lib/mongoid/contexts/paging.rb +50 -0
  32. data/lib/mongoid/criteria.rb +248 -0
  33. data/lib/mongoid/criterion/complex.rb +21 -0
  34. data/lib/mongoid/criterion/exclusion.rb +65 -0
  35. data/lib/mongoid/criterion/inclusion.rb +110 -0
  36. data/lib/mongoid/criterion/optional.rb +189 -0
  37. data/lib/mongoid/cursor.rb +81 -0
  38. data/lib/mongoid/deprecation.rb +21 -0
  39. data/lib/mongoid/dirty.rb +252 -0
  40. data/lib/mongoid/document.rb +210 -0
  41. data/lib/mongoid/errors.rb +131 -0
  42. data/lib/mongoid/extensions.rb +115 -0
  43. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  44. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  45. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  46. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  47. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  48. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  49. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  50. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  51. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  52. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  53. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  54. data/lib/mongoid/extensions/hash/accessors.rb +42 -0
  55. data/lib/mongoid/extensions/hash/assimilation.rb +40 -0
  56. data/lib/mongoid/extensions/hash/conversions.rb +42 -0
  57. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  58. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  59. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  60. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  61. data/lib/mongoid/extensions/object/conversions.rb +21 -0
  62. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  63. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  64. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  65. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  66. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  67. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  68. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  69. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  70. data/lib/mongoid/extras.rb +61 -0
  71. data/lib/mongoid/factory.rb +20 -0
  72. data/lib/mongoid/field.rb +83 -0
  73. data/lib/mongoid/fields.rb +62 -0
  74. data/lib/mongoid/finders.rb +145 -0
  75. data/lib/mongoid/hierarchy.rb +74 -0
  76. data/lib/mongoid/identity.rb +47 -0
  77. data/lib/mongoid/indexes.rb +27 -0
  78. data/lib/mongoid/javascript.rb +21 -0
  79. data/lib/mongoid/javascript/functions.yml +37 -0
  80. data/lib/mongoid/logger.rb +19 -0
  81. data/lib/mongoid/matchers.rb +35 -0
  82. data/lib/mongoid/matchers/all.rb +11 -0
  83. data/lib/mongoid/matchers/default.rb +26 -0
  84. data/lib/mongoid/matchers/exists.rb +13 -0
  85. data/lib/mongoid/matchers/gt.rb +11 -0
  86. data/lib/mongoid/matchers/gte.rb +11 -0
  87. data/lib/mongoid/matchers/in.rb +11 -0
  88. data/lib/mongoid/matchers/lt.rb +11 -0
  89. data/lib/mongoid/matchers/lte.rb +11 -0
  90. data/lib/mongoid/matchers/ne.rb +11 -0
  91. data/lib/mongoid/matchers/nin.rb +11 -0
  92. data/lib/mongoid/matchers/size.rb +11 -0
  93. data/lib/mongoid/memoization.rb +33 -0
  94. data/lib/mongoid/named_scope.rb +37 -0
  95. data/lib/mongoid/paranoia.rb +106 -0
  96. data/lib/mongoid/paths.rb +61 -0
  97. data/lib/mongoid/persistence.rb +216 -0
  98. data/lib/mongoid/persistence/command.rb +39 -0
  99. data/lib/mongoid/persistence/insert.rb +48 -0
  100. data/lib/mongoid/persistence/insert_embedded.rb +44 -0
  101. data/lib/mongoid/persistence/remove.rb +39 -0
  102. data/lib/mongoid/persistence/remove_all.rb +38 -0
  103. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  104. data/lib/mongoid/persistence/update.rb +71 -0
  105. data/lib/mongoid/railtie.rb +67 -0
  106. data/lib/mongoid/railties/database.rake +60 -0
  107. data/lib/mongoid/scope.rb +75 -0
  108. data/lib/mongoid/state.rb +32 -0
  109. data/lib/mongoid/timestamps.rb +27 -0
  110. data/lib/mongoid/validations.rb +51 -0
  111. data/lib/mongoid/validations/associated.rb +32 -0
  112. data/lib/mongoid/validations/locale/en.yml +5 -0
  113. data/lib/mongoid/validations/uniqueness.rb +56 -0
  114. data/lib/mongoid/version.rb +4 -0
  115. data/lib/mongoid/versioning.rb +26 -0
  116. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  117. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +24 -0
  118. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  119. data/lib/rails/generators/mongoid/model/templates/model.rb +15 -0
  120. data/lib/rails/generators/mongoid_generator.rb +61 -0
  121. data/spec/integration/mongoid/association_attributes_spec.rb +71 -0
  122. data/spec/integration/mongoid/associations_spec.rb +768 -0
  123. data/spec/integration/mongoid/attributes_spec.rb +59 -0
  124. data/spec/integration/mongoid/callback_spec.rb +33 -0
  125. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  126. data/spec/integration/mongoid/criteria_spec.rb +281 -0
  127. data/spec/integration/mongoid/dirty_spec.rb +85 -0
  128. data/spec/integration/mongoid/document_spec.rb +741 -0
  129. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  130. data/spec/integration/mongoid/finders_spec.rb +119 -0
  131. data/spec/integration/mongoid/inheritance_spec.rb +171 -0
  132. data/spec/integration/mongoid/named_scope_spec.rb +58 -0
  133. data/spec/integration/mongoid/paranoia_spec.rb +44 -0
  134. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  135. data/spec/integration/mongoid/persistence_spec.rb +311 -0
  136. data/spec/integration/mongoid/validations/uniqueness_spec.rb +206 -0
  137. data/spec/models/account.rb +5 -0
  138. data/spec/models/address.rb +40 -0
  139. data/spec/models/agent.rb +7 -0
  140. data/spec/models/animal.rb +15 -0
  141. data/spec/models/answer.rb +4 -0
  142. data/spec/models/callbacks.rb +47 -0
  143. data/spec/models/category.rb +13 -0
  144. data/spec/models/comment.rb +10 -0
  145. data/spec/models/country_code.rb +6 -0
  146. data/spec/models/employer.rb +5 -0
  147. data/spec/models/favorite.rb +8 -0
  148. data/spec/models/game.rb +9 -0
  149. data/spec/models/inheritance.rb +72 -0
  150. data/spec/models/location.rb +5 -0
  151. data/spec/models/login.rb +6 -0
  152. data/spec/models/mixed_drink.rb +4 -0
  153. data/spec/models/name.rb +13 -0
  154. data/spec/models/namespacing.rb +11 -0
  155. data/spec/models/paranoid_post.rb +18 -0
  156. data/spec/models/parents.rb +32 -0
  157. data/spec/models/patient.rb +15 -0
  158. data/spec/models/person.rb +106 -0
  159. data/spec/models/pet.rb +7 -0
  160. data/spec/models/pet_owner.rb +6 -0
  161. data/spec/models/phone.rb +7 -0
  162. data/spec/models/post.rb +25 -0
  163. data/spec/models/preference.rb +7 -0
  164. data/spec/models/question.rb +8 -0
  165. data/spec/models/survey.rb +6 -0
  166. data/spec/models/translation.rb +5 -0
  167. data/spec/models/user.rb +6 -0
  168. data/spec/models/user_accout.rb +5 -0
  169. data/spec/models/vet_visit.rb +5 -0
  170. data/spec/models/video.rb +5 -0
  171. data/spec/spec_helper.rb +33 -0
  172. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  173. data/spec/unit/mongoid/associations/embeds_many_spec.rb +626 -0
  174. data/spec/unit/mongoid/associations/embeds_one_spec.rb +287 -0
  175. data/spec/unit/mongoid/associations/foreign_key_spec.rb +90 -0
  176. data/spec/unit/mongoid/associations/meta_data_spec.rb +110 -0
  177. data/spec/unit/mongoid/associations/options_spec.rb +215 -0
  178. data/spec/unit/mongoid/associations/referenced_in_spec.rb +145 -0
  179. data/spec/unit/mongoid/associations/references_many_as_array_spec.rb +424 -0
  180. data/spec/unit/mongoid/associations/references_many_spec.rb +502 -0
  181. data/spec/unit/mongoid/associations/references_one_spec.rb +204 -0
  182. data/spec/unit/mongoid/associations_spec.rb +688 -0
  183. data/spec/unit/mongoid/atomicity_spec.rb +164 -0
  184. data/spec/unit/mongoid/attributes_spec.rb +646 -0
  185. data/spec/unit/mongoid/callbacks_spec.rb +85 -0
  186. data/spec/unit/mongoid/collection_spec.rb +187 -0
  187. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  188. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  189. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  190. data/spec/unit/mongoid/collections_spec.rb +98 -0
  191. data/spec/unit/mongoid/config_spec.rb +298 -0
  192. data/spec/unit/mongoid/contexts/enumerable_spec.rb +447 -0
  193. data/spec/unit/mongoid/contexts/mongo_spec.rb +703 -0
  194. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  195. data/spec/unit/mongoid/criteria_spec.rb +873 -0
  196. data/spec/unit/mongoid/criterion/complex_spec.rb +17 -0
  197. data/spec/unit/mongoid/criterion/exclusion_spec.rb +121 -0
  198. data/spec/unit/mongoid/criterion/inclusion_spec.rb +274 -0
  199. data/spec/unit/mongoid/criterion/optional_spec.rb +483 -0
  200. data/spec/unit/mongoid/cursor_spec.rb +80 -0
  201. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  202. data/spec/unit/mongoid/dirty_spec.rb +430 -0
  203. data/spec/unit/mongoid/document_spec.rb +623 -0
  204. data/spec/unit/mongoid/errors_spec.rb +154 -0
  205. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  206. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  207. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +52 -0
  208. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  209. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +36 -0
  210. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  211. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  212. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +145 -0
  213. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +14 -0
  214. data/spec/unit/mongoid/extensions/false_class/equality_spec.rb +35 -0
  215. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  216. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  217. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +59 -0
  218. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +35 -0
  219. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  220. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  221. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  222. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +29 -0
  223. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +44 -0
  224. data/spec/unit/mongoid/extensions/objectid/conversions_spec.rb +22 -0
  225. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  226. data/spec/unit/mongoid/extensions/set/conversions_spec.rb +21 -0
  227. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +28 -0
  228. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  229. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +107 -0
  230. data/spec/unit/mongoid/extensions/time_conversions_spec.rb +186 -0
  231. data/spec/unit/mongoid/extensions/true_class/equality_spec.rb +35 -0
  232. data/spec/unit/mongoid/extras_spec.rb +102 -0
  233. data/spec/unit/mongoid/factory_spec.rb +31 -0
  234. data/spec/unit/mongoid/field_spec.rb +169 -0
  235. data/spec/unit/mongoid/fields_spec.rb +181 -0
  236. data/spec/unit/mongoid/finders_spec.rb +439 -0
  237. data/spec/unit/mongoid/hierarchy_spec.rb +68 -0
  238. data/spec/unit/mongoid/identity_spec.rb +109 -0
  239. data/spec/unit/mongoid/indexes_spec.rb +99 -0
  240. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  241. data/spec/unit/mongoid/logger_spec.rb +38 -0
  242. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  243. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  244. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  245. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  246. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  247. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  248. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  249. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  250. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  251. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  252. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  253. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  254. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  255. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  256. data/spec/unit/mongoid/paranoia_spec.rb +108 -0
  257. data/spec/unit/mongoid/paths_spec.rb +272 -0
  258. data/spec/unit/mongoid/persistence/insert_embedded_spec.rb +154 -0
  259. data/spec/unit/mongoid/persistence/insert_spec.rb +144 -0
  260. data/spec/unit/mongoid/persistence/remove_all_spec.rb +82 -0
  261. data/spec/unit/mongoid/persistence/remove_embedded_spec.rb +152 -0
  262. data/spec/unit/mongoid/persistence/remove_spec.rb +89 -0
  263. data/spec/unit/mongoid/persistence/update_spec.rb +177 -0
  264. data/spec/unit/mongoid/persistence_spec.rb +452 -0
  265. data/spec/unit/mongoid/scope_spec.rb +240 -0
  266. data/spec/unit/mongoid/serialization_spec.rb +43 -0
  267. data/spec/unit/mongoid/state_spec.rb +94 -0
  268. data/spec/unit/mongoid/timestamps_spec.rb +30 -0
  269. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  270. data/spec/unit/mongoid/validations/uniqueness_spec.rb +201 -0
  271. data/spec/unit/mongoid/validations_spec.rb +43 -0
  272. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  273. data/spec/unit/mongoid_spec.rb +46 -0
  274. metadata +433 -0
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ module ForeignKey #:nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods #:nodoc:
8
+ # Determine the value for the foreign key constriant field in the
9
+ # database, based on the type of association or if the actual value was
10
+ # supplied as an option.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>contraint(:posts, {}, :references_one)</tt>
15
+ #
16
+ # Returns
17
+ #
18
+ # A +String+ for the foreign key field.
19
+ def constraint(name, options, association)
20
+ key = options[:foreign_key]
21
+
22
+ # Always return the supplied foreign_key option if it was supplied -
23
+ # the user should always be ble to override.
24
+ return key.to_s if key
25
+
26
+ case association
27
+ when :one, :many then self.name.foreign_key
28
+ when :many_as_array then "#{name.to_s.singularize}_ids"
29
+ else name.to_s.foreign_key
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # This class contains metadata about association proxies.
5
+ class MetaData
6
+
7
+ attr_reader :association, :options
8
+
9
+ delegate :macro, :to => :association
10
+
11
+ # Delegate all methods on +Options+ to the options instance.
12
+ Associations::Options.public_instance_methods(false).each do |name|
13
+ define_method(name) { |*args| @options.send(name) }
14
+ end
15
+
16
+ # Return true if this meta data is for an embedded association.
17
+ #
18
+ # Example:
19
+ #
20
+ # <tt>metadata.embedded?</tt>
21
+ def embedded?
22
+ [ EmbedsOne, EmbedsMany ].include?(association)
23
+ end
24
+
25
+ # Create the new associations MetaData object, which holds the type of
26
+ # the association and its options, with convenience methods for getting
27
+ # that information.
28
+ #
29
+ # Options:
30
+ #
31
+ # association: The association type as a class instance.
32
+ # options: The association options
33
+ def initialize(association, options)
34
+ @association, @options = association, options
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class Options #:nodoc:
5
+
6
+ # Create the new +Options+ object, which provides convenience methods for
7
+ # accessing values out of an options +Hash+.
8
+ def initialize(attributes = {})
9
+ @attributes = attributes
10
+ end
11
+
12
+ # Returns the extension if it exists, nil if not.
13
+ def extension
14
+ @attributes[:extend]
15
+ end
16
+
17
+ # Returns true is the options have extensions.
18
+ def extension?
19
+ !extension.nil?
20
+ end
21
+
22
+ # Return the foreign key if it exists, otherwise inflect it from the
23
+ # associated class name.
24
+ def foreign_key
25
+ key = @attributes[:foreign_key] || klass.name.to_s.foreign_key
26
+ key.to_s
27
+ end
28
+
29
+ # Returns the name of the inverse_of association
30
+ def inverse_of
31
+ @attributes[:inverse_of]
32
+ end
33
+
34
+ # Return a +Class+ for the options. See #class_name
35
+ def klass
36
+ class_name.constantize
37
+ end
38
+
39
+ # Return a +String+ representing the associated class_name. If a class_name
40
+ # was provided, then the constantized class_name will be returned. If not,
41
+ # a constant based on the association name will be returned.
42
+ def class_name
43
+ @attributes[:class_name] || name.to_s.classify
44
+ end
45
+
46
+ # Returns the association name of the options.
47
+ def name
48
+ @attributes[:name].to_s
49
+ end
50
+
51
+ # Returns whether or not this association is polymorphic.
52
+ def polymorphic
53
+ @attributes[:polymorphic] == true
54
+ end
55
+
56
+ # Used with references_many to save as array of ids.
57
+ def stored_as
58
+ @attributes[:stored_as]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Associations #:nodoc
4
+ class Proxy #:nodoc
5
+ instance_methods.each do |method|
6
+ undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$)/
7
+ end
8
+ attr_reader \
9
+ :options,
10
+ :target
11
+
12
+ # Default behavior of method missing should be to delegate all calls
13
+ # to the target of the proxy. This can be overridden in special cases.
14
+ def method_missing(name, *args, &block)
15
+ @target.send(name, *args, &block)
16
+ end
17
+
18
+ # If anonymous extensions are added this will take care of them.
19
+ def extends(options)
20
+ extend Module.new(&options.extension) if options.extension?
21
+ end
22
+
23
+ # Sets up the parent, klass, foreign_key, options
24
+ def setup(document, options)
25
+ @parent = document
26
+ @klass = options.klass
27
+ @options = options
28
+ @foreign_key = options.foreign_key
29
+ extends(options)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents a relational association to a "parent" object.
5
+ class ReferencedIn < Proxy
6
+
7
+ # Initializing a related association only requires looking up the object
8
+ # by its id.
9
+ #
10
+ # Options:
11
+ #
12
+ # document: The +Document+ that contains the relationship.
13
+ # options: The association +Options+.
14
+ def initialize(document, foreign_key, options, target = nil)
15
+ @options = options
16
+ @target = target || options.klass.find(foreign_key)
17
+ extends(options)
18
+ end
19
+
20
+ class << self
21
+ # Instantiate a new +ReferencedIn+ or return nil if the foreign key is
22
+ # nil. It is preferrable to use this method over the traditional call
23
+ # to new.
24
+ #
25
+ # Options:
26
+ #
27
+ # document: The +Document+ that contains the relationship.
28
+ # options: The association +Options+.
29
+ def instantiate(document, options, target = nil)
30
+ foreign_key = document.send(options.foreign_key)
31
+ return nil if foreign_key.blank? && target.nil?
32
+ new(document, foreign_key, options, target)
33
+ end
34
+
35
+ # Returns the macro used to create the association.
36
+ def macro
37
+ :referenced_in
38
+ end
39
+
40
+ # Perform an update of the relationship of the parent and child. This
41
+ # will assimilate the child +Document+ into the parent's object graph.
42
+ #
43
+ # Options:
44
+ #
45
+ # target: The target(parent) object
46
+ # document: The +Document+ to update.
47
+ # options: The association +Options+
48
+ #
49
+ # Example:
50
+ #
51
+ # <tt>ReferencedIn.update(person, game, options)</tt>
52
+ def update(target, document, options)
53
+ document.send("#{options.foreign_key}=", target ? target.id : nil)
54
+ instantiate(document, options, target)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,245 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-many association with an object in a
5
+ # separate collection or database.
6
+ class ReferencesMany < Proxy
7
+
8
+ # Appends the object to the +Array+, setting its parent in
9
+ # the process.
10
+ def <<(*objects)
11
+ load_target
12
+ objects.flatten.each do |object|
13
+ object.send("#{@foreign_key}=", @parent.id)
14
+ @target << object
15
+ object.save unless @parent.new_record?
16
+ end
17
+ end
18
+
19
+ alias :concat :<<
20
+ alias :push :<<
21
+
22
+ # Builds a new Document and adds it to the association collection. The
23
+ # document created will be of the same class as the others in the
24
+ # association, and the attributes will be passed into the constructor.
25
+ #
26
+ # Returns the newly created object.
27
+ def build(attributes = nil)
28
+ load_target
29
+ name = determine_name
30
+ object = @klass.instantiate((attributes || {}).merge(name => @parent))
31
+ @target << object
32
+ object
33
+ end
34
+
35
+ # Creates a new Document and adds it to the association collection. The
36
+ # document created will be of the same class as the others in the
37
+ # association, and the attributes will be passed into the constructor and
38
+ # the new object will then be saved.
39
+ #
40
+ # Returns the newly created object.
41
+ def create(attributes)
42
+ build(attributes).tap(&:save)
43
+ end
44
+
45
+ # Creates a new Document and adds it to the association collection. If
46
+ # validation fails an error is raised.
47
+ #
48
+ # Returns the newly created object.
49
+ def create!(attributes)
50
+ build(attributes).tap(&:save!)
51
+ end
52
+
53
+ # Delete all the associated objects.
54
+ #
55
+ # Example:
56
+ #
57
+ # <tt>person.posts.delete_all</tt>
58
+ #
59
+ # Returns:
60
+ #
61
+ # The number of objects deleted.
62
+ def delete_all(conditions = {})
63
+ remove(:delete_all, conditions[:conditions])
64
+ end
65
+
66
+ # Destroy all the associated objects.
67
+ #
68
+ # Example:
69
+ #
70
+ # <tt>person.posts.destroy_all</tt>
71
+ #
72
+ # Returns:
73
+ #
74
+ # The number of objects destroyed.
75
+ def destroy_all(conditions = {})
76
+ remove(:destroy_all, conditions[:conditions])
77
+ end
78
+
79
+ # Finds a document in this association.
80
+ # If an id is passed, will return the document for that id.
81
+ def find(id_or_type, options = {})
82
+ return self.id_criteria(id_or_type) unless id_or_type.is_a?(Symbol)
83
+ options[:conditions] = (options[:conditions] || {}).merge(@foreign_key.to_sym => @parent.id)
84
+ @klass.find(id_or_type, options)
85
+ end
86
+
87
+ # Initializing a related association only requires looking up the objects
88
+ # by their ids.
89
+ #
90
+ # Options:
91
+ #
92
+ # document: The +Document+ that contains the relationship.
93
+ # options: The association +Options+.
94
+ def initialize(document, options, target = nil)
95
+ setup(document, options)
96
+ @target = target || query.call
97
+ end
98
+
99
+ # Override the default behavior to allow the criteria to get reset on
100
+ # each call into the association.
101
+ #
102
+ # Example:
103
+ #
104
+ # person.posts.where(:title => "New")
105
+ # person.posts # resets the criteria
106
+ #
107
+ # Returns:
108
+ #
109
+ # A Criteria object or Array.
110
+ def method_missing(name, *args, &block)
111
+ @target = query.call unless @target.is_a?(Array)
112
+ @target.send(name, *args, &block)
113
+ end
114
+
115
+ # Used for setting associations via a nested attributes setter from the
116
+ # parent +Document+.
117
+ #
118
+ # Options:
119
+ #
120
+ # attributes: A +Hash+ of integer keys and +Hash+ values.
121
+ #
122
+ # Returns:
123
+ #
124
+ # The newly build target Document.
125
+ def nested_build(attributes, options = {})
126
+ attributes.each do |index, attrs|
127
+ begin
128
+ document = find(index.to_i)
129
+ if options && options[:allow_destroy] && attrs['_destroy']
130
+ @target.delete(document)
131
+ document.destroy
132
+ else
133
+ document.write_attributes(attrs)
134
+ end
135
+ rescue Errors::DocumentNotFound
136
+ build(attrs)
137
+ end
138
+ end
139
+ end
140
+
141
+ protected
142
+ # Load the target entries if the parent document is new.
143
+ def load_target
144
+ @target = @target.entries if @parent.new_record?
145
+ end
146
+
147
+ def determine_name
148
+ @proxy ||= class << self; self; end
149
+ @proxy.send(:determine_name, @parent, @options)
150
+ end
151
+
152
+ # The default query used for retrieving the documents from the database.
153
+ # In this case we use the common API between Mongoid, ActiveRecord, and
154
+ # DataMapper so we can do one-to-many relationships with data in other
155
+ # databases.
156
+ #
157
+ # Example:
158
+ #
159
+ # <tt>association.query</tt>
160
+ #
161
+ # Returns:
162
+ #
163
+ # A +Criteria+ if a Mongoid association.
164
+ # An +Array+ of objects if an ActiveRecord association
165
+ # A +Collection+ if a DataMapper association.
166
+ def query
167
+ @query ||= lambda { @klass.all(:conditions => { @foreign_key => @parent.id }) }
168
+ end
169
+
170
+ # Remove the objects based on conditions.
171
+ def remove(method, conditions)
172
+ selector = { @foreign_key => @parent.id }.merge(conditions || {})
173
+ removed = @klass.send(method, :conditions => selector)
174
+ reset; removed
175
+ end
176
+
177
+ # Reset the memoized association on the parent. This will execute the
178
+ # database query again.
179
+ #
180
+ # Example:
181
+ #
182
+ # <tt>association.reset</tt>
183
+ #
184
+ # Returns:
185
+ #
186
+ # See #query rdoc for return values.
187
+ def reset
188
+ @parent.send(:reset, @options.name) { query.call }
189
+ end
190
+
191
+ class << self
192
+ # Preferred method for creating the new +ReferencesMany+ association.
193
+ #
194
+ # Options:
195
+ #
196
+ # document: The +Document+ that contains the relationship.
197
+ # options: The association +Options+.
198
+ def instantiate(document, options, target = nil)
199
+ new(document, options, target)
200
+ end
201
+
202
+ # Returns the macro used to create the association.
203
+ def macro
204
+ :references_many
205
+ end
206
+
207
+ # Perform an update of the relationship of the parent and child. This
208
+ # will assimilate the child +Document+ into the parent's object graph.
209
+ #
210
+ # Options:
211
+ #
212
+ # related: The related object
213
+ # parent: The parent +Document+ to update.
214
+ # options: The association +Options+
215
+ #
216
+ # Example:
217
+ #
218
+ # <tt>RelatesToOne.update(game, person, options)</tt>
219
+ def update(target, document, options)
220
+ name = determine_name(document, options)
221
+ target.each { |child| child.send("#{name}=", document) }
222
+ instantiate(document, options, target)
223
+ end
224
+
225
+ protected
226
+ def determine_name(document, options)
227
+ target = document.class
228
+
229
+ if (inverse = options.inverse_of) && inverse.is_a?(Array)
230
+ inverse = [*inverse].detect { |name| target.respond_to?(name) }
231
+ end
232
+
233
+ if !inverse
234
+ association = options.klass.associations.values.detect do |metadata|
235
+ metadata.options.klass == target
236
+ end
237
+ inverse = association.name if association
238
+ end
239
+
240
+ inverse || target.to_s.underscore
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end