humanoid 1.2.7

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 (210) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +29 -0
  3. data/HISTORY +342 -0
  4. data/MIT_LICENSE +20 -0
  5. data/README.rdoc +56 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/caliper.yml +4 -0
  9. data/humanoid.gemspec +374 -0
  10. data/lib/humanoid.rb +111 -0
  11. data/lib/humanoid/associations.rb +258 -0
  12. data/lib/humanoid/associations/belongs_to.rb +64 -0
  13. data/lib/humanoid/associations/belongs_to_related.rb +62 -0
  14. data/lib/humanoid/associations/has_many.rb +180 -0
  15. data/lib/humanoid/associations/has_many_related.rb +109 -0
  16. data/lib/humanoid/associations/has_one.rb +95 -0
  17. data/lib/humanoid/associations/has_one_related.rb +81 -0
  18. data/lib/humanoid/associations/options.rb +57 -0
  19. data/lib/humanoid/associations/proxy.rb +31 -0
  20. data/lib/humanoid/attributes.rb +184 -0
  21. data/lib/humanoid/callbacks.rb +23 -0
  22. data/lib/humanoid/collection.rb +118 -0
  23. data/lib/humanoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/humanoid/collections/master.rb +28 -0
  25. data/lib/humanoid/collections/mimic.rb +46 -0
  26. data/lib/humanoid/collections/operations.rb +41 -0
  27. data/lib/humanoid/collections/slaves.rb +44 -0
  28. data/lib/humanoid/commands.rb +182 -0
  29. data/lib/humanoid/commands/create.rb +21 -0
  30. data/lib/humanoid/commands/delete.rb +16 -0
  31. data/lib/humanoid/commands/delete_all.rb +23 -0
  32. data/lib/humanoid/commands/deletion.rb +18 -0
  33. data/lib/humanoid/commands/destroy.rb +19 -0
  34. data/lib/humanoid/commands/destroy_all.rb +23 -0
  35. data/lib/humanoid/commands/save.rb +27 -0
  36. data/lib/humanoid/components.rb +24 -0
  37. data/lib/humanoid/config.rb +84 -0
  38. data/lib/humanoid/contexts.rb +25 -0
  39. data/lib/humanoid/contexts/enumerable.rb +117 -0
  40. data/lib/humanoid/contexts/ids.rb +25 -0
  41. data/lib/humanoid/contexts/mongo.rb +224 -0
  42. data/lib/humanoid/contexts/paging.rb +42 -0
  43. data/lib/humanoid/criteria.rb +259 -0
  44. data/lib/humanoid/criterion/complex.rb +21 -0
  45. data/lib/humanoid/criterion/exclusion.rb +65 -0
  46. data/lib/humanoid/criterion/inclusion.rb +91 -0
  47. data/lib/humanoid/criterion/optional.rb +128 -0
  48. data/lib/humanoid/cursor.rb +82 -0
  49. data/lib/humanoid/document.rb +300 -0
  50. data/lib/humanoid/enslavement.rb +38 -0
  51. data/lib/humanoid/errors.rb +77 -0
  52. data/lib/humanoid/extensions.rb +84 -0
  53. data/lib/humanoid/extensions/array/accessors.rb +17 -0
  54. data/lib/humanoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/humanoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/humanoid/extensions/array/conversions.rb +29 -0
  57. data/lib/humanoid/extensions/array/parentization.rb +13 -0
  58. data/lib/humanoid/extensions/boolean/conversions.rb +16 -0
  59. data/lib/humanoid/extensions/date/conversions.rb +15 -0
  60. data/lib/humanoid/extensions/datetime/conversions.rb +17 -0
  61. data/lib/humanoid/extensions/float/conversions.rb +16 -0
  62. data/lib/humanoid/extensions/hash/accessors.rb +38 -0
  63. data/lib/humanoid/extensions/hash/assimilation.rb +30 -0
  64. data/lib/humanoid/extensions/hash/conversions.rb +15 -0
  65. data/lib/humanoid/extensions/hash/criteria_helpers.rb +20 -0
  66. data/lib/humanoid/extensions/hash/scoping.rb +12 -0
  67. data/lib/humanoid/extensions/integer/conversions.rb +16 -0
  68. data/lib/humanoid/extensions/nil/assimilation.rb +13 -0
  69. data/lib/humanoid/extensions/object/conversions.rb +33 -0
  70. data/lib/humanoid/extensions/proc/scoping.rb +12 -0
  71. data/lib/humanoid/extensions/string/conversions.rb +15 -0
  72. data/lib/humanoid/extensions/string/inflections.rb +97 -0
  73. data/lib/humanoid/extensions/symbol/inflections.rb +36 -0
  74. data/lib/humanoid/extensions/time/conversions.rb +18 -0
  75. data/lib/humanoid/factory.rb +19 -0
  76. data/lib/humanoid/field.rb +39 -0
  77. data/lib/humanoid/fields.rb +62 -0
  78. data/lib/humanoid/finders.rb +224 -0
  79. data/lib/humanoid/identity.rb +39 -0
  80. data/lib/humanoid/indexes.rb +30 -0
  81. data/lib/humanoid/matchers.rb +36 -0
  82. data/lib/humanoid/matchers/all.rb +11 -0
  83. data/lib/humanoid/matchers/default.rb +26 -0
  84. data/lib/humanoid/matchers/exists.rb +13 -0
  85. data/lib/humanoid/matchers/gt.rb +11 -0
  86. data/lib/humanoid/matchers/gte.rb +11 -0
  87. data/lib/humanoid/matchers/in.rb +11 -0
  88. data/lib/humanoid/matchers/lt.rb +11 -0
  89. data/lib/humanoid/matchers/lte.rb +11 -0
  90. data/lib/humanoid/matchers/ne.rb +11 -0
  91. data/lib/humanoid/matchers/nin.rb +11 -0
  92. data/lib/humanoid/matchers/size.rb +11 -0
  93. data/lib/humanoid/memoization.rb +27 -0
  94. data/lib/humanoid/named_scope.rb +40 -0
  95. data/lib/humanoid/scope.rb +75 -0
  96. data/lib/humanoid/timestamps.rb +30 -0
  97. data/lib/humanoid/versioning.rb +28 -0
  98. data/perf/benchmark.rb +77 -0
  99. data/spec/integration/humanoid/associations_spec.rb +301 -0
  100. data/spec/integration/humanoid/attributes_spec.rb +22 -0
  101. data/spec/integration/humanoid/commands_spec.rb +216 -0
  102. data/spec/integration/humanoid/contexts/enumerable_spec.rb +33 -0
  103. data/spec/integration/humanoid/criteria_spec.rb +224 -0
  104. data/spec/integration/humanoid/document_spec.rb +587 -0
  105. data/spec/integration/humanoid/extensions_spec.rb +26 -0
  106. data/spec/integration/humanoid/finders_spec.rb +119 -0
  107. data/spec/integration/humanoid/inheritance_spec.rb +137 -0
  108. data/spec/integration/humanoid/named_scope_spec.rb +46 -0
  109. data/spec/models/address.rb +39 -0
  110. data/spec/models/animal.rb +6 -0
  111. data/spec/models/comment.rb +8 -0
  112. data/spec/models/country_code.rb +6 -0
  113. data/spec/models/employer.rb +5 -0
  114. data/spec/models/game.rb +6 -0
  115. data/spec/models/inheritance.rb +56 -0
  116. data/spec/models/location.rb +5 -0
  117. data/spec/models/mixed_drink.rb +4 -0
  118. data/spec/models/name.rb +13 -0
  119. data/spec/models/namespacing.rb +11 -0
  120. data/spec/models/patient.rb +4 -0
  121. data/spec/models/person.rb +98 -0
  122. data/spec/models/pet.rb +7 -0
  123. data/spec/models/pet_owner.rb +6 -0
  124. data/spec/models/phone.rb +7 -0
  125. data/spec/models/post.rb +15 -0
  126. data/spec/models/translation.rb +5 -0
  127. data/spec/models/vet_visit.rb +5 -0
  128. data/spec/spec.opts +3 -0
  129. data/spec/spec_helper.rb +31 -0
  130. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +141 -0
  131. data/spec/unit/mongoid/associations/belongs_to_spec.rb +193 -0
  132. data/spec/unit/mongoid/associations/has_many_related_spec.rb +387 -0
  133. data/spec/unit/mongoid/associations/has_many_spec.rb +471 -0
  134. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  135. data/spec/unit/mongoid/associations/has_one_spec.rb +282 -0
  136. data/spec/unit/mongoid/associations/options_spec.rb +191 -0
  137. data/spec/unit/mongoid/associations_spec.rb +545 -0
  138. data/spec/unit/mongoid/attributes_spec.rb +484 -0
  139. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  140. data/spec/unit/mongoid/collection_spec.rb +171 -0
  141. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  142. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  143. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  144. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  145. data/spec/unit/mongoid/commands/create_spec.rb +30 -0
  146. data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
  147. data/spec/unit/mongoid/commands/delete_spec.rb +35 -0
  148. data/spec/unit/mongoid/commands/destroy_all_spec.rb +23 -0
  149. data/spec/unit/mongoid/commands/destroy_spec.rb +44 -0
  150. data/spec/unit/mongoid/commands/save_spec.rb +105 -0
  151. data/spec/unit/mongoid/commands_spec.rb +282 -0
  152. data/spec/unit/mongoid/config_spec.rb +165 -0
  153. data/spec/unit/mongoid/contexts/enumerable_spec.rb +374 -0
  154. data/spec/unit/mongoid/contexts/mongo_spec.rb +505 -0
  155. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  156. data/spec/unit/mongoid/criteria_spec.rb +769 -0
  157. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  158. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  159. data/spec/unit/mongoid/criterion/inclusion_spec.rb +211 -0
  160. data/spec/unit/mongoid/criterion/optional_spec.rb +329 -0
  161. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  162. data/spec/unit/mongoid/document_spec.rb +986 -0
  163. data/spec/unit/mongoid/enslavement_spec.rb +63 -0
  164. data/spec/unit/mongoid/errors_spec.rb +103 -0
  165. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  166. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  167. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  168. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  169. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  170. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  171. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +70 -0
  172. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  173. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  174. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  175. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  176. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  177. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  178. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  179. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  180. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +43 -0
  181. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  182. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  183. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  184. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  185. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  186. data/spec/unit/mongoid/factory_spec.rb +31 -0
  187. data/spec/unit/mongoid/field_spec.rb +81 -0
  188. data/spec/unit/mongoid/fields_spec.rb +158 -0
  189. data/spec/unit/mongoid/finders_spec.rb +368 -0
  190. data/spec/unit/mongoid/identity_spec.rb +88 -0
  191. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  192. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  193. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  194. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  195. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  196. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  197. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  198. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  199. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  200. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  201. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  202. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  203. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  204. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  205. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  206. data/spec/unit/mongoid/scope_spec.rb +240 -0
  207. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  208. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  209. data/spec/unit/mongoid_spec.rb +37 -0
  210. metadata +431 -0
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Paging
5
+ # Paginates the documents.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>context.paginate</tt>
10
+ #
11
+ # Returns:
12
+ #
13
+ # A collection of documents paginated.
14
+ def paginate
15
+ @collection ||= execute(true)
16
+ WillPaginate::Collection.create(page, per_page, count) do |pager|
17
+ pager.replace(@collection.to_a)
18
+ end
19
+ end
20
+
21
+ # Either returns the page option and removes it from the options, or
22
+ # returns a default value of 1.
23
+ #
24
+ # Returns:
25
+ #
26
+ # An +Integer+ page number.
27
+ def page
28
+ skips, limits = options[:skip], options[:limit]
29
+ (skips && limits) ? (skips + limits) / limits : 1
30
+ end
31
+
32
+ # Get the number of results per page or the default of 20.
33
+ #
34
+ # Returns:
35
+ #
36
+ # The +Integer+ number of documents in each page.
37
+ def per_page
38
+ (options[:limit] || 20).to_i
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,259 @@
1
+ # encoding: utf-8
2
+ require "humanoid/criterion/complex"
3
+ require "humanoid/criterion/exclusion"
4
+ require "humanoid/criterion/inclusion"
5
+ require "humanoid/criterion/optional"
6
+
7
+ module Humanoid #:nodoc:
8
+ # The +Criteria+ class is the core object needed in Humanoid to retrieve
9
+ # objects from the database. It is a DSL that essentially sets up the
10
+ # selector and options arguments that get passed on to a <tt>Mongo::Collection</tt>
11
+ # in the Ruby driver. Each method on the +Criteria+ returns self to they
12
+ # can be chained in order to create a readable criterion to be executed
13
+ # against the database.
14
+ #
15
+ # Example setup:
16
+ #
17
+ # <tt>criteria = Criteria.new</tt>
18
+ #
19
+ # <tt>criteria.only(:field).where(:field => "value").skip(20).limit(20)</tt>
20
+ #
21
+ # <tt>criteria.execute</tt>
22
+ class Criteria
23
+ include Criterion::Exclusion
24
+ include Criterion::Inclusion
25
+ include Criterion::Optional
26
+ include Enumerable
27
+
28
+ attr_reader :collection, :ids, :klass, :options, :selector
29
+
30
+ attr_accessor :documents
31
+
32
+ delegate \
33
+ :aggregate,
34
+ :count,
35
+ :execute,
36
+ :first,
37
+ :group,
38
+ :id_criteria,
39
+ :last,
40
+ :max,
41
+ :min,
42
+ :one,
43
+ :page,
44
+ :paginate,
45
+ :per_page,
46
+ :sum, :to => :context
47
+
48
+ # Concatinate the criteria with another enumerable. If the other is a
49
+ # +Criteria+ then it needs to get the collection from it.
50
+ def +(other)
51
+ entries + comparable(other)
52
+ end
53
+
54
+ # Returns the difference between the criteria and another enumerable. If
55
+ # the other is a +Criteria+ then it needs to get the collection from it.
56
+ def -(other)
57
+ entries - comparable(other)
58
+ end
59
+
60
+ # Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
61
+ # of this +Criteria+ or the criteria itself.
62
+ #
63
+ # This will force a database load when called if an enumerable is passed.
64
+ #
65
+ # Options:
66
+ #
67
+ # other: The other +Enumerable+ or +Criteria+ to compare to.
68
+ def ==(other)
69
+ case other
70
+ when Criteria
71
+ self.selector == other.selector && self.options == other.options
72
+ when Enumerable
73
+ return (execute.entries == other)
74
+ else
75
+ return false
76
+ end
77
+ end
78
+
79
+ # Returns true if the criteria is empty.
80
+ #
81
+ # Example:
82
+ #
83
+ # <tt>criteria.blank?</tt>
84
+ def blank?
85
+ count < 1
86
+ end
87
+
88
+ alias :empty? :blank?
89
+
90
+ # Return or create the context in which this criteria should be executed.
91
+ #
92
+ # This will return an Enumerable context if the class is embedded,
93
+ # otherwise it will return a Mongo context for root classes.
94
+ def context
95
+ @context ||= Contexts.context_for(self)
96
+ end
97
+
98
+ # Iterate over each +Document+ in the results. This can take an optional
99
+ # block to pass to each argument in the results.
100
+ #
101
+ # Example:
102
+ #
103
+ # <tt>criteria.each { |doc| p doc }</tt>
104
+ def each(&block)
105
+ return caching(&block) if cached?
106
+ if block_given?
107
+ execute.each { |doc| yield doc }
108
+ end
109
+ self
110
+ end
111
+
112
+ # Merges the supplied argument hash into a single criteria
113
+ #
114
+ # Options:
115
+ #
116
+ # criteria_conditions: Hash of criteria keys, and parameter values
117
+ #
118
+ # Example:
119
+ #
120
+ # <tt>criteria.fuse(:where => { :field => "value"}, :limit => 20)</tt>
121
+ #
122
+ # Returns <tt>self</tt>
123
+ def fuse(criteria_conditions = {})
124
+ criteria_conditions.inject(self) do |criteria, (key, value)|
125
+ criteria.send(key, value)
126
+ end
127
+ end
128
+
129
+ # Create the new +Criteria+ object. This will initialize the selector
130
+ # and options hashes, as well as the type of criteria.
131
+ #
132
+ # Options:
133
+ #
134
+ # type: One of :all, :first:, or :last
135
+ # klass: The class to execute on.
136
+ def initialize(klass)
137
+ @selector, @options, @klass, @documents = {}, {}, klass, []
138
+ end
139
+
140
+ # Merges another object into this +Criteria+. The other object may be a
141
+ # +Criteria+ or a +Hash+. This is used to combine multiple scopes together,
142
+ # where a chained scope situation may be desired.
143
+ #
144
+ # Options:
145
+ #
146
+ # other: The +Criteria+ or +Hash+ to merge with.
147
+ #
148
+ # Example:
149
+ #
150
+ # <tt>criteria.merge({ :conditions => { :title => "Sir" } })</tt>
151
+ def merge(other)
152
+ @selector.update(other.selector)
153
+ @options.update(other.options)
154
+ @documents = other.documents
155
+ end
156
+
157
+ # Used for chaining +Criteria+ scopes together in the for of class methods
158
+ # on the +Document+ the criteria is for.
159
+ #
160
+ # Options:
161
+ #
162
+ # name: The name of the class method on the +Document+ to chain.
163
+ # args: The arguments passed to the method.
164
+ #
165
+ # Returns: <tt>Criteria</tt>
166
+ def method_missing(name, *args)
167
+ if @klass.respond_to?(name)
168
+ new_scope = @klass.send(name)
169
+ new_scope.merge(self)
170
+ return new_scope
171
+ else
172
+ return entries.send(name, *args)
173
+ end
174
+ end
175
+
176
+ alias :to_ary :to_a
177
+
178
+ # Returns the selector and options as a +Hash+ that would be passed to a
179
+ # scope for use with named scopes.
180
+ def scoped
181
+ { :where => @selector }.merge(@options)
182
+ end
183
+
184
+ # Translate the supplied arguments into a +Criteria+ object.
185
+ #
186
+ # If the passed in args is a single +String+, then it will
187
+ # construct an id +Criteria+ from it.
188
+ #
189
+ # If the passed in args are a type and a hash, then it will construct
190
+ # the +Criteria+ with the proper selector, options, and type.
191
+ #
192
+ # Options:
193
+ #
194
+ # args: either a +String+ or a +Symbol+, +Hash combination.
195
+ #
196
+ # Example:
197
+ #
198
+ # <tt>Criteria.translate(Person, "4ab2bc4b8ad548971900005c")</tt>
199
+ # <tt>Criteria.translate(Person, :conditions => { :field => "value"}, :limit => 20)</tt>
200
+ def self.translate(*args)
201
+ klass = args[0]
202
+ params = args[1] || {}
203
+ unless params.is_a?(Hash)
204
+ return new(klass).id_criteria(params)
205
+ end
206
+ return new(klass).where(params.delete(:conditions) || {}).extras(params)
207
+ end
208
+
209
+ protected
210
+
211
+ # Iterate over each +Document+ in the results and cache the collection.
212
+ def caching(&block)
213
+ @collection ||= execute
214
+ if block_given?
215
+ docs = []
216
+ @collection.each do |doc|
217
+ docs << doc
218
+ yield doc
219
+ end
220
+ @collection = docs
221
+ end
222
+ self
223
+ end
224
+
225
+ # Filters the unused options out of the options +Hash+. Currently this
226
+ # takes into account the "page" and "per_page" options that would be passed
227
+ # in if using will_paginate.
228
+ #
229
+ # Example:
230
+ #
231
+ # Given a criteria with a selector of { :page => 1, :per_page => 40 }
232
+ #
233
+ # <tt>criteria.filter_options</tt> # selector: { :skip => 0, :limit => 40 }
234
+ def filter_options
235
+ page_num = @options.delete(:page)
236
+ per_page_num = @options.delete(:per_page)
237
+ if (page_num || per_page_num)
238
+ @options[:limit] = limits = (per_page_num || 20).to_i
239
+ @options[:skip] = (page_num || 1).to_i * limits - limits
240
+ end
241
+ end
242
+
243
+ # Return the entries of the other criteria or the object. Used for
244
+ # comparing criteria or an enumerable.
245
+ def comparable(other)
246
+ other.is_a?(Criteria) ? other.entries : other
247
+ end
248
+
249
+ # Update the selector setting the operator on the value for each key in the
250
+ # supplied attributes +Hash+.
251
+ #
252
+ # Example:
253
+ #
254
+ # <tt>criteria.update_selector({ :field => "value" }, "$in")</tt>
255
+ def update_selector(attributes, operator)
256
+ attributes.each { |key, value| @selector[key] = { operator => value } }; self
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Humanoid #: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
+ end
20
+ end
21
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ module Humanoid #: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
+ # exclusions: 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(exclusions)
44
+ exclusions.each { |key, value| @selector[key] = { "$nin" => value } }; self
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
+ @options[:fields] = args.flatten if args.any?; self
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Inclusion
5
+ # Adds a criterion to the +Criteria+ that specifies values that must all
6
+ # be matched in order to return results. Similar to an "in" clause but the
7
+ # underlying conditional logic is an "AND" and not an "OR". The MongoDB
8
+ # conditional operator that will be used is "$all".
9
+ #
10
+ # Options:
11
+ #
12
+ # attributes: A +Hash+ where the key is the field name and the value is an
13
+ # +Array+ of values that must all match.
14
+ #
15
+ # Example:
16
+ #
17
+ # <tt>criteria.all(:field => ["value1", "value2"])</tt>
18
+ #
19
+ # <tt>criteria.all(:field1 => ["value1", "value2"], :field2 => ["value1"])</tt>
20
+ #
21
+ # Returns: <tt>self</tt>
22
+ def all(attributes = {})
23
+ update_selector(attributes, "$all")
24
+ end
25
+
26
+ # Adds a criterion to the +Criteria+ that specifies values that must
27
+ # be matched in order to return results. This is similar to a SQL "WHERE"
28
+ # clause. This is the actual selector that will be provided to MongoDB,
29
+ # similar to the Javascript object that is used when performing a find()
30
+ # in the MongoDB console.
31
+ #
32
+ # Options:
33
+ #
34
+ # selectior: A +Hash+ that must match the attributes of the +Document+.
35
+ #
36
+ # Example:
37
+ #
38
+ # <tt>criteria.and(:field1 => "value1", :field2 => 15)</tt>
39
+ #
40
+ # Returns: <tt>self</tt>
41
+ def and(selector = nil)
42
+ where(selector)
43
+ end
44
+
45
+ # Adds a criterion to the +Criteria+ that specifies values where any can
46
+ # be matched in order to return results. This is similar to an SQL "IN"
47
+ # clause. The MongoDB conditional operator that will be used is "$in".
48
+ #
49
+ # Options:
50
+ #
51
+ # attributes: A +Hash+ where the key is the field name and the value is an
52
+ # +Array+ of values that any can match.
53
+ #
54
+ # Example:
55
+ #
56
+ # <tt>criteria.in(:field => ["value1", "value2"])</tt>
57
+ #
58
+ # <tt>criteria.in(:field1 => ["value1", "value2"], :field2 => ["value1"])</tt>
59
+ #
60
+ # Returns: <tt>self</tt>
61
+ def in(attributes = {})
62
+ update_selector(attributes, "$in")
63
+ end
64
+
65
+ # Adds a criterion to the +Criteria+ that specifies values that must
66
+ # be matched in order to return results. This is similar to a SQL "WHERE"
67
+ # clause. This is the actual selector that will be provided to MongoDB,
68
+ # similar to the Javascript object that is used when performing a find()
69
+ # in the MongoDB console.
70
+ #
71
+ # Options:
72
+ #
73
+ # selectior: A +Hash+ that must match the attributes of the +Document+.
74
+ #
75
+ # Example:
76
+ #
77
+ # <tt>criteria.where(:field1 => "value1", :field2 => 15)</tt>
78
+ #
79
+ # Returns: <tt>self</tt>
80
+ def where(selector = nil)
81
+ case selector
82
+ when String
83
+ @selector.update("$where" => selector)
84
+ else
85
+ @selector.update(selector ? selector.expand_complex_criteria : {})
86
+ end
87
+ self
88
+ end
89
+ end
90
+ end
91
+ end