mongoid-braxton 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +50 -0
  3. data/Rakefile +51 -0
  4. data/lib/config/locales/bg.yml +41 -0
  5. data/lib/config/locales/de.yml +41 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +41 -0
  8. data/lib/config/locales/fr.yml +42 -0
  9. data/lib/config/locales/hu.yml +44 -0
  10. data/lib/config/locales/id.yml +46 -0
  11. data/lib/config/locales/it.yml +39 -0
  12. data/lib/config/locales/ja.yml +40 -0
  13. data/lib/config/locales/kr.yml +65 -0
  14. data/lib/config/locales/nl.yml +39 -0
  15. data/lib/config/locales/pl.yml +39 -0
  16. data/lib/config/locales/pt-BR.yml +40 -0
  17. data/lib/config/locales/pt.yml +40 -0
  18. data/lib/config/locales/ro.yml +46 -0
  19. data/lib/config/locales/ru.yml +41 -0
  20. data/lib/config/locales/sv.yml +40 -0
  21. data/lib/config/locales/vi.yml +45 -0
  22. data/lib/config/locales/zh-CN.yml +33 -0
  23. data/lib/mongoid.rb +140 -0
  24. data/lib/mongoid/atomicity.rb +111 -0
  25. data/lib/mongoid/attributes.rb +185 -0
  26. data/lib/mongoid/attributes/processing.rb +145 -0
  27. data/lib/mongoid/callbacks.rb +23 -0
  28. data/lib/mongoid/collection.rb +137 -0
  29. data/lib/mongoid/collections.rb +71 -0
  30. data/lib/mongoid/collections/master.rb +37 -0
  31. data/lib/mongoid/collections/operations.rb +42 -0
  32. data/lib/mongoid/collections/retry.rb +39 -0
  33. data/lib/mongoid/components.rb +45 -0
  34. data/lib/mongoid/config.rb +349 -0
  35. data/lib/mongoid/config/database.rb +167 -0
  36. data/lib/mongoid/config/replset_database.rb +78 -0
  37. data/lib/mongoid/contexts.rb +19 -0
  38. data/lib/mongoid/contexts/enumerable.rb +275 -0
  39. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  40. data/lib/mongoid/contexts/mongo.rb +345 -0
  41. data/lib/mongoid/copyable.rb +46 -0
  42. data/lib/mongoid/criteria.rb +357 -0
  43. data/lib/mongoid/criterion/builder.rb +34 -0
  44. data/lib/mongoid/criterion/complex.rb +34 -0
  45. data/lib/mongoid/criterion/creational.rb +34 -0
  46. data/lib/mongoid/criterion/exclusion.rb +108 -0
  47. data/lib/mongoid/criterion/inclusion.rb +198 -0
  48. data/lib/mongoid/criterion/inspection.rb +22 -0
  49. data/lib/mongoid/criterion/optional.rb +193 -0
  50. data/lib/mongoid/criterion/selector.rb +143 -0
  51. data/lib/mongoid/criterion/unconvertable.rb +20 -0
  52. data/lib/mongoid/cursor.rb +86 -0
  53. data/lib/mongoid/default_scope.rb +36 -0
  54. data/lib/mongoid/dirty.rb +253 -0
  55. data/lib/mongoid/document.rb +284 -0
  56. data/lib/mongoid/errors.rb +13 -0
  57. data/lib/mongoid/errors/document_not_found.rb +29 -0
  58. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  59. data/lib/mongoid/errors/invalid_database.rb +20 -0
  60. data/lib/mongoid/errors/invalid_field.rb +19 -0
  61. data/lib/mongoid/errors/invalid_options.rb +16 -0
  62. data/lib/mongoid/errors/invalid_type.rb +26 -0
  63. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  64. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  65. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  66. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  67. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  68. data/lib/mongoid/errors/validations.rb +24 -0
  69. data/lib/mongoid/extensions.rb +123 -0
  70. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  71. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  72. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  74. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  75. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  76. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  77. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  78. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  79. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  80. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  81. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  84. data/lib/mongoid/extensions/object/checks.rb +32 -0
  85. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  86. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  87. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  88. data/lib/mongoid/extensions/object_id/conversions.rb +96 -0
  89. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  90. data/lib/mongoid/extensions/range/conversions.rb +25 -0
  91. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  92. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  93. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  94. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  95. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  96. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  97. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  98. data/lib/mongoid/extras.rb +42 -0
  99. data/lib/mongoid/factory.rb +37 -0
  100. data/lib/mongoid/field.rb +162 -0
  101. data/lib/mongoid/fields.rb +183 -0
  102. data/lib/mongoid/finders.rb +127 -0
  103. data/lib/mongoid/hierarchy.rb +85 -0
  104. data/lib/mongoid/identity.rb +92 -0
  105. data/lib/mongoid/indexes.rb +38 -0
  106. data/lib/mongoid/inspection.rb +54 -0
  107. data/lib/mongoid/javascript.rb +21 -0
  108. data/lib/mongoid/javascript/functions.yml +37 -0
  109. data/lib/mongoid/json.rb +16 -0
  110. data/lib/mongoid/keys.rb +131 -0
  111. data/lib/mongoid/logger.rb +18 -0
  112. data/lib/mongoid/matchers.rb +32 -0
  113. data/lib/mongoid/matchers/all.rb +11 -0
  114. data/lib/mongoid/matchers/default.rb +70 -0
  115. data/lib/mongoid/matchers/exists.rb +13 -0
  116. data/lib/mongoid/matchers/gt.rb +11 -0
  117. data/lib/mongoid/matchers/gte.rb +11 -0
  118. data/lib/mongoid/matchers/in.rb +11 -0
  119. data/lib/mongoid/matchers/lt.rb +11 -0
  120. data/lib/mongoid/matchers/lte.rb +11 -0
  121. data/lib/mongoid/matchers/ne.rb +11 -0
  122. data/lib/mongoid/matchers/nin.rb +11 -0
  123. data/lib/mongoid/matchers/or.rb +30 -0
  124. data/lib/mongoid/matchers/size.rb +11 -0
  125. data/lib/mongoid/matchers/strategies.rb +63 -0
  126. data/lib/mongoid/multi_database.rb +11 -0
  127. data/lib/mongoid/multi_parameter_attributes.rb +82 -0
  128. data/lib/mongoid/named_scope.rb +137 -0
  129. data/lib/mongoid/nested_attributes.rb +51 -0
  130. data/lib/mongoid/observer.rb +67 -0
  131. data/lib/mongoid/paranoia.rb +103 -0
  132. data/lib/mongoid/paths.rb +61 -0
  133. data/lib/mongoid/persistence.rb +240 -0
  134. data/lib/mongoid/persistence/atomic.rb +88 -0
  135. data/lib/mongoid/persistence/atomic/add_to_set.rb +32 -0
  136. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  137. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  139. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  140. data/lib/mongoid/persistence/command.rb +71 -0
  141. data/lib/mongoid/persistence/insert.rb +53 -0
  142. data/lib/mongoid/persistence/insert_embedded.rb +43 -0
  143. data/lib/mongoid/persistence/remove.rb +44 -0
  144. data/lib/mongoid/persistence/remove_all.rb +40 -0
  145. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  146. data/lib/mongoid/persistence/update.rb +77 -0
  147. data/lib/mongoid/railtie.rb +139 -0
  148. data/lib/mongoid/railties/database.rake +171 -0
  149. data/lib/mongoid/railties/document.rb +12 -0
  150. data/lib/mongoid/relations.rb +107 -0
  151. data/lib/mongoid/relations/accessors.rb +175 -0
  152. data/lib/mongoid/relations/auto_save.rb +34 -0
  153. data/lib/mongoid/relations/binding.rb +26 -0
  154. data/lib/mongoid/relations/bindings.rb +9 -0
  155. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  156. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  157. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  158. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  159. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  160. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +103 -0
  161. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  162. data/lib/mongoid/relations/builder.rb +42 -0
  163. data/lib/mongoid/relations/builders.rb +79 -0
  164. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  165. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  166. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  167. data/lib/mongoid/relations/builders/nested_attributes/many.rb +126 -0
  168. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  169. data/lib/mongoid/relations/builders/referenced/in.rb +29 -0
  170. data/lib/mongoid/relations/builders/referenced/many.rb +47 -0
  171. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  172. data/lib/mongoid/relations/builders/referenced/one.rb +27 -0
  173. data/lib/mongoid/relations/cascading.rb +55 -0
  174. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  175. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  176. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  177. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  178. data/lib/mongoid/relations/constraint.rb +42 -0
  179. data/lib/mongoid/relations/cyclic.rb +103 -0
  180. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  181. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  182. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  183. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  184. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  185. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  186. data/lib/mongoid/relations/embedded/in.rb +173 -0
  187. data/lib/mongoid/relations/embedded/many.rb +499 -0
  188. data/lib/mongoid/relations/embedded/one.rb +170 -0
  189. data/lib/mongoid/relations/macros.rb +310 -0
  190. data/lib/mongoid/relations/many.rb +215 -0
  191. data/lib/mongoid/relations/metadata.rb +539 -0
  192. data/lib/mongoid/relations/nested_builder.rb +68 -0
  193. data/lib/mongoid/relations/one.rb +47 -0
  194. data/lib/mongoid/relations/polymorphic.rb +54 -0
  195. data/lib/mongoid/relations/proxy.rb +143 -0
  196. data/lib/mongoid/relations/referenced/batch.rb +71 -0
  197. data/lib/mongoid/relations/referenced/batch/insert.rb +57 -0
  198. data/lib/mongoid/relations/referenced/in.rb +216 -0
  199. data/lib/mongoid/relations/referenced/many.rb +516 -0
  200. data/lib/mongoid/relations/referenced/many_to_many.rb +396 -0
  201. data/lib/mongoid/relations/referenced/one.rb +222 -0
  202. data/lib/mongoid/relations/reflections.rb +45 -0
  203. data/lib/mongoid/safe.rb +23 -0
  204. data/lib/mongoid/safety.rb +207 -0
  205. data/lib/mongoid/scope.rb +31 -0
  206. data/lib/mongoid/serialization.rb +99 -0
  207. data/lib/mongoid/sharding.rb +51 -0
  208. data/lib/mongoid/state.rb +67 -0
  209. data/lib/mongoid/timestamps.rb +14 -0
  210. data/lib/mongoid/timestamps/created.rb +31 -0
  211. data/lib/mongoid/timestamps/updated.rb +33 -0
  212. data/lib/mongoid/validations.rb +124 -0
  213. data/lib/mongoid/validations/associated.rb +44 -0
  214. data/lib/mongoid/validations/referenced.rb +58 -0
  215. data/lib/mongoid/validations/uniqueness.rb +85 -0
  216. data/lib/mongoid/version.rb +4 -0
  217. data/lib/mongoid/versioning.rb +113 -0
  218. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  219. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +20 -0
  220. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  221. data/lib/rails/generators/mongoid/model/templates/model.rb +19 -0
  222. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  223. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  224. data/lib/rails/generators/mongoid_generator.rb +70 -0
  225. data/lib/rails/mongoid.rb +58 -0
  226. metadata +406 -0
@@ -0,0 +1,215 @@
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
+
11
+ # Appends a document or array of documents to the relation. Will set
12
+ # the parent and update the index in the process.
13
+ #
14
+ # @example Append a document.
15
+ # person.addresses << address
16
+ #
17
+ # @example Push a document.
18
+ # person.addresses.push(address)
19
+ #
20
+ # @example Concat with other documents.
21
+ # person.addresses.concat([ address_one, address_two ])
22
+ #
23
+ # @param [ Document, Array<Document> ] *args Any number of documents.
24
+ def <<(*args)
25
+ options = default_options(args)
26
+ args.flatten.each do |doc|
27
+ return doc unless doc
28
+ append(doc, options)
29
+ doc.save if base.persisted? && !options[:binding]
30
+ end
31
+ end
32
+ alias :concat :<<
33
+ alias :push :<<
34
+
35
+ # Builds a new document in the relation and appends it to the target.
36
+ # Takes an optional type if you want to specify a subclass.
37
+ #
38
+ # @example Build a new document on the relation.
39
+ # person.people.build(:name => "Bozo")
40
+ #
41
+ # @param [ Hash ] attributes The attributes to build the document with.
42
+ # @param [ Class ] type Optional class to build the document with.
43
+ #
44
+ # @return [ Document ] The new document.
45
+ def build(attributes = {}, type = nil, &block)
46
+ instantiated(type).tap do |doc|
47
+ doc.write_attributes(attributes)
48
+ doc.identify
49
+ append(doc, default_options(:binding => true))
50
+ block.call(doc) if block
51
+ end
52
+ end
53
+ alias :new :build
54
+
55
+ # Creates a new document on the references many relation. This will
56
+ # save the document if the parent has been persisted.
57
+ #
58
+ # @example Create and save the new document.
59
+ # person.posts.create(:text => "Testing")
60
+ #
61
+ # @param [ Hash ] attributes The attributes to create with.
62
+ # @param [ Class ] type The optional type of document to create.
63
+ #
64
+ # @return [ Document ] The newly created document.
65
+ def create(attributes = nil, type = nil, &block)
66
+ build(attributes, type).tap do |doc|
67
+ block.call(doc) if block
68
+ doc.save if base.persisted?
69
+ end
70
+ end
71
+
72
+ # Creates a new document on the references many relation. This will
73
+ # save the document if the parent has been persisted and will raise an
74
+ # error if validation fails.
75
+ #
76
+ # @example Create and save the new document.
77
+ # person.posts.create!(:text => "Testing")
78
+ #
79
+ # @param [ Hash ] attributes The attributes to create with.
80
+ # @param [ Class ] type The optional type of document to create.
81
+ #
82
+ # @raise [ Errors::Validations ] If validation failed.
83
+ #
84
+ # @return [ Document ] The newly created document.
85
+ def create!(attributes = nil, type = nil)
86
+ build(attributes, type).tap do |doc|
87
+ doc.save! if base.persisted?
88
+ end
89
+ end
90
+
91
+ # Determine if any documents in this relation exist in the database.
92
+ #
93
+ # @example Are there persisted documents?
94
+ # person.posts.exists?
95
+ #
96
+ # @return [ true, false ] True is persisted documents exist, false if not.
97
+ def exists?
98
+ count > 0
99
+ end
100
+
101
+ # Find the first document given the conditions, or creates a new document
102
+ # with the conditions that were supplied.
103
+ #
104
+ # @example Find or create.
105
+ # person.posts.find_or_create_by(:title => "Testing")
106
+ #
107
+ # @param [ Hash ] attrs The attributes to search or create with.
108
+ #
109
+ # @return [ Document ] An existing document or newly created one.
110
+ def find_or_create_by(attrs = {}, &block)
111
+ find_or(:create, attrs, &block)
112
+ end
113
+
114
+ # Find the first +Document+ given the conditions, or instantiates a new document
115
+ # with the conditions that were supplied
116
+ #
117
+ # @example Find or initialize.
118
+ # person.posts.find_or_initialize_by(:title => "Test")
119
+ #
120
+ # @param [ Hash ] attrs The attributes to search or initialize with.
121
+ #
122
+ # @return [ Document ] An existing document or newly instantiated one.
123
+ def find_or_initialize_by(attrs = {}, &block)
124
+ find_or(:build, attrs, &block)
125
+ end
126
+
127
+ # This proxy can never be nil.
128
+ #
129
+ # @example Is the proxy nil?
130
+ # relation.nil?
131
+ #
132
+ # @return [ false ] Always false.
133
+ #
134
+ # @since 2.0.0
135
+ def nil?
136
+ false
137
+ end
138
+
139
+ # Since method_missing is overridden we should override this as well.
140
+ #
141
+ # @example Does the proxy respond to the method?
142
+ # relation.respond_to?(:name)
143
+ #
144
+ # @param [ Symbol ] name The method name.
145
+ #
146
+ # @return [ true, false ] If the proxy responds to the method.
147
+ #
148
+ # @since 2.0.0
149
+ def respond_to?(name)
150
+ [].respond_to?(name) || methods.include?(name)
151
+ end
152
+
153
+ # Gets the document as a serializable hash, used by ActiveModel's JSON and
154
+ # XML serializers. This override is just to be able to pass the :include
155
+ # and :except options to get associations in the hash.
156
+ #
157
+ # @example Get the serializable hash.
158
+ # relation.serializable_hash
159
+ #
160
+ # @param [ Hash ] options The options to pass.
161
+ #
162
+ # @option options [ Symbol ] :include What relations to include
163
+ # @option options [ Symbol ] :only Limit the fields to only these.
164
+ # @option options [ Symbol ] :except Dont include these fields.
165
+ #
166
+ # @return [ Hash ] The documents, ready to be serialized.
167
+ #
168
+ # @since 2.0.0.rc.6
169
+ def serializable_hash(options = {})
170
+ target.map { |document| document.serializable_hash(options) }
171
+ end
172
+
173
+ # Always returns the number of documents that are in memory.
174
+ #
175
+ # @example Get the number of loaded documents.
176
+ # relation.size
177
+ #
178
+ # @return [ Integer ] The number of documents in memory.
179
+ #
180
+ # @since 2.0.0
181
+ def size
182
+ target.size
183
+ end
184
+ alias :length :size
185
+
186
+ private
187
+
188
+ # Get the default options used in binding functions.
189
+ #
190
+ # @example Get the default options.
191
+ # relation.default_options(:continue => true)
192
+ #
193
+ # @param [ Hash, Array ] args The arguments to parse from.
194
+ #
195
+ # @return [ Hash ] The options merged with the actuals.
196
+ def default_options(args = {})
197
+ options = args.is_a?(Hash) ? args : args.extract_options!
198
+ Mongoid.binding_defaults.merge(options)
199
+ end
200
+
201
+ # Find the first object given the supplied attributes or create/initialize it.
202
+ #
203
+ # @example Find or create|initialize.
204
+ # person.addresses.find_or(:create, :street => "Bond")
205
+ #
206
+ # @param [ Symbol ] method The method name, create or new.
207
+ # @param [ Hash ] attrs The attributes to build with.
208
+ #
209
+ # @return [ Document ] A matching document or a new/created one.
210
+ def find_or(method, attrs = {}, &block)
211
+ find(:first, :conditions => attrs) || send(method, attrs, &block)
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,539 @@
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
+ # Gets a relation builder associated with the relation this metadata is
12
+ # for.
13
+ #
14
+ # @example Get the builder.
15
+ # metadata.builder(document)
16
+ #
17
+ # @param [ Object ] object A document or attributes to give the builder.
18
+ #
19
+ # @return [ Builder ] The builder for the relation.
20
+ #
21
+ # @since 2.0.0.rc.1
22
+ def builder(object)
23
+ relation.builder(self, object)
24
+ end
25
+
26
+ # Returns the name of the strategy used for handling dependent relations.
27
+ #
28
+ # @example Get the strategy.
29
+ # metadata.cascade_strategy
30
+ #
31
+ # @return [ Object ] The cascading strategy to use.
32
+ #
33
+ # @since 2.0.0.rc.1
34
+ def cascade_strategy
35
+ if dependent?
36
+ strategy =
37
+ %{Mongoid::Relations::Cascading::#{dependent.to_s.classify}}
38
+ strategy.constantize
39
+ else
40
+ return nil
41
+ end
42
+ end
43
+
44
+ # Returns the name of the class that this relation contains. If the
45
+ # class_name was provided as an option this will return that, otherwise
46
+ # it will determine the name from the name property.
47
+ #
48
+ # @example Get the class name.
49
+ # metadata.class_name
50
+ #
51
+ # @return [ String ] The name of the relation's proxied class.
52
+ #
53
+ # @since 2.0.0.rc.1
54
+ def class_name
55
+ @class_name ||= (self[:class_name] || classify)
56
+ end
57
+
58
+ def constraint
59
+ @constraint ||= Constraint.new(self)
60
+ end
61
+
62
+ # Will determine if the relation is an embedded one or not. Currently
63
+ # only checks against embeds one and many.
64
+ #
65
+ # @example Is the document embedded.
66
+ # metadata.embedded?
67
+ #
68
+ # @return [ true, false ] True if embedded, false if not.
69
+ #
70
+ # @since 2.0.0.rc.1
71
+ def embedded?
72
+ @embedded ||= (macro == :embeds_one || macro == :embeds_many)
73
+ end
74
+
75
+ # Returns the extension of the relation. This can be a proc or module.
76
+ #
77
+ # @example Get the relation extension.
78
+ # metadata.extension
79
+ #
80
+ # @return [ Proc ] The extension or nil.
81
+ #
82
+ # @since 2.0.0.rc.1
83
+ def extension
84
+ self[:extend]
85
+ end
86
+
87
+ # Tells whether an extension definition exist for this relation.
88
+ #
89
+ # @example Is an extension defined?
90
+ # metadata.extension?
91
+ #
92
+ # @return [ true, false ] True if an extension exists, false if not.
93
+ #
94
+ # @since 2.0.0.rc.1
95
+ def extension?
96
+ !!extension
97
+ end
98
+
99
+ # Handles all the logic for figuring out what the foreign_key is for each
100
+ # relations query. The logic is as follows:
101
+ #
102
+ # 1. If the developer defined a custom key, use that.
103
+ # 2. If the relation stores a foreign key,
104
+ # use the class_name_id strategy.
105
+ # 3. If the relation does not store the key,
106
+ # use the inverse_class_name_id strategy.
107
+ #
108
+ # @example Get the foreign key.
109
+ # metadata.foreign_key
110
+ #
111
+ # @return [ String ] The foreign key for the relation.
112
+ #
113
+ # @since 2.0.0.rc.1
114
+ def foreign_key
115
+ @foreign_key ||= determine_foreign_key
116
+ end
117
+
118
+ # Returns the name of the method used to set the foreign key on a
119
+ # document.
120
+ #
121
+ # @example Get the setter for the foreign key.
122
+ # metadata.foreign_key_setter
123
+ #
124
+ # @return [ String ] The foreign_key plus =.
125
+ #
126
+ # @since 2.0.0.rc.1
127
+ def foreign_key_setter
128
+ @foreign_key_setter ||= "#{foreign_key}="
129
+ end
130
+
131
+ # Tells whether a foreign key index exists on the relation.
132
+ #
133
+ # @example Is the key indexed?
134
+ # metadata.indexed?
135
+ #
136
+ # @return [ true, false ] True if an index exists, false if not.
137
+ #
138
+ # @since 2.0.0.rc.1
139
+ def indexed?
140
+ !!self[:index]
141
+ end
142
+
143
+ # Instantiate new metadata for a relation.
144
+ #
145
+ # @example Create the new metadata.
146
+ # Metadata.new(:name => :addresses)
147
+ #
148
+ # @param [ Hash ] properties The relation options.
149
+ #
150
+ # @since 2.0.0.rc.1
151
+ def initialize(properties = {})
152
+ merge!(properties)
153
+ end
154
+
155
+ # Since a lot of the information from the metadata is inferred and not
156
+ # explicitly stored in the hash, the inspection needs to be much more
157
+ # detailed.
158
+ #
159
+ # @example Inspect the metadata.
160
+ # metadata.inspect
161
+ #
162
+ # @return [ String ] Oodles of information in a nice format.
163
+ #
164
+ # @since 2.0.0.rc.1
165
+ def inspect
166
+ "#<Mongoid::Relations::Metadata\n" <<
167
+ " class_name: #{class_name},\n" <<
168
+ " cyclic: #{cyclic || "No"},\n" <<
169
+ " dependent: #{dependent || "None"},\n" <<
170
+ " inverse_of: #{inverse_of || "N/A"},\n" <<
171
+ " inverse_setter: #{inverse_setter},\n" <<
172
+ " inverse_type: #{inverse_type || "N/A"},\n" <<
173
+ " inverse_type_setter: #{inverse_type_setter || "N/A"},\n" <<
174
+ " key: #{key},\n" <<
175
+ " macro: #{macro},\n" <<
176
+ " name: #{name},\n" <<
177
+ " polymorphic: #{polymorphic? ? "Yes" : "No"},\n" <<
178
+ " relation: #{relation},\n" <<
179
+ " setter: #{setter}>\n"
180
+ end
181
+
182
+ # Get the name of the inverse relation if it exists. If this is a
183
+ # polymorphic relation then just return the :as option that was defined.
184
+ #
185
+ # @example Get the name of the inverse.
186
+ # metadata.inverse
187
+ #
188
+ # @param [ Document ] other The document to aid in the discovery.
189
+ #
190
+ # @return [ Symbol ] The inverse name.
191
+ #
192
+ # @since 2.0.0.rc.1
193
+ def inverse(other = nil)
194
+ return self[:inverse_of] if inverse_of?
195
+ return self[:as] || lookup_inverse(other) if polymorphic?
196
+ @inverse ||= (cyclic? ? cyclic_inverse : inverse_relation)
197
+ end
198
+
199
+ # Used for relational many to many only. This determines the name of the
200
+ # foreign key field on the inverse side of the relation, since in this
201
+ # case there are keys on both sides.
202
+ #
203
+ # @example Find the inverse foreign key
204
+ # metadata.inverse_foreign_key
205
+ #
206
+ # @return [ String ] The foreign key on the inverse.
207
+ #
208
+ # @since 2.0.0.rc.1
209
+ def inverse_foreign_key
210
+ @inverse_foreign_key ||=
211
+ ( inverse_of ? inverse_of.to_s.singularize : inverse_class_name.underscore ) <<
212
+ relation.foreign_key_suffix
213
+ end
214
+
215
+ # Returns the inverse class of the proxied relation.
216
+ #
217
+ # @example Get the inverse class.
218
+ # metadata.inverse_klass
219
+ #
220
+ # @return [ Class ] The class of the inverse of the relation.
221
+ #
222
+ # @since 2.0.0.rc.1
223
+ def inverse_klass
224
+ @inverse_klass ||= inverse_class_name.constantize
225
+ end
226
+
227
+ # Returns the setter for the inverse side of the relation.
228
+ #
229
+ # @example Get the inverse setter.
230
+ # metadata.inverse_setter
231
+ #
232
+ # @param [ Document ] other A document to aid in the discovery.
233
+ #
234
+ # @return [ String ] The inverse setter name.
235
+ #
236
+ # @since 2.0.0.rc.1
237
+ def inverse_setter(other = nil)
238
+ inverse(other).to_s << "="
239
+ end
240
+
241
+ # Returns the name of the field in which to store the name of the class
242
+ # for the polymorphic relation.
243
+ #
244
+ # @example Get the name of the field.
245
+ # metadata.inverse_type
246
+ #
247
+ # @return [ String ] The name of the field for storing the type.
248
+ #
249
+ # @since 2.0.0.rc.1
250
+ def inverse_type
251
+ if relation.stores_foreign_key? && polymorphic?
252
+ (polymorphic? ? name.to_s : class_name.underscore) << "_type"
253
+ else
254
+ return nil
255
+ end
256
+ end
257
+
258
+ # Gets the setter for the field that sets the type of document on a
259
+ # polymorphic relation.
260
+ #
261
+ # @example Get the inverse type setter.
262
+ # metadata.inverse_type_setter
263
+ #
264
+ # @return [ String ] The name of the setter.
265
+ #
266
+ # @since 2.0.0.rc.1
267
+ def inverse_type_setter
268
+ inverse_type ? inverse_type << "=" : nil
269
+ end
270
+
271
+ # This returns the key that is to be used to grab the attributes for the
272
+ # relation or the foreign key or id that a referenced relation will use
273
+ # to query for the object.
274
+ #
275
+ # @example Get the lookup key.
276
+ # metadata.key
277
+ #
278
+ # @return [ String ] The association name, foreign key name, or _id.
279
+ #
280
+ # @since 2.0.0.rc.1
281
+ def key
282
+ @key ||= determine_key
283
+ end
284
+
285
+ # Returns the class of the proxied relation.
286
+ #
287
+ # @example Get the class.
288
+ # metadata.klass
289
+ #
290
+ # @return [ Class ] The class of the relation.
291
+ #
292
+ # @since 2.0.0.rc.1
293
+ def klass
294
+ @klass ||= class_name.constantize
295
+ end
296
+
297
+ # Returns the macro for the relation of this metadata.
298
+ #
299
+ # @example Get the macro.
300
+ # metadata.macro
301
+ #
302
+ # @return [ Symbol ] The macro.
303
+ #
304
+ # @since 2.0.0.rc.1
305
+ def macro
306
+ relation.macro
307
+ end
308
+
309
+ # Gets a relation nested builder associated with the relation this metadata
310
+ # is for. Nested builders are used in conjunction with nested attributes.
311
+ #
312
+ # @example Get the nested builder.
313
+ # metadata.nested_builder(attributes, options)
314
+ #
315
+ # @param [ Hash ] attributes The attributes to build the relation with.
316
+ # @param [ Hash ] options Options for the nested builder.
317
+ #
318
+ # @return [ NestedBuilder ] The nested builder for the relation.
319
+ #
320
+ # @since 2.0.0.rc.1
321
+ def nested_builder(attributes, options)
322
+ relation.nested_builder(self, attributes, options)
323
+ end
324
+
325
+ # Returns true if the relation is polymorphic.
326
+ #
327
+ # @example Is the relation polymorphic?
328
+ # metadata.polymorphic?
329
+ #
330
+ # @return [ true, false ] True if the relation is polymorphic, false if not.
331
+ #
332
+ # @since 2.0.0.rc.1
333
+ def polymorphic?
334
+ @polymorphic ||= (!!self[:as] || !!self[:polymorphic])
335
+ end
336
+
337
+ # Gets the method name used to set this relation.
338
+ #
339
+ # @example Get the setter.
340
+ # metadata = Metadata.new(:name => :person)
341
+ # metadata.setter # => "person="
342
+ #
343
+ # @return [ String ] The name plus "=".
344
+ #
345
+ # @since 2.0.0.rc.1
346
+ def setter
347
+ @setter ||= "#{name.to_s}="
348
+ end
349
+
350
+ # Are we validating this relation automatically?
351
+ #
352
+ # @example Is automatic validation on?
353
+ # metadata.validate?
354
+ #
355
+ # @return [ true, false ] True unless explictly set to false.
356
+ #
357
+ # @since 2.0.0.rc.1
358
+ def validate?
359
+ self[:validate] != false
360
+ end
361
+
362
+ private
363
+
364
+ # Returns the class name for the relation.
365
+ #
366
+ # @example Get the class name.
367
+ # metadata.classify
368
+ #
369
+ # @return [ String ] If embedded_in, the camelized, else classified.
370
+ #
371
+ # @since 2.0.0.rc.1
372
+ def classify
373
+ macro == :embedded_in ? name.to_s.camelize : name.to_s.classify
374
+ end
375
+
376
+ # Get the name of the inverse relation in a cyclic relation.
377
+ #
378
+ # @example Get the cyclic inverse name.
379
+ #
380
+ # class Role
381
+ # include Mongoid::Document
382
+ # embedded_in :parent_role, :cyclic => true
383
+ # embeds_many :child_roles, :cyclic => true
384
+ # end
385
+ #
386
+ # metadata = Metadata.new(:name => :parent_role)
387
+ # metadata.cyclic_inverse # => "child_roles"
388
+ #
389
+ # @return [ String ] The cyclic inverse name.
390
+ #
391
+ # @since 2.0.0.rc.1
392
+ def cyclic_inverse
393
+ @cyclic_inverse ||= determine_cyclic_inverse
394
+ end
395
+
396
+ # Determine the cyclic inverse. Performance improvement with the
397
+ # memoization.
398
+ #
399
+ # @example Determine the inverse.
400
+ # metadata.determine_cyclic_inverse
401
+ #
402
+ # @return [ String ] The cyclic inverse name.
403
+ #
404
+ # @since 2.0.0.rc.1
405
+ def determine_cyclic_inverse
406
+ underscored = class_name.underscore
407
+ klass.relations.each_pair do |key, meta|
408
+ if key =~ /#{underscored.singularize}|#{underscored.pluralize}/ &&
409
+ meta.relation != relation
410
+ return key.to_sym
411
+ end
412
+ end
413
+ end
414
+
415
+ # Determine the value for the relation's foreign key. Performance
416
+ # improvement.
417
+ #
418
+ # @example Determine the foreign key.
419
+ # metadata.determine_foreign_key
420
+ #
421
+ # @return [ String ] The foreign key.
422
+ #
423
+ # @since 2.0.0.rc.1
424
+ def determine_foreign_key
425
+ return self[:foreign_key].to_s if self[:foreign_key]
426
+ suffix = relation.foreign_key_suffix
427
+ if relation.stores_foreign_key?
428
+ if relation.macro == :references_and_referenced_in_many
429
+ "#{name.to_s.singularize}#{suffix}"
430
+ else
431
+ "#{name}#{suffix}"
432
+ end
433
+ else
434
+ if polymorphic?
435
+ "#{self[:as]}#{suffix}"
436
+ else
437
+ inverse_of ? "#{inverse_of}#{suffix}" : inverse_class_name.foreign_key
438
+ end
439
+ end
440
+ end
441
+
442
+ # Determine the inverse relation. Memoizing #inverse_relation and adding
443
+ # this method dropped 5 seconds off the test suite as a performance
444
+ # improvement.
445
+ #
446
+ # @example Determine the inverse.
447
+ # metadata.determine_inverse_relation
448
+ #
449
+ # @return [ Symbol ] The name of the inverse.
450
+ #
451
+ # @since 2.0.0.rc.1
452
+ def determine_inverse_relation
453
+ klass.relations.each_pair do |key, meta|
454
+ if key == inverse_klass.name.underscore ||
455
+ meta.class_name == inverse_class_name
456
+ return key.to_sym
457
+ end
458
+ end
459
+ return nil
460
+ end
461
+
462
+ # Determine the key for the relation in the attributes.
463
+ #
464
+ # @example Get the key.
465
+ # metadata.determine_key
466
+ #
467
+ # @return [ String ] The key in the attributes.
468
+ #
469
+ # @since 2.0.0.rc.1
470
+ def determine_key
471
+ return name.to_s if relation.embedded?
472
+ relation.stores_foreign_key? ? foreign_key : "_id"
473
+ end
474
+
475
+ # Determine the name of the inverse relation.
476
+ #
477
+ # @example Get the inverse name.
478
+ # metadata.inverse_relation
479
+ #
480
+ # @return [ Symbol ] The name of the inverse relation.
481
+ #
482
+ # @since 2.0.0.rc.1
483
+ def inverse_relation
484
+ @inverse_relation ||= determine_inverse_relation
485
+ end
486
+
487
+ # Infer the name of the inverse relation from the class.
488
+ #
489
+ # @example Get the inverse name
490
+ # metadata.inverse_name
491
+ #
492
+ # @return [ String ] The inverse class name underscored.
493
+ #
494
+ # @since 2.0.0.rc.1
495
+ def inverse_name
496
+ @inverse_name ||= inverse_klass.name.underscore
497
+ end
498
+
499
+ # For polymorphic children, we need to figure out the inverse from the
500
+ # actual instance on the other side, since we cannot know the exact class
501
+ # name to infer it from at load time.
502
+ #
503
+ # @example Find the inverse.
504
+ # metadata.lookup_inverse(other)
505
+ #
506
+ # @param [ Document ] : The inverse document.
507
+ #
508
+ # @return [ String ] The inverse name.
509
+ #
510
+ # @since 2.0.0.rc.1
511
+ def lookup_inverse(other)
512
+ return nil unless other
513
+ other.to_a.first.relations.each_pair do |key, meta|
514
+ return meta.name if meta.as == name
515
+ end
516
+ end
517
+
518
+ # Handles two different cases - the first is a convenience for JSON like
519
+ # access to the hash instead of having to call []. The second is a
520
+ # delegation of the "*?" methods to has_key? as a convenience to check
521
+ # for existence of a value.
522
+ #
523
+ # @example Extras provided by this method.
524
+ # metadata.name
525
+ # metadata.name?
526
+ #
527
+ # @param [ Symbol ] name The name of the method.
528
+ # @param [ Array ] args The arguments passed to the method.
529
+ #
530
+ # @return [ Object ] Either the value or a boolen.
531
+ #
532
+ # @since 2.0.0.rc.1
533
+ def method_missing(name, *args)
534
+ method = name.to_s
535
+ method =~ /\?/ ? has_key?(method.sub('?', '').to_sym) : self[name]
536
+ end
537
+ end
538
+ end
539
+ end