mongoid-braxton 2.0.2

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 (226) 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 +41 -0
  5. data/lib/config/locales/de.yml +41 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +41 -0
  8. data/lib/config/locales/fr.yml +42 -0
  9. data/lib/config/locales/hu.yml +44 -0
  10. data/lib/config/locales/id.yml +46 -0
  11. data/lib/config/locales/it.yml +39 -0
  12. data/lib/config/locales/ja.yml +40 -0
  13. data/lib/config/locales/kr.yml +65 -0
  14. data/lib/config/locales/nl.yml +39 -0
  15. data/lib/config/locales/pl.yml +39 -0
  16. data/lib/config/locales/pt-BR.yml +40 -0
  17. data/lib/config/locales/pt.yml +40 -0
  18. data/lib/config/locales/ro.yml +46 -0
  19. data/lib/config/locales/ru.yml +41 -0
  20. data/lib/config/locales/sv.yml +40 -0
  21. data/lib/config/locales/vi.yml +45 -0
  22. data/lib/config/locales/zh-CN.yml +33 -0
  23. data/lib/mongoid.rb +140 -0
  24. data/lib/mongoid/atomicity.rb +111 -0
  25. data/lib/mongoid/attributes.rb +185 -0
  26. data/lib/mongoid/attributes/processing.rb +145 -0
  27. data/lib/mongoid/callbacks.rb +23 -0
  28. data/lib/mongoid/collection.rb +137 -0
  29. data/lib/mongoid/collections.rb +71 -0
  30. data/lib/mongoid/collections/master.rb +37 -0
  31. data/lib/mongoid/collections/operations.rb +42 -0
  32. data/lib/mongoid/collections/retry.rb +39 -0
  33. data/lib/mongoid/components.rb +45 -0
  34. data/lib/mongoid/config.rb +349 -0
  35. data/lib/mongoid/config/database.rb +167 -0
  36. data/lib/mongoid/config/replset_database.rb +78 -0
  37. data/lib/mongoid/contexts.rb +19 -0
  38. data/lib/mongoid/contexts/enumerable.rb +275 -0
  39. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  40. data/lib/mongoid/contexts/mongo.rb +345 -0
  41. data/lib/mongoid/copyable.rb +46 -0
  42. data/lib/mongoid/criteria.rb +357 -0
  43. data/lib/mongoid/criterion/builder.rb +34 -0
  44. data/lib/mongoid/criterion/complex.rb +34 -0
  45. data/lib/mongoid/criterion/creational.rb +34 -0
  46. data/lib/mongoid/criterion/exclusion.rb +108 -0
  47. data/lib/mongoid/criterion/inclusion.rb +198 -0
  48. data/lib/mongoid/criterion/inspection.rb +22 -0
  49. data/lib/mongoid/criterion/optional.rb +193 -0
  50. data/lib/mongoid/criterion/selector.rb +143 -0
  51. data/lib/mongoid/criterion/unconvertable.rb +20 -0
  52. data/lib/mongoid/cursor.rb +86 -0
  53. data/lib/mongoid/default_scope.rb +36 -0
  54. data/lib/mongoid/dirty.rb +253 -0
  55. data/lib/mongoid/document.rb +284 -0
  56. data/lib/mongoid/errors.rb +13 -0
  57. data/lib/mongoid/errors/document_not_found.rb +29 -0
  58. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  59. data/lib/mongoid/errors/invalid_database.rb +20 -0
  60. data/lib/mongoid/errors/invalid_field.rb +19 -0
  61. data/lib/mongoid/errors/invalid_options.rb +16 -0
  62. data/lib/mongoid/errors/invalid_type.rb +26 -0
  63. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  64. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  65. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  66. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  67. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  68. data/lib/mongoid/errors/validations.rb +24 -0
  69. data/lib/mongoid/extensions.rb +123 -0
  70. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  71. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  72. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  74. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  75. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  76. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  77. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  78. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  79. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  80. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  81. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  84. data/lib/mongoid/extensions/object/checks.rb +32 -0
  85. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  86. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  87. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  88. data/lib/mongoid/extensions/object_id/conversions.rb +96 -0
  89. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  90. data/lib/mongoid/extensions/range/conversions.rb +25 -0
  91. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  92. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  93. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  94. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  95. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  96. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  97. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  98. data/lib/mongoid/extras.rb +42 -0
  99. data/lib/mongoid/factory.rb +37 -0
  100. data/lib/mongoid/field.rb +162 -0
  101. data/lib/mongoid/fields.rb +183 -0
  102. data/lib/mongoid/finders.rb +127 -0
  103. data/lib/mongoid/hierarchy.rb +85 -0
  104. data/lib/mongoid/identity.rb +92 -0
  105. data/lib/mongoid/indexes.rb +38 -0
  106. data/lib/mongoid/inspection.rb +54 -0
  107. data/lib/mongoid/javascript.rb +21 -0
  108. data/lib/mongoid/javascript/functions.yml +37 -0
  109. data/lib/mongoid/json.rb +16 -0
  110. data/lib/mongoid/keys.rb +131 -0
  111. data/lib/mongoid/logger.rb +18 -0
  112. data/lib/mongoid/matchers.rb +32 -0
  113. data/lib/mongoid/matchers/all.rb +11 -0
  114. data/lib/mongoid/matchers/default.rb +70 -0
  115. data/lib/mongoid/matchers/exists.rb +13 -0
  116. data/lib/mongoid/matchers/gt.rb +11 -0
  117. data/lib/mongoid/matchers/gte.rb +11 -0
  118. data/lib/mongoid/matchers/in.rb +11 -0
  119. data/lib/mongoid/matchers/lt.rb +11 -0
  120. data/lib/mongoid/matchers/lte.rb +11 -0
  121. data/lib/mongoid/matchers/ne.rb +11 -0
  122. data/lib/mongoid/matchers/nin.rb +11 -0
  123. data/lib/mongoid/matchers/or.rb +30 -0
  124. data/lib/mongoid/matchers/size.rb +11 -0
  125. data/lib/mongoid/matchers/strategies.rb +63 -0
  126. data/lib/mongoid/multi_database.rb +11 -0
  127. data/lib/mongoid/multi_parameter_attributes.rb +82 -0
  128. data/lib/mongoid/named_scope.rb +137 -0
  129. data/lib/mongoid/nested_attributes.rb +51 -0
  130. data/lib/mongoid/observer.rb +67 -0
  131. data/lib/mongoid/paranoia.rb +103 -0
  132. data/lib/mongoid/paths.rb +61 -0
  133. data/lib/mongoid/persistence.rb +240 -0
  134. data/lib/mongoid/persistence/atomic.rb +88 -0
  135. data/lib/mongoid/persistence/atomic/add_to_set.rb +32 -0
  136. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  137. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  139. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  140. data/lib/mongoid/persistence/command.rb +71 -0
  141. data/lib/mongoid/persistence/insert.rb +53 -0
  142. data/lib/mongoid/persistence/insert_embedded.rb +43 -0
  143. data/lib/mongoid/persistence/remove.rb +44 -0
  144. data/lib/mongoid/persistence/remove_all.rb +40 -0
  145. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  146. data/lib/mongoid/persistence/update.rb +77 -0
  147. data/lib/mongoid/railtie.rb +139 -0
  148. data/lib/mongoid/railties/database.rake +171 -0
  149. data/lib/mongoid/railties/document.rb +12 -0
  150. data/lib/mongoid/relations.rb +107 -0
  151. data/lib/mongoid/relations/accessors.rb +175 -0
  152. data/lib/mongoid/relations/auto_save.rb +34 -0
  153. data/lib/mongoid/relations/binding.rb +26 -0
  154. data/lib/mongoid/relations/bindings.rb +9 -0
  155. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  156. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  157. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  158. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  159. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  160. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +103 -0
  161. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  162. data/lib/mongoid/relations/builder.rb +42 -0
  163. data/lib/mongoid/relations/builders.rb +79 -0
  164. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  165. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  166. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  167. data/lib/mongoid/relations/builders/nested_attributes/many.rb +126 -0
  168. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  169. data/lib/mongoid/relations/builders/referenced/in.rb +29 -0
  170. data/lib/mongoid/relations/builders/referenced/many.rb +47 -0
  171. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  172. data/lib/mongoid/relations/builders/referenced/one.rb +27 -0
  173. data/lib/mongoid/relations/cascading.rb +55 -0
  174. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  175. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  176. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  177. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  178. data/lib/mongoid/relations/constraint.rb +42 -0
  179. data/lib/mongoid/relations/cyclic.rb +103 -0
  180. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  181. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  182. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  183. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  184. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  185. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  186. data/lib/mongoid/relations/embedded/in.rb +173 -0
  187. data/lib/mongoid/relations/embedded/many.rb +499 -0
  188. data/lib/mongoid/relations/embedded/one.rb +170 -0
  189. data/lib/mongoid/relations/macros.rb +310 -0
  190. data/lib/mongoid/relations/many.rb +215 -0
  191. data/lib/mongoid/relations/metadata.rb +539 -0
  192. data/lib/mongoid/relations/nested_builder.rb +68 -0
  193. data/lib/mongoid/relations/one.rb +47 -0
  194. data/lib/mongoid/relations/polymorphic.rb +54 -0
  195. data/lib/mongoid/relations/proxy.rb +143 -0
  196. data/lib/mongoid/relations/referenced/batch.rb +71 -0
  197. data/lib/mongoid/relations/referenced/batch/insert.rb +57 -0
  198. data/lib/mongoid/relations/referenced/in.rb +216 -0
  199. data/lib/mongoid/relations/referenced/many.rb +516 -0
  200. data/lib/mongoid/relations/referenced/many_to_many.rb +396 -0
  201. data/lib/mongoid/relations/referenced/one.rb +222 -0
  202. data/lib/mongoid/relations/reflections.rb +45 -0
  203. data/lib/mongoid/safe.rb +23 -0
  204. data/lib/mongoid/safety.rb +207 -0
  205. data/lib/mongoid/scope.rb +31 -0
  206. data/lib/mongoid/serialization.rb +99 -0
  207. data/lib/mongoid/sharding.rb +51 -0
  208. data/lib/mongoid/state.rb +67 -0
  209. data/lib/mongoid/timestamps.rb +14 -0
  210. data/lib/mongoid/timestamps/created.rb +31 -0
  211. data/lib/mongoid/timestamps/updated.rb +33 -0
  212. data/lib/mongoid/validations.rb +124 -0
  213. data/lib/mongoid/validations/associated.rb +44 -0
  214. data/lib/mongoid/validations/referenced.rb +58 -0
  215. data/lib/mongoid/validations/uniqueness.rb +85 -0
  216. data/lib/mongoid/version.rb +4 -0
  217. data/lib/mongoid/versioning.rb +113 -0
  218. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  219. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +20 -0
  220. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  221. data/lib/rails/generators/mongoid/model/templates/model.rb +19 -0
  222. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  223. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  224. data/lib/rails/generators/mongoid_generator.rb +70 -0
  225. data/lib/rails/mongoid.rb +58 -0
  226. metadata +406 -0
@@ -0,0 +1,216 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Referenced #:nodoc:
5
+
6
+ # This class handles all behaviour for relations that are either
7
+ # one-to-many or one-to-one, where the foreign key is store on this side
8
+ # of the relation and the reference is to document(s) in another
9
+ # collection.
10
+ class In < Relations::One
11
+
12
+ # Binds the base object to the inverse of the relation. This is so we
13
+ # are referenced to the actual objects themselves and dont hit the
14
+ # database twice when setting the relations up.
15
+ #
16
+ # This is called after first creating the relation, or if a new object
17
+ # is set on the relation.
18
+ #
19
+ # @example Bind the relation.
20
+ # game.person.bind
21
+ #
22
+ # @param [ Hash ] options The options to bind with.
23
+ #
24
+ # @option options [ true, false ] :binding Are we in build mode?
25
+ # @option options [ true, false ] :continue Continue binding the
26
+ # inverse?
27
+ #
28
+ # @since 2.0.0.rc.1
29
+ def bind(options = {})
30
+ binding.bind(options)
31
+ end
32
+
33
+ # Instantiate a new referenced_in relation.
34
+ #
35
+ # @example Create the new relation.
36
+ # Referenced::In.new(game, person, metadata)
37
+ #
38
+ # @param [ Document ] base The document this relation hangs off of.
39
+ # @param [ Document, Array<Document> ] target The target (parent) of the
40
+ # relation.
41
+ # @param [ Metadata ] metadata The relation's metadata.
42
+ def initialize(base, target, metadata)
43
+ init(base, target, metadata) do
44
+ characterize_one(target)
45
+ end
46
+ end
47
+
48
+ # Substitutes the supplied target documents for the existing document
49
+ # in the relation.
50
+ #
51
+ # @example Substitute the relation.
52
+ # name.substitute(new_name)
53
+ #
54
+ # @param [ Document, Array<Document> ] new_target The replacement.
55
+ # @param [ true, false ] building Are we in build mode?
56
+ #
57
+ # @return [ In, nil ] The relation or nil.
58
+ #
59
+ # @since 2.0.0.rc.1
60
+ def substitute(new_target, options = {})
61
+ old_target = target
62
+ tap do |relation|
63
+ relation.target = new_target
64
+ if new_target
65
+ bind(options)
66
+ else
67
+ unbind(old_target, options)
68
+ nil
69
+ end
70
+ end
71
+ end
72
+
73
+ # Unbinds the base object to the inverse of the relation. This occurs
74
+ # when setting a side of the relation to nil.
75
+ #
76
+ # @example Unbind the relation.
77
+ # game.person.unbind
78
+ #
79
+ # @param [ Document, Array<Document> ] old_target The previous target.
80
+ # @param [ Hash ] options The options to bind with.
81
+ #
82
+ # @option options [ true, false ] :binding Are we in build mode?
83
+ # @option options [ true, false ] :continue Continue binding the
84
+ # inverse?
85
+ #
86
+ # @since 2.0.0.rc.1
87
+ def unbind(old_target, options = {})
88
+ binding(old_target).unbind(options)
89
+ end
90
+
91
+ private
92
+
93
+ # Instantiate the binding associated with this relation.
94
+ #
95
+ # @example Get the binding object.
96
+ # binding([ address ])
97
+ #
98
+ # @param [ Document, Array<Document> ] new_target The replacement.
99
+ #
100
+ # @return [ Binding ] The binding object.
101
+ #
102
+ # @since 2.0.0.rc.1
103
+ def binding(new_target = nil)
104
+ Bindings::Referenced::In.new(base, new_target || target, metadata)
105
+ end
106
+
107
+ class << self
108
+
109
+ # Return the builder that is responsible for generating the documents
110
+ # that will be used by this relation.
111
+ #
112
+ # @example Get the builder.
113
+ # Referenced::In.builder(meta, object)
114
+ #
115
+ # @param [ Metadata ] meta The metadata of the relation.
116
+ # @param [ Document, Hash ] object A document or attributes to build
117
+ # with.
118
+ #
119
+ # @return [ Builder ] A new builder object.
120
+ #
121
+ # @since 2.0.0.rc.1
122
+ def builder(meta, object)
123
+ Builders::Referenced::In.new(meta, object)
124
+ end
125
+
126
+ # Returns true if the relation is an embedded one. In this case
127
+ # always false.
128
+ #
129
+ # @example Is this relation embedded?
130
+ # Referenced::In.embedded?
131
+ #
132
+ # @return [ false ] Always false.
133
+ #
134
+ # @since 2.0.0.rc.1
135
+ def embedded?
136
+ false
137
+ end
138
+
139
+ # Get the default value for the foreign key.
140
+ #
141
+ # @example Get the default.
142
+ # Referenced::In.foreign_key_default
143
+ #
144
+ # @return [ nil ] Always nil.
145
+ #
146
+ # @since 2.0.0.rc.1
147
+ def foreign_key_default
148
+ nil
149
+ end
150
+
151
+ # Returns the suffix of the foreign key field, either "_id" or "_ids".
152
+ #
153
+ # @example Get the suffix for the foreign key.
154
+ # Referenced::In.foreign_key_suffix
155
+ #
156
+ # @return [ String ] "_id"
157
+ #
158
+ # @since 2.0.0.rc.1
159
+ def foreign_key_suffix
160
+ "_id"
161
+ end
162
+
163
+ # Returns the macro for this relation. Used mostly as a helper in
164
+ # reflection.
165
+ #
166
+ # @example Get the macro.
167
+ # Referenced::In.macro
168
+ #
169
+ # @return [ Symbol ] :referenced_in
170
+ def macro
171
+ :referenced_in
172
+ end
173
+
174
+ # Return the nested builder that is responsible for generating the documents
175
+ # that will be used by this relation.
176
+ #
177
+ # @example Get the nested builder.
178
+ # Referenced::In.builder(attributes, options)
179
+ #
180
+ # @param [ Metadata ] metadata The relation metadata.
181
+ # @param [ Hash ] attributes The attributes to build with.
182
+ # @param [ Hash ] options The options for the builder.
183
+ #
184
+ # @option options [ true, false ] :allow_destroy Can documents be
185
+ # deleted?
186
+ # @option options [ Integer ] :limit Max number of documents to
187
+ # create at once.
188
+ # @option options [ Proc, Symbol ] :reject_if If documents match this
189
+ # option then they are ignored.
190
+ # @option options [ true, false ] :update_only Only existing documents
191
+ # can be modified.
192
+ #
193
+ # @return [ NestedBuilder ] A newly instantiated nested builder object.
194
+ #
195
+ # @since 2.0.0.rc.1
196
+ def nested_builder(metadata, attributes, options)
197
+ Builders::NestedAttributes::One.new(metadata, attributes, options)
198
+ end
199
+
200
+ # Tells the caller if this relation is one that stores the foreign
201
+ # key on its own objects.
202
+ #
203
+ # @example Does this relation store a foreign key?
204
+ # Referenced::In.stores_foreign_key?
205
+ #
206
+ # @return [ true ] Always true.
207
+ #
208
+ # @since 2.0.0.rc.1
209
+ def stores_foreign_key?
210
+ true
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,516 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Referenced #:nodoc:
5
+
6
+ # This class defines the behaviour for all relations that are a
7
+ # one-to-many between documents in different collections.
8
+ class Many < Relations::Many
9
+ include Batch
10
+
11
+ # Appends a document or array of documents to the relation. Will set
12
+ # the parent and update the index in the process.
13
+ #
14
+ # @example Append a document.
15
+ # person.posts << post
16
+ #
17
+ # @example Push a document.
18
+ # person.posts.push(post)
19
+ #
20
+ # @example Concat with other documents.
21
+ # person.posts.concat([ post_one, post_two ])
22
+ #
23
+ # @param [ Document, Array<Document> ] *args Any number of documents.
24
+ def <<(*args)
25
+ options = default_options(args)
26
+ batched do
27
+ args.flatten.each do |doc|
28
+ return doc unless doc
29
+ append(doc, options)
30
+ doc.save if base.persisted? && !options[:binding]
31
+ end
32
+ end
33
+ end
34
+ alias :concat :<<
35
+ alias :push :<<
36
+
37
+ # Binds the base object to the inverse of the relation. This is so we
38
+ # are referenced to the actual objects themselves and dont hit the
39
+ # database twice when setting the relations up.
40
+ #
41
+ # This is called after first creating the relation, or if a new object
42
+ # is set on the relation.
43
+ #
44
+ # @example Bind the relation.
45
+ # person.posts.bind
46
+ #
47
+ # @param [ Hash ] options The options to bind with.
48
+ #
49
+ # @option options [ true, false ] :binding Are we in build mode?
50
+ # @option options [ true, false ] :continue Continue binding the
51
+ # inverse?
52
+ #
53
+ # @since 2.0.0.rc.1
54
+ def bind(options = {})
55
+ binding.bind(options)
56
+ target.map(&:save) if base.persisted? && !options[:binding]
57
+ end
58
+
59
+ alias :concat :<<
60
+ alias :push :<<
61
+
62
+ # Clear the relation. Will delete the documents from the db if they are
63
+ # already persisted.
64
+ #
65
+ # @example Clear the relation.
66
+ # person.posts.clear
67
+ #
68
+ # @return [ Many ] The relation emptied.
69
+ def clear(options = {})
70
+ load! and tap do |relation|
71
+ relation.unbind(default_options(options))
72
+ target.clear
73
+ end
74
+ end
75
+
76
+ # Returns a count of the number of documents in the association that have
77
+ # actually been persisted to the database.
78
+ #
79
+ # Use #size if you want the total number of documents in memory.
80
+ #
81
+ # @example Get the count of persisted documents.
82
+ # person.posts.count
83
+ #
84
+ # @return [ Integer ] The total number of persisted documents.
85
+ def count
86
+ criteria.count
87
+ end
88
+
89
+ # Creates a new document on the references many relation. This will
90
+ # save the document if the parent has been persisted.
91
+ #
92
+ # @example Create and save the new document.
93
+ # person.posts.create(:text => "Testing")
94
+ #
95
+ # @param [ Hash ] attributes The attributes to create with.
96
+ # @param [ Class ] type The optional type of document to create.
97
+ #
98
+ # @return [ Document ] The newly created document.
99
+ def create(attributes = nil, type = nil, &block)
100
+ build(attributes, type, &block).tap do |doc|
101
+ base.persisted? ? doc.save : raise_unsaved(doc)
102
+ end
103
+ end
104
+
105
+ # Creates a new document on the references many relation. This will
106
+ # save the document if the parent has been persisted and will raise an
107
+ # error if validation fails.
108
+ #
109
+ # @example Create and save the new document.
110
+ # person.posts.create!(:text => "Testing")
111
+ #
112
+ # @param [ Hash ] attributes The attributes to create with.
113
+ # @param [ Class ] type The optional type of document to create.
114
+ #
115
+ # @raise [ Errors::Validations ] If validation failed.
116
+ #
117
+ # @return [ Document ] The newly created document.
118
+ def create!(attributes = nil, type = nil, &block)
119
+ build(attributes, type, &block).tap do |doc|
120
+ base.persisted? ? doc.save! : raise_unsaved(doc)
121
+ end
122
+ end
123
+
124
+ # Deletes all related documents from the database given the supplied
125
+ # conditions.
126
+ #
127
+ # @example Delete all documents in the relation.
128
+ # person.posts.delete_all
129
+ #
130
+ # @example Conditonally delete all documents in the relation.
131
+ # person.posts.delete_all(:conditions => { :title => "Testing" })
132
+ #
133
+ # @param [ Hash ] conditions Optional conditions to delete with.
134
+ #
135
+ # @return [ Integer ] The number of documents deleted.
136
+ def delete_all(conditions = nil)
137
+ raise_mixed if klass.embedded?
138
+ selector = (conditions || {})[:conditions] || {}
139
+ target.delete_if { |doc| doc.matches?(selector) }
140
+ metadata.klass.delete_all(
141
+ :conditions => criteria.selector.merge(selector)
142
+ )
143
+ end
144
+
145
+ # Destroys all related documents from the database given the supplied
146
+ # conditions.
147
+ #
148
+ # @example Destroy all documents in the relation.
149
+ # person.posts.destroy_all
150
+ #
151
+ # @example Conditonally destroy all documents in the relation.
152
+ # person.posts.destroy_all(:conditions => { :title => "Testing" })
153
+ #
154
+ # @param [ Hash ] conditions Optional conditions to destroy with.
155
+ #
156
+ # @return [ Integer ] The number of documents destroyd.
157
+ def destroy_all(conditions = nil)
158
+ raise_mixed if klass.embedded?
159
+ selector = (conditions || {})[:conditions] || {}
160
+ target.delete_if { |doc| doc.matches?(selector) }
161
+ metadata.klass.destroy_all(
162
+ :conditions => criteria.selector.merge(selector)
163
+ )
164
+ end
165
+
166
+ # Find the matchind document on the association, either based on id or
167
+ # conditions.
168
+ #
169
+ # @example Find by an id.
170
+ # person.posts.find(BSON::ObjectId.new)
171
+ #
172
+ # @example Find by multiple ids.
173
+ # person.posts.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
174
+ #
175
+ # @example Conditionally find all matching documents.
176
+ # person.posts.find(:all, :conditions => { :title => "Sir" })
177
+ #
178
+ # @example Conditionally find the first document.
179
+ # person.posts.find(:first, :conditions => { :title => "Sir" })
180
+ #
181
+ # @example Conditionally find the last document.
182
+ # person.posts.find(:last, :conditions => { :title => "Sir" })
183
+ #
184
+ # @param [ Symbol, BSON::ObjectId, Array<BSON::ObjectId> ] arg The
185
+ # argument to search with.
186
+ # @param [ Hash ] options The options to search with.
187
+ #
188
+ # @return [ Document, Criteria ] The matching document(s).
189
+ def find(*args)
190
+ criteria.find(*args)
191
+ end
192
+
193
+ # Instantiate a new references_many relation. Will set the foreign key
194
+ # and the base on the inverse object.
195
+ #
196
+ # @example Create the new relation.
197
+ # Referenced::Many.new(base, target, metadata)
198
+ #
199
+ # @param [ Document ] base The document this relation hangs off of.
200
+ # @param [ Array<Document> ] target The target of the relation.
201
+ # @param [ Metadata ] metadata The relation's metadata.
202
+ def initialize(base, target, metadata)
203
+ init(base, target, metadata)
204
+ end
205
+
206
+ # Will load the target into an array if the target had not already been
207
+ # loaded.
208
+ #
209
+ # @example Load the relation into memory.
210
+ # relation.load!
211
+ #
212
+ # @return [ Many ] The relation.
213
+ #
214
+ # @since 2.0.0.rc.5
215
+ def load!(options = {})
216
+ raise_mixed if klass.embedded?
217
+ tap do |relation|
218
+ unless relation.loaded?
219
+ relation.target = target.entries
220
+ relation.bind(options)
221
+ relation.loaded = true
222
+ end
223
+ end
224
+ end
225
+
226
+ # Removes all associations between the base document and the target
227
+ # documents by deleting the foreign keys and the references, orphaning
228
+ # the target documents in the process.
229
+ #
230
+ # @example Nullify the relation.
231
+ # person.posts.nullify
232
+ #
233
+ # @since 2.0.0.rc.1
234
+ def nullify
235
+ clear(:binding => true, :continue => true, :nullify => true)
236
+ end
237
+ alias :nullify_all :nullify
238
+
239
+ # Substitutes the supplied target documents for the existing documents
240
+ # in the relation. If the new target is nil, perform the necessary
241
+ # deletion.
242
+ #
243
+ # @example Replace the relation.
244
+ # person.posts.substitute(new_name)
245
+ #
246
+ # @param [ Array<Document> ] target The replacement target.
247
+ # @param [ Hash ] options The options to bind with.
248
+ #
249
+ # @option options [ true, false ] :binding Are we in build mode?
250
+ # @option options [ true, false ] :continue Continue binding the
251
+ # inverse?
252
+ #
253
+ # @return [ Many ] The relation.
254
+ #
255
+ # @since 2.0.0.rc.1
256
+ def substitute(target, options = {})
257
+ tap { target ? (@target = target.to_a; bind(options)) : (@target = unbind(options)) }
258
+ end
259
+
260
+ # Unbinds the base object to the inverse of the relation. This occurs
261
+ # when setting a side of the relation to nil.
262
+ #
263
+ # Will delete the object if necessary.
264
+ #
265
+ # @example Unbind the target.
266
+ # person.posts.unbind
267
+ #
268
+ # @param [ Hash ] options The options to bind with.
269
+ #
270
+ # @option options [ true, false ] :binding Are we in build mode?
271
+ # @option options [ true, false ] :continue Continue binding the
272
+ # inverse?
273
+ #
274
+ # @since 2.0.0.rc.1
275
+ def unbind(options = {})
276
+ binding.unbind(options)
277
+ unless base.new_record?
278
+ target.each(&:delete) unless options[:binding]
279
+ target.each(&:save) if options[:nullify]
280
+ end
281
+ []
282
+ end
283
+
284
+ private
285
+
286
+ # Appends the document to the target array, updating the index on the
287
+ # document at the same time.
288
+ #
289
+ # @example Append the document to the relation.
290
+ # relation.append(document)
291
+ #
292
+ # @param [ Document ] document The document to append to the target.
293
+ #
294
+ # @since 2.0.0.rc.1
295
+ def append(document, options = {})
296
+ init_target if !initialized? && !loaded?
297
+ target.push(document)
298
+ characterize_one(document)
299
+ binding.bind_one(document, options)
300
+ end
301
+
302
+ # Instantiate the binding associated with this relation.
303
+ #
304
+ # @example Get the binding.
305
+ # relation.binding([ address ])
306
+ #
307
+ # @param [ Array<Document> ] new_target The new documents to bind with.
308
+ #
309
+ # @return [ Binding ] The binding.
310
+ #
311
+ # @since 2.0.0.rc.1
312
+ def binding(new_target = nil)
313
+ Bindings::Referenced::Many.new(base, new_target || target, metadata)
314
+ end
315
+
316
+ # Get the collection of the relation in question.
317
+ #
318
+ # @example Get the collection of the relation.
319
+ # relation.collection
320
+ #
321
+ # @return [ Collection ] The collection of the relation.
322
+ #
323
+ # @since 2.0.2, batch-relational-insert
324
+ def collection
325
+ metadata.klass.collection
326
+ end
327
+
328
+ # Get the value for the foreign key in convertable or unconvertable
329
+ # form.
330
+ #
331
+ # @todo Durran: Find a common place for this.
332
+ #
333
+ # @example Get the value.
334
+ # relation.convertable
335
+ #
336
+ # @return [ String, Unconvertable, BSON::ObjectId ] The string or object id.
337
+ #
338
+ # @since 2.0.2
339
+ def convertable
340
+ inverse = metadata.inverse_klass
341
+ if inverse.using_object_ids? || base.id.is_a?(BSON::ObjectId)
342
+ base.id
343
+ else
344
+ Mongoid::Criterion::Unconvertable.new(base.id)
345
+ end
346
+ end
347
+
348
+ # Returns the criteria object for the target class with its documents set
349
+ # to target.
350
+ #
351
+ # @example Get a criteria for the relation.
352
+ # relation.criteria
353
+ #
354
+ # @return [ Criteria ] A new criteria.
355
+ def criteria
356
+ raise_mixed if klass.embedded?
357
+ metadata.klass.where(metadata.foreign_key => convertable)
358
+ end
359
+
360
+ # Tells if the target array been initialized.
361
+ #
362
+ # @example Is the target initialized?
363
+ # relation.initialized?
364
+ #
365
+ # @return [ true, false ] If the target is an array.
366
+ #
367
+ # @since 2.0.0
368
+ def initialized?
369
+ !!@initialized
370
+ end
371
+
372
+ # Initializes the target of the proxy as an empty array instead of
373
+ # hitting the database.
374
+ #
375
+ # @example Initialize the target.
376
+ # relation.init_target
377
+ #
378
+ # @raise [ Errors::MixedRelations ] If the class is embedded.
379
+ #
380
+ # @return [ true ] Always true.
381
+ #
382
+ # @since 2.0.0
383
+ def init_target
384
+ raise_mixed if klass.embedded?
385
+ @target = []
386
+ @initialized = true
387
+ end
388
+
389
+ # If the target array does not respond to the supplied method then try to
390
+ # find a named scope or criteria on the class and send the call there.
391
+ #
392
+ # If the method exists on the array, use the default proxy behavior.
393
+ #
394
+ # @param [ Symbol, String ] name The name of the method.
395
+ # @param [ Array ] args The method args
396
+ # @param [ Proc ] block Optional block to pass.
397
+ #
398
+ # @return [ Criteria, Object ] A Criteria or return value from the target.
399
+ def method_missing(name, *args, &block)
400
+ load!(:binding => true) and return super if [].respond_to?(name)
401
+ klass = metadata.klass
402
+ klass.send(:with_scope, criteria) do
403
+ criteria.send(name, *args, &block)
404
+ end
405
+ end
406
+
407
+ class << self
408
+
409
+ # Return the builder that is responsible for generating the documents
410
+ # that will be used by this relation.
411
+ #
412
+ # @example Get the builder.
413
+ # Referenced::Many.builder(meta, object)
414
+ #
415
+ # @param [ Metadata ] meta The metadata of the relation.
416
+ # @param [ Document, Hash ] object A document or attributes to build
417
+ # with.
418
+ #
419
+ # @return [ Builder ] A new builder object.
420
+ #
421
+ # @since 2.0.0.rc.1
422
+ def builder(meta, object)
423
+ Builders::Referenced::Many.new(meta, object || [])
424
+ end
425
+
426
+ # Returns true if the relation is an embedded one. In this case
427
+ # always false.
428
+ #
429
+ # @example Is this relation embedded?
430
+ # Referenced::Many.embedded?
431
+ #
432
+ # @return [ false ] Always false.
433
+ #
434
+ # @since 2.0.0.rc.1
435
+ def embedded?
436
+ false
437
+ end
438
+
439
+ # Get the default value for the foreign key.
440
+ #
441
+ # @example Get the default.
442
+ # Referenced::Many.foreign_key_default
443
+ #
444
+ # @return [ nil ] Always nil.
445
+ #
446
+ # @since 2.0.0.rc.1
447
+ def foreign_key_default
448
+ nil
449
+ end
450
+
451
+ # Returns the suffix of the foreign key field, either "_id" or "_ids".
452
+ #
453
+ # @example Get the suffix for the foreign key.
454
+ # Referenced::Many.foreign_key_suffix
455
+ #
456
+ # @return [ String ] "_id"
457
+ #
458
+ # @since 2.0.0.rc.1
459
+ def foreign_key_suffix
460
+ "_id"
461
+ end
462
+
463
+ # Returns the macro for this relation. Used mostly as a helper in
464
+ # reflection.
465
+ #
466
+ # @example Get the macro.
467
+ # Referenced::Many.macro
468
+ #
469
+ # @return [ Symbol ] :references_many
470
+ def macro
471
+ :references_many
472
+ end
473
+
474
+ # Return the nested builder that is responsible for generating the documents
475
+ # that will be used by this relation.
476
+ #
477
+ # @example Get the nested builder.
478
+ # Referenced::Many.builder(attributes, options)
479
+ #
480
+ # @param [ Metadata ] metadata The relation metadata.
481
+ # @param [ Hash ] attributes The attributes to build with.
482
+ # @param [ Hash ] options The options for the builder.
483
+ #
484
+ # @option options [ true, false ] :allow_destroy Can documents be
485
+ # deleted?
486
+ # @option options [ Integer ] :limit Max number of documents to
487
+ # create at once.
488
+ # @option options [ Proc, Symbol ] :reject_if If documents match this
489
+ # option then they are ignored.
490
+ # @option options [ true, false ] :update_only Only existing documents
491
+ # can be modified.
492
+ #
493
+ # @return [ NestedBuilder ] A newly instantiated nested builder object.
494
+ #
495
+ # @since 2.0.0.rc.1
496
+ def nested_builder(metadata, attributes, options)
497
+ Builders::NestedAttributes::Many.new(metadata, attributes, options)
498
+ end
499
+
500
+ # Tells the caller if this relation is one that stores the foreign
501
+ # key on its own objects.
502
+ #
503
+ # @example Does this relation store a foreign key?
504
+ # Referenced::Many.stores_foreign_key?
505
+ #
506
+ # @return [ false ] Always false.
507
+ #
508
+ # @since 2.0.0.rc.1
509
+ def stores_foreign_key?
510
+ false
511
+ end
512
+ end
513
+ end
514
+ end
515
+ end
516
+ end