mongoid 2.0.2 → 2.1.0

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