mongoid-pre 2.0.0.beta1

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 (246) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +24 -0
  3. data/MIT_LICENSE +20 -0
  4. data/README.rdoc +49 -0
  5. data/Rakefile +52 -0
  6. data/VERSION +1 -0
  7. data/caliper.yml +4 -0
  8. data/lib/mongoid.rb +135 -0
  9. data/lib/mongoid/associations.rb +263 -0
  10. data/lib/mongoid/associations/belongs_to_related.rb +59 -0
  11. data/lib/mongoid/associations/embedded_in.rb +64 -0
  12. data/lib/mongoid/associations/embeds_many.rb +193 -0
  13. data/lib/mongoid/associations/embeds_one.rb +95 -0
  14. data/lib/mongoid/associations/has_many_related.rb +133 -0
  15. data/lib/mongoid/associations/has_one_related.rb +81 -0
  16. data/lib/mongoid/associations/meta_data.rb +28 -0
  17. data/lib/mongoid/associations/options.rb +52 -0
  18. data/lib/mongoid/associations/proxy.rb +31 -0
  19. data/lib/mongoid/attributes.rb +185 -0
  20. data/lib/mongoid/callbacks.rb +18 -0
  21. data/lib/mongoid/collection.rb +119 -0
  22. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  23. data/lib/mongoid/collections/master.rb +28 -0
  24. data/lib/mongoid/collections/mimic.rb +46 -0
  25. data/lib/mongoid/collections/operations.rb +41 -0
  26. data/lib/mongoid/collections/slaves.rb +44 -0
  27. data/lib/mongoid/commands.rb +161 -0
  28. data/lib/mongoid/commands/create.rb +19 -0
  29. data/lib/mongoid/commands/delete.rb +16 -0
  30. data/lib/mongoid/commands/delete_all.rb +25 -0
  31. data/lib/mongoid/commands/deletion.rb +18 -0
  32. data/lib/mongoid/commands/destroy.rb +17 -0
  33. data/lib/mongoid/commands/destroy_all.rb +25 -0
  34. data/lib/mongoid/commands/save.rb +30 -0
  35. data/lib/mongoid/components.rb +31 -0
  36. data/lib/mongoid/config.rb +86 -0
  37. data/lib/mongoid/contexts.rb +25 -0
  38. data/lib/mongoid/contexts/enumerable.rb +151 -0
  39. data/lib/mongoid/contexts/ids.rb +25 -0
  40. data/lib/mongoid/contexts/mongo.rb +285 -0
  41. data/lib/mongoid/contexts/paging.rb +42 -0
  42. data/lib/mongoid/criteria.rb +239 -0
  43. data/lib/mongoid/criterion/complex.rb +21 -0
  44. data/lib/mongoid/criterion/exclusion.rb +65 -0
  45. data/lib/mongoid/criterion/inclusion.rb +93 -0
  46. data/lib/mongoid/criterion/optional.rb +136 -0
  47. data/lib/mongoid/cursor.rb +82 -0
  48. data/lib/mongoid/deprecation.rb +22 -0
  49. data/lib/mongoid/dirty.rb +203 -0
  50. data/lib/mongoid/document.rb +306 -0
  51. data/lib/mongoid/errors.rb +77 -0
  52. data/lib/mongoid/extensions.rb +99 -0
  53. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  54. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/mongoid/extensions/array/conversions.rb +27 -0
  57. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  58. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  59. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  60. data/lib/mongoid/extensions/boolean/conversions.rb +16 -0
  61. data/lib/mongoid/extensions/date/conversions.rb +15 -0
  62. data/lib/mongoid/extensions/datetime/conversions.rb +17 -0
  63. data/lib/mongoid/extensions/float/conversions.rb +16 -0
  64. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  65. data/lib/mongoid/extensions/hash/assimilation.rb +30 -0
  66. data/lib/mongoid/extensions/hash/conversions.rb +15 -0
  67. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  68. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  69. data/lib/mongoid/extensions/integer/conversions.rb +16 -0
  70. data/lib/mongoid/extensions/nil/assimilation.rb +13 -0
  71. data/lib/mongoid/extensions/object/conversions.rb +27 -0
  72. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  73. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  74. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  75. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  76. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  77. data/lib/mongoid/extensions/time/conversions.rb +18 -0
  78. data/lib/mongoid/extras.rb +61 -0
  79. data/lib/mongoid/factory.rb +19 -0
  80. data/lib/mongoid/field.rb +52 -0
  81. data/lib/mongoid/fields.rb +62 -0
  82. data/lib/mongoid/finders.rb +136 -0
  83. data/lib/mongoid/identity.rb +39 -0
  84. data/lib/mongoid/indexes.rb +27 -0
  85. data/lib/mongoid/javascript.rb +21 -0
  86. data/lib/mongoid/javascript/functions.yml +37 -0
  87. data/lib/mongoid/matchers.rb +36 -0
  88. data/lib/mongoid/matchers/all.rb +11 -0
  89. data/lib/mongoid/matchers/default.rb +26 -0
  90. data/lib/mongoid/matchers/exists.rb +13 -0
  91. data/lib/mongoid/matchers/gt.rb +11 -0
  92. data/lib/mongoid/matchers/gte.rb +11 -0
  93. data/lib/mongoid/matchers/in.rb +11 -0
  94. data/lib/mongoid/matchers/lt.rb +11 -0
  95. data/lib/mongoid/matchers/lte.rb +11 -0
  96. data/lib/mongoid/matchers/ne.rb +11 -0
  97. data/lib/mongoid/matchers/nin.rb +11 -0
  98. data/lib/mongoid/matchers/size.rb +11 -0
  99. data/lib/mongoid/memoization.rb +27 -0
  100. data/lib/mongoid/named_scope.rb +42 -0
  101. data/lib/mongoid/observable.rb +30 -0
  102. data/lib/mongoid/paths.rb +54 -0
  103. data/lib/mongoid/persistence.rb +27 -0
  104. data/lib/mongoid/persistence/command.rb +20 -0
  105. data/lib/mongoid/persistence/insert.rb +71 -0
  106. data/lib/mongoid/persistence/update.rb +78 -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 +4 -0
  113. data/lib/mongoid/validations/uniqueness.rb +22 -0
  114. data/lib/mongoid/versioning.rb +26 -0
  115. data/mongoid.gemspec +413 -0
  116. data/perf/benchmark.rb +77 -0
  117. data/spec/integration/mongoid/associations_spec.rb +340 -0
  118. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  119. data/spec/integration/mongoid/commands_spec.rb +230 -0
  120. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  121. data/spec/integration/mongoid/criteria_spec.rb +272 -0
  122. data/spec/integration/mongoid/dirty_spec.rb +70 -0
  123. data/spec/integration/mongoid/document_spec.rb +650 -0
  124. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  125. data/spec/integration/mongoid/finders_spec.rb +119 -0
  126. data/spec/integration/mongoid/inheritance_spec.rb +137 -0
  127. data/spec/integration/mongoid/named_scope_spec.rb +46 -0
  128. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  129. data/spec/models/address.rb +39 -0
  130. data/spec/models/animal.rb +6 -0
  131. data/spec/models/callbacks.rb +18 -0
  132. data/spec/models/comment.rb +8 -0
  133. data/spec/models/country_code.rb +6 -0
  134. data/spec/models/employer.rb +5 -0
  135. data/spec/models/game.rb +7 -0
  136. data/spec/models/inheritance.rb +56 -0
  137. data/spec/models/location.rb +5 -0
  138. data/spec/models/mixed_drink.rb +4 -0
  139. data/spec/models/name.rb +13 -0
  140. data/spec/models/namespacing.rb +11 -0
  141. data/spec/models/patient.rb +6 -0
  142. data/spec/models/person.rb +99 -0
  143. data/spec/models/pet.rb +7 -0
  144. data/spec/models/pet_owner.rb +6 -0
  145. data/spec/models/phone.rb +7 -0
  146. data/spec/models/post.rb +15 -0
  147. data/spec/models/translation.rb +5 -0
  148. data/spec/models/vet_visit.rb +5 -0
  149. data/spec/spec.opts +3 -0
  150. data/spec/spec_helper.rb +31 -0
  151. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +145 -0
  152. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  153. data/spec/unit/mongoid/associations/embeds_many_spec.rb +516 -0
  154. data/spec/unit/mongoid/associations/embeds_one_spec.rb +282 -0
  155. data/spec/unit/mongoid/associations/has_many_related_spec.rb +418 -0
  156. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  157. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  158. data/spec/unit/mongoid/associations/options_spec.rb +192 -0
  159. data/spec/unit/mongoid/associations_spec.rb +595 -0
  160. data/spec/unit/mongoid/attributes_spec.rb +507 -0
  161. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  162. data/spec/unit/mongoid/collection_spec.rb +187 -0
  163. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  164. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  165. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  166. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  167. data/spec/unit/mongoid/commands/create_spec.rb +31 -0
  168. data/spec/unit/mongoid/commands/delete_all_spec.rb +59 -0
  169. data/spec/unit/mongoid/commands/delete_spec.rb +38 -0
  170. data/spec/unit/mongoid/commands/destroy_all_spec.rb +21 -0
  171. data/spec/unit/mongoid/commands/destroy_spec.rb +51 -0
  172. data/spec/unit/mongoid/commands/save_spec.rb +107 -0
  173. data/spec/unit/mongoid/commands_spec.rb +270 -0
  174. data/spec/unit/mongoid/config_spec.rb +176 -0
  175. data/spec/unit/mongoid/contexts/enumerable_spec.rb +421 -0
  176. data/spec/unit/mongoid/contexts/mongo_spec.rb +682 -0
  177. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  178. data/spec/unit/mongoid/criteria_spec.rb +824 -0
  179. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  180. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  181. data/spec/unit/mongoid/criterion/inclusion_spec.rb +219 -0
  182. data/spec/unit/mongoid/criterion/optional_spec.rb +319 -0
  183. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  184. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  185. data/spec/unit/mongoid/dirty_spec.rb +286 -0
  186. data/spec/unit/mongoid/document_spec.rb +818 -0
  187. data/spec/unit/mongoid/errors_spec.rb +103 -0
  188. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  189. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  190. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  191. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  192. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +22 -0
  193. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  194. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  195. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  196. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +67 -0
  197. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  198. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  199. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  200. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  201. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  202. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  203. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  204. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  205. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +57 -0
  206. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  207. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  208. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  209. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  210. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  211. data/spec/unit/mongoid/extras_spec.rb +102 -0
  212. data/spec/unit/mongoid/factory_spec.rb +31 -0
  213. data/spec/unit/mongoid/field_spec.rb +143 -0
  214. data/spec/unit/mongoid/fields_spec.rb +181 -0
  215. data/spec/unit/mongoid/finders_spec.rb +404 -0
  216. data/spec/unit/mongoid/identity_spec.rb +109 -0
  217. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  218. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  219. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  220. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  221. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  222. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  223. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  224. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  225. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  226. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  227. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  228. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  229. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  230. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  231. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  232. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  233. data/spec/unit/mongoid/observable_spec.rb +46 -0
  234. data/spec/unit/mongoid/paths_spec.rb +124 -0
  235. data/spec/unit/mongoid/persistence/insert_spec.rb +175 -0
  236. data/spec/unit/mongoid/persistence/update_spec.rb +148 -0
  237. data/spec/unit/mongoid/persistence_spec.rb +40 -0
  238. data/spec/unit/mongoid/scope_spec.rb +240 -0
  239. data/spec/unit/mongoid/state_spec.rb +83 -0
  240. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  241. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  242. data/spec/unit/mongoid/validations/uniqueness_spec.rb +47 -0
  243. data/spec/unit/mongoid/validations_spec.rb +190 -0
  244. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  245. data/spec/unit/mongoid_spec.rb +46 -0
  246. metadata +476 -0
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class BelongsToRelated #:nodoc:
5
+ include 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 +BelongsToRelated+ 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
+ foreign_key.blank? ? nil : new(document, foreign_key, options, target)
32
+ end
33
+
34
+ # Returns the macro used to create the association.
35
+ def macro
36
+ :belongs_to_related
37
+ end
38
+
39
+ # Perform an update of the relationship of the parent and child. This
40
+ # will assimilate the child +Document+ into the parent's object graph.
41
+ #
42
+ # Options:
43
+ #
44
+ # target: The target(parent) object
45
+ # document: The +Document+ to update.
46
+ # options: The association +Options+
47
+ #
48
+ # Example:
49
+ #
50
+ # <tt>BelongsToRelated.update(person, game, options)</tt>
51
+ def update(target, document, options)
52
+ document.send("#{options.foreign_key}=", target ? target.id : nil)
53
+ instantiate(document, options, target)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class EmbeddedIn #:nodoc:
5
+ include Proxy
6
+
7
+ # Creates the new association by setting the internal
8
+ # target as the passed in Document. This should be the
9
+ # parent.
10
+ #
11
+ # All method calls on this object will then be delegated
12
+ # to the internal document itself.
13
+ #
14
+ # Options:
15
+ #
16
+ # target: The parent +Document+
17
+ # options: The association options
18
+ def initialize(target, options)
19
+ @target, @options = target, options
20
+ extends(options)
21
+ end
22
+
23
+ # Returns the parent document. The id param is present for
24
+ # compatibility with rails, however this could be overwritten
25
+ # in the future.
26
+ def find(id)
27
+ @target
28
+ end
29
+
30
+ class << self
31
+ # Creates the new association by setting the internal
32
+ # document as the passed in Document. This should be the
33
+ # parent.
34
+ #
35
+ # Options:
36
+ #
37
+ # document: The parent +Document+
38
+ # options: The association options
39
+ def instantiate(document, options)
40
+ target = document._parent
41
+ target.nil? ? nil : new(target, options)
42
+ end
43
+
44
+ # Returns the macro used to create the association.
45
+ def macro
46
+ :embedded_in
47
+ end
48
+
49
+ # Perform an update of the relationship of the parent and child. This
50
+ # is initialized by setting a parent object as the association on the
51
+ # +Document+. Will properly set an embeds_one or an embeds_many.
52
+ #
53
+ # Returns:
54
+ #
55
+ # A new +EmbeddedIn+ association proxy.
56
+ def update(target, child, options)
57
+ child.parentize(target, options.inverse_of)
58
+ child.notify
59
+ instantiate(child, options)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,193 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class EmbedsMany
5
+ include Proxy
6
+
7
+ attr_accessor :association_name, :klass
8
+
9
+ # Appends the object to the +Array+, setting its parent in
10
+ # the process.
11
+ def <<(*documents)
12
+ documents.flatten.each do |doc|
13
+ doc.parentize(@parent, @association_name)
14
+ @target << doc
15
+ doc.notify
16
+ end
17
+ end
18
+
19
+ alias :concat :<<
20
+ alias :push :<<
21
+
22
+ # Clears the association, and notifies the parents of the removal.
23
+ def clear
24
+ unless @target.empty?
25
+ document = @target.first
26
+ document.notify_observers(document, true)
27
+ @target.clear
28
+ end
29
+ end
30
+
31
+ # Builds a new Document and adds it to the association collection. The
32
+ # document created will be of the same class as the others in the
33
+ # association, and the attributes will be passed into the constructor.
34
+ #
35
+ # Returns:
36
+ #
37
+ # The newly created Document.
38
+ def build(attrs = {}, type = nil)
39
+ document = type ? type.instantiate : @klass.instantiate
40
+ document.parentize(@parent, @association_name)
41
+ document.write_attributes(attrs)
42
+ @target << document
43
+ document
44
+ end
45
+
46
+ # Creates a new Document and adds it to the association collection. The
47
+ # document created will be of the same class as the others in the
48
+ # association, and the attributes will be passed into the constructor and
49
+ # the new object will then be saved.
50
+ #
51
+ # Returns:
52
+ #
53
+ # The newly created Document.
54
+ def create(attrs = {}, type = nil)
55
+ object = build(attrs, type)
56
+ object.run_callbacks(:create) { object.save }; object
57
+ end
58
+
59
+ # Creates a new Document and adds it to the association collection. The
60
+ # document created will be of the same class as the others in the
61
+ # association, and the attributes will be passed into the constructor and
62
+ # the new object will then be saved. If validation fails an error will
63
+ # get raised.
64
+ #
65
+ # Returns:
66
+ #
67
+ # The newly created Document.
68
+ def create!(attrs = {}, type = nil)
69
+ document = create(attrs, type)
70
+ errors = document.errors
71
+ raise Errors::Validations.new(errors) unless errors.empty?
72
+ document
73
+ end
74
+
75
+ # Finds a document in this association.
76
+ #
77
+ # If :all is passed, returns all the documents
78
+ #
79
+ # If an id is passed, will return the document for that id.
80
+ #
81
+ # Returns:
82
+ #
83
+ # Array or single Document.
84
+ def find(param)
85
+ return @target if param == :all
86
+ return detect { |document| document.id == param }
87
+ end
88
+
89
+ # Creates the new association by finding the attributes in
90
+ # the parent document with its name, and instantiating a
91
+ # new document for each one found. These will then be put in an
92
+ # internal array.
93
+ #
94
+ # This then delegated all methods to the array class since this is
95
+ # essentially a proxy to an array itself.
96
+ #
97
+ # Options:
98
+ #
99
+ # parent: The parent document to the association.
100
+ # options: The association options.
101
+ def initialize(parent, options)
102
+ @parent, @association_name = parent, options.name
103
+ @klass, @options = options.klass, options
104
+ initialize_each(parent.raw_attributes[@association_name])
105
+ extends(options)
106
+ end
107
+
108
+ # If the target array does not respond to the supplied method then try to
109
+ # find a named scope or criteria on the class and send the call there.
110
+ #
111
+ # If the method exists on the array, use the default proxy behavior.
112
+ def method_missing(name, *args, &block)
113
+ unless @target.respond_to?(name)
114
+ object = @klass.send(name, *args)
115
+ object.documents = @target
116
+ return object
117
+ end
118
+ super
119
+ end
120
+
121
+ # Used for setting associations via a nested attributes setter from the
122
+ # parent +Document+.
123
+ #
124
+ # Options:
125
+ #
126
+ # attributes: A +Hash+ of integer keys and +Hash+ values.
127
+ #
128
+ # Returns:
129
+ #
130
+ # The newly build target Document.
131
+ def nested_build(attributes)
132
+ attributes.values.each do |attrs|
133
+ build(attrs)
134
+ end
135
+ end
136
+
137
+ # Paginate the association. Will create a new criteria, set the documents
138
+ # on it and execute in an enumerable context.
139
+ #
140
+ # Options:
141
+ #
142
+ # options: A +Hash+ of pagination options.
143
+ #
144
+ # Returns:
145
+ #
146
+ # A +WillPaginate::Collection+.
147
+ def paginate(options)
148
+ criteria = Mongoid::Criteria.translate(@klass, options)
149
+ criteria.documents = @target
150
+ criteria.paginate
151
+ end
152
+
153
+ protected
154
+ # Initializes each of the attributes in the hash.
155
+ def initialize_each(attributes)
156
+ @target = attributes ? attributes.collect do |attrs|
157
+ klass = attrs.klass
158
+ child = klass ? klass.instantiate(attrs) : @klass.instantiate(attrs)
159
+ child.parentize(@parent, @association_name)
160
+ child
161
+ end : []
162
+ end
163
+
164
+ class << self
165
+
166
+ # Preferred method of creating a new +EmbedsMany+ association. It will
167
+ # delegate to new.
168
+ #
169
+ # Options:
170
+ #
171
+ # document: The parent +Document+
172
+ # options: The association options
173
+ def instantiate(document, options)
174
+ new(document, options)
175
+ end
176
+
177
+ # Returns the macro used to create the association.
178
+ def macro
179
+ :embeds_many
180
+ end
181
+
182
+ # Perform an update of the relationship of the parent and child. This
183
+ # is initialized by setting the has_many to the supplied +Enumerable+
184
+ # and setting up the parentization.
185
+ def update(children, parent, options)
186
+ parent.remove_attribute(options.name)
187
+ children.assimilate(parent, options)
188
+ instantiate(parent, options)
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class EmbedsOne #:nodoc:
5
+ include Proxy
6
+
7
+ # Build a new object for the association.
8
+ def build(attrs = {}, type = nil)
9
+ @target = attrs.assimilate(@parent, @options, type); self
10
+ end
11
+
12
+ # Creates the new association by finding the attributes in
13
+ # the parent document with its name, and instantiating a
14
+ # new document for it.
15
+ #
16
+ # All method calls on this object will then be delegated
17
+ # to the internal document itself.
18
+ #
19
+ # Options:
20
+ #
21
+ # document: The parent +Document+
22
+ # attributes: The attributes of the target object.
23
+ # options: The association options.
24
+ #
25
+ # Returns:
26
+ #
27
+ # A new +HashOne+ association proxy.
28
+ def initialize(document, attrs, options)
29
+ @parent, @options = document, options
30
+ @target = attrs.assimilate(@parent, @options, attrs.klass)
31
+ extends(options)
32
+ end
33
+
34
+ # Used for setting the association via a nested attributes setter on the
35
+ # parent +Document+. Called when using accepts_nested_attributes_for.
36
+ #
37
+ # Options:
38
+ #
39
+ # attributes: The attributes for the new association
40
+ #
41
+ # Returns:
42
+ #
43
+ # A new target document.
44
+ def nested_build(attributes)
45
+ build(attributes)
46
+ end
47
+
48
+ class << self
49
+ # Preferred method of instantiating a new +EmbedsOne+, since nil values
50
+ # will be handled properly.
51
+ #
52
+ # Options:
53
+ #
54
+ # document: The parent +Document+
55
+ # options: The association options.
56
+ #
57
+ # Returns:
58
+ #
59
+ # A new +EmbedsOne+ association proxy.
60
+ def instantiate(document, options)
61
+ attributes = document.raw_attributes[options.name]
62
+ return nil if attributes.blank?
63
+ new(document, attributes, options)
64
+ end
65
+
66
+ # Returns the macro used to create the association.
67
+ def macro
68
+ :embeds_one
69
+ end
70
+
71
+ # Perform an update of the relationship of the parent and child. This
72
+ # will assimilate the child +Document+ into the parent's object graph.
73
+ #
74
+ # Options:
75
+ #
76
+ # child: The child +Document+ or +Hash+.
77
+ # parent: The parent +Document+ to update.
78
+ # options: The association +Options+
79
+ #
80
+ # Example:
81
+ #
82
+ # <tt>EmbedsOne.update({:first_name => "Hank"}, person, options)</tt>
83
+ #
84
+ # Returns:
85
+ #
86
+ # A new +EmbedsOne+ association proxy.
87
+ def update(child, parent, options)
88
+ child.assimilate(parent, options)
89
+ instantiate(parent, options)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,133 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Associations #:nodoc:
4
+ class HasManyRelated #:nodoc:
5
+ include Proxy
6
+
7
+ # Appends the object to the +Array+, setting its parent in
8
+ # the process.
9
+ def <<(*objects)
10
+ load_target
11
+ objects.flatten.each do |object|
12
+ object.send("#{@foreign_key}=", @parent.id)
13
+ @target << object
14
+ object.save unless @parent.new_record?
15
+ end
16
+ end
17
+
18
+ # Builds a new Document and adds it to the association collection. The
19
+ # document created will be of the same class as the others in the
20
+ # association, and the attributes will be passed into the constructor.
21
+ #
22
+ # Returns the newly created object.
23
+ def build(attributes = {})
24
+ load_target
25
+ name = @parent.class.to_s.underscore
26
+ object = @klass.instantiate(attributes.merge(name => @parent))
27
+ @target << object
28
+ object
29
+ end
30
+
31
+ # Delegates to <<
32
+ def concat(*objects)
33
+ self << objects
34
+ end
35
+
36
+ # Creates a new Document and adds it to the association collection. The
37
+ # document created will be of the same class as the others in the
38
+ # association, and the attributes will be passed into the constructor and
39
+ # the new object will then be saved.
40
+ #
41
+ # Returns the newly created object.
42
+ def create(attributes)
43
+ object = build(attributes)
44
+ object.run_callbacks(:create) { object.save }; object
45
+ end
46
+
47
+ # Finds a document in this association.
48
+ # If an id is passed, will return the document for that id.
49
+ def find(*args)
50
+ args[1][:conditions].merge!(@foreign_key.to_sym => @parent.id) if args.size > 1
51
+ @klass.find(*args)
52
+ end
53
+
54
+ # Initializing a related association only requires looking up the objects
55
+ # by their ids.
56
+ #
57
+ # Options:
58
+ #
59
+ # document: The +Document+ that contains the relationship.
60
+ # options: The association +Options+.
61
+ def initialize(document, options, target = nil)
62
+ @parent, @klass = document, options.klass
63
+ @foreign_key = options.foreign_key
64
+ @base = lambda { @klass.all(:conditions => { @foreign_key => document.id }) }
65
+ @target = target || @base.call
66
+ extends(options)
67
+ end
68
+
69
+ # Override the default behavior to allow the criteria to get reset on
70
+ # each call into the association.
71
+ #
72
+ # Example:
73
+ #
74
+ # person.posts.where(:title => "New")
75
+ # person.posts # resets the criteria
76
+ #
77
+ # Returns:
78
+ #
79
+ # A Criteria object or Array.
80
+ def method_missing(name, *args, &block)
81
+ @target = @base.call unless @target.is_a?(Array)
82
+ @target.send(name, *args, &block)
83
+ end
84
+
85
+ # Delegates to <<
86
+ def push(*objects)
87
+ self << objects
88
+ end
89
+
90
+ protected
91
+ # Load the target entries if the document is new.
92
+ def load_target
93
+ @target = @target.entries if @parent.new_record?
94
+ end
95
+
96
+ class << self
97
+ # Preferred method for creating the new +HasManyRelated+ association.
98
+ #
99
+ # Options:
100
+ #
101
+ # document: The +Document+ that contains the relationship.
102
+ # options: The association +Options+.
103
+ def instantiate(document, options, target = nil)
104
+ new(document, options, target)
105
+ end
106
+
107
+ # Returns the macro used to create the association.
108
+ def macro
109
+ :has_many_related
110
+ end
111
+
112
+ # Perform an update of the relationship of the parent and child. This
113
+ # will assimilate the child +Document+ into the parent's object graph.
114
+ #
115
+ # Options:
116
+ #
117
+ # related: The related object
118
+ # parent: The parent +Document+ to update.
119
+ # options: The association +Options+
120
+ #
121
+ # Example:
122
+ #
123
+ # <tt>RelatesToOne.update(game, person, options)</tt>
124
+ def update(target, document, options)
125
+ name = document.class.to_s.underscore
126
+ target.each { |child| child.send("#{name}=", document) }
127
+ instantiate(document, options, target)
128
+ end
129
+ end
130
+
131
+ end
132
+ end
133
+ end