mongoid 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. data/README.rdoc +3 -1
  2. data/Rakefile +3 -2
  3. data/lib/config/locales/bg.yml +6 -0
  4. data/lib/config/locales/de.yml +6 -0
  5. data/lib/config/locales/en-GB.yml +48 -0
  6. data/lib/config/locales/en.yml +6 -3
  7. data/lib/config/locales/es.yml +6 -0
  8. data/lib/config/locales/fr.yml +6 -0
  9. data/lib/config/locales/hi.yml +39 -0
  10. data/lib/config/locales/hu.yml +13 -7
  11. data/lib/config/locales/id.yml +3 -0
  12. data/lib/config/locales/it.yml +7 -1
  13. data/lib/config/locales/ja.yml +4 -1
  14. data/lib/config/locales/kr.yml +9 -34
  15. data/lib/config/locales/nl.yml +6 -0
  16. data/lib/config/locales/pl.yml +6 -0
  17. data/lib/config/locales/pt-BR.yml +6 -0
  18. data/lib/config/locales/pt.yml +6 -0
  19. data/lib/config/locales/ro.yml +6 -0
  20. data/lib/config/locales/ru.yml +6 -0
  21. data/lib/config/locales/sv.yml +6 -0
  22. data/lib/config/locales/vi.yml +3 -0
  23. data/lib/config/locales/zh-CN.yml +6 -0
  24. data/lib/mongoid.rb +51 -45
  25. data/lib/mongoid/atomic.rb +145 -0
  26. data/lib/mongoid/atomic/modifiers.rb +109 -0
  27. data/lib/mongoid/atomic/paths.rb +3 -0
  28. data/lib/mongoid/atomic/paths/embedded.rb +43 -0
  29. data/lib/mongoid/atomic/paths/embedded/many.rb +44 -0
  30. data/lib/mongoid/atomic/paths/embedded/one.rb +43 -0
  31. data/lib/mongoid/atomic/paths/root.rb +40 -0
  32. data/lib/mongoid/attributes.rb +12 -23
  33. data/lib/mongoid/attributes/processing.rb +5 -5
  34. data/lib/mongoid/callbacks.rb +2 -0
  35. data/lib/mongoid/collection.rb +12 -59
  36. data/lib/mongoid/collections.rb +23 -20
  37. data/lib/mongoid/collections/master.rb +6 -4
  38. data/lib/mongoid/collections/operations.rb +1 -0
  39. data/lib/mongoid/collections/retry.rb +7 -0
  40. data/lib/mongoid/components.rb +2 -2
  41. data/lib/mongoid/config.rb +42 -55
  42. data/lib/mongoid/config/database.rb +6 -2
  43. data/lib/mongoid/config/replset_database.rb +7 -3
  44. data/lib/mongoid/contexts.rb +9 -3
  45. data/lib/mongoid/contexts/enumerable.rb +7 -3
  46. data/lib/mongoid/contexts/mongo.rb +139 -101
  47. data/lib/mongoid/criteria.rb +86 -69
  48. data/lib/mongoid/criterion/complex.rb +32 -5
  49. data/lib/mongoid/criterion/inclusion.rb +4 -2
  50. data/lib/mongoid/criterion/optional.rb +111 -86
  51. data/lib/mongoid/criterion/selector.rb +8 -4
  52. data/lib/mongoid/cursor.rb +27 -27
  53. data/lib/mongoid/dirty.rb +54 -214
  54. data/lib/mongoid/document.rb +37 -39
  55. data/lib/mongoid/errors/document_not_found.rb +3 -4
  56. data/lib/mongoid/errors/invalid_collection.rb +2 -3
  57. data/lib/mongoid/errors/invalid_database.rb +2 -3
  58. data/lib/mongoid/errors/invalid_field.rb +2 -3
  59. data/lib/mongoid/errors/invalid_options.rb +19 -7
  60. data/lib/mongoid/errors/invalid_type.rb +2 -3
  61. data/lib/mongoid/errors/mongoid_error.rb +5 -6
  62. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +2 -3
  63. data/lib/mongoid/errors/unsupported_version.rb +2 -3
  64. data/lib/mongoid/errors/validations.rb +2 -3
  65. data/lib/mongoid/extensions.rb +8 -62
  66. data/lib/mongoid/extensions/array/deletion.rb +29 -0
  67. data/lib/mongoid/extensions/false_class/equality.rb +14 -1
  68. data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -10
  69. data/lib/mongoid/extensions/hash/scoping.rb +14 -1
  70. data/lib/mongoid/extensions/nil/collectionization.rb +12 -1
  71. data/lib/mongoid/extensions/object/reflections.rb +33 -2
  72. data/lib/mongoid/extensions/object_id/conversions.rb +2 -36
  73. data/lib/mongoid/extensions/proc/scoping.rb +14 -1
  74. data/lib/mongoid/extensions/string/conversions.rb +4 -16
  75. data/lib/mongoid/extensions/string/inflections.rb +35 -14
  76. data/lib/mongoid/extensions/symbol/inflections.rb +38 -12
  77. data/lib/mongoid/extensions/true_class/equality.rb +14 -1
  78. data/lib/mongoid/extras.rb +11 -30
  79. data/lib/mongoid/factory.rb +1 -1
  80. data/lib/mongoid/fields.rb +121 -29
  81. data/lib/mongoid/fields/mappings.rb +36 -0
  82. data/lib/mongoid/fields/serializable.rb +131 -0
  83. data/lib/mongoid/fields/serializable/array.rb +64 -0
  84. data/lib/mongoid/fields/serializable/big_decimal.rb +42 -0
  85. data/lib/mongoid/fields/serializable/bignum.rb +10 -0
  86. data/lib/mongoid/fields/serializable/binary.rb +11 -0
  87. data/lib/mongoid/fields/serializable/boolean.rb +44 -0
  88. data/lib/mongoid/fields/serializable/date.rb +51 -0
  89. data/lib/mongoid/fields/serializable/date_time.rb +28 -0
  90. data/lib/mongoid/fields/serializable/fixnum.rb +10 -0
  91. data/lib/mongoid/fields/serializable/float.rb +33 -0
  92. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +56 -0
  93. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +43 -0
  94. data/lib/mongoid/fields/serializable/hash.rb +25 -0
  95. data/lib/mongoid/fields/serializable/integer.rb +33 -0
  96. data/lib/mongoid/fields/serializable/object.rb +11 -0
  97. data/lib/mongoid/fields/serializable/object_id.rb +32 -0
  98. data/lib/mongoid/fields/serializable/range.rb +42 -0
  99. data/lib/mongoid/fields/serializable/set.rb +42 -0
  100. data/lib/mongoid/fields/serializable/string.rb +28 -0
  101. data/lib/mongoid/fields/serializable/symbol.rb +28 -0
  102. data/lib/mongoid/fields/serializable/time.rb +12 -0
  103. data/lib/mongoid/fields/serializable/time_with_zone.rb +12 -0
  104. data/lib/mongoid/fields/serializable/timekeeping.rb +102 -0
  105. data/lib/mongoid/finders.rb +61 -37
  106. data/lib/mongoid/hierarchy.rb +43 -8
  107. data/lib/mongoid/identity_map.rb +106 -0
  108. data/lib/mongoid/indexes.rb +17 -1
  109. data/lib/mongoid/javascript.rb +2 -3
  110. data/lib/mongoid/keys.rb +10 -21
  111. data/lib/mongoid/logger.rb +22 -1
  112. data/lib/mongoid/matchers/all.rb +10 -0
  113. data/lib/mongoid/matchers/default.rb +1 -1
  114. data/lib/mongoid/matchers/exists.rb +10 -0
  115. data/lib/mongoid/matchers/gt.rb +10 -0
  116. data/lib/mongoid/matchers/gte.rb +10 -0
  117. data/lib/mongoid/matchers/in.rb +10 -0
  118. data/lib/mongoid/matchers/lt.rb +10 -0
  119. data/lib/mongoid/matchers/lte.rb +10 -0
  120. data/lib/mongoid/matchers/ne.rb +10 -0
  121. data/lib/mongoid/matchers/nin.rb +10 -0
  122. data/lib/mongoid/matchers/or.rb +7 -4
  123. data/lib/mongoid/matchers/size.rb +10 -0
  124. data/lib/mongoid/multi_database.rb +26 -6
  125. data/lib/mongoid/multi_parameter_attributes.rb +40 -17
  126. data/lib/mongoid/named_scope.rb +1 -2
  127. data/lib/mongoid/nested_attributes.rb +4 -1
  128. data/lib/mongoid/observer.rb +108 -5
  129. data/lib/mongoid/paranoia.rb +26 -26
  130. data/lib/mongoid/persistence.rb +15 -21
  131. data/lib/mongoid/persistence/atomic.rb +135 -0
  132. data/lib/mongoid/persistence/atomic/add_to_set.rb +11 -8
  133. data/lib/mongoid/persistence/atomic/bit.rb +37 -0
  134. data/lib/mongoid/persistence/atomic/inc.rb +9 -6
  135. data/lib/mongoid/persistence/atomic/operation.rb +48 -7
  136. data/lib/mongoid/persistence/atomic/pop.rb +34 -0
  137. data/lib/mongoid/persistence/atomic/pull.rb +34 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +10 -9
  139. data/lib/mongoid/persistence/atomic/push.rb +8 -5
  140. data/lib/mongoid/persistence/atomic/push_all.rb +31 -0
  141. data/lib/mongoid/persistence/atomic/rename.rb +31 -0
  142. data/lib/mongoid/persistence/atomic/set.rb +30 -0
  143. data/lib/mongoid/persistence/atomic/unset.rb +28 -0
  144. data/lib/mongoid/persistence/deletion.rb +32 -0
  145. data/lib/mongoid/persistence/insertion.rb +41 -0
  146. data/lib/mongoid/persistence/modification.rb +37 -0
  147. data/lib/mongoid/persistence/operations.rb +214 -0
  148. data/lib/mongoid/persistence/operations/embedded/insert.rb +42 -0
  149. data/lib/mongoid/persistence/operations/embedded/remove.rb +40 -0
  150. data/lib/mongoid/persistence/operations/insert.rb +34 -0
  151. data/lib/mongoid/persistence/operations/remove.rb +33 -0
  152. data/lib/mongoid/persistence/operations/update.rb +53 -0
  153. data/lib/mongoid/railtie.rb +21 -33
  154. data/lib/mongoid/railties/database.rake +12 -12
  155. data/lib/mongoid/relations.rb +9 -5
  156. data/lib/mongoid/relations/accessors.rb +15 -36
  157. data/lib/mongoid/relations/auto_save.rb +2 -2
  158. data/lib/mongoid/relations/binding.rb +28 -1
  159. data/lib/mongoid/relations/bindings/embedded/in.rb +17 -30
  160. data/lib/mongoid/relations/bindings/embedded/many.rb +16 -21
  161. data/lib/mongoid/relations/bindings/embedded/one.rb +11 -16
  162. data/lib/mongoid/relations/bindings/referenced/in.rb +31 -32
  163. data/lib/mongoid/relations/bindings/referenced/many.rb +19 -61
  164. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +15 -63
  165. data/lib/mongoid/relations/bindings/referenced/one.rb +18 -26
  166. data/lib/mongoid/relations/builder.rb +4 -2
  167. data/lib/mongoid/relations/builders.rb +21 -2
  168. data/lib/mongoid/relations/builders/embedded/in.rb +5 -1
  169. data/lib/mongoid/relations/builders/embedded/many.rb +12 -4
  170. data/lib/mongoid/relations/builders/embedded/one.rb +5 -1
  171. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -2
  172. data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
  173. data/lib/mongoid/relations/builders/referenced/in.rb +2 -5
  174. data/lib/mongoid/relations/builders/referenced/many.rb +2 -3
  175. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +14 -5
  176. data/lib/mongoid/relations/builders/referenced/one.rb +2 -3
  177. data/lib/mongoid/relations/embedded/atomic.rb +2 -2
  178. data/lib/mongoid/relations/embedded/in.rb +72 -41
  179. data/lib/mongoid/relations/embedded/many.rb +116 -120
  180. data/lib/mongoid/relations/embedded/one.rb +59 -41
  181. data/lib/mongoid/relations/embedded/sort.rb +31 -0
  182. data/lib/mongoid/relations/macros.rb +28 -24
  183. data/lib/mongoid/relations/many.rb +10 -103
  184. data/lib/mongoid/relations/metadata.rb +335 -38
  185. data/lib/mongoid/relations/one.rb +7 -32
  186. data/lib/mongoid/relations/options.rb +47 -0
  187. data/lib/mongoid/relations/proxy.rb +29 -28
  188. data/lib/mongoid/relations/referenced/batch.rb +2 -3
  189. data/lib/mongoid/relations/referenced/in.rb +66 -53
  190. data/lib/mongoid/relations/referenced/many.rb +216 -143
  191. data/lib/mongoid/relations/referenced/many_to_many.rb +132 -163
  192. data/lib/mongoid/relations/referenced/one.rb +76 -58
  193. data/lib/mongoid/relations/synchronization.rb +113 -0
  194. data/lib/mongoid/relations/targets.rb +2 -0
  195. data/lib/mongoid/relations/targets/enumerable.rb +329 -0
  196. data/lib/mongoid/safety.rb +24 -156
  197. data/lib/mongoid/serialization.rb +21 -0
  198. data/lib/mongoid/state.rb +34 -0
  199. data/lib/mongoid/threaded.rb +175 -0
  200. data/lib/mongoid/timestamps/updated.rb +1 -1
  201. data/lib/mongoid/validations.rb +3 -7
  202. data/lib/mongoid/version.rb +1 -1
  203. data/lib/mongoid/versioning.rb +61 -7
  204. data/lib/rack/mongoid.rb +2 -0
  205. data/lib/rack/mongoid/middleware/identity_map.rb +38 -0
  206. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -1
  207. data/lib/rails/generators/mongoid/model/templates/{model.rb → model.rb.tt} +0 -0
  208. data/lib/rails/generators/mongoid/observer/observer_generator.rb +1 -1
  209. data/lib/rails/generators/mongoid/observer/templates/{observer.rb → observer.rb.tt} +0 -0
  210. data/lib/rails/mongoid.rb +17 -17
  211. metadata +136 -102
  212. data/lib/mongoid/atomicity.rb +0 -111
  213. data/lib/mongoid/collections/cyclic_iterator.rb +0 -34
  214. data/lib/mongoid/collections/slaves.rb +0 -61
  215. data/lib/mongoid/extensions/array/conversions.rb +0 -23
  216. data/lib/mongoid/extensions/array/parentization.rb +0 -13
  217. data/lib/mongoid/extensions/big_decimal/conversions.rb +0 -19
  218. data/lib/mongoid/extensions/binary/conversions.rb +0 -17
  219. data/lib/mongoid/extensions/boolean/conversions.rb +0 -27
  220. data/lib/mongoid/extensions/date/conversions.rb +0 -25
  221. data/lib/mongoid/extensions/datetime/conversions.rb +0 -12
  222. data/lib/mongoid/extensions/float/conversions.rb +0 -20
  223. data/lib/mongoid/extensions/hash/conversions.rb +0 -19
  224. data/lib/mongoid/extensions/integer/conversions.rb +0 -20
  225. data/lib/mongoid/extensions/object/conversions.rb +0 -25
  226. data/lib/mongoid/extensions/range/conversions.rb +0 -25
  227. data/lib/mongoid/extensions/set/conversions.rb +0 -20
  228. data/lib/mongoid/extensions/symbol/conversions.rb +0 -21
  229. data/lib/mongoid/extensions/time_conversions.rb +0 -38
  230. data/lib/mongoid/field.rb +0 -162
  231. data/lib/mongoid/paths.rb +0 -61
  232. data/lib/mongoid/persistence/command.rb +0 -71
  233. data/lib/mongoid/persistence/insert.rb +0 -53
  234. data/lib/mongoid/persistence/insert_embedded.rb +0 -43
  235. data/lib/mongoid/persistence/remove.rb +0 -44
  236. data/lib/mongoid/persistence/remove_all.rb +0 -40
  237. data/lib/mongoid/persistence/remove_embedded.rb +0 -48
  238. data/lib/mongoid/persistence/update.rb +0 -77
  239. data/lib/mongoid/safe.rb +0 -23
  240. data/lib/mongoid/validations/referenced.rb +0 -58
@@ -13,18 +13,15 @@ module Mongoid #:nodoc:
13
13
 
14
14
  # The +Criteria+ class is the core object needed in Mongoid to retrieve
15
15
  # objects from the database. It is a DSL that essentially sets up the
16
- # selector and options arguments that get passed on to a <tt>Mongo::Collection</tt>
16
+ # selector and options arguments that get passed on to a Mongo::Collection
17
17
  # in the Ruby driver. Each method on the +Criteria+ returns self to they
18
18
  # can be chained in order to create a readable criterion to be executed
19
19
  # against the database.
20
20
  #
21
- # Example setup:
22
- #
23
- # <tt>criteria = Criteria.new</tt>
24
- #
25
- # <tt>criteria.only(:field).where(:field => "value").skip(20).limit(20)</tt>
26
- #
27
- # <tt>criteria.execute</tt>
21
+ # @example Create and execute a criteria.
22
+ # criteria = Criteria.new
23
+ # criteria.only(:field).where(:field => "value").skip(20).limit(20)
24
+ # criteria.execute
28
25
  class Criteria
29
26
  include Enumerable
30
27
  include Criterion::Builder
@@ -45,6 +42,7 @@ module Mongoid #:nodoc:
45
42
  :field_list
46
43
 
47
44
  delegate \
45
+ :add_to_set,
48
46
  :aggregate,
49
47
  :avg,
50
48
  :blank?,
@@ -62,6 +60,7 @@ module Mongoid #:nodoc:
62
60
  :max,
63
61
  :min,
64
62
  :one,
63
+ :pull,
65
64
  :shift,
66
65
  :sum,
67
66
  :update,
@@ -69,12 +68,22 @@ module Mongoid #:nodoc:
69
68
 
70
69
  # Concatinate the criteria with another enumerable. If the other is a
71
70
  # +Criteria+ then it needs to get the collection from it.
71
+ #
72
+ # @example Concat 2 criteria.
73
+ # criteria + criteria
74
+ #
75
+ # @param [ Criteria ] other The other criteria.
72
76
  def +(other)
73
77
  entries + comparable(other)
74
78
  end
75
79
 
76
80
  # Returns the difference between the criteria and another enumerable. If
77
81
  # the other is a +Criteria+ then it needs to get the collection from it.
82
+ #
83
+ # @example Get the difference of 2 criteria.
84
+ # criteria - criteria
85
+ #
86
+ # @param [ Criteria ] other The other criteria.
78
87
  def -(other)
79
88
  entries - comparable(other)
80
89
  end
@@ -82,11 +91,11 @@ module Mongoid #:nodoc:
82
91
  # Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
83
92
  # of this +Criteria+ or the criteria itself.
84
93
  #
85
- # This will force a database load when called if an enumerable is passed.
94
+ # @note This will force a database load when called if an enumerable is passed.
86
95
  #
87
- # Options:
96
+ # @param [ Object ] other The other +Enumerable+ or +Criteria+ to compare to.
88
97
  #
89
- # other: The other +Enumerable+ or +Criteria+ to compare to.
98
+ # @return [ true, false ] If the objects are equal.
90
99
  def ==(other)
91
100
  case other
92
101
  when Criteria
@@ -102,6 +111,11 @@ module Mongoid #:nodoc:
102
111
  #
103
112
  # This will return an Enumerable context if the class is embedded,
104
113
  # otherwise it will return a Mongo context for root classes.
114
+ #
115
+ # @example Get the appropriate context.
116
+ # criteria.context
117
+ #
118
+ # @return [ Mongo, Enumerable ] The appropriate context.
105
119
  def context
106
120
  @context ||= Contexts.context_for(self, embedded)
107
121
  end
@@ -109,18 +123,20 @@ module Mongoid #:nodoc:
109
123
  # Iterate over each +Document+ in the results. This can take an optional
110
124
  # block to pass to each argument in the results.
111
125
  #
112
- # Example:
126
+ # @example Iterate over the criteria results.
127
+ # criteria.each { |doc| p doc }
113
128
  #
114
- # <tt>criteria.each { |doc| p doc }</tt>
129
+ # @return [ Criteria ] The criteria itself.
115
130
  def each(&block)
116
131
  tap { context.iterate(&block) }
117
132
  end
118
133
 
119
- # Return true if the criteria has some Document or not
134
+ # Return true if the criteria has some Document or not.
120
135
  #
121
- # Example:
136
+ # @example Are there any documents for the criteria?
137
+ # criteria.exists?
122
138
  #
123
- # <tt>criteria.exists?</tt>
139
+ # @return [ true, false ] If documents match.
124
140
  def exists?
125
141
  context.count > 0
126
142
  end
@@ -141,15 +157,12 @@ module Mongoid #:nodoc:
141
157
 
142
158
  # Merges the supplied argument hash into a single criteria
143
159
  #
144
- # Options:
160
+ # @example Fuse the criteria and the object.
161
+ # criteria.fuse(:where => { :field => "value"}, :limit => 20)
145
162
  #
146
- # criteria_conditions: Hash of criteria keys, and parameter values
163
+ # @param [ Hash ] criteria_conditions Criteria keys and values.
147
164
  #
148
- # Example:
149
- #
150
- # <tt>criteria.fuse(:where => { :field => "value"}, :limit => 20)</tt>
151
- #
152
- # Returns <tt>self</tt>
165
+ # @return [ Criteria ] self.
153
166
  def fuse(criteria_conditions = {})
154
167
  criteria_conditions.inject(self) do |criteria, (key, value)|
155
168
  criteria.send(key, value)
@@ -159,10 +172,11 @@ module Mongoid #:nodoc:
159
172
  # Create the new +Criteria+ object. This will initialize the selector
160
173
  # and options hashes, as well as the type of criteria.
161
174
  #
162
- # Options:
175
+ # @example Instantiate a new criteria.
176
+ # Criteria.new(Model, true)
163
177
  #
164
- # type: One of :all, :first:, or :last
165
- # klass: The class to execute on.
178
+ # @param [ Class ] klass The model the criteria is for.
179
+ # @param [ true, false ] embedded Is the criteria for embedded docs.
166
180
  def initialize(klass, embedded = false)
167
181
  @selector = Criterion::Selector.new(klass)
168
182
  @options, @klass, @documents, @embedded = {}, klass, [], embedded
@@ -172,13 +186,15 @@ module Mongoid #:nodoc:
172
186
  # +Criteria+ or a +Hash+. This is used to combine multiple scopes together,
173
187
  # where a chained scope situation may be desired.
174
188
  #
175
- # Options:
189
+ # @example Merge the criteria with a conditions hash.
190
+ # criteria.merge({ :conditions => { :title => "Sir" } })
176
191
  #
177
- # other: The +Criteria+ or +Hash+ to merge with.
192
+ # @example Merge the criteria with another criteria.
193
+ # criteri.merge(other_criteria)
178
194
  #
179
- # Example:
195
+ # @param [ Criteria, Hash ] other The other criterion to merge with.
180
196
  #
181
- # <tt>criteria.merge({ :conditions => { :title => "Sir" } })</tt>
197
+ # @return [ Criteria ] A cloned self.
182
198
  def merge(other)
183
199
  clone.tap do |crit|
184
200
  if other.is_a?(Criteria)
@@ -193,36 +209,15 @@ module Mongoid #:nodoc:
193
209
  end
194
210
  end
195
211
 
196
- # Used for chaining +Criteria+ scopes together in the for of class methods
197
- # on the +Document+ the criteria is for.
198
- #
199
- # Options:
200
- #
201
- # name: The name of the class method on the +Document+ to chain.
202
- # args: The arguments passed to the method.
203
- # block: Optional block to pass
204
- #
205
- # Returns: <tt>Criteria</tt>
206
- def method_missing(name, *args, &block)
207
- if @klass.respond_to?(name)
208
- @klass.send(:with_scope, self) do
209
- @klass.send(name, *args, &block)
210
- end
211
- else
212
- return entries.send(name, *args)
213
- end
214
- end
215
-
216
212
  # Returns true if criteria responds to the given method.
217
213
  #
218
- # Options:
219
- #
220
- # name: The name of the class method on the +Document+.
221
- # include_private: The arguments passed to the method.
214
+ # @example Does the criteria respond to the method?
215
+ # crtiteria.respond_to?(:each)
222
216
  #
223
- # Example:
217
+ # @param [ Symbol ] name The name of the class method on the +Document+.
218
+ # @param [ true, false ] include_private Whether to include privates.
224
219
  #
225
- # <tt>criteria.respond_to?(:batch_update)</tt>
220
+ # @return [ true, false ] If the criteria responds to the method.
226
221
  def respond_to?(name, include_private = false)
227
222
  # don't include klass private methods because method_missing won't call them
228
223
  super || @klass.respond_to?(name) || entries.respond_to?(name, include_private)
@@ -230,6 +225,11 @@ module Mongoid #:nodoc:
230
225
 
231
226
  # Returns the selector and options as a +Hash+ that would be passed to a
232
227
  # scope for use with named scopes.
228
+ #
229
+ # @example Get the criteria as a scoped hash.
230
+ # criteria.scoped
231
+ #
232
+ # @return [ Hash ] The criteria as a scoped hash.
233
233
  def scoped
234
234
  scope_options = @options.dup
235
235
  sorting = scope_options.delete(:sort)
@@ -303,6 +303,13 @@ module Mongoid #:nodoc:
303
303
 
304
304
  # Return the entries of the other criteria or the object. Used for
305
305
  # comparing criteria or an enumerable.
306
+ #
307
+ # @example Get the comparable version.
308
+ # criteria.comparable(other)
309
+ #
310
+ # @param [ Criteria ] other Another criteria.
311
+ #
312
+ # @return [ Array ] The array to compare with.
306
313
  def comparable(other)
307
314
  other.is_a?(Criteria) ? other.entries : other
308
315
  end
@@ -310,36 +317,46 @@ module Mongoid #:nodoc:
310
317
  # Clone or dup the current +Criteria+. This will return a new criteria with
311
318
  # the selector, options, klass, embedded options, etc intact.
312
319
  #
313
- # Example:
314
- #
315
- # <tt>criteria.clone</tt>
316
- # <tt>criteria.dup</tt>
320
+ # @example Clone a criteria.
321
+ # criteria.clone
317
322
  #
318
- # Options:
323
+ # @example Dup a criteria.
324
+ # criteria.dup
319
325
  #
320
- # other: The criteria getting cloned.
326
+ # @param [ Criteria ] other The criteria getting cloned.
321
327
  #
322
- # Returns:
323
- #
324
- # A new identical criteria
328
+ # @return [ nil ] nil.
325
329
  def initialize_copy(other)
326
330
  @selector = other.selector.dup
327
331
  @options = other.options.dup
328
332
  @context = nil
329
333
  end
330
334
 
335
+ # Used for chaining +Criteria+ scopes together in the for of class methods
336
+ # on the +Document+ the criteria is for.
337
+ def method_missing(name, *args, &block)
338
+ if @klass.respond_to?(name)
339
+ @klass.send(:with_scope, self) do
340
+ @klass.send(name, *args, &block)
341
+ end
342
+ else
343
+ return entries.send(name, *args)
344
+ end
345
+ end
346
+
331
347
  # Update the selector setting the operator on the value for each key in the
332
348
  # supplied attributes +Hash+.
333
349
  #
334
- # Example:
335
- #
336
- # <tt>criteria.update_selector({ :field => "value" }, "$in")</tt>
350
+ # @example Update the selector.
351
+ # criteria.update_selector({ :field => "value" }, "$in")
337
352
  #
353
+ # @param [ Hash, Array ] attributes The values to convert and apply.
354
+ # @param [ String ] operator The MongoDB operator.
338
355
  # @param [ Symbol ] combine The operator to use when combining sets.
339
356
  def update_selector(attributes, operator, combine = :+)
340
357
  clone.tap do |crit|
341
358
  converted = BSON::ObjectId.convert(klass, attributes || {})
342
- converted.each do |key, value|
359
+ converted.each_pair do |key, value|
343
360
  unless crit.selector[key]
344
361
  crit.selector[key] = { operator => value }
345
362
  else
@@ -1,30 +1,57 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Criterion #:nodoc:
4
+
4
5
  # Complex criterion are used when performing operations on symbols to get
5
6
  # get a shorthand syntax for where clauses.
6
7
  #
7
- # Example:
8
- #
9
- # <tt>{ :field => { "$lt" => "value" } }</tt>
10
- # becomes:
11
- # <tt> { :field.lt => "value }</tt>
8
+ # @example Conversion of a simple to complex criterion.
9
+ # { :field => { "$lt" => "value" } }
10
+ # becomes:
11
+ # { :field.lt => "value }
12
12
  class Complex
13
13
  attr_accessor :key, :operator
14
14
 
15
15
  # Create the new complex criterion.
16
+ #
17
+ # @example Instantiate a new complex criterion.
18
+ # Complex.new(:key => :field, :operator => "$gt")
19
+ #
20
+ # @param [ Hash ] opts The options to convert.
16
21
  def initialize(opts = {})
17
22
  @key, @operator = opts[:key], opts[:operator]
18
23
  end
19
24
 
25
+ # Get the criterion as a hash.
26
+ #
27
+ # @example Get the criterion as a hash.
28
+ # criterion.hash
29
+ #
30
+ # @return [ Hash ] The keys and operators.
20
31
  def hash
21
32
  [@key, @operator].hash
22
33
  end
23
34
 
35
+ # Is the criterion equal to the other?
36
+ #
37
+ # @example Check equality.
38
+ # criterion.eql?(other)
39
+ #
40
+ # @param [ Complex ] other The other complex criterion.
41
+ #
42
+ # @return [ true, false ] If they are equal.
24
43
  def eql?(other)
25
44
  self == (other)
26
45
  end
27
46
 
47
+ # Is the criterion equal to the other?
48
+ #
49
+ # @example Check equality.
50
+ # criterion == other
51
+ #
52
+ # @param [ Complex ] other The other complex criterion.
53
+ #
54
+ # @return [ true, false ] If they are equal.
28
55
  def ==(other)
29
56
  return false unless other.is_a?(self.class)
30
57
  self.key == other.key && self.operator == other.operator
@@ -67,7 +67,7 @@ module Mongoid #:nodoc:
67
67
  clone.tap do |crit|
68
68
  criterion = @selector["$or"] || []
69
69
  converted = BSON::ObjectId.convert(klass, args.flatten)
70
- expanded = converted.collect(&:expand_complex_criteria)
70
+ expanded = converted.collect { |hash| hash.expand_complex_criteria }
71
71
  crit.selector["$or"] = criterion.concat(expanded)
72
72
  end
73
73
  end
@@ -159,7 +159,9 @@ module Mongoid #:nodoc:
159
159
  end
160
160
 
161
161
  selector.each_pair do |key, value|
162
- if crit.selector.has_key?(key) && crit.selector[key].respond_to?(:merge!)
162
+ if crit.selector.has_key?(key) &&
163
+ crit.selector[key].respond_to?(:merge!) &&
164
+ value.respond_to?(:merge!)
163
165
  crit.selector[key] =
164
166
  crit.selector[key].merge!(value) do |key, old, new|
165
167
  key == '$in' ? old & new : new
@@ -6,13 +6,17 @@ module Mongoid #:nodoc:
6
6
  # Adds fields to be sorted in ascending order. Will add them in the order
7
7
  # they were passed into the method.
8
8
  #
9
- # Example:
9
+ # @example Sort in ascending order.
10
+ # criteria.ascending(:title, :dob)
11
+ # criteria.asc(:title, :dob)
10
12
  #
11
- # <tt>criteria.ascending(:title, :dob)</tt>
13
+ # @param [ Array<Symbol> ] fields The fields to sort on.
14
+ #
15
+ # @return [ Criteria ] The cloned criteria.
12
16
  def ascending(*fields)
13
17
  clone.tap do |crit|
14
18
  crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
15
- fields.flatten.each { |field| crit.options[:sort] << [ field, :asc ] }
19
+ fields.flatten.each { |field| merge_options(crit.options[:sort], [ field, :asc ]) }
16
20
  end
17
21
  end
18
22
  alias :asc :ascending
@@ -22,18 +26,20 @@ module Mongoid #:nodoc:
22
26
  # times, however this is not advisable when working with large data sets
23
27
  # as the entire results will get stored in memory.
24
28
  #
25
- # Example:
29
+ # @example Flag the criteria as cached.
30
+ # criteria.cache
26
31
  #
27
- # <tt>criteria.cache</tt>
32
+ # @return [ Criteria ] The cloned criteria.
28
33
  def cache
29
34
  clone.tap { |crit| crit.options.merge!(:cache => true) }
30
35
  end
31
36
 
32
37
  # Will return true if the cache option has been set.
33
38
  #
34
- # Example:
39
+ # @example Is the criteria cached?
40
+ # criteria.cached?
35
41
  #
36
- # <tt>criteria.cached?</tt>
42
+ # @return [ true, false ] If the criteria is flagged as cached.
37
43
  def cached?
38
44
  options[:cache] == true
39
45
  end
@@ -41,48 +47,30 @@ module Mongoid #:nodoc:
41
47
  # Adds fields to be sorted in descending order. Will add them in the order
42
48
  # they were passed into the method.
43
49
  #
44
- # Example:
50
+ # @example Sort the criteria in descending order.
51
+ # criteria.descending(:title, :dob)
52
+ # criteria.desc(:title, :dob)
53
+ #
54
+ # @param [ Array<Symbol> ] fields The fields to sort on.
45
55
  #
46
- # <tt>criteria.descending(:title, :dob)</tt>
56
+ # @return [ Criteria ] The cloned criteria.
47
57
  def descending(*fields)
48
58
  clone.tap do |crit|
49
59
  crit.options[:sort] = [] unless options[:sort] || fields.first.nil?
50
- fields.flatten.each { |field| crit.options[:sort] << [ field, :desc ] }
60
+ fields.flatten.each { |field| merge_options(crit.options[:sort], [ field, :desc ]) }
51
61
  end
52
62
  end
53
63
  alias :desc :descending
54
64
 
55
- # Flags the criteria to execute against a read-only slave in the pool
56
- # instead of master.
57
- #
58
- # Example:
59
- #
60
- # <tt>criteria.enslave</tt>
61
- def enslave
62
- clone.tap { |crit| crit.options.merge!(:enslave => true) }
63
- end
64
-
65
- # Will return true if the criteria is enslaved.
66
- #
67
- # Example:
68
- #
69
- # <tt>criteria.enslaved?</tt>
70
- def enslaved?
71
- options[:enslave] == true
72
- end
73
-
74
65
  # Adds a criterion to the +Criteria+ that specifies additional options
75
66
  # to be passed to the Ruby driver, in the exact format for the driver.
76
67
  #
77
- # Options:
78
- #
79
- # extras: A +Hash+ that gets set to the driver options.
68
+ # @example Add extra params to the criteria.
69
+ # criteria.extras(:limit => 20, :skip => 40)
80
70
  #
81
- # Example:
71
+ # @param [ Hash ] extras The extra driver options.
82
72
  #
83
- # <tt>criteria.extras(:limit => 20, :skip => 40)</tt>
84
- #
85
- # Returns: <tt>self</tt>
73
+ # @return [ Criteria ] The cloned criteria.
86
74
  def extras(extras)
87
75
  clone.tap do |crit|
88
76
  crit.options.merge!(extras)
@@ -91,16 +79,15 @@ module Mongoid #:nodoc:
91
79
 
92
80
  # Adds a criterion to the +Criteria+ that specifies an id that must be matched.
93
81
  #
94
- # Options:
95
- #
96
- # object_id: A single id or an array of ids in +String+ or <tt>BSON::ObjectId</tt> format
82
+ # @example Add a single id criteria.
83
+ # criteria.for_ids("4ab2bc4b8ad548971900005c")
97
84
  #
98
- # Example:
85
+ # @example Add multiple id criteria.
86
+ # criteria.for_ids(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])
99
87
  #
100
- # <tt>criteria.for_ids("4ab2bc4b8ad548971900005c")</tt>
101
- # <tt>criteria.for_ids(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])</tt>
88
+ # @param [ Array ] ids: A single id or an array of ids.
102
89
  #
103
- # Returns: <tt>self</tt>
90
+ # @return [ Criteria ] The cloned criteria.
104
91
  def for_ids(*ids)
105
92
  ids.flatten!
106
93
  if ids.size > 1
@@ -116,18 +103,15 @@ module Mongoid #:nodoc:
116
103
  end
117
104
 
118
105
  # Adds a criterion to the +Criteria+ that specifies the maximum number of
119
- # results to return. This is mostly used in conjunction with <tt>skip()</tt>
106
+ # results to return. This is mostly used in conjunction with skip()
120
107
  # to handle paginated results.
121
108
  #
122
- # Options:
123
- #
124
- # value: An +Integer+ specifying the max number of results. Defaults to 20.
109
+ # @example Limit the result set size.
110
+ # criteria.limit(100)
125
111
  #
126
- # Example:
112
+ # @param [ Integer ] value The max number of results.
127
113
  #
128
- # <tt>criteria.limit(100)</tt>
129
- #
130
- # Returns: <tt>self</tt>
114
+ # @return [ Criteria ] The cloned criteria.
131
115
  def limit(value = 20)
132
116
  clone.tap { |crit| crit.options[:limit] = value }
133
117
  end
@@ -135,6 +119,11 @@ module Mongoid #:nodoc:
135
119
  # Returns the offset option. If a per_page option is in the list then it
136
120
  # will replace it with a skip parameter and return the same value. Defaults
137
121
  # to 20 if nothing was provided.
122
+ #
123
+ # @example Get the offset.
124
+ # criteria.offset(10)
125
+ #
126
+ # @return [ Integer ] The number of documents to skip.
138
127
  def offset(*args)
139
128
  args.size > 0 ? skip(args.first) : options[:skip]
140
129
  end
@@ -142,30 +131,25 @@ module Mongoid #:nodoc:
142
131
  # Adds a criterion to the +Criteria+ that specifies the sort order of
143
132
  # the returned documents in the database. Similar to a SQL "ORDER BY".
144
133
  #
145
- # Options:
146
- #
147
- # params: An +Array+ of [field, direction] sorting pairs.
148
- #
149
- # Example:
134
+ # @example Order by specific fields.
135
+ # criteria.order_by([[:field1, :asc], [:field2, :desc]])
150
136
  #
151
- # <tt>criteria.order_by([[:field1, :asc], [:field2, :desc]])</tt>
137
+ # @param [ Array ] params: An +Array+ of [field, direction] sorting pairs.
152
138
  #
153
- # Returns: <tt>self</tt>
139
+ # @return [ Criteria ] The cloned criteria.
154
140
  def order_by(*args)
155
141
  clone.tap do |crit|
142
+ arguments = args.size == 1 ? args.first : args
156
143
  crit.options[:sort] = [] unless options[:sort] || args.first.nil?
157
- arguments = args.first
158
- case arguments
159
- when Hash
160
- arguments.each do |field, direction|
161
- crit.options[:sort] << [ field, direction ]
162
- end
163
- when Array
164
- crit.options[:sort].concat(arguments)
165
- when Complex
166
- args.flatten.each do |complex|
167
- crit.options[:sort] << [ complex.key, complex.operator.to_sym ]
144
+ if arguments.is_a?(Array)
145
+ #[:name, :asc]
146
+ if arguments.size == 2 && (arguments.first.is_a?(Symbol) || arguments.first.is_a?(String))
147
+ build_order_options(arguments, crit)
148
+ else
149
+ arguments.each { |argument| build_order_options(argument, crit) }
168
150
  end
151
+ else
152
+ build_order_options(arguments, crit)
169
153
  end
170
154
  end
171
155
  end
@@ -173,40 +157,81 @@ module Mongoid #:nodoc:
173
157
 
174
158
  # Adds a criterion to the +Criteria+ that specifies how many results to skip
175
159
  # when returning Documents. This is mostly used in conjunction with
176
- # <tt>limit()</tt> to handle paginated results, and is similar to the
160
+ # limit() to handle paginated results, and is similar to the
177
161
  # traditional "offset" parameter.
178
162
  #
179
- # Options:
180
- #
181
- # value: An +Integer+ specifying the number of results to skip. Defaults to 0.
163
+ # @example Skip a specified number of documents.
164
+ # criteria.skip(20)
182
165
  #
183
- # Example:
166
+ # @param [ Integer ] value The number of results to skip.
184
167
  #
185
- # <tt>criteria.skip(20)</tt>
186
- #
187
- # Returns: <tt>self</tt>
168
+ # @return [ Criteria ] The cloned criteria.
188
169
  def skip(value = 0)
189
170
  clone.tap { |crit| crit.options[:skip] = value }
190
171
  end
191
172
 
192
173
  # Adds a criterion to the +Criteria+ that specifies a type or an Array of
193
- # type that must be matched.
194
- #
195
- # Options:
196
- #
197
- # types : An +Array+ of types of a +String+ representing the Type of you search
174
+ # types that must be matched.
198
175
  #
199
- # Example:
176
+ # @example Match only specific models.
177
+ # criteria.type('Browser')
178
+ # criteria.type(['Firefox', 'Browser'])
200
179
  #
201
- # <tt>criteria.type('Browser')</tt>
202
- # <tt>criteria.type(['Firefox', 'Browser'])</tt>
180
+ # @param [ Array<String> ] types The types to match against.
203
181
  #
204
- # Returns: <tt>self</tt>
182
+ # @return [ Criteria ] The cloned criteria.
205
183
  def type(types)
206
184
  types = [types] unless types.is_a?(Array)
207
185
  any_in(:_type => types)
208
186
  end
209
187
 
188
+ private
189
+
190
+ # Build ordering options from given arguments on given criteria
191
+ #
192
+ # @example build order options
193
+ # criteria.build_order_options(:name.asc, criteria)
194
+ #
195
+ #
196
+ # @param [ <Hash>, <Array>, <Complex> ] argument to build criteria from
197
+ # @param [ Criterion ] criterion to change
198
+ def build_order_options(arguments, crit)
199
+ case arguments
200
+ when Hash
201
+ if arguments.size > 1
202
+ raise ArgumentError, "Please don't use hash to define multiple orders " +
203
+ "due to the fact that hash doesn't have order this may cause unpredictable results"
204
+ end
205
+ arguments.each_pair do |field, direction|
206
+ merge_options(crit.options[:sort], [ field, direction ])
207
+ end
208
+ when Array
209
+ merge_options(crit.options[:sort],arguments)
210
+ when Complex
211
+ merge_options(crit.options[:sort], [ arguments.key, arguments.operator.to_sym ])
212
+ end
213
+ end
214
+
215
+ # Merge options for order_by criterion
216
+ # Allow only one order direction for same field
217
+ #
218
+ # @example Merge ordering options
219
+ # criteria.merge_options([[:title, :asc], [:created_at, :asc]], [:title, :desc])
220
+ #
221
+ #
222
+ # @param [ Array<Array> ] Existing options
223
+ # @param [ Array ] New option for merge.
224
+ #
225
+ # @since 2.1.0
226
+ def merge_options(options, new_option)
227
+ old_option = options.assoc(new_option.first)
228
+
229
+ if old_option
230
+ options[options.index(old_option)] = new_option.flatten
231
+ else
232
+ options << new_option.flatten
233
+ end
234
+ end
210
235
  end
211
236
  end
212
237
  end