mongoid-braxton 2.0.2

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