mongoid-multi-db 3.0.0

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 (276) hide show
  1. data/CHANGELOG.md +615 -0
  2. data/LICENSE +20 -0
  3. data/README.md +62 -0
  4. data/Rakefile +49 -0
  5. data/lib/config/locales/bg.yml +54 -0
  6. data/lib/config/locales/de.yml +54 -0
  7. data/lib/config/locales/en-GB.yml +55 -0
  8. data/lib/config/locales/en.yml +55 -0
  9. data/lib/config/locales/es.yml +52 -0
  10. data/lib/config/locales/fr.yml +55 -0
  11. data/lib/config/locales/hi.yml +46 -0
  12. data/lib/config/locales/hu.yml +57 -0
  13. data/lib/config/locales/id.yml +55 -0
  14. data/lib/config/locales/it.yml +52 -0
  15. data/lib/config/locales/ja.yml +50 -0
  16. data/lib/config/locales/kr.yml +47 -0
  17. data/lib/config/locales/nl.yml +52 -0
  18. data/lib/config/locales/pl.yml +52 -0
  19. data/lib/config/locales/pt-BR.yml +53 -0
  20. data/lib/config/locales/pt.yml +53 -0
  21. data/lib/config/locales/ro.yml +59 -0
  22. data/lib/config/locales/ru.yml +54 -0
  23. data/lib/config/locales/sv.yml +53 -0
  24. data/lib/config/locales/vi.yml +55 -0
  25. data/lib/config/locales/zh-CN.yml +46 -0
  26. data/lib/mongoid.rb +148 -0
  27. data/lib/mongoid/atomic.rb +230 -0
  28. data/lib/mongoid/atomic/modifiers.rb +243 -0
  29. data/lib/mongoid/atomic/paths.rb +3 -0
  30. data/lib/mongoid/atomic/paths/embedded.rb +43 -0
  31. data/lib/mongoid/atomic/paths/embedded/many.rb +44 -0
  32. data/lib/mongoid/atomic/paths/embedded/one.rb +43 -0
  33. data/lib/mongoid/atomic/paths/root.rb +40 -0
  34. data/lib/mongoid/attributes.rb +234 -0
  35. data/lib/mongoid/attributes/processing.rb +146 -0
  36. data/lib/mongoid/callbacks.rb +135 -0
  37. data/lib/mongoid/collection.rb +153 -0
  38. data/lib/mongoid/collection_proxy.rb +59 -0
  39. data/lib/mongoid/collections.rb +120 -0
  40. data/lib/mongoid/collections/master.rb +45 -0
  41. data/lib/mongoid/collections/operations.rb +44 -0
  42. data/lib/mongoid/collections/retry.rb +46 -0
  43. data/lib/mongoid/components.rb +96 -0
  44. data/lib/mongoid/config.rb +347 -0
  45. data/lib/mongoid/config/database.rb +186 -0
  46. data/lib/mongoid/config/replset_database.rb +82 -0
  47. data/lib/mongoid/connection_proxy.rb +30 -0
  48. data/lib/mongoid/contexts.rb +25 -0
  49. data/lib/mongoid/contexts/enumerable.rb +288 -0
  50. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  51. data/lib/mongoid/contexts/mongo.rb +409 -0
  52. data/lib/mongoid/copyable.rb +48 -0
  53. data/lib/mongoid/criteria.rb +418 -0
  54. data/lib/mongoid/criterion/builder.rb +34 -0
  55. data/lib/mongoid/criterion/complex.rb +84 -0
  56. data/lib/mongoid/criterion/creational.rb +34 -0
  57. data/lib/mongoid/criterion/exclusion.rb +108 -0
  58. data/lib/mongoid/criterion/inclusion.rb +305 -0
  59. data/lib/mongoid/criterion/inspection.rb +22 -0
  60. data/lib/mongoid/criterion/optional.rb +232 -0
  61. data/lib/mongoid/criterion/selector.rb +153 -0
  62. data/lib/mongoid/cursor.rb +86 -0
  63. data/lib/mongoid/database_proxy.rb +97 -0
  64. data/lib/mongoid/default_scope.rb +36 -0
  65. data/lib/mongoid/dirty.rb +110 -0
  66. data/lib/mongoid/document.rb +280 -0
  67. data/lib/mongoid/errors.rb +17 -0
  68. data/lib/mongoid/errors/callback.rb +26 -0
  69. data/lib/mongoid/errors/document_not_found.rb +28 -0
  70. data/lib/mongoid/errors/eager_load.rb +25 -0
  71. data/lib/mongoid/errors/invalid_collection.rb +18 -0
  72. data/lib/mongoid/errors/invalid_database.rb +19 -0
  73. data/lib/mongoid/errors/invalid_field.rb +18 -0
  74. data/lib/mongoid/errors/invalid_find.rb +19 -0
  75. data/lib/mongoid/errors/invalid_options.rb +28 -0
  76. data/lib/mongoid/errors/invalid_time.rb +25 -0
  77. data/lib/mongoid/errors/invalid_type.rb +25 -0
  78. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  79. data/lib/mongoid/errors/mongoid_error.rb +26 -0
  80. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +20 -0
  81. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  82. data/lib/mongoid/errors/unsupported_version.rb +20 -0
  83. data/lib/mongoid/errors/validations.rb +23 -0
  84. data/lib/mongoid/extensions.rb +82 -0
  85. data/lib/mongoid/extensions/array/deletion.rb +29 -0
  86. data/lib/mongoid/extensions/false_class/equality.rb +26 -0
  87. data/lib/mongoid/extensions/hash/criteria_helpers.rb +45 -0
  88. data/lib/mongoid/extensions/hash/scoping.rb +25 -0
  89. data/lib/mongoid/extensions/integer/checks.rb +23 -0
  90. data/lib/mongoid/extensions/nil/collectionization.rb +23 -0
  91. data/lib/mongoid/extensions/object/checks.rb +29 -0
  92. data/lib/mongoid/extensions/object/reflections.rb +48 -0
  93. data/lib/mongoid/extensions/object/substitutable.rb +15 -0
  94. data/lib/mongoid/extensions/object/yoda.rb +44 -0
  95. data/lib/mongoid/extensions/object_id/conversions.rb +60 -0
  96. data/lib/mongoid/extensions/proc/scoping.rb +25 -0
  97. data/lib/mongoid/extensions/string/checks.rb +36 -0
  98. data/lib/mongoid/extensions/string/conversions.rb +22 -0
  99. data/lib/mongoid/extensions/string/inflections.rb +118 -0
  100. data/lib/mongoid/extensions/symbol/checks.rb +23 -0
  101. data/lib/mongoid/extensions/symbol/inflections.rb +66 -0
  102. data/lib/mongoid/extensions/true_class/equality.rb +26 -0
  103. data/lib/mongoid/extras.rb +31 -0
  104. data/lib/mongoid/factory.rb +46 -0
  105. data/lib/mongoid/fields.rb +332 -0
  106. data/lib/mongoid/fields/mappings.rb +41 -0
  107. data/lib/mongoid/fields/serializable.rb +201 -0
  108. data/lib/mongoid/fields/serializable/array.rb +49 -0
  109. data/lib/mongoid/fields/serializable/big_decimal.rb +42 -0
  110. data/lib/mongoid/fields/serializable/bignum.rb +10 -0
  111. data/lib/mongoid/fields/serializable/binary.rb +11 -0
  112. data/lib/mongoid/fields/serializable/boolean.rb +43 -0
  113. data/lib/mongoid/fields/serializable/date.rb +51 -0
  114. data/lib/mongoid/fields/serializable/date_time.rb +28 -0
  115. data/lib/mongoid/fields/serializable/fixnum.rb +10 -0
  116. data/lib/mongoid/fields/serializable/float.rb +32 -0
  117. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +42 -0
  118. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +47 -0
  119. data/lib/mongoid/fields/serializable/hash.rb +11 -0
  120. data/lib/mongoid/fields/serializable/integer.rb +44 -0
  121. data/lib/mongoid/fields/serializable/localized.rb +41 -0
  122. data/lib/mongoid/fields/serializable/nil_class.rb +38 -0
  123. data/lib/mongoid/fields/serializable/object.rb +11 -0
  124. data/lib/mongoid/fields/serializable/object_id.rb +31 -0
  125. data/lib/mongoid/fields/serializable/range.rb +42 -0
  126. data/lib/mongoid/fields/serializable/set.rb +42 -0
  127. data/lib/mongoid/fields/serializable/string.rb +27 -0
  128. data/lib/mongoid/fields/serializable/symbol.rb +27 -0
  129. data/lib/mongoid/fields/serializable/time.rb +23 -0
  130. data/lib/mongoid/fields/serializable/time_with_zone.rb +23 -0
  131. data/lib/mongoid/fields/serializable/timekeeping.rb +106 -0
  132. data/lib/mongoid/finders.rb +152 -0
  133. data/lib/mongoid/hierarchy.rb +120 -0
  134. data/lib/mongoid/identity.rb +92 -0
  135. data/lib/mongoid/identity_map.rb +119 -0
  136. data/lib/mongoid/indexes.rb +54 -0
  137. data/lib/mongoid/inspection.rb +54 -0
  138. data/lib/mongoid/javascript.rb +20 -0
  139. data/lib/mongoid/javascript/functions.yml +63 -0
  140. data/lib/mongoid/json.rb +16 -0
  141. data/lib/mongoid/keys.rb +144 -0
  142. data/lib/mongoid/logger.rb +39 -0
  143. data/lib/mongoid/matchers.rb +32 -0
  144. data/lib/mongoid/matchers/all.rb +21 -0
  145. data/lib/mongoid/matchers/and.rb +30 -0
  146. data/lib/mongoid/matchers/default.rb +70 -0
  147. data/lib/mongoid/matchers/exists.rb +23 -0
  148. data/lib/mongoid/matchers/gt.rb +21 -0
  149. data/lib/mongoid/matchers/gte.rb +21 -0
  150. data/lib/mongoid/matchers/in.rb +21 -0
  151. data/lib/mongoid/matchers/lt.rb +21 -0
  152. data/lib/mongoid/matchers/lte.rb +21 -0
  153. data/lib/mongoid/matchers/ne.rb +21 -0
  154. data/lib/mongoid/matchers/nin.rb +21 -0
  155. data/lib/mongoid/matchers/or.rb +33 -0
  156. data/lib/mongoid/matchers/size.rb +21 -0
  157. data/lib/mongoid/matchers/strategies.rb +93 -0
  158. data/lib/mongoid/multi_database.rb +31 -0
  159. data/lib/mongoid/multi_parameter_attributes.rb +106 -0
  160. data/lib/mongoid/named_scope.rb +146 -0
  161. data/lib/mongoid/nested_attributes.rb +54 -0
  162. data/lib/mongoid/observer.rb +170 -0
  163. data/lib/mongoid/paranoia.rb +158 -0
  164. data/lib/mongoid/persistence.rb +264 -0
  165. data/lib/mongoid/persistence/atomic.rb +223 -0
  166. data/lib/mongoid/persistence/atomic/add_to_set.rb +35 -0
  167. data/lib/mongoid/persistence/atomic/bit.rb +37 -0
  168. data/lib/mongoid/persistence/atomic/inc.rb +31 -0
  169. data/lib/mongoid/persistence/atomic/operation.rb +85 -0
  170. data/lib/mongoid/persistence/atomic/pop.rb +34 -0
  171. data/lib/mongoid/persistence/atomic/pull.rb +34 -0
  172. data/lib/mongoid/persistence/atomic/pull_all.rb +34 -0
  173. data/lib/mongoid/persistence/atomic/push.rb +31 -0
  174. data/lib/mongoid/persistence/atomic/push_all.rb +31 -0
  175. data/lib/mongoid/persistence/atomic/rename.rb +31 -0
  176. data/lib/mongoid/persistence/atomic/sets.rb +30 -0
  177. data/lib/mongoid/persistence/atomic/unset.rb +28 -0
  178. data/lib/mongoid/persistence/deletion.rb +32 -0
  179. data/lib/mongoid/persistence/insertion.rb +41 -0
  180. data/lib/mongoid/persistence/modification.rb +37 -0
  181. data/lib/mongoid/persistence/operations.rb +211 -0
  182. data/lib/mongoid/persistence/operations/embedded/insert.rb +42 -0
  183. data/lib/mongoid/persistence/operations/embedded/remove.rb +40 -0
  184. data/lib/mongoid/persistence/operations/insert.rb +34 -0
  185. data/lib/mongoid/persistence/operations/remove.rb +33 -0
  186. data/lib/mongoid/persistence/operations/update.rb +64 -0
  187. data/lib/mongoid/railtie.rb +126 -0
  188. data/lib/mongoid/railties/database.rake +182 -0
  189. data/lib/mongoid/railties/document.rb +12 -0
  190. data/lib/mongoid/relations.rb +144 -0
  191. data/lib/mongoid/relations/accessors.rb +138 -0
  192. data/lib/mongoid/relations/auto_save.rb +38 -0
  193. data/lib/mongoid/relations/binding.rb +26 -0
  194. data/lib/mongoid/relations/bindings.rb +9 -0
  195. data/lib/mongoid/relations/bindings/embedded/in.rb +69 -0
  196. data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
  197. data/lib/mongoid/relations/bindings/embedded/one.rb +61 -0
  198. data/lib/mongoid/relations/bindings/referenced/in.rb +76 -0
  199. data/lib/mongoid/relations/bindings/referenced/many.rb +54 -0
  200. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +51 -0
  201. data/lib/mongoid/relations/bindings/referenced/one.rb +58 -0
  202. data/lib/mongoid/relations/builder.rb +57 -0
  203. data/lib/mongoid/relations/builders.rb +83 -0
  204. data/lib/mongoid/relations/builders/embedded/in.rb +29 -0
  205. data/lib/mongoid/relations/builders/embedded/many.rb +40 -0
  206. data/lib/mongoid/relations/builders/embedded/one.rb +30 -0
  207. data/lib/mongoid/relations/builders/nested_attributes/many.rb +110 -0
  208. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  209. data/lib/mongoid/relations/builders/referenced/in.rb +26 -0
  210. data/lib/mongoid/relations/builders/referenced/many.rb +27 -0
  211. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +38 -0
  212. data/lib/mongoid/relations/builders/referenced/one.rb +26 -0
  213. data/lib/mongoid/relations/cascading.rb +56 -0
  214. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  215. data/lib/mongoid/relations/cascading/destroy.rb +26 -0
  216. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  217. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  218. data/lib/mongoid/relations/constraint.rb +42 -0
  219. data/lib/mongoid/relations/conversions.rb +35 -0
  220. data/lib/mongoid/relations/cyclic.rb +103 -0
  221. data/lib/mongoid/relations/embedded/atomic.rb +89 -0
  222. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  223. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  224. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  225. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  226. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  227. data/lib/mongoid/relations/embedded/in.rb +220 -0
  228. data/lib/mongoid/relations/embedded/many.rb +560 -0
  229. data/lib/mongoid/relations/embedded/one.rb +206 -0
  230. data/lib/mongoid/relations/embedded/sort.rb +31 -0
  231. data/lib/mongoid/relations/macros.rb +310 -0
  232. data/lib/mongoid/relations/many.rb +135 -0
  233. data/lib/mongoid/relations/metadata.rb +919 -0
  234. data/lib/mongoid/relations/nested_builder.rb +75 -0
  235. data/lib/mongoid/relations/one.rb +36 -0
  236. data/lib/mongoid/relations/options.rb +47 -0
  237. data/lib/mongoid/relations/polymorphic.rb +40 -0
  238. data/lib/mongoid/relations/proxy.rb +145 -0
  239. data/lib/mongoid/relations/referenced/batch.rb +72 -0
  240. data/lib/mongoid/relations/referenced/batch/insert.rb +57 -0
  241. data/lib/mongoid/relations/referenced/in.rb +262 -0
  242. data/lib/mongoid/relations/referenced/many.rb +623 -0
  243. data/lib/mongoid/relations/referenced/many_to_many.rb +396 -0
  244. data/lib/mongoid/relations/referenced/one.rb +272 -0
  245. data/lib/mongoid/relations/reflections.rb +62 -0
  246. data/lib/mongoid/relations/synchronization.rb +153 -0
  247. data/lib/mongoid/relations/targets.rb +2 -0
  248. data/lib/mongoid/relations/targets/enumerable.rb +372 -0
  249. data/lib/mongoid/reloading.rb +91 -0
  250. data/lib/mongoid/safety.rb +105 -0
  251. data/lib/mongoid/scope.rb +31 -0
  252. data/lib/mongoid/serialization.rb +134 -0
  253. data/lib/mongoid/sharding.rb +61 -0
  254. data/lib/mongoid/state.rb +97 -0
  255. data/lib/mongoid/threaded.rb +530 -0
  256. data/lib/mongoid/threaded/lifecycle.rb +192 -0
  257. data/lib/mongoid/timestamps.rb +15 -0
  258. data/lib/mongoid/timestamps/created.rb +24 -0
  259. data/lib/mongoid/timestamps/timeless.rb +50 -0
  260. data/lib/mongoid/timestamps/updated.rb +26 -0
  261. data/lib/mongoid/validations.rb +140 -0
  262. data/lib/mongoid/validations/associated.rb +46 -0
  263. data/lib/mongoid/validations/uniqueness.rb +145 -0
  264. data/lib/mongoid/version.rb +4 -0
  265. data/lib/mongoid/versioning.rb +185 -0
  266. data/lib/rack/mongoid.rb +2 -0
  267. data/lib/rack/mongoid/middleware/identity_map.rb +38 -0
  268. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  269. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +20 -0
  270. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  271. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +19 -0
  272. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  273. data/lib/rails/generators/mongoid/observer/templates/observer.rb.tt +4 -0
  274. data/lib/rails/generators/mongoid_generator.rb +70 -0
  275. data/lib/rails/mongoid.rb +91 -0
  276. metadata +465 -0
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # The reflections module provides convenience methods that can retrieve
6
+ # useful information about associations.
7
+ module Reflections
8
+ extend ActiveSupport::Concern
9
+
10
+ # Returns the relation metadata for the supplied name.
11
+ #
12
+ # @example Find relation metadata by name.
13
+ # person.reflect_on_association(:addresses)
14
+ #
15
+ # @param [ String, Symbol ] name The name of the relation to find.
16
+ #
17
+ # @return [ Metadata ] The matching relation metadata.
18
+ def reflect_on_association(name)
19
+ self.class.reflect_on_association(name)
20
+ end
21
+
22
+ # Returns all relation metadata for the supplied macros.
23
+ #
24
+ # @example Find multiple relation metadata by macro.
25
+ # person.reflect_on_all_associations(:embeds_many)
26
+ #
27
+ # @param [ Array<String, Symbol> ] *macros The relation macros.
28
+ #
29
+ # @return [ Array<Metadata> ] The matching relation metadata.
30
+ def reflect_on_all_associations(*macros)
31
+ self.class.reflect_on_all_associations(*macros)
32
+ end
33
+
34
+ module ClassMethods #:nodoc
35
+
36
+ # Returns the relation metadata for the supplied name.
37
+ #
38
+ # @example Find relation metadata by name.
39
+ # Person.reflect_on_association(:addresses)
40
+ #
41
+ # @param [ String, Symbol ] name The name of the relation to find.
42
+ #
43
+ # @return [ Metadata ] The matching relation metadata.
44
+ def reflect_on_association(name)
45
+ relations[name.to_s]
46
+ end
47
+
48
+ # Returns all relation metadata for the supplied macros.
49
+ #
50
+ # @example Find multiple relation metadata by macro.
51
+ # Person.reflect_on_all_associations(:embeds_many)
52
+ #
53
+ # @param [ Array<String, Symbol> ] *macros The relation macros.
54
+ #
55
+ # @return [ Array<Metadata> ] The matching relation metadata.
56
+ def reflect_on_all_associations(*macros)
57
+ relations.values.select { |meta| macros.include?(meta.macro) }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This module handles the behaviour for synchronizing foreign keys between
6
+ # both sides of a many to many relations.
7
+ module Synchronization
8
+ extend ActiveSupport::Concern
9
+
10
+ # Is the document able to be synced on the inverse side? This is only if
11
+ # the key has changed and the relation bindings have not been run.
12
+ #
13
+ # @example Are the foreign keys syncable?
14
+ # document.syncable?(metadata)
15
+ #
16
+ # @param [ Metadata ] metadata The relation metadata.
17
+ #
18
+ # @return [ true, false ] If we can sync.
19
+ #
20
+ # @since 2.1.0
21
+ def syncable?(metadata)
22
+ !synced?(metadata.foreign_key) && send(metadata.foreign_key_check)
23
+ end
24
+
25
+ # Get the synced foreign keys.
26
+ #
27
+ # @example Get the synced foreign keys.
28
+ # document.synced
29
+ #
30
+ # @return [ Hash ] The synced foreign keys.
31
+ #
32
+ # @since 2.1.0
33
+ def synced
34
+ @synced ||= {}
35
+ end
36
+
37
+ # Has the document been synced for the foreign key?
38
+ #
39
+ # @todo Change the sync to be key based.
40
+ #
41
+ # @example Has the document been synced?
42
+ # document.synced?
43
+ #
44
+ # @param [ String ] foreign_key The foreign key.
45
+ #
46
+ # @return [ true, false ] If we can sync.
47
+ #
48
+ # @since 2.1.0
49
+ def synced?(foreign_key)
50
+ !!synced[foreign_key]
51
+ end
52
+
53
+ # Update the inverse keys on destroy.
54
+ #
55
+ # @example Update the inverse keys.
56
+ # document.remove_inverse_keys(metadata)
57
+ #
58
+ # @param [ Metadata ] meta The document metadata.
59
+ #
60
+ # @return [ Object ] The updated values.
61
+ #
62
+ # @since 2.2.1
63
+ def remove_inverse_keys(meta)
64
+ meta.criteria(send(meta.foreign_key)).pull(meta.inverse_foreign_key, id)
65
+ end
66
+
67
+ # Update the inverse keys for the relation.
68
+ #
69
+ # @example Update the inverse keys
70
+ # document.update_inverse_keys(metadata)
71
+ #
72
+ # @param [ Metadata ] meta The document metadata.
73
+ #
74
+ # @return [ Object ] The updated values.
75
+ #
76
+ # @since 2.1.0
77
+ def update_inverse_keys(meta)
78
+ return unless changes.has_key?(meta.foreign_key)
79
+ old, new = changes[meta.foreign_key]
80
+ adds, subs = new - (old || []), (old || []) - new
81
+ meta.criteria(adds).add_to_set(meta.inverse_foreign_key, id) unless adds.empty?
82
+ meta.criteria(subs).pull(meta.inverse_foreign_key, id) unless subs.empty?
83
+ end
84
+
85
+ module ClassMethods #:nodoc:
86
+
87
+ # Set up the syncing of many to many foreign keys.
88
+ #
89
+ # @example Set up the syncing.
90
+ # Person.synced(metadata)
91
+ #
92
+ # @param [ Metadata ] metadata The relation metadata.
93
+ #
94
+ # @since 2.1.0
95
+ def synced(metadata)
96
+ unless metadata.forced_nil_inverse?
97
+ synced_save(metadata)
98
+ synced_destroy(metadata)
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ # Set up the sync of inverse keys that needs to happen on a save.
105
+ #
106
+ # If the foreign key field has changed and the document is not
107
+ # synced, $addToSet the new ids, $pull the ones no longer in the
108
+ # array from the inverse side.
109
+ #
110
+ # @example Set up the save syncing.
111
+ # Person.synced_save(metadata)
112
+ #
113
+ # @param [ Metadata ] metadata The relation metadata.
114
+ #
115
+ # @return [ Class ] The class getting set up.
116
+ #
117
+ # @since 2.1.0
118
+ def synced_save(metadata)
119
+ tap do
120
+ set_callback(
121
+ :save,
122
+ :after,
123
+ :if => lambda { |doc| doc.syncable?(metadata) }
124
+ ) do |doc|
125
+ doc.update_inverse_keys(metadata)
126
+ end
127
+ end
128
+ end
129
+
130
+ # Set up the sync of inverse keys that needs to happen on a destroy.
131
+ #
132
+ # @example Set up the destroy syncing.
133
+ # Person.synced_destroy(metadata)
134
+ #
135
+ # @param [ Metadata ] metadata The relation metadata.
136
+ #
137
+ # @return [ Class ] The class getting set up.
138
+ #
139
+ # @since 2.2.1
140
+ def synced_destroy(metadata)
141
+ tap do
142
+ set_callback(
143
+ :destroy,
144
+ :after
145
+ ) do |doc|
146
+ doc.remove_inverse_keys(metadata)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,2 @@
1
+ # encoding: utf-8
2
+ require "mongoid/relations/targets/enumerable"
@@ -0,0 +1,372 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Targets #:nodoc:
5
+
6
+ # This class is the wrapper for all relational associations that have a
7
+ # target that can be a criteria or array of loaded documents. This
8
+ # handles both cases or a combination of the two.
9
+ class Enumerable
10
+ include ::Enumerable
11
+
12
+ # The three main instance variables are collections of documents.
13
+ #
14
+ # @attribute [rw] added Documents that have been appended.
15
+ # @attribute [rw] loaded Persisted documents that have been loaded.
16
+ # @attribute [rw] unloaded A criteria representing persisted docs.
17
+ attr_accessor :added, :loaded, :unloaded
18
+
19
+ delegate :===, :is_a?, :kind_of?, :to => :added
20
+
21
+ # Check if the enumerable is equal to the other object.
22
+ #
23
+ # @example Check equality.
24
+ # enumerable == []
25
+ #
26
+ # @param [ Enumerable ] other The other enumerable.
27
+ #
28
+ # @return [ true, false ] If the objects are equal.
29
+ #
30
+ # @since 2.1.0
31
+ def ==(other)
32
+ return false unless other.respond_to?(:entries)
33
+ entries == other.entries
34
+ end
35
+
36
+ # Append a document to the enumerable.
37
+ #
38
+ # @example Append the document.
39
+ # enumerable << document
40
+ #
41
+ # @param [ Document ] document The document to append.
42
+ #
43
+ # @return [ Document ] The document.
44
+ #
45
+ # @since 2.1.0
46
+ def <<(document)
47
+ added.push(document)
48
+ end
49
+ alias :push :<<
50
+
51
+ # Clears out all the documents in this enumerable. If passed a block it
52
+ # will yield to each document that is in memory.
53
+ #
54
+ # @example Clear out the enumerable.
55
+ # enumerable.clear
56
+ #
57
+ # @example Clear out the enumerable with a block.
58
+ # enumerable.clear do |doc|
59
+ # doc.unbind
60
+ # end
61
+ #
62
+ # @return [ Array<Document> ] The cleared out added docs.
63
+ #
64
+ # @since 2.1.0
65
+ def clear
66
+ if block_given?
67
+ in_memory { |doc| yield(doc) }
68
+ end
69
+ loaded.clear and added.clear
70
+ end
71
+
72
+ # Clones each document in the enumerable.
73
+ #
74
+ # @note This loads all documents into memory.
75
+ #
76
+ # @example Clone the enumerable.
77
+ # enumerable.clone
78
+ #
79
+ # @return [ Array<Document> ] An array clone of the enumerable.
80
+ #
81
+ # @since 2.1.6
82
+ def clone
83
+ collect { |doc| doc.clone }
84
+ end
85
+
86
+ # Delete the supplied document from the enumerable.
87
+ #
88
+ # @example Delete the document.
89
+ # enumerable.delete(document)
90
+ #
91
+ # @param [ Document ] document The document to delete.
92
+ #
93
+ # @return [ Document ] The deleted document.
94
+ #
95
+ # @since 2.1.0
96
+ def delete(document)
97
+ (loaded.delete(document) || added.delete(document)).tap do |doc|
98
+ unless doc
99
+ if unloaded && unloaded.where(:_id => document.id).exists?
100
+ yield(document) if block_given?
101
+ return document
102
+ end
103
+ end
104
+ yield(doc) if block_given?
105
+ end
106
+ end
107
+
108
+ # Deletes every document in the enumerable for where the block returns
109
+ # true.
110
+ #
111
+ # @note This operation loads all documents from the database.
112
+ #
113
+ # @example Delete all matching documents.
114
+ # enumerable.delete_if do |doc|
115
+ # dod.id == id
116
+ # end
117
+ #
118
+ # @return [ Array<Document> ] The remaining docs.
119
+ #
120
+ # @since 2.1.0
121
+ def delete_if(&block)
122
+ load_all!
123
+ tap do
124
+ loaded.delete_if(&block)
125
+ added.delete_if(&block)
126
+ end
127
+ end
128
+
129
+ # Iterating over this enumerable has to handle a few different
130
+ # scenarios.
131
+ #
132
+ # If the enumerable has its criteria loaded into memory then it yields
133
+ # to all the loaded docs and all the added docs.
134
+ #
135
+ # If the enumerable has not loaded the criteria then it iterates over
136
+ # the cursor while loading the documents and then iterates over the
137
+ # added docs.
138
+ #
139
+ # @example Iterate over the enumerable.
140
+ # enumerable.each do |doc|
141
+ # puts doc
142
+ # end
143
+ #
144
+ # @return [ true ] That the enumerable is now loaded.
145
+ #
146
+ # @since 2.1.0
147
+ def each
148
+ if loaded?
149
+ loaded.each do |doc|
150
+ yield(doc)
151
+ end
152
+ else
153
+ unloaded.each do |doc|
154
+ yield(doc)
155
+ loaded.push(added.delete_one(doc) || doc)
156
+ end
157
+ end
158
+ added.each do |doc|
159
+ yield(doc)
160
+ end
161
+ @executed = true
162
+ end
163
+
164
+ # Is the enumerable empty? Will determine if the count is zero based on
165
+ # whether or not it is loaded.
166
+ #
167
+ # @example Is the enumerable empty?
168
+ # enumerable.empty?
169
+ #
170
+ # @return [ true, false ] If the enumerable is empty.
171
+ #
172
+ # @since 2.1.0
173
+ def empty?
174
+ if loaded?
175
+ in_memory.count == 0
176
+ else
177
+ unloaded.count + added.count == 0
178
+ end
179
+ end
180
+
181
+ # Get the first document in the enumerable. Will check the persisted
182
+ # documents first. Does not load the entire enumerable.
183
+ #
184
+ # @example Get the first document.
185
+ # enumerable.first
186
+ #
187
+ # @return [ Document ] The first document found.
188
+ #
189
+ # @since 2.1.0
190
+ def first
191
+ added.first || (loaded? ? loaded.first : unloaded.first)
192
+ end
193
+
194
+ # Initialize the new enumerable either with a criteria or an array.
195
+ #
196
+ # @example Initialize the enumerable with a criteria.
197
+ # Enumberable.new(Post.where(:person_id => id))
198
+ #
199
+ # @example Initialize the enumerable with an array.
200
+ # Enumerable.new([ post ])
201
+ #
202
+ # @param [ Criteria, Array<Document> ] target The wrapped object.
203
+ #
204
+ # @since 2.1.0
205
+ def initialize(target)
206
+ if target.is_a?(Criteria)
207
+ @added, @loaded, @unloaded = [], [], target
208
+ else
209
+ @added, @executed, @loaded = [], true, target
210
+ end
211
+ end
212
+
213
+ # Inspection will just inspect the entries for nice array-style
214
+ # printing.
215
+ #
216
+ # @example Inspect the enumerable.
217
+ # enumerable.inspect
218
+ #
219
+ # @return [ String ] The inspected enum.
220
+ #
221
+ # @since 2.1.0
222
+ def inspect
223
+ entries.inspect
224
+ end
225
+
226
+ # Return all the documents in the enumerable that have been loaded or
227
+ # added.
228
+ #
229
+ # @note When passed a block it yields to each document.
230
+ #
231
+ # @example Get the in memory docs.
232
+ # enumerable.in_memory
233
+ #
234
+ # @return [ Array<Document> ] The in memory docs.
235
+ #
236
+ # @since 2.1.0
237
+ def in_memory
238
+ (loaded + added).tap do |docs|
239
+ docs.each { |doc| yield(doc) } if block_given?
240
+ end
241
+ end
242
+
243
+ # Get the last document in the enumerable. Will check the new
244
+ # documents first. Does not load the entire enumerable.
245
+ #
246
+ # @example Get the last document.
247
+ # enumerable.last
248
+ #
249
+ # @return [ Document ] The last document found.
250
+ #
251
+ # @since 2.1.0
252
+ def last
253
+ added.last || (loaded? ? loaded.last : unloaded.last)
254
+ end
255
+
256
+ # Loads all the documents in the enumerable from the database.
257
+ #
258
+ # @example Load all the documents.
259
+ # enumerable.load_all!
260
+ #
261
+ # @return [ true ] That the enumerable is loaded.
262
+ #
263
+ # @since 2.1.0
264
+ alias :load_all! :entries
265
+
266
+ # Has the enumerable been loaded? This will be true if the criteria has
267
+ # been executed or we manually load the entire thing.
268
+ #
269
+ # @example Is the enumerable loaded?
270
+ # enumerable.loaded?
271
+ #
272
+ # @return [ true, false ] If the enumerable has been loaded.
273
+ #
274
+ # @since 2.1.0
275
+ def loaded?
276
+ !!@executed
277
+ end
278
+
279
+ # Reset the enumerable back to it's persisted state.
280
+ #
281
+ # @example Reset the enumerable.
282
+ # enumerable.reset
283
+ #
284
+ # @return [ false ] Always false.
285
+ #
286
+ # @since 2.1.0
287
+ def reset
288
+ loaded.clear and added.clear
289
+ @executed = false
290
+ end
291
+
292
+ # Does this enumerable respond to the provided method?
293
+ #
294
+ # @example Does the enumerable respond to the method?
295
+ # enumerable.respond_to?(:sum)
296
+ #
297
+ # @param [ String, Symbol ] name The name of the method.
298
+ # @param [ true, false ] include_private Whether to include private
299
+ # methods.
300
+ #
301
+ # @return [ true, false ] Whether the enumerable responds.
302
+ #
303
+ # @since 2.1.0
304
+ def respond_to?(name, include_private = false)
305
+ [].respond_to?(name, include_private) || super
306
+ end
307
+
308
+ # Gets the total size of this enumerable. This is a combination of all
309
+ # the persisted and unpersisted documents.
310
+ #
311
+ # @example Get the size.
312
+ # enumerable.size
313
+ #
314
+ # @return [ Integer ] The size of the enumerable.
315
+ #
316
+ # @since 2.1.0
317
+ def size
318
+ (unloaded ? unloaded.count : loaded.count) + added.count{ |d| d.new? }
319
+ end
320
+ alias :length :size
321
+
322
+ # Send #to_json to the entries.
323
+ #
324
+ # @example Get the enumerable as json.
325
+ # enumerable.to_json
326
+ #
327
+ # @param [ Hash ] options Optional parameters.
328
+ #
329
+ # @return [ String ] The entries all loaded as a string.
330
+ #
331
+ # @since 2.2.0
332
+ def to_json(options = {})
333
+ entries.to_json(options)
334
+ end
335
+
336
+ # Send #as_json to the entries, without encoding.
337
+ #
338
+ # @example Get the enumerable as json.
339
+ # enumerable.as_json
340
+ #
341
+ # @param [ Hash ] options Optional parameters.
342
+ #
343
+ # @return [ Hash ] The entries all loaded as a hash.
344
+ #
345
+ # @since 2.2.0
346
+ def as_json(options = {})
347
+ entries.as_json(options)
348
+ end
349
+
350
+ # Return all the unique documents in the enumerable.
351
+ #
352
+ # @note This operation loads all documents from the database.
353
+ #
354
+ # @example Get all the unique documents.
355
+ # enumerable.uniq
356
+ #
357
+ # @return [ Array<Document> ] The unique documents.
358
+ #
359
+ # @since 2.1.0
360
+ def uniq
361
+ entries.uniq
362
+ end
363
+
364
+ private
365
+
366
+ def method_missing(name, *args, &block)
367
+ entries.send(name, *args, &block)
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end