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,135 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This is the superclass for all many to one and many to many relation
6
+ # proxies.
7
+ class Many < Proxy
8
+
9
+ delegate :avg, :max, :min, :sum, :to => :criteria
10
+ delegate :length, :size, :to => :target
11
+
12
+ # Is the relation empty?
13
+ #
14
+ # @example Is the relation empty??
15
+ # person.addresses.blank?
16
+ #
17
+ # @return [ true, false ] If the relation is empty or not.
18
+ #
19
+ # @since 2.1.0
20
+ def blank?
21
+ size == 0
22
+ end
23
+
24
+ # Determine if any documents in this relation exist in the database.
25
+ #
26
+ # @example Are there persisted documents?
27
+ # person.posts.exists?
28
+ #
29
+ # @return [ true, false ] True is persisted documents exist, false if not.
30
+ def exists?
31
+ count > 0
32
+ end
33
+
34
+ # Find the first document given the conditions, or creates a new document
35
+ # with the conditions that were supplied.
36
+ #
37
+ # @example Find or create.
38
+ # person.posts.find_or_create_by(:title => "Testing")
39
+ #
40
+ # @param [ Hash ] attrs The attributes to search or create with.
41
+ #
42
+ # @return [ Document ] An existing document or newly created one.
43
+ def find_or_create_by(attrs = {}, &block)
44
+ find_or(:create, attrs, &block)
45
+ end
46
+
47
+ # Find the first +Document+ given the conditions, or instantiates a new document
48
+ # with the conditions that were supplied
49
+ #
50
+ # @example Find or initialize.
51
+ # person.posts.find_or_initialize_by(:title => "Test")
52
+ #
53
+ # @param [ Hash ] attrs The attributes to search or initialize with.
54
+ #
55
+ # @return [ Document ] An existing document or newly instantiated one.
56
+ def find_or_initialize_by(attrs = {}, &block)
57
+ find_or(:build, attrs, &block)
58
+ end
59
+
60
+ # This proxy can never be nil.
61
+ #
62
+ # @example Is the proxy nil?
63
+ # relation.nil?
64
+ #
65
+ # @return [ false ] Always false.
66
+ #
67
+ # @since 2.0.0
68
+ def nil?
69
+ false
70
+ end
71
+
72
+ # Since method_missing is overridden we should override this as well.
73
+ #
74
+ # @example Does the proxy respond to the method?
75
+ # relation.respond_to?(:name)
76
+ #
77
+ # @param [ Symbol ] name The method name.
78
+ #
79
+ # @return [ true, false ] If the proxy responds to the method.
80
+ #
81
+ # @since 2.0.0
82
+ def respond_to?(name, include_private = false)
83
+ [].respond_to?(name, include_private) ||
84
+ klass.respond_to?(name, include_private) || super
85
+ end
86
+
87
+ # This is public access to the relation's criteria.
88
+ #
89
+ # @example Get the scoped relation.
90
+ # relation.scoped
91
+ #
92
+ # @return [ Criteria ] The scoped criteria.
93
+ #
94
+ # @since 2.1.0
95
+ def scoped
96
+ criteria
97
+ end
98
+
99
+ # Gets the document as a serializable hash, used by ActiveModel's JSON and
100
+ # XML serializers. This override is just to be able to pass the :include
101
+ # and :except options to get associations in the hash.
102
+ #
103
+ # @example Get the serializable hash.
104
+ # relation.serializable_hash
105
+ #
106
+ # @param [ Hash ] options The options to pass.
107
+ #
108
+ # @option options [ Symbol ] :include What relations to include
109
+ # @option options [ Symbol ] :only Limit the fields to only these.
110
+ # @option options [ Symbol ] :except Dont include these fields.
111
+ #
112
+ # @return [ Hash ] The documents, ready to be serialized.
113
+ #
114
+ # @since 2.0.0.rc.6
115
+ def serializable_hash(options = {})
116
+ target.map { |document| document.serializable_hash(options) }
117
+ end
118
+
119
+ private
120
+
121
+ # Find the first object given the supplied attributes or create/initialize it.
122
+ #
123
+ # @example Find or create|initialize.
124
+ # person.addresses.find_or(:create, :street => "Bond")
125
+ #
126
+ # @param [ Symbol ] method The method name, create or new.
127
+ # @param [ Hash ] attrs The attributes to build with.
128
+ #
129
+ # @return [ Document ] A matching document or a new/created one.
130
+ def find_or(method, attrs = {}, &block)
131
+ find(:first, :conditions => attrs) || send(method, attrs, &block)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,919 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # The "Grand Poobah" of information about any relation is this class. It
6
+ # contains everything you could ever possible want to know.
7
+ class Metadata < Hash
8
+
9
+ delegate :foreign_key_default, :stores_foreign_key?, :to => :relation
10
+
11
+ # Returns the as option of the relation.
12
+ #
13
+ # @example Get the as option.
14
+ # metadata.as
15
+ #
16
+ # @return [ true, false ] The as option.
17
+ #
18
+ # @since 2.1.0
19
+ def as
20
+ self[:as]
21
+ end
22
+
23
+ # Tells whether an as option exists.
24
+ #
25
+ # @example Is the as option set?
26
+ # metadata.as?
27
+ #
28
+ # @return [ true, false ] True if an as exists, false if not.
29
+ #
30
+ # @since 2.0.0.rc.1
31
+ def as?
32
+ !!as
33
+ end
34
+
35
+ # Returns the autosave option of the relation.
36
+ #
37
+ # @example Get the autosave option.
38
+ # metadata.autosave
39
+ #
40
+ # @return [ true, false ] The autosave option.
41
+ #
42
+ # @since 2.1.0
43
+ def autosave
44
+ self[:autosave]
45
+ end
46
+
47
+ # Does the metadata have a autosave option?
48
+ #
49
+ # @example Is the relation autosaving?
50
+ # metadata.autosave?
51
+ #
52
+ # @return [ true, false ] If the relation autosaves.
53
+ #
54
+ # @since 2.1.0
55
+ def autosave?
56
+ !!autosave
57
+ end
58
+
59
+ # Gets a relation builder associated with the relation this metadata is
60
+ # for.
61
+ #
62
+ # @example Get the builder.
63
+ # metadata.builder(document)
64
+ #
65
+ # @param [ Document ] base The base document.
66
+ # @param [ Object ] object A document or attributes to give the builder.
67
+ #
68
+ # @return [ Builder ] The builder for the relation.
69
+ #
70
+ # @since 2.0.0.rc.1
71
+ def builder(base, object)
72
+ relation.builder(base, self, object)
73
+ end
74
+
75
+ # Returns the name of the strategy used for handling dependent relations.
76
+ #
77
+ # @example Get the strategy.
78
+ # metadata.cascade_strategy
79
+ #
80
+ # @return [ Object ] The cascading strategy to use.
81
+ #
82
+ # @since 2.0.0.rc.1
83
+ def cascade_strategy
84
+ if dependent?
85
+ strategy =
86
+ %{Mongoid::Relations::Cascading::#{dependent.to_s.classify}}
87
+ strategy.constantize
88
+ else
89
+ return nil
90
+ end
91
+ end
92
+
93
+ # Is this an embedded relations that allows callbacks to cascade down to
94
+ # it?
95
+ #
96
+ # @example Does the relation have cascading callbacks?
97
+ # metadata.cascading_callbacks?
98
+ #
99
+ # @return [ true, false ] If the relation cascades callbacks.
100
+ #
101
+ # @since 2.3.0
102
+ def cascading_callbacks?
103
+ !!self[:cascade_callbacks]
104
+ end
105
+
106
+ # Returns the name of the class that this relation contains. If the
107
+ # class_name was provided as an option this will return that, otherwise
108
+ # it will determine the name from the name property.
109
+ #
110
+ # @example Get the class name.
111
+ # metadata.class_name
112
+ #
113
+ # @return [ String ] The name of the relation's proxied class.
114
+ #
115
+ # @since 2.0.0.rc.1
116
+ def class_name
117
+ @class_name ||= (self[:class_name] || classify).sub(/^::/,"")
118
+ end
119
+
120
+ # Get the foreign key contraint for the metadata.
121
+ #
122
+ # @example Get the constaint.
123
+ # metadata.constraint
124
+ #
125
+ # @return [ Constraint ] The constraint.
126
+ #
127
+ # @since 2.0.0.rc.1
128
+ def constraint
129
+ @constraint ||= Constraint.new(self)
130
+ end
131
+
132
+ # Get the criteria that is used to query for this metadata's relation.
133
+ #
134
+ # @example Get the criteria.
135
+ # metadata.criteria([ id_one, id_two ])
136
+ #
137
+ # @param [ Object ] object The foreign key used for the query.
138
+ #
139
+ # @return [ Criteria ] The criteria.
140
+ #
141
+ # @since 2.1.0
142
+ def criteria(object, type = nil)
143
+ query = relation.criteria(self, object, type)
144
+ order ? query.order_by(order) : query
145
+ end
146
+
147
+ # Returns the cyclic option of the relation.
148
+ #
149
+ # @example Get the cyclic option.
150
+ # metadata.cyclic
151
+ #
152
+ # @return [ true, false ] The cyclic option.
153
+ #
154
+ # @since 2.1.0
155
+ def cyclic
156
+ self[:cyclic]
157
+ end
158
+
159
+ # Does the metadata have a cyclic option?
160
+ #
161
+ # @example Is the metadata cyclic?
162
+ # metadata.cyclic?
163
+ #
164
+ # @return [ true, false ] If the metadata is cyclic.
165
+ #
166
+ # @since 2.1.0
167
+ def cyclic?
168
+ !!cyclic
169
+ end
170
+
171
+ # Returns the dependent option of the relation.
172
+ #
173
+ # @example Get the dependent option.
174
+ # metadata.dependent
175
+ #
176
+ # @return [ Symbol ] The dependent option.
177
+ #
178
+ # @since 2.1.0
179
+ def dependent
180
+ self[:dependent]
181
+ end
182
+
183
+ # Does the metadata have a dependent option?
184
+ #
185
+ # @example Is the metadata performing cascades?
186
+ # metadata.dependent?
187
+ #
188
+ # @return [ true, false ] If the metadata cascades.
189
+ #
190
+ # @since 2.1.0
191
+ def dependent?
192
+ !!dependent
193
+ end
194
+
195
+ # Get the criteria needed to eager load this relation.
196
+ #
197
+ # @example Get the eager loading criteria.
198
+ # metadata.eager_load(criteria)
199
+ #
200
+ # @param [ Criteria ] criteria The criteria to load from.
201
+ #
202
+ # @return [ Criteria ] The eager loading criteria.
203
+ #
204
+ # @since 2.2.0
205
+ def eager_load(criteria)
206
+ relation.eager_load(self, criteria.clone)
207
+ end
208
+
209
+ # Will determine if the relation is an embedded one or not. Currently
210
+ # only checks against embeds one and many.
211
+ #
212
+ # @example Is the document embedded.
213
+ # metadata.embedded?
214
+ #
215
+ # @return [ true, false ] True if embedded, false if not.
216
+ #
217
+ # @since 2.0.0.rc.1
218
+ def embedded?
219
+ @embedded ||= (macro == :embeds_one || macro == :embeds_many)
220
+ end
221
+
222
+ # Returns the extension of the relation.
223
+ #
224
+ # @example Get the relation extension.
225
+ # metadata.extension
226
+ #
227
+ # @return [ Module ] The extension or nil.
228
+ #
229
+ # @since 2.0.0.rc.1
230
+ def extension
231
+ self[:extend]
232
+ end
233
+
234
+ # Tells whether an extension definition exist for this relation.
235
+ #
236
+ # @example Is an extension defined?
237
+ # metadata.extension?
238
+ #
239
+ # @return [ true, false ] True if an extension exists, false if not.
240
+ #
241
+ # @since 2.0.0.rc.1
242
+ def extension?
243
+ !!extension
244
+ end
245
+
246
+ # Does this metadata have a forced nil inverse_of defined. (Used in many
247
+ # to manies)
248
+ #
249
+ # @example Is this a forced nil inverse?
250
+ # metadata.forced_nil_inverse?
251
+ #
252
+ # @return [ true, false ] If inverse_of has been explicitly set to nil.
253
+ #
254
+ # @since 2.3.3
255
+ def forced_nil_inverse?
256
+ has_key?(:inverse_of) && inverse_of.nil?
257
+ end
258
+
259
+ # Handles all the logic for figuring out what the foreign_key is for each
260
+ # relations query. The logic is as follows:
261
+ #
262
+ # 1. If the developer defined a custom key, use that.
263
+ # 2. If the relation stores a foreign key,
264
+ # use the class_name_id strategy.
265
+ # 3. If the relation does not store the key,
266
+ # use the inverse_class_name_id strategy.
267
+ #
268
+ # @example Get the foreign key.
269
+ # metadata.foreign_key
270
+ #
271
+ # @return [ String ] The foreign key for the relation.
272
+ #
273
+ # @since 2.0.0.rc.1
274
+ def foreign_key
275
+ @foreign_key ||= determine_foreign_key
276
+ end
277
+
278
+ # Get the name of the method to check if the foreign key has changed.
279
+ #
280
+ # @example Get the foreign key check method.
281
+ # metadata.foreign_key_check
282
+ #
283
+ # @return [ String ] The foreign key check.
284
+ #
285
+ # @since 2.1.0
286
+ def foreign_key_check
287
+ @foreign_key_check ||= "#{foreign_key}_changed?"
288
+ end
289
+
290
+ # Returns the name of the method used to set the foreign key on a
291
+ # document.
292
+ #
293
+ # @example Get the setter for the foreign key.
294
+ # metadata.foreign_key_setter
295
+ #
296
+ # @return [ String ] The foreign_key plus =.
297
+ #
298
+ # @since 2.0.0.rc.1
299
+ def foreign_key_setter
300
+ @foreign_key_setter ||= "#{foreign_key}="
301
+ end
302
+
303
+ # Returns the index option of the relation.
304
+ #
305
+ # @example Get the index option.
306
+ # metadata.index
307
+ #
308
+ # @return [ true, false ] The index option.
309
+ #
310
+ # @since 2.1.0
311
+ def index
312
+ self[:index]
313
+ end
314
+
315
+ # Tells whether a foreign key index exists on the relation.
316
+ #
317
+ # @example Is the key indexed?
318
+ # metadata.indexed?
319
+ #
320
+ # @return [ true, false ] True if an index exists, false if not.
321
+ #
322
+ # @since 2.0.0.rc.1
323
+ def indexed?
324
+ !!index
325
+ end
326
+
327
+ # Instantiate new metadata for a relation.
328
+ #
329
+ # @example Create the new metadata.
330
+ # Metadata.new(:name => :addresses)
331
+ #
332
+ # @param [ Hash ] properties The relation options.
333
+ #
334
+ # @since 2.0.0.rc.1
335
+ def initialize(properties = {})
336
+ Options.validate!(properties)
337
+ merge!(properties)
338
+ end
339
+
340
+ # Since a lot of the information from the metadata is inferred and not
341
+ # explicitly stored in the hash, the inspection needs to be much more
342
+ # detailed.
343
+ #
344
+ # @example Inspect the metadata.
345
+ # metadata.inspect
346
+ #
347
+ # @return [ String ] Oodles of information in a nice format.
348
+ #
349
+ # @since 2.0.0.rc.1
350
+ def inspect
351
+ "#<Mongoid::Relations::Metadata\n" <<
352
+ " class_name: #{class_name},\n" <<
353
+ " cyclic: #{cyclic || "No"},\n" <<
354
+ " dependent: #{dependent || "None"},\n" <<
355
+ " inverse_of: #{inverse_of || "N/A"},\n" <<
356
+ " key: #{key},\n" <<
357
+ " macro: #{macro},\n" <<
358
+ " name: #{name},\n" <<
359
+ " order: #{order.inspect || "No"},\n" <<
360
+ " polymorphic: #{polymorphic? || "No"},\n" <<
361
+ " relation: #{relation},\n" <<
362
+ " setter: #{setter},\n" <<
363
+ " versioned: #{versioned? || "No"}>\n"
364
+ end
365
+
366
+ # Get the name of the inverse relation if it exists. If this is a
367
+ # polymorphic relation then just return the :as option that was defined.
368
+ #
369
+ # @example Get the name of the inverse.
370
+ # metadata.inverse
371
+ #
372
+ # @param [ Document ] other The document to aid in the discovery.
373
+ #
374
+ # @return [ Symbol ] The inverse name.
375
+ #
376
+ # @since 2.0.0.rc.1
377
+ def inverse(other = nil)
378
+ return self[:inverse_of] if has_key?(:inverse_of)
379
+ return self[:as] || lookup_inverse(other) if polymorphic?
380
+ @inverse ||= (cyclic? ? cyclic_inverse : inverse_relation)
381
+ end
382
+
383
+ # Returns the inverse_class_name option of the relation.
384
+ #
385
+ # @example Get the inverse_class_name option.
386
+ # metadata.inverse_class_name
387
+ #
388
+ # @return [ true, false ] The inverse_class_name option.
389
+ #
390
+ # @since 2.1.0
391
+ def inverse_class_name
392
+ self[:inverse_class_name]
393
+ end
394
+
395
+ # Returns the if the inverse class name option exists.
396
+ #
397
+ # @example Is an inverse class name defined?
398
+ # metadata.inverse_class_name?
399
+ #
400
+ # @return [ true, false ] If the inverse if defined.
401
+ #
402
+ # @since 2.1.0
403
+ def inverse_class_name?
404
+ !!inverse_class_name
405
+ end
406
+
407
+ # Used for relational many to many only. This determines the name of the
408
+ # foreign key field on the inverse side of the relation, since in this
409
+ # case there are keys on both sides.
410
+ #
411
+ # @example Find the inverse foreign key
412
+ # metadata.inverse_foreign_key
413
+ #
414
+ # @return [ String ] The foreign key on the inverse.
415
+ #
416
+ # @since 2.0.0.rc.1
417
+ def inverse_foreign_key
418
+ @inverse_foreign_key ||= determine_inverse_foreign_key
419
+ end
420
+
421
+ # Returns the inverse class of the proxied relation.
422
+ #
423
+ # @example Get the inverse class.
424
+ # metadata.inverse_klass
425
+ #
426
+ # @return [ Class ] The class of the inverse of the relation.
427
+ #
428
+ # @since 2.0.0.rc.1
429
+ def inverse_klass
430
+ @inverse_klass ||= inverse_class_name.constantize
431
+ end
432
+
433
+ # Get the metadata for the inverse relation.
434
+ #
435
+ # @example Get the inverse metadata.
436
+ # metadata.inverse_metadata(doc)
437
+ #
438
+ # @param [ Document ] document The document to check.
439
+ #
440
+ # @return [ Metadata ] The inverse metadata.
441
+ #
442
+ # @since 2.1.0
443
+ def inverse_metadata(document)
444
+ document.reflect_on_association(inverse(document))
445
+ end
446
+
447
+ # Returns the inverse_of option of the relation.
448
+ #
449
+ # @example Get the inverse_of option.
450
+ # metadata.inverse_of
451
+ #
452
+ # @return [ true, false ] The inverse_of option.
453
+ #
454
+ # @since 2.1.0
455
+ def inverse_of
456
+ self[:inverse_of]
457
+ end
458
+
459
+ # Does the metadata have a inverse_of option?
460
+ #
461
+ # @example Is an inverse_of defined?
462
+ # metadata.inverse_of?
463
+ #
464
+ # @return [ true, false ] If the relation has an inverse_of defined.
465
+ #
466
+ # @since 2.1.0
467
+ def inverse_of?
468
+ !!inverse_of
469
+ end
470
+
471
+ # Returns the setter for the inverse side of the relation.
472
+ #
473
+ # @example Get the inverse setter.
474
+ # metadata.inverse_setter
475
+ #
476
+ # @param [ Document ] other A document to aid in the discovery.
477
+ #
478
+ # @return [ String ] The inverse setter name.
479
+ #
480
+ # @since 2.0.0.rc.1
481
+ def inverse_setter(other = nil)
482
+ "#{inverse(other)}="
483
+ end
484
+
485
+ # Returns the name of the field in which to store the name of the class
486
+ # for the polymorphic relation.
487
+ #
488
+ # @example Get the name of the field.
489
+ # metadata.inverse_type
490
+ #
491
+ # @return [ String ] The name of the field for storing the type.
492
+ #
493
+ # @since 2.0.0.rc.1
494
+ def inverse_type
495
+ @inverse_type ||=
496
+ relation.stores_foreign_key? && polymorphic? ? "#{name}_type" : nil
497
+ end
498
+
499
+ # Gets the setter for the field that sets the type of document on a
500
+ # polymorphic relation.
501
+ #
502
+ # @example Get the inverse type setter.
503
+ # metadata.inverse_type_setter
504
+ #
505
+ # @return [ String ] The name of the setter.
506
+ #
507
+ # @since 2.0.0.rc.1
508
+ def inverse_type_setter
509
+ @inverse_type_setter ||= inverse_type ? "#{inverse_type}=" : nil
510
+ end
511
+
512
+ # This returns the key that is to be used to grab the attributes for the
513
+ # relation or the foreign key or id that a referenced relation will use
514
+ # to query for the object.
515
+ #
516
+ # @example Get the lookup key.
517
+ # metadata.key
518
+ #
519
+ # @return [ String ] The association name, foreign key name, or _id.
520
+ #
521
+ # @since 2.0.0.rc.1
522
+ def key
523
+ @key ||= determine_key
524
+ end
525
+
526
+ # Returns the class of the proxied relation.
527
+ #
528
+ # @example Get the class.
529
+ # metadata.klass
530
+ #
531
+ # @return [ Class ] The class of the relation.
532
+ #
533
+ # @since 2.0.0.rc.1
534
+ def klass
535
+ @klass ||= class_name.constantize
536
+ end
537
+
538
+ # Is this metadata representing a one to many or many to many relation?
539
+ #
540
+ # @example Is the relation a many?
541
+ # metadata.many?
542
+ #
543
+ # @return [ true, false ] If the relation is a many.
544
+ #
545
+ # @since 2.1.6
546
+ def many?
547
+ @many ||= (relation.macro.to_s =~ /many/)
548
+ end
549
+
550
+ # Returns the macro for the relation of this metadata.
551
+ #
552
+ # @example Get the macro.
553
+ # metadata.macro
554
+ #
555
+ # @return [ Symbol ] The macro.
556
+ #
557
+ # @since 2.0.0.rc.1
558
+ def macro
559
+ relation.macro
560
+ end
561
+
562
+ # Get the name associated with this metadata.
563
+ #
564
+ # @example Get the name.
565
+ # metadata.name
566
+ #
567
+ # @return [ Symbol ] The name.
568
+ #
569
+ # @since 2.1.0
570
+ def name
571
+ self[:name]
572
+ end
573
+
574
+ # Is the name defined?
575
+ #
576
+ # @example Is the name defined?
577
+ # metadata.name?
578
+ #
579
+ # @return [ true, false ] If the name is defined.
580
+ #
581
+ # @since 2.1.0
582
+ def name?
583
+ !!name
584
+ end
585
+
586
+ # Does the relation have a destructive dependent option specified. This
587
+ # is true for :dependent => :delete and :dependent => :destroy.
588
+ #
589
+ # @example Is the relation destructive?
590
+ # metadata.destructive?
591
+ #
592
+ # @return [ true, false ] If the relation is destructive.
593
+ #
594
+ # @since 2.1.0
595
+ def destructive?
596
+ @destructive ||= (dependent == :delete || dependent == :destroy)
597
+ end
598
+
599
+ # Gets a relation nested builder associated with the relation this metadata
600
+ # is for. Nested builders are used in conjunction with nested attributes.
601
+ #
602
+ # @example Get the nested builder.
603
+ # metadata.nested_builder(attributes, options)
604
+ #
605
+ # @param [ Hash ] attributes The attributes to build the relation with.
606
+ # @param [ Hash ] options Options for the nested builder.
607
+ #
608
+ # @return [ NestedBuilder ] The nested builder for the relation.
609
+ #
610
+ # @since 2.0.0.rc.1
611
+ def nested_builder(attributes, options)
612
+ relation.nested_builder(self, attributes, options)
613
+ end
614
+
615
+ # Get the path calculator for the supplied document.
616
+ #
617
+ # @example Get the path calculator.
618
+ # metadata.path(document)
619
+ #
620
+ # @param [ Document ] document The document to calculate on.
621
+ #
622
+ # @return [ Object ] The atomic path calculator.
623
+ #
624
+ # @since 2.1.0
625
+ def path(document)
626
+ relation.path(document)
627
+ end
628
+
629
+ # Returns true if the relation is polymorphic.
630
+ #
631
+ # @example Is the relation polymorphic?
632
+ # metadata.polymorphic?
633
+ #
634
+ # @return [ true, false ] True if the relation is polymorphic, false if not.
635
+ #
636
+ # @since 2.0.0.rc.1
637
+ def polymorphic?
638
+ @polymorphic ||= (!!self[:as] || !!self[:polymorphic])
639
+ end
640
+
641
+ # Get the relation associated with this metadata.
642
+ #
643
+ # @example Get the relation.
644
+ # metadata.relation
645
+ #
646
+ # @return [ Proxy ] The relation proxy class.
647
+ #
648
+ # @since 2.1.0
649
+ def relation
650
+ self[:relation]
651
+ end
652
+
653
+ # Gets the method name used to set this relation.
654
+ #
655
+ # @example Get the setter.
656
+ # metadata = Metadata.new(:name => :person)
657
+ # metadata.setter # => "person="
658
+ #
659
+ # @return [ String ] The name plus "=".
660
+ #
661
+ # @since 2.0.0.rc.1
662
+ def setter
663
+ @setter ||= "#{name.to_s}="
664
+ end
665
+
666
+ # Returns the name of the field in which to store the name of the class
667
+ # for the polymorphic relation.
668
+ #
669
+ # @example Get the name of the field.
670
+ # metadata.inverse_type
671
+ #
672
+ # @return [ String ] The name of the field for storing the type.
673
+ #
674
+ # @since 2.0.0.rc.1
675
+ def type
676
+ @type ||= polymorphic? ? "#{as.to_s}_type" : nil
677
+ end
678
+
679
+ # Gets the setter for the field that sets the type of document on a
680
+ # polymorphic relation.
681
+ #
682
+ # @example Get the inverse type setter.
683
+ # metadata.inverse_type_setter
684
+ #
685
+ # @return [ String ] The name of the setter.
686
+ #
687
+ # @since 2.0.0.rc.1
688
+ def type_setter
689
+ @type_setter ||= type ? "#{type}=" : nil
690
+ end
691
+
692
+ # Are we validating this relation automatically?
693
+ #
694
+ # @example Is automatic validation on?
695
+ # metadata.validate?
696
+ #
697
+ # @return [ true, false ] True unless explictly set to false.
698
+ #
699
+ # @since 2.0.0.rc.1
700
+ def validate?
701
+ unless self[:validate].nil?
702
+ self[:validate]
703
+ else
704
+ self[:validate] = relation.validation_default
705
+ end
706
+ end
707
+
708
+ # Is this relation using Mongoid's internal versioning system?
709
+ #
710
+ # @example Is this relation versioned?
711
+ # metadata.versioned?
712
+ #
713
+ # @return [ true, false ] If the relation uses Mongoid versioning.
714
+ #
715
+ # @since 2.1.0
716
+ def versioned?
717
+ !!self[:versioned]
718
+ end
719
+
720
+ # Returns default order for this association.
721
+ #
722
+ # @example Get default order
723
+ # metadata.order
724
+ #
725
+ # @return [ Criterion::Complex, nil] nil if doesn't set
726
+ #
727
+ # @since 2.1.0
728
+ def order
729
+ self[:order]
730
+ end
731
+
732
+ # Is a default order set?
733
+ #
734
+ # @example Is the order set?
735
+ # metadata.order?
736
+ #
737
+ # @return [ true, false ] If the order is set.
738
+ #
739
+ # @since 2.1.0
740
+ def order?
741
+ !!order
742
+ end
743
+
744
+ private
745
+
746
+ # Returns the class name for the relation.
747
+ #
748
+ # @example Get the class name.
749
+ # metadata.classify
750
+ #
751
+ # @return [ String ] If embedded_in, the camelized, else classified.
752
+ #
753
+ # @since 2.0.0.rc.1
754
+ def classify
755
+ macro == :embedded_in ? name.to_s.camelize : name.to_s.classify
756
+ end
757
+
758
+ # Get the name of the inverse relation in a cyclic relation.
759
+ #
760
+ # @example Get the cyclic inverse name.
761
+ #
762
+ # class Role
763
+ # include Mongoid::Document
764
+ # embedded_in :parent_role, :cyclic => true
765
+ # embeds_many :child_roles, :cyclic => true
766
+ # end
767
+ #
768
+ # metadata = Metadata.new(:name => :parent_role)
769
+ # metadata.cyclic_inverse # => "child_roles"
770
+ #
771
+ # @return [ String ] The cyclic inverse name.
772
+ #
773
+ # @since 2.0.0.rc.1
774
+ def cyclic_inverse
775
+ @cyclic_inverse ||= determine_cyclic_inverse
776
+ end
777
+
778
+ # Determine the cyclic inverse. Performance improvement with the
779
+ # memoization.
780
+ #
781
+ # @example Determine the inverse.
782
+ # metadata.determine_cyclic_inverse
783
+ #
784
+ # @return [ String ] The cyclic inverse name.
785
+ #
786
+ # @since 2.0.0.rc.1
787
+ def determine_cyclic_inverse
788
+ underscored = class_name.demodulize.underscore
789
+ klass.relations.each_pair do |key, meta|
790
+ if key =~ /#{underscored.singularize}|#{underscored.pluralize}/ &&
791
+ meta.relation != relation
792
+ return key.to_sym
793
+ end
794
+ end
795
+ end
796
+
797
+ # Determine the value for the relation's foreign key. Performance
798
+ # improvement.
799
+ #
800
+ # @example Determine the foreign key.
801
+ # metadata.determine_foreign_key
802
+ #
803
+ # @return [ String ] The foreign key.
804
+ #
805
+ # @since 2.0.0.rc.1
806
+ def determine_foreign_key
807
+ return self[:foreign_key].to_s if self[:foreign_key]
808
+ suffix = relation.foreign_key_suffix
809
+ if relation.stores_foreign_key?
810
+ if relation.macro == :references_and_referenced_in_many
811
+ "#{name.to_s.singularize}#{suffix}"
812
+ else
813
+ "#{name}#{suffix}"
814
+ end
815
+ else
816
+ if polymorphic?
817
+ "#{self[:as]}#{suffix}"
818
+ else
819
+ inverse_of ? "#{inverse_of}#{suffix}" : inverse_class_name.foreign_key
820
+ end
821
+ end
822
+ end
823
+
824
+ # Determine the inverse foreign key of the relation.
825
+ #
826
+ # @example Determine the inverse foreign key.
827
+ # metadata.determine_inverse_foreign_key
828
+ #
829
+ # @return [ String ] The inverse.
830
+ #
831
+ # @since 2.3.2
832
+ def determine_inverse_foreign_key
833
+ if has_key?(:inverse_of)
834
+ inverse_of ? "#{inverse_of.to_s.singularize}#{relation.foreign_key_suffix}" : nil
835
+ else
836
+ "#{inverse_class_name.demodulize.underscore}#{relation.foreign_key_suffix}"
837
+ end
838
+ end
839
+
840
+ # Determine the inverse relation. Memoizing #inverse_relation and adding
841
+ # this method dropped 5 seconds off the test suite as a performance
842
+ # improvement.
843
+ #
844
+ # @example Determine the inverse.
845
+ # metadata.determine_inverse_relation
846
+ #
847
+ # @return [ Symbol ] The name of the inverse.
848
+ #
849
+ # @since 2.0.0.rc.1
850
+ def determine_inverse_relation
851
+ default = klass.relations[inverse_klass.name.underscore]
852
+ return default.name if default
853
+ klass.relations.each_pair do |key, meta|
854
+ next if meta.versioned? || meta.name == name
855
+ if meta.class_name == inverse_class_name
856
+ return key.to_sym
857
+ end
858
+ end
859
+ return nil
860
+ end
861
+
862
+ # Determine the key for the relation in the attributes.
863
+ #
864
+ # @example Get the key.
865
+ # metadata.determine_key
866
+ #
867
+ # @return [ String ] The key in the attributes.
868
+ #
869
+ # @since 2.0.0.rc.1
870
+ def determine_key
871
+ return name.to_s if relation.embedded?
872
+ relation.stores_foreign_key? ? foreign_key : "_id"
873
+ end
874
+
875
+ # Determine the name of the inverse relation.
876
+ #
877
+ # @example Get the inverse name.
878
+ # metadata.inverse_relation
879
+ #
880
+ # @return [ Symbol ] The name of the inverse relation.
881
+ #
882
+ # @since 2.0.0.rc.1
883
+ def inverse_relation
884
+ @inverse_relation ||= determine_inverse_relation
885
+ end
886
+
887
+ # Infer the name of the inverse relation from the class.
888
+ #
889
+ # @example Get the inverse name
890
+ # metadata.inverse_name
891
+ #
892
+ # @return [ String ] The inverse class name underscored.
893
+ #
894
+ # @since 2.0.0.rc.1
895
+ def inverse_name
896
+ @inverse_name ||= inverse_klass.name.underscore
897
+ end
898
+
899
+ # For polymorphic children, we need to figure out the inverse from the
900
+ # actual instance on the other side, since we cannot know the exact class
901
+ # name to infer it from at load time.
902
+ #
903
+ # @example Find the inverse.
904
+ # metadata.lookup_inverse(other)
905
+ #
906
+ # @param [ Document ] : The inverse document.
907
+ #
908
+ # @return [ String ] The inverse name.
909
+ #
910
+ # @since 2.0.0.rc.1
911
+ def lookup_inverse(other)
912
+ return nil unless other
913
+ other.class.relations.each_pair do |key, meta|
914
+ return meta.name if meta.as == name
915
+ end
916
+ end
917
+ end
918
+ end
919
+ end