stonegao-mongoid 2.0.0.rc.6

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 (199) 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 +44 -0
  5. data/lib/config/locales/de.yml +44 -0
  6. data/lib/config/locales/en.yml +45 -0
  7. data/lib/config/locales/es.yml +44 -0
  8. data/lib/config/locales/fr.yml +45 -0
  9. data/lib/config/locales/hu.yml +47 -0
  10. data/lib/config/locales/it.yml +42 -0
  11. data/lib/config/locales/kr.yml +68 -0
  12. data/lib/config/locales/nl.yml +42 -0
  13. data/lib/config/locales/pl.yml +42 -0
  14. data/lib/config/locales/pt-br.yml +43 -0
  15. data/lib/config/locales/pt.yml +43 -0
  16. data/lib/config/locales/ro.yml +49 -0
  17. data/lib/config/locales/sv.yml +43 -0
  18. data/lib/config/locales/zh-CN.yml +34 -0
  19. data/lib/mongoid/atomicity.rb +111 -0
  20. data/lib/mongoid/attributes.rb +251 -0
  21. data/lib/mongoid/callbacks.rb +13 -0
  22. data/lib/mongoid/collection.rb +137 -0
  23. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/mongoid/collections/master.rb +29 -0
  25. data/lib/mongoid/collections/operations.rb +42 -0
  26. data/lib/mongoid/collections/slaves.rb +45 -0
  27. data/lib/mongoid/collections.rb +70 -0
  28. data/lib/mongoid/components.rb +45 -0
  29. data/lib/mongoid/config/database.rb +167 -0
  30. data/lib/mongoid/config/replset_database.rb +48 -0
  31. data/lib/mongoid/config.rb +343 -0
  32. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  33. data/lib/mongoid/contexts/enumerable.rb +226 -0
  34. data/lib/mongoid/contexts/ids.rb +25 -0
  35. data/lib/mongoid/contexts/mongo.rb +345 -0
  36. data/lib/mongoid/contexts/paging.rb +50 -0
  37. data/lib/mongoid/contexts.rb +21 -0
  38. data/lib/mongoid/copyable.rb +44 -0
  39. data/lib/mongoid/criteria.rb +325 -0
  40. data/lib/mongoid/criterion/complex.rb +34 -0
  41. data/lib/mongoid/criterion/creational.rb +34 -0
  42. data/lib/mongoid/criterion/exclusion.rb +67 -0
  43. data/lib/mongoid/criterion/inclusion.rb +134 -0
  44. data/lib/mongoid/criterion/inspection.rb +20 -0
  45. data/lib/mongoid/criterion/optional.rb +213 -0
  46. data/lib/mongoid/criterion/selector.rb +74 -0
  47. data/lib/mongoid/cursor.rb +81 -0
  48. data/lib/mongoid/default_scope.rb +28 -0
  49. data/lib/mongoid/dirty.rb +251 -0
  50. data/lib/mongoid/document.rb +256 -0
  51. data/lib/mongoid/errors/document_not_found.rb +29 -0
  52. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  53. data/lib/mongoid/errors/invalid_database.rb +20 -0
  54. data/lib/mongoid/errors/invalid_field.rb +19 -0
  55. data/lib/mongoid/errors/invalid_options.rb +16 -0
  56. data/lib/mongoid/errors/invalid_type.rb +26 -0
  57. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  58. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  59. data/lib/mongoid/errors/unsaved_document.rb +23 -0
  60. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  61. data/lib/mongoid/errors/validations.rb +24 -0
  62. data/lib/mongoid/errors.rb +12 -0
  63. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  64. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  65. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  66. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  67. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  68. data/lib/mongoid/extensions/date/conversions.rb +25 -0
  69. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  70. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  71. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  72. data/lib/mongoid/extensions/hash/conversions.rb +19 -0
  73. data/lib/mongoid/extensions/hash/criteria_helpers.rb +22 -0
  74. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  75. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  76. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  77. data/lib/mongoid/extensions/object/conversions.rb +25 -0
  78. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  79. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  80. data/lib/mongoid/extensions/object_id/conversions.rb +57 -0
  81. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  82. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  83. data/lib/mongoid/extensions/string/conversions.rb +34 -0
  84. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  85. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  86. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  87. data/lib/mongoid/extensions/time_conversions.rb +38 -0
  88. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  89. data/lib/mongoid/extensions.rb +116 -0
  90. data/lib/mongoid/extras.rb +61 -0
  91. data/lib/mongoid/factory.rb +20 -0
  92. data/lib/mongoid/field.rb +95 -0
  93. data/lib/mongoid/fields.rb +138 -0
  94. data/lib/mongoid/finders.rb +173 -0
  95. data/lib/mongoid/hierarchy.rb +85 -0
  96. data/lib/mongoid/identity.rb +89 -0
  97. data/lib/mongoid/indexes.rb +38 -0
  98. data/lib/mongoid/inspection.rb +58 -0
  99. data/lib/mongoid/javascript/functions.yml +37 -0
  100. data/lib/mongoid/javascript.rb +21 -0
  101. data/lib/mongoid/json.rb +16 -0
  102. data/lib/mongoid/keys.rb +77 -0
  103. data/lib/mongoid/logger.rb +18 -0
  104. data/lib/mongoid/matchers/all.rb +11 -0
  105. data/lib/mongoid/matchers/default.rb +27 -0
  106. data/lib/mongoid/matchers/exists.rb +13 -0
  107. data/lib/mongoid/matchers/gt.rb +11 -0
  108. data/lib/mongoid/matchers/gte.rb +11 -0
  109. data/lib/mongoid/matchers/in.rb +11 -0
  110. data/lib/mongoid/matchers/lt.rb +11 -0
  111. data/lib/mongoid/matchers/lte.rb +11 -0
  112. data/lib/mongoid/matchers/ne.rb +11 -0
  113. data/lib/mongoid/matchers/nin.rb +11 -0
  114. data/lib/mongoid/matchers/size.rb +11 -0
  115. data/lib/mongoid/matchers.rb +55 -0
  116. data/lib/mongoid/modifiers/command.rb +18 -0
  117. data/lib/mongoid/modifiers/inc.rb +24 -0
  118. data/lib/mongoid/modifiers.rb +24 -0
  119. data/lib/mongoid/multi_database.rb +11 -0
  120. data/lib/mongoid/multi_parameter_attributes.rb +80 -0
  121. data/lib/mongoid/named_scope.rb +36 -0
  122. data/lib/mongoid/nested_attributes.rb +43 -0
  123. data/lib/mongoid/paranoia.rb +103 -0
  124. data/lib/mongoid/paths.rb +61 -0
  125. data/lib/mongoid/persistence/command.rb +59 -0
  126. data/lib/mongoid/persistence/insert.rb +53 -0
  127. data/lib/mongoid/persistence/insert_embedded.rb +42 -0
  128. data/lib/mongoid/persistence/remove.rb +44 -0
  129. data/lib/mongoid/persistence/remove_all.rb +40 -0
  130. data/lib/mongoid/persistence/remove_embedded.rb +48 -0
  131. data/lib/mongoid/persistence/update.rb +76 -0
  132. data/lib/mongoid/persistence.rb +237 -0
  133. data/lib/mongoid/railtie.rb +129 -0
  134. data/lib/mongoid/railties/database.rake +171 -0
  135. data/lib/mongoid/railties/document.rb +12 -0
  136. data/lib/mongoid/relations/accessors.rb +157 -0
  137. data/lib/mongoid/relations/auto_save.rb +34 -0
  138. data/lib/mongoid/relations/binding.rb +26 -0
  139. data/lib/mongoid/relations/bindings/embedded/in.rb +82 -0
  140. data/lib/mongoid/relations/bindings/embedded/many.rb +98 -0
  141. data/lib/mongoid/relations/bindings/embedded/one.rb +66 -0
  142. data/lib/mongoid/relations/bindings/referenced/in.rb +74 -0
  143. data/lib/mongoid/relations/bindings/referenced/many.rb +96 -0
  144. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +99 -0
  145. data/lib/mongoid/relations/bindings/referenced/one.rb +66 -0
  146. data/lib/mongoid/relations/bindings.rb +9 -0
  147. data/lib/mongoid/relations/builder.rb +42 -0
  148. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  149. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  150. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  151. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  152. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  153. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  154. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  155. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  156. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  157. data/lib/mongoid/relations/builders.rb +79 -0
  158. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  159. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  160. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  161. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  162. data/lib/mongoid/relations/cascading.rb +55 -0
  163. data/lib/mongoid/relations/constraint.rb +45 -0
  164. data/lib/mongoid/relations/cyclic.rb +97 -0
  165. data/lib/mongoid/relations/embedded/in.rb +173 -0
  166. data/lib/mongoid/relations/embedded/many.rb +483 -0
  167. data/lib/mongoid/relations/embedded/one.rb +170 -0
  168. data/lib/mongoid/relations/macros.rb +306 -0
  169. data/lib/mongoid/relations/many.rb +171 -0
  170. data/lib/mongoid/relations/metadata.rb +533 -0
  171. data/lib/mongoid/relations/nested_builder.rb +68 -0
  172. data/lib/mongoid/relations/one.rb +47 -0
  173. data/lib/mongoid/relations/polymorphic.rb +54 -0
  174. data/lib/mongoid/relations/proxy.rb +128 -0
  175. data/lib/mongoid/relations/referenced/in.rb +216 -0
  176. data/lib/mongoid/relations/referenced/many.rb +443 -0
  177. data/lib/mongoid/relations/referenced/many_to_many.rb +344 -0
  178. data/lib/mongoid/relations/referenced/one.rb +206 -0
  179. data/lib/mongoid/relations/reflections.rb +45 -0
  180. data/lib/mongoid/relations.rb +105 -0
  181. data/lib/mongoid/safe.rb +23 -0
  182. data/lib/mongoid/safety.rb +207 -0
  183. data/lib/mongoid/scope.rb +31 -0
  184. data/lib/mongoid/serialization.rb +99 -0
  185. data/lib/mongoid/state.rb +66 -0
  186. data/lib/mongoid/timestamps.rb +38 -0
  187. data/lib/mongoid/validations/associated.rb +42 -0
  188. data/lib/mongoid/validations/uniqueness.rb +85 -0
  189. data/lib/mongoid/validations.rb +117 -0
  190. data/lib/mongoid/version.rb +4 -0
  191. data/lib/mongoid/versioning.rb +51 -0
  192. data/lib/mongoid.rb +139 -0
  193. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  194. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +23 -0
  195. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  196. data/lib/rails/generators/mongoid/model/templates/model.rb +17 -0
  197. data/lib/rails/generators/mongoid_generator.rb +61 -0
  198. data/lib/rails/mongoid.rb +57 -0
  199. metadata +380 -0
@@ -0,0 +1,325 @@
1
+ # encoding: utf-8
2
+ require "mongoid/criterion/creational"
3
+ require "mongoid/criterion/complex"
4
+ require "mongoid/criterion/exclusion"
5
+ require "mongoid/criterion/inclusion"
6
+ require "mongoid/criterion/inspection"
7
+ require "mongoid/criterion/optional"
8
+ require "mongoid/criterion/selector"
9
+
10
+ module Mongoid #:nodoc:
11
+
12
+ # The +Criteria+ class is the core object needed in Mongoid to retrieve
13
+ # objects from the database. It is a DSL that essentially sets up the
14
+ # selector and options arguments that get passed on to a <tt>Mongo::Collection</tt>
15
+ # in the Ruby driver. Each method on the +Criteria+ returns self to they
16
+ # can be chained in order to create a readable criterion to be executed
17
+ # against the database.
18
+ #
19
+ # Example setup:
20
+ #
21
+ # <tt>criteria = Criteria.new</tt>
22
+ #
23
+ # <tt>criteria.only(:field).where(:field => "value").skip(20).limit(20)</tt>
24
+ #
25
+ # <tt>criteria.execute</tt>
26
+ class Criteria
27
+ include Enumerable
28
+ include Criterion::Creational
29
+ include Criterion::Exclusion
30
+ include Criterion::Inclusion
31
+ include Criterion::Inspection
32
+ include Criterion::Optional
33
+
34
+ attr_accessor :collection, :documents, :embedded, :ids, :klass, :options, :selector
35
+
36
+ delegate \
37
+ :aggregate,
38
+ :avg,
39
+ :blank?,
40
+ :count,
41
+ :delete,
42
+ :delete_all,
43
+ :destroy,
44
+ :destroy_all,
45
+ :distinct,
46
+ :empty?,
47
+ :execute,
48
+ :first,
49
+ :group,
50
+ :id_criteria,
51
+ :last,
52
+ :max,
53
+ :min,
54
+ :one,
55
+ :page,
56
+ :paginate,
57
+ :per_page,
58
+ :shift,
59
+ :sum,
60
+ :update,
61
+ :update_all, :to => :context
62
+
63
+ # Concatinate the criteria with another enumerable. If the other is a
64
+ # +Criteria+ then it needs to get the collection from it.
65
+ def +(other)
66
+ entries + comparable(other)
67
+ end
68
+
69
+ # Returns the difference between the criteria and another enumerable. If
70
+ # the other is a +Criteria+ then it needs to get the collection from it.
71
+ def -(other)
72
+ entries - comparable(other)
73
+ end
74
+
75
+ # Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
76
+ # of this +Criteria+ or the criteria itself.
77
+ #
78
+ # This will force a database load when called if an enumerable is passed.
79
+ #
80
+ # Options:
81
+ #
82
+ # other: The other +Enumerable+ or +Criteria+ to compare to.
83
+ def ==(other)
84
+ case other
85
+ when Criteria
86
+ self.selector == other.selector && self.options == other.options
87
+ when Enumerable
88
+ return (execute.entries == other)
89
+ else
90
+ return false
91
+ end
92
+ end
93
+
94
+ # Return or create the context in which this criteria should be executed.
95
+ #
96
+ # This will return an Enumerable context if the class is embedded,
97
+ # otherwise it will return a Mongo context for root classes.
98
+ def context
99
+ @context ||= Contexts.context_for(self, embedded)
100
+ end
101
+
102
+ # Iterate over each +Document+ in the results. This can take an optional
103
+ # block to pass to each argument in the results.
104
+ #
105
+ # Example:
106
+ #
107
+ # <tt>criteria.each { |doc| p doc }</tt>
108
+ def each(&block)
109
+ tap { context.iterate(&block) }
110
+ end
111
+
112
+ # Return true if the criteria has some Document or not
113
+ #
114
+ # Example:
115
+ #
116
+ # <tt>criteria.exists?</tt>
117
+ def exists?
118
+ context.count > 0
119
+ end
120
+
121
+ # Merges the supplied argument hash into a single criteria
122
+ #
123
+ # Options:
124
+ #
125
+ # criteria_conditions: Hash of criteria keys, and parameter values
126
+ #
127
+ # Example:
128
+ #
129
+ # <tt>criteria.fuse(:where => { :field => "value"}, :limit => 20)</tt>
130
+ #
131
+ # Returns <tt>self</tt>
132
+ def fuse(criteria_conditions = {})
133
+ criteria_conditions.inject(self) do |criteria, (key, value)|
134
+ criteria.send(key, value)
135
+ end
136
+ end
137
+
138
+ # Create the new +Criteria+ object. This will initialize the selector
139
+ # and options hashes, as well as the type of criteria.
140
+ #
141
+ # Options:
142
+ #
143
+ # type: One of :all, :first:, or :last
144
+ # klass: The class to execute on.
145
+ def initialize(klass, embedded = false)
146
+ @selector = Criterion::Selector.new(klass)
147
+ @options, @klass, @documents, @embedded = {}, klass, [], embedded
148
+ end
149
+
150
+ # Merges another object into this +Criteria+. The other object may be a
151
+ # +Criteria+ or a +Hash+. This is used to combine multiple scopes together,
152
+ # where a chained scope situation may be desired.
153
+ #
154
+ # Options:
155
+ #
156
+ # other: The +Criteria+ or +Hash+ to merge with.
157
+ #
158
+ # Example:
159
+ #
160
+ # <tt>criteria.merge({ :conditions => { :title => "Sir" } })</tt>
161
+ def merge(other)
162
+ clone.tap do |crit|
163
+ crit.selector.update(other.selector)
164
+ crit.options.update(other.options)
165
+ crit.documents = other.documents
166
+ end
167
+ end
168
+
169
+ # Used for chaining +Criteria+ scopes together in the for of class methods
170
+ # on the +Document+ the criteria is for.
171
+ #
172
+ # Options:
173
+ #
174
+ # name: The name of the class method on the +Document+ to chain.
175
+ # args: The arguments passed to the method.
176
+ #
177
+ # Returns: <tt>Criteria</tt>
178
+ def method_missing(name, *args)
179
+ if @klass.respond_to?(name)
180
+ @klass.send(:with_scope, self) do
181
+ @klass.send(name, *args)
182
+ end
183
+ else
184
+ return entries.send(name, *args)
185
+ end
186
+ end
187
+
188
+ # Returns the selector and options as a +Hash+ that would be passed to a
189
+ # scope for use with named scopes.
190
+ def scoped
191
+ scope_options = @options.dup
192
+ sorting = scope_options.delete(:sort)
193
+ scope_options[:order_by] = sorting if sorting
194
+ { :where => @selector }.merge(scope_options)
195
+ end
196
+ alias :to_ary :to_a
197
+
198
+ class << self
199
+
200
+ # Encaspulates the behavior of taking arguments and parsing them into a
201
+ # finder type and a corresponding criteria object.
202
+ #
203
+ # Example:
204
+ #
205
+ # <tt>Criteria.parse!(Person, :all, :conditions => {})</tt>
206
+ #
207
+ # Options:
208
+ #
209
+ # klass: The klass to create the criteria for.
210
+ # args: An assortment of finder options.
211
+ #
212
+ # Returns:
213
+ #
214
+ # An Array with the type and criteria.
215
+ def parse!(klass, embedded, *args)
216
+ if args[0].nil?
217
+ Errors::InvalidOptions.new("Calling Document#find with nil is invalid")
218
+ end
219
+ type = args.delete_at(0) if args[0].is_a?(Symbol)
220
+ criteria = translate(klass, embedded, *args)
221
+ return [ type, criteria ]
222
+ end
223
+
224
+ # Translate the supplied arguments into a +Criteria+ object.
225
+ #
226
+ # If the passed in args is a single +String+, then it will
227
+ # construct an id +Criteria+ from it.
228
+ #
229
+ # If the passed in args are a type and a hash, then it will construct
230
+ # the +Criteria+ with the proper selector, options, and type.
231
+ #
232
+ # Options:
233
+ #
234
+ # args: either a +String+ or a +Symbol+, +Hash combination.
235
+ #
236
+ # Example:
237
+ #
238
+ # <tt>Criteria.translate(Person, "4ab2bc4b8ad548971900005c")</tt>
239
+ # <tt>Criteria.translate(Person, :conditions => { :field => "value"}, :limit => 20)</tt>
240
+ def translate(*args)
241
+ klass = args[0]
242
+ embedded = args[1]
243
+ params = args[2] || {}
244
+ unless params.is_a?(Hash)
245
+ return klass.criteria(embedded).id_criteria(params)
246
+ end
247
+ conditions = params.delete(:conditions) || {}
248
+ if conditions.include?(:id)
249
+ conditions[:_id] = conditions[:id]
250
+ conditions.delete(:id)
251
+ end
252
+ return klass.criteria(embedded).where(conditions).extras(params)
253
+ end
254
+ end
255
+
256
+ protected
257
+
258
+ # Return the entries of the other criteria or the object. Used for
259
+ # comparing criteria or an enumerable.
260
+ def comparable(other)
261
+ other.is_a?(Criteria) ? other.entries : other
262
+ end
263
+
264
+ # Filters the unused options out of the options +Hash+. Currently this
265
+ # takes into account the "page" and "per_page" options that would be passed
266
+ # in if using will_paginate.
267
+ #
268
+ # Example:
269
+ #
270
+ # Given a criteria with a selector of { :page => 1, :per_page => 40 }
271
+ #
272
+ # <tt>criteria.filter_options</tt> # selector: { :skip => 0, :limit => 40 }
273
+ def filter_options
274
+ page_num = @options.delete(:page)
275
+ per_page_num = @options.delete(:per_page)
276
+ if (page_num || per_page_num)
277
+ @options[:limit] = limits = (per_page_num || 20).to_i
278
+ @options[:skip] = (page_num || 1).to_i * limits - limits
279
+ end
280
+ end
281
+
282
+ # Clone or dup the current +Criteria+. This will return a new criteria with
283
+ # the selector, options, klass, embedded options, etc intact.
284
+ #
285
+ # Example:
286
+ #
287
+ # <tt>criteria.clone</tt>
288
+ # <tt>criteria.dup</tt>
289
+ #
290
+ # Options:
291
+ #
292
+ # other: The criteria getting cloned.
293
+ #
294
+ # Returns:
295
+ #
296
+ # A new identical criteria
297
+ def initialize_copy(other)
298
+ @selector = other.selector.dup
299
+ @options = other.options.dup
300
+ end
301
+
302
+ # Update the selector setting the operator on the value for each key in the
303
+ # supplied attributes +Hash+.
304
+ #
305
+ # Example:
306
+ #
307
+ # <tt>criteria.update_selector({ :field => "value" }, "$in")</tt>
308
+ def update_selector(attributes, operator)
309
+ clone.tap do |crit|
310
+ attributes.each do |key, value|
311
+ unless crit.selector[key]
312
+ crit.selector[key] = { operator => value }
313
+ else
314
+ if crit.selector[key].has_key?(operator)
315
+ new_value = crit.selector[key].values.first + value
316
+ crit.selector[key] = { operator => new_value }
317
+ else
318
+ crit.selector[key][operator] = value
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ # Complex criterion are used when performing operations on symbols to get
5
+ # get a shorthand syntax for where clauses.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>{ :field => { "$lt" => "value" } }</tt>
10
+ # becomes:
11
+ # <tt> { :field.lt => "value }</tt>
12
+ class Complex
13
+ attr_accessor :key, :operator
14
+
15
+ # Create the new complex criterion.
16
+ def initialize(opts = {})
17
+ @key, @operator = opts[:key], opts[:operator]
18
+ end
19
+
20
+ def hash
21
+ [@key, @operator].hash
22
+ end
23
+
24
+ def eql?(other)
25
+ self == (other)
26
+ end
27
+
28
+ def ==(other)
29
+ return false unless other.is_a?(self.class)
30
+ self.key == other.key && self.operator == other.operator
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+
5
+ # This module defines criteria behavior for creating documents in the
6
+ # database for specified conditions.
7
+ module Creational
8
+
9
+ # Create a document in the database given the selector and return it.
10
+ # Complex criteria, such as $in and $or operations will get ignored.
11
+ #
12
+ # @example Create the document.
13
+ # Person.where(:title => "Sir").create
14
+ #
15
+ # @example Create with selectors getting ignored.
16
+ # Person.where(:age.gt => 5).create
17
+ #
18
+ # @return [ Document ] A newly created document.
19
+ #
20
+ # @since 2.0.0.rc.1
21
+ def create
22
+ klass.create(
23
+ selector.inject({}) do |hash, (key, value)|
24
+ hash.tap do |attrs|
25
+ unless key.to_s =~ /\$/ || value.is_a?(Hash)
26
+ attrs[key] = value
27
+ end
28
+ end
29
+ end
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Exclusion
5
+ # Adds a criterion to the +Criteria+ that specifies values that are not allowed
6
+ # to match any document in the database. The MongoDB conditional operator that
7
+ # will be used is "$ne".
8
+ #
9
+ # Options:
10
+ #
11
+ # attributes: A +Hash+ where the key is the field name and the value is a
12
+ # value that must not be equal to the corresponding field value in the database.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>criteria.excludes(:field => "value1")</tt>
17
+ #
18
+ # <tt>criteria.excludes(:field1 => "value1", :field2 => "value1")</tt>
19
+ #
20
+ # Returns: <tt>self</tt>
21
+ def excludes(attributes = {})
22
+ mongo_id = attributes.delete(:id)
23
+ attributes = attributes.merge(:_id => mongo_id) if mongo_id
24
+ update_selector(attributes, "$ne")
25
+ end
26
+
27
+ # Adds a criterion to the +Criteria+ that specifies values where none
28
+ # should match in order to return results. This is similar to an SQL "NOT IN"
29
+ # clause. The MongoDB conditional operator that will be used is "$nin".
30
+ #
31
+ # Options:
32
+ #
33
+ # attributes: A +Hash+ where the key is the field name and the value is an
34
+ # +Array+ of values that none can match.
35
+ #
36
+ # Example:
37
+ #
38
+ # <tt>criteria.not_in(:field => ["value1", "value2"])</tt>
39
+ #
40
+ # <tt>criteria.not_in(:field1 => ["value1", "value2"], :field2 => ["value1"])</tt>
41
+ #
42
+ # Returns: <tt>self</tt>
43
+ def not_in(attributes)
44
+ update_selector(attributes, "$nin")
45
+ end
46
+
47
+ # Adds a criterion to the +Criteria+ that specifies the fields that will
48
+ # get returned from the Document. Used mainly for list views that do not
49
+ # require all fields to be present. This is similar to SQL "SELECT" values.
50
+ #
51
+ # Options:
52
+ #
53
+ # args: A list of field names to retrict the returned fields to.
54
+ #
55
+ # Example:
56
+ #
57
+ # <tt>criteria.only(:field1, :field2, :field3)</tt>
58
+ #
59
+ # Returns: <tt>self</tt>
60
+ def only(*args)
61
+ clone.tap do |crit|
62
+ crit.options[:fields] = args.flatten if args.any?
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Inclusion
5
+
6
+ # Adds a criterion to the +Criteria+ that specifies values that must all
7
+ # be matched in order to return results. Similar to an "in" clause but the
8
+ # underlying conditional logic is an "AND" and not an "OR". The MongoDB
9
+ # conditional operator that will be used is "$all".
10
+ #
11
+ # @example Adding the criterion.
12
+ # criteria.all(:field => ["value1", "value2"])
13
+ # criteria.all(:field1 => ["value1", "value2"], :field2 => ["value1"])
14
+ #
15
+ # @param [ Hash ] attributes Name/value pairs that all must match.
16
+ #
17
+ # @return [ Criteria ] A new criteria with the added selector.
18
+ def all(attributes = {})
19
+ update_selector(attributes, "$all")
20
+ end
21
+ alias :all_in :all
22
+
23
+ # Adds a criterion to the +Criteria+ that specifies values that must
24
+ # be matched in order to return results. This is similar to a SQL "WHERE"
25
+ # clause. This is the actual selector that will be provided to MongoDB,
26
+ # similar to the Javascript object that is used when performing a find()
27
+ # in the MongoDB console.
28
+ #
29
+ # @example Adding the criterion.
30
+ # criteria.and(:field1 => "value1", :field2 => 15)
31
+ #
32
+ # @param [ Hash ] selectior Name/value pairs that all must match.
33
+ #
34
+ # @return [ Criteria ] A new criteria with the added selector.
35
+ def and(selector = nil)
36
+ where(selector)
37
+ end
38
+
39
+ # Adds a criterion to the +Criteria+ that specifies a set of expressions
40
+ # to match if any of them return true. This is a $or query in MongoDB and
41
+ # is similar to a SQL OR. This is named #any_of and aliased "or" for
42
+ # readability.
43
+ #
44
+ # @example Adding the criterion.
45
+ # criteria.any_of({ :field1 => "value" }, { :field2 => "value2" })
46
+ #
47
+ # @param [ Array<Hash> ] args A list of name/value pairs any can match.
48
+ #
49
+ # @return [ Criteria ] A new criteria with the added selector.
50
+ def any_of(*args)
51
+ clone.tap do |crit|
52
+ criterion = @selector["$or"] || []
53
+ expanded = args.collect(&:expand_complex_criteria)
54
+ crit.selector["$or"] = criterion.concat(expanded)
55
+ end
56
+ end
57
+ alias :or :any_of
58
+
59
+ # Using the existing criteria, find a document by a single id, multiple
60
+ # ids, or using a conditions hash.
61
+ #
62
+ # @example Find a single document by id.
63
+ # Person.where(:title => "Sir").find(id)
64
+ #
65
+ # @example Find multiple documents by ids.
66
+ # Person.where(:title => "Sir").find([ id_one, id_two ])
67
+ #
68
+ # @return [ Document, Array<Document> ] The matching document(s).
69
+ #
70
+ # @since 2.0.0.rc.1
71
+ def find(*args)
72
+ id_criteria(*args)
73
+ end
74
+
75
+ # Adds a criterion to the +Criteria+ that specifies values where any can
76
+ # be matched in order to return results. This is similar to an SQL "IN"
77
+ # clause. The MongoDB conditional operator that will be used is "$in".
78
+ #
79
+ # @example Adding the criterion.
80
+ # criteria.in(:field => ["value1", "value2"])
81
+ # criteria.in(:field1 => ["value1", "value2"], :field2 => ["value1"])
82
+ #
83
+ # @param [ Hash ] attributes Name/value pairs any can match.
84
+ #
85
+ # @return [ Criteria ] A new criteria with the added selector.
86
+ def in(attributes = {})
87
+ update_selector(attributes, "$in")
88
+ end
89
+ alias :any_in :in
90
+
91
+ # Adds a criterion to the +Criteria+ that specifies values to do
92
+ # geospacial searches by. The field must be indexed with the "2d" option.
93
+ #
94
+ # @example Adding the criterion.
95
+ # criteria.near(:field1 => [30, -44])
96
+ #
97
+ # @param [ Hash ] attributes The fields with lat/long values.
98
+ #
99
+ # @return [ Criteria ] A new criteria with the added selector.
100
+ def near(attributes = {})
101
+ update_selector(attributes, "$near")
102
+ end
103
+
104
+ # Adds a criterion to the +Criteria+ that specifies values that must
105
+ # be matched in order to return results. This is similar to a SQL "WHERE"
106
+ # clause. This is the actual selector that will be provided to MongoDB,
107
+ # similar to the Javascript object that is used when performing a find()
108
+ # in the MongoDB console.
109
+ #
110
+ # @example Adding the criterion.
111
+ # criteria.where(:field1 => "value1", :field2 => 15)
112
+ #
113
+ # @param [ Hash ] selector Name/value pairs where all must match.
114
+ #
115
+ # @return [ Criteria ] A new criteria with the added selector.
116
+ def where(selector = nil)
117
+ clone.tap do |crit|
118
+ selector = case selector
119
+ when String then {"$where" => selector}
120
+ else selector ? selector.expand_complex_criteria : {}
121
+ end
122
+
123
+ selector.each_pair do |key, value|
124
+ if crit.selector.has_key?(key) && crit.selector[key].respond_to?(:merge!)
125
+ crit.selector[key].merge!(value)
126
+ else
127
+ crit.selector[key] = value
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Inspection #:nodoc:
5
+
6
+ # Get a pretty string representation of the criteria, including the
7
+ # selector, options, matching count and documents for inspection.
8
+ #
9
+ # @example Inspect the criteria.
10
+ # criteria.inspect
11
+ #
12
+ # @return [ String ] The inspection string.
13
+ def inspect
14
+ "#<Mongoid::Criteria\n" <<
15
+ " selector: #{selector.inspect},\n" <<
16
+ " options: #{options.inspect}>\n"
17
+ end
18
+ end
19
+ end
20
+ end