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,78 @@
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, stored as an array of ids on the parent
6
+ # document.
7
+ class ReferencesManyAsArray < ReferencesMany
8
+
9
+ # Append a document to this association. This will also set the appended
10
+ # document's id on the inverse association as well.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>person.preferences << Preference.new(:name => "VGA")</tt>
15
+ def <<(*objects)
16
+ @target = @target.entries
17
+ objects.flatten.each do |object|
18
+ # First set the documents id on the parent array of ids.
19
+ @parent.send(@foreign_key) << object.id
20
+ # Then we need to set the parent's id on the documents array of ids
21
+ # to get the inverse side of the association as well. Note, need a
22
+ # clean way to handle this with new documents - we want to set the
23
+ # actual objects as well, but dont want to get in an infinite loop
24
+ # while doing so.
25
+ object.send(reverse_key(object)) << @parent.id
26
+ @target << object
27
+ end
28
+ end
29
+
30
+ alias :concat :<<
31
+ alias :push :<<
32
+
33
+ # Builds a new Document and adds it to the association collection. The
34
+ # document created will be of the same class as the others in the
35
+ # association, and the attributes will be passed into the constructor.
36
+ #
37
+ # Returns the newly created object.
38
+ def build(attributes = nil)
39
+ load_target
40
+ document = @klass.instantiate(attributes || {})
41
+ push(document); document
42
+ end
43
+
44
+ protected
45
+ # Find the inverse key for the supplied document.
46
+ def reverse_key(document)
47
+ document.send(@options.inverse_of).options.foreign_key
48
+ end
49
+
50
+ # The default query used for retrieving the documents from the database.
51
+ def query
52
+ @query ||= lambda { @klass.any_in(:_id => @parent.send(@foreign_key)) }
53
+ end
54
+
55
+ class << self
56
+ # Perform an update of the relationship of the parent and child. This
57
+ # will assimilate the child +Document+ into the parent's object graph.
58
+ #
59
+ # Options:
60
+ #
61
+ # related: The related object
62
+ # parent: The parent +Document+ to update.
63
+ # options: The association +Options+
64
+ #
65
+ # Example:
66
+ #
67
+ # <tt>RelatesToManyAsArray.update(preferences, person, options)</tt>
68
+ def update(target, document, options)
69
+ target.each do |child|
70
+ name = child.associations[options.inverse_of.to_s].options.name
71
+ child.send(name) << document
72
+ end
73
+ instantiate(document, options, target)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ # Represents an relational one-to-one association with an object in a
5
+ # separate collection or database.
6
+ class ReferencesOne < Proxy
7
+
8
+ delegate :nil?, :to => :target
9
+
10
+ # Builds a new Document and sets it as the association.
11
+ #
12
+ # Returns the newly created object.
13
+ def build(attributes = {})
14
+ @target = @klass.instantiate(attributes)
15
+ inverse = @target.associations.values.detect do |metadata|
16
+ metadata.options.klass == @parent.class
17
+ end
18
+ name = inverse.name
19
+ @target.send("#{name}=", @parent)
20
+ @target
21
+ end
22
+
23
+ # Builds a new Document and sets it as the association, then saves the
24
+ # newly created document.
25
+ #
26
+ # Returns the newly created object.
27
+ def create(attributes)
28
+ build(attributes).tap(&:save)
29
+ end
30
+
31
+ # Initializing a related association only requires looking up the objects
32
+ # by their ids.
33
+ #
34
+ # Options:
35
+ #
36
+ # document: The +Document+ that contains the relationship.
37
+ # options: The association +Options+.
38
+ def initialize(document, options, target = nil)
39
+ @parent, @klass = document, options.klass
40
+ @foreign_key = options.foreign_key
41
+ @target = target || @klass.first(:conditions => { @foreign_key => @parent.id })
42
+ extends(options)
43
+ end
44
+
45
+ # Used for setting the association via a nested attributes setter on the
46
+ # parent +Document+. Called when using accepts_nested_attributes_for.
47
+ #
48
+ # Options:
49
+ #
50
+ # attributes: The attributes for the new association
51
+ #
52
+ # Returns:
53
+ #
54
+ # A new target document.
55
+ def nested_build(attributes, options = nil)
56
+ build(attributes) unless @target.blank? && options[:update_only]
57
+ end
58
+
59
+ class << self
60
+ # Preferred method for creating the new +RelatesToMany+ association.
61
+ #
62
+ # Options:
63
+ #
64
+ # document: The +Document+ that contains the relationship.
65
+ # options: The association +Options+.
66
+ def instantiate(document, options, target = nil)
67
+ new(document, options, target)
68
+ end
69
+
70
+ # Returns the macro used to create the association.
71
+ def macro
72
+ :references_one
73
+ end
74
+
75
+ # Perform an update of the relationship of the parent and child. This
76
+ # will assimilate the child +Document+ into the parent's object graph.
77
+ #
78
+ # Options:
79
+ #
80
+ # related: The related object to update.
81
+ # document: The parent +Document+.
82
+ # options: The association +Options+
83
+ #
84
+ # Example:
85
+ #
86
+ # <tt>HasOneToRelated.update(game, person, options)</tt>
87
+ def update(target, document, options)
88
+ if target
89
+ name = document.class.to_s.underscore
90
+ target.send("#{name}=", document)
91
+ return instantiate(document, options, target)
92
+ end
93
+ target
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Atomicity #:nodoc:
4
+ extend ActiveSupport::Concern
5
+
6
+ # Get all the atomic updates that need to happen for the current
7
+ # +Document+. This includes all changes that need to happen in the
8
+ # entire hierarchy that exists below where the save call was made.
9
+ #
10
+ # Example:
11
+ #
12
+ # <tt>person.save</tt> # Saves entire tree
13
+ #
14
+ # Returns:
15
+ #
16
+ # A +Hash+ of all atomic updates that need to occur.
17
+ def _updates
18
+ processed = {}
19
+
20
+ _children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
21
+ changes = child._sets
22
+ updates["$set"].update(changes)
23
+ processed[child.class] = true unless changes.empty?
24
+
25
+ target = processed.has_key?(child.class) ? :other : "$pushAll"
26
+
27
+ child._pushes.each do |attr, val|
28
+ if updates[target].has_key?(attr)
29
+ updates[target][attr] << val
30
+ else
31
+ updates[target].update({attr => [val]})
32
+ end
33
+ end
34
+ updates
35
+ end.delete_if do |key, value|
36
+ value.empty?
37
+ end
38
+ end
39
+
40
+ protected
41
+ # Get all the push attributes that need to occur.
42
+ def _pushes
43
+ (new_record? && embedded_many? && !_parent.new_record?) ? { _path => raw_attributes } : {}
44
+ end
45
+
46
+ # Get all the attributes that need to be set.
47
+ def _sets
48
+ if changed? && !new_record?
49
+ setters
50
+ else
51
+ embedded_one? && new_record? ? { _path => raw_attributes } : {}
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,242 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Attributes
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ class_inheritable_accessor :_protected_fields
7
+ self._protected_fields = []
8
+ end
9
+
10
+ # Get the id associated with this object. This will pull the _id value out
11
+ # of the attributes +Hash+.
12
+ def id
13
+ @attributes["_id"]
14
+ end
15
+
16
+ # Set the id of the +Document+ to a new one.
17
+ def id=(new_id)
18
+ @attributes["_id"] = new_id
19
+ end
20
+
21
+ alias :_id :id
22
+ alias :_id= :id=
23
+
24
+ # Used for allowing accessor methods for dynamic attributes.
25
+ def method_missing(name, *args)
26
+ attr = name.to_s
27
+ return super unless @attributes.has_key?(attr.reader)
28
+ if attr.writer?
29
+ # "args.size > 1" allows to simulate 1.8 behavior of "*args"
30
+ write_attribute(attr.reader, (args.size > 1) ? args : args.first)
31
+ else
32
+ read_attribute(attr.reader)
33
+ end
34
+ end
35
+
36
+ # Process the provided attributes casting them to their proper values if a
37
+ # field exists for them on the +Document+. This will be limited to only the
38
+ # attributes provided in the suppied +Hash+ so that no extra nil values get
39
+ # put into the document's attributes.
40
+ def process(attrs = nil)
41
+ (attrs || {}).each_pair do |key, value|
42
+ if set_allowed?(key)
43
+ write_attribute(key, value)
44
+ elsif write_allowed?(key)
45
+ if associations.include?(key.to_s) and associations[key.to_s].embedded? and value.is_a?(Hash)
46
+ if association = send(key)
47
+ association.nested_build(value)
48
+ else
49
+ send("build_#{key}", value)
50
+ end
51
+ else
52
+ send("#{key}=", value)
53
+ end
54
+ end
55
+ end
56
+ setup_modifications
57
+ end
58
+
59
+ # Read a value from the +Document+ attributes. If the value does not exist
60
+ # it will return nil.
61
+ #
62
+ # Options:
63
+ #
64
+ # name: The name of the attribute to get.
65
+ #
66
+ # Example:
67
+ #
68
+ # <tt>person.read_attribute(:title)</tt>
69
+ def read_attribute(name)
70
+ access = name.to_s
71
+ value = @attributes[access]
72
+ typed_value = fields.has_key?(access) ? fields[access].get(value) : value
73
+ accessed(access, typed_value)
74
+ end
75
+
76
+ # Remove a value from the +Document+ attributes. If the value does not exist
77
+ # it will fail gracefully.
78
+ #
79
+ # Options:
80
+ #
81
+ # name: The name of the attribute to remove.
82
+ #
83
+ # Example:
84
+ #
85
+ # <tt>person.remove_attribute(:title)</tt>
86
+ def remove_attribute(name)
87
+ access = name.to_s
88
+ modify(access, @attributes.delete(name.to_s), nil)
89
+ end
90
+
91
+ # Returns true when attribute is present.
92
+ #
93
+ # Options:
94
+ #
95
+ # name: The name of the attribute to request presence on.
96
+ def attribute_present?(name)
97
+ value = read_attribute(name)
98
+ !value.blank?
99
+ end
100
+
101
+ # Returns the object type. This corresponds to the name of the class that
102
+ # this +Document+ is, which is used in determining the class to
103
+ # instantiate in various cases.
104
+ def _type
105
+ @attributes["_type"]
106
+ end
107
+
108
+ # Set the type of the +Document+. This should be the name of the class.
109
+ def _type=(new_type)
110
+ @attributes["_type"] = new_type
111
+ end
112
+
113
+ # Write a single attribute to the +Document+ attribute +Hash+. This will
114
+ # also fire the before and after update callbacks, and perform any
115
+ # necessary typecasting.
116
+ #
117
+ # Options:
118
+ #
119
+ # name: The name of the attribute to update.
120
+ # value: The value to set for the attribute.
121
+ #
122
+ # Example:
123
+ #
124
+ # <tt>person.write_attribute(:title, "Mr.")</tt>
125
+ #
126
+ # This will also cause the observing +Document+ to notify it's parent if
127
+ # there is any.
128
+ def write_attribute(name, value)
129
+ access = name.to_s
130
+ typed_value = fields.has_key?(access) ? fields[access].set(value) : value
131
+ modify(access, @attributes[access], typed_value)
132
+ notify if !id.blank? && new_record?
133
+ end
134
+
135
+ # Writes the supplied attributes +Hash+ to the +Document+. This will only
136
+ # overwrite existing attributes if they are present in the new +Hash+, all
137
+ # others will be preserved.
138
+ #
139
+ # Options:
140
+ #
141
+ # attrs: The +Hash+ of new attributes to set on the +Document+
142
+ #
143
+ # Example:
144
+ #
145
+ # <tt>person.write_attributes(:title => "Mr.")</tt>
146
+ #
147
+ # This will also cause the observing +Document+ to notify it's parent if
148
+ # there is any.
149
+ def write_attributes(attrs = nil)
150
+ process(attrs || {})
151
+ identified = !id.blank?
152
+ if new_record? && !identified
153
+ identify; notify
154
+ end
155
+ end
156
+ alias :attributes= :write_attributes
157
+
158
+ protected
159
+ # apply default values to attributes - calling procs as required
160
+ def default_attributes
161
+ default_values = defaults
162
+ default_values.each_pair do |key, val|
163
+ default_values[key] = val.call if val.respond_to?(:call)
164
+ end
165
+ default_values || {}
166
+ end
167
+
168
+ # Return true if dynamic field setting is enabled.
169
+ def set_allowed?(key)
170
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
171
+ end
172
+
173
+ # Used when supplying a :reject_if block as an option to
174
+ # accepts_nested_attributes_for
175
+ def reject(attributes, options)
176
+ rejector = options[:reject_if]
177
+ if rejector
178
+ attributes.delete_if do |key, value|
179
+ rejector.call(value)
180
+ end
181
+ end
182
+ end
183
+
184
+ # Used when supplying a :limit as an option to accepts_nested_attributes_for
185
+ def limit(attributes, name, options)
186
+ if options[:limit] && attributes.size > options[:limit]
187
+ raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit])
188
+ end
189
+ end
190
+
191
+ # Return true if writing to the given field is allowed
192
+ def write_allowed?(key)
193
+ name = key.to_s
194
+ !self._protected_fields.include?(name)
195
+ end
196
+
197
+ module ClassMethods
198
+ # Defines attribute setters for the associations specified by the names.
199
+ # This will work for a has one or has many association.
200
+ #
201
+ # Example:
202
+ #
203
+ # class Person
204
+ # include Mongoid::Document
205
+ # embeds_one :name
206
+ # embeds_many :addresses
207
+ #
208
+ # accepts_nested_attributes_for :name, :addresses
209
+ # end
210
+ def accepts_nested_attributes_for(*args)
211
+ associations = args.flatten
212
+ options = associations.last.is_a?(Hash) ? associations.pop : {}
213
+ associations.each do |name|
214
+ define_method("#{name}_attributes=") do |attrs|
215
+ reject(attrs, options)
216
+ limit(attrs, name, options)
217
+ association = send(name)
218
+ if association
219
+ # observe(association, true)
220
+ association.nested_build(attrs, options)
221
+ else
222
+ send("build_#{name}", attrs, options)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ # Defines fields that cannot be set via mass assignment.
229
+ #
230
+ # Example:
231
+ #
232
+ # class Person
233
+ # include Mongoid::Document
234
+ # field :security_code
235
+ # attr_protected :security_code
236
+ # end
237
+ def attr_protected(*names)
238
+ _protected_fields.concat(names.flatten.map(&:to_s))
239
+ end
240
+ end
241
+ end
242
+ end