mongoid-multi-db 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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