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,25 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Contexts do
4
+
5
+ context ".context_for" do
6
+ let(:klass) { stub('klass', :embedded => false) }
7
+ let(:criteria) { stub('criteria', :klass => klass) }
8
+
9
+ context "when criteria is for a top-level Humanoid::Document" do
10
+ it "creates a Mongo context" do
11
+ Humanoid::Contexts::Mongo.expects(:new).with(criteria)
12
+ Humanoid::Contexts.context_for(criteria)
13
+ end
14
+ end
15
+
16
+ context "when criteria is for an embedded Humanoid::Document" do
17
+ it "creates a Mongo context" do
18
+ klass.stubs(:embedded).returns(true)
19
+ Humanoid::Contexts::Enumerable.expects(:new).with(criteria)
20
+ Humanoid::Contexts.context_for(criteria)
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,769 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Criteria do
4
+
5
+ before do
6
+ @criteria = Humanoid::Criteria.new(Person)
7
+ @canvas_criteria = Humanoid::Criteria.new(Canvas)
8
+ end
9
+
10
+ describe "#+" do
11
+
12
+ before do
13
+ @sir = Person.new(:title => "Sir")
14
+ @canvas = Canvas.new
15
+ end
16
+
17
+ context "when the criteria has not been executed" do
18
+
19
+ before do
20
+ @collection = mock
21
+ @cursor = stub(:count => 1)
22
+ @cursor.expects(:each).at_least_once.yields(@sir)
23
+ Person.expects(:collection).at_least_once.returns(@collection)
24
+ @collection.expects(:find).at_least_once.returns(@cursor)
25
+ end
26
+
27
+ it "executes the criteria and concats the results" do
28
+ results = @criteria + [ @canvas ]
29
+ results.should == [ @sir, @canvas ]
30
+ end
31
+
32
+ end
33
+
34
+ context "when the other is a criteria" do
35
+
36
+ before do
37
+ @collection = mock
38
+ @canvas_collection = mock
39
+ @cursor = stub(:count => 1)
40
+ @canvas_cursor = stub(:count => 1)
41
+ @cursor.expects(:each).at_least_once.yields(@sir)
42
+ @canvas_cursor.expects(:each).at_least_once.yields(@canvas)
43
+ Person.expects(:collection).at_least_once.returns(@collection)
44
+ @collection.expects(:find).at_least_once.returns(@cursor)
45
+ Canvas.expects(:collection).at_least_once.returns(@canvas_collection)
46
+ @canvas_collection.expects(:find).at_least_once.returns(@canvas_cursor)
47
+ end
48
+
49
+ it "concats the results" do
50
+ results = @criteria + @canvas_criteria
51
+ results.should == [ @sir, @canvas ]
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ describe "#-" do
59
+
60
+ before do
61
+ @sir = Person.new(:title => "Sir")
62
+ @madam = Person.new(:title => "Madam")
63
+ end
64
+
65
+ context "when the criteria has not been executed" do
66
+
67
+ before do
68
+ @collection = mock
69
+ @cursor = stub(:count => 1)
70
+ @cursor.expects(:each).yields(@madam)
71
+ Person.expects(:collection).returns(@collection)
72
+ @collection.expects(:find).returns(@cursor)
73
+ end
74
+
75
+ it "executes the criteria and returns the difference" do
76
+ results = @criteria - [ @sir ]
77
+ results.should == [ @madam ]
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ describe "#[]" do
85
+
86
+ before do
87
+ @criteria.where(:title => "Sir")
88
+ @collection = stub
89
+ @person = Person.new(:title => "Sir")
90
+ @cursor = stub(:count => 10)
91
+ @cursor.expects(:each).yields(@person)
92
+ end
93
+
94
+ context "when the criteria has not been executed" do
95
+
96
+ before do
97
+ Person.expects(:collection).returns(@collection)
98
+ @collection.expects(:find).with({ :title => "Sir", :_type => { "$in" => ["Doctor", "Person"] } }, {}).returns(@cursor)
99
+ end
100
+
101
+ it "executes the criteria and returns the element at the index" do
102
+ @criteria[0].should == @person
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ describe "#aggregate" do
110
+
111
+ before do
112
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
113
+ @criteria.instance_variable_set(:@context, @context)
114
+ end
115
+
116
+ it "delegates to the context" do
117
+ @context.expects(:aggregate)
118
+ @criteria.aggregate
119
+ end
120
+
121
+ end
122
+
123
+ describe "#blank?" do
124
+
125
+ before do
126
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
127
+ @criteria.instance_variable_set(:@context, @context)
128
+ end
129
+
130
+ context "when the count is 0" do
131
+
132
+ before do
133
+ @context.expects(:count).returns(0)
134
+ end
135
+
136
+ it "returns true" do
137
+ @criteria.blank?.should be_true
138
+ end
139
+ end
140
+
141
+ context "when the count is greater than 0" do
142
+
143
+ before do
144
+ @context.expects(:count).returns(10)
145
+ end
146
+
147
+ it "returns false" do
148
+ @criteria.blank?.should be_false
149
+ end
150
+ end
151
+ end
152
+
153
+ describe "#context" do
154
+
155
+ context "when the context has been set" do
156
+
157
+ before do
158
+ @context = stub
159
+ @criteria.instance_variable_set(:@context, @context)
160
+ end
161
+
162
+ it "returns the memoized context" do
163
+ @criteria.context.should == @context
164
+ end
165
+
166
+ end
167
+
168
+ context "when the context has not been set" do
169
+
170
+ before do
171
+ @context = stub
172
+ end
173
+
174
+ it "creates a new context" do
175
+ Humanoid::Contexts::Mongo.expects(:new).with(@criteria).returns(@context)
176
+ @criteria.context.should == @context
177
+ end
178
+
179
+ end
180
+
181
+ context "when the class is embedded" do
182
+
183
+ before do
184
+ @criteria = Humanoid::Criteria.new(Address)
185
+ end
186
+
187
+ it "returns an enumerable context" do
188
+ @criteria.context.should be_a_kind_of(Humanoid::Contexts::Enumerable)
189
+ end
190
+
191
+ end
192
+
193
+ context "when the class is not embedded" do
194
+
195
+ it "returns a mongo context" do
196
+ @criteria.context.should be_a_kind_of(Humanoid::Contexts::Mongo)
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ describe "#entries" do
204
+
205
+ context "filtering" do
206
+
207
+ before do
208
+ @collection = mock
209
+ Person.expects(:collection).returns(@collection)
210
+ @criteria = Humanoid::Criteria.new(Person).extras(:page => 1, :per_page => 20)
211
+ @collection.expects(:find).with(@criteria.selector, @criteria.options).returns([])
212
+ end
213
+
214
+ it "filters out unused params" do
215
+ @criteria.entries
216
+ @criteria.options[:page].should be_nil
217
+ @criteria.options[:per_page].should be_nil
218
+ end
219
+
220
+ end
221
+
222
+ context "when type is :all" do
223
+
224
+ before do
225
+ @collection = mock
226
+ Person.expects(:collection).returns(@collection)
227
+ @criteria = Humanoid::Criteria.new(Person).extras(:page => 1, :per_page => 20)
228
+ @cursor = stub(:count => 44)
229
+ @cursor.expects(:each)
230
+ @collection.expects(:find).with(@criteria.selector, @criteria.options).returns(@cursor)
231
+ end
232
+
233
+ it "does not add the count instance variable" do
234
+ @criteria.entries.should == []
235
+ @criteria.instance_variable_get(:@count).should be_nil
236
+ end
237
+
238
+ end
239
+
240
+ context "when type is not :first" do
241
+
242
+ it "calls find on the collection with the selector and options" do
243
+ criteria = Humanoid::Criteria.new(Person)
244
+ collection = mock
245
+ Person.expects(:collection).returns(collection)
246
+ collection.expects(:find).with(criteria.selector, criteria.options).returns([])
247
+ criteria.entries.should == []
248
+ end
249
+
250
+ end
251
+
252
+ end
253
+
254
+ describe "#count" do
255
+
256
+ before do
257
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
258
+ @criteria.instance_variable_set(:@context, @context)
259
+ end
260
+
261
+ it "delegates to the context" do
262
+ @context.expects(:count).returns(10)
263
+ @criteria.count.should == 10
264
+ end
265
+
266
+ end
267
+
268
+ describe "#each" do
269
+
270
+ before do
271
+ @criteria.where(:title => "Sir")
272
+ @collection = stub
273
+ @person = Person.new(:title => "Sir")
274
+ @cursor = stub(:count => 10)
275
+ end
276
+
277
+ context "when the criteria has not been executed" do
278
+
279
+ before do
280
+ Person.expects(:collection).returns(@collection)
281
+ @collection.expects(:find).with({ :_type => { "$in" => ["Doctor", "Person"] }, :title => "Sir" }, {}).returns(@cursor)
282
+ @cursor.expects(:each).yields(@person)
283
+ end
284
+
285
+ it "executes the criteria" do
286
+ @criteria.each do |doc|
287
+ doc.should == @person
288
+ end
289
+ end
290
+
291
+ end
292
+
293
+ context "when the criteria has been executed" do
294
+
295
+ before do
296
+ Person.expects(:collection).returns(@collection)
297
+ @collection.expects(:find).with({ :_type => { "$in" => ["Doctor", "Person"] }, :title => "Sir" }, {}).returns(@cursor)
298
+ @cursor.expects(:each).yields(@person)
299
+ end
300
+
301
+ it "calls each on the existing results" do
302
+ @criteria.each do |person|
303
+ person.should == @person
304
+ end
305
+ end
306
+
307
+ end
308
+
309
+ context "when no block is passed" do
310
+
311
+ it "returns self" do
312
+ @criteria.each.should == @criteria
313
+ end
314
+
315
+ end
316
+
317
+ context "when caching" do
318
+
319
+ before do
320
+ Person.expects(:collection).returns(@collection)
321
+ @collection.expects(:find).with({ :_type => { "$in" => ["Doctor", "Person"] }, :title => "Sir" }, {}).returns(@cursor)
322
+ @cursor.expects(:each).yields(@person)
323
+ @criteria.cache
324
+ @criteria.each do |doc|
325
+ doc.should == @person
326
+ end
327
+ end
328
+
329
+ it "caches the results of the cursor iteration" do
330
+ @criteria.each do |doc|
331
+ doc.should == @person
332
+ end
333
+ # Do it again for sanity's sake.
334
+ @criteria.each do |doc|
335
+ doc.should == @person
336
+ end
337
+ end
338
+
339
+ end
340
+
341
+ end
342
+
343
+ describe "#first" do
344
+
345
+ before do
346
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
347
+ @criteria.instance_variable_set(:@context, @context)
348
+ end
349
+
350
+ it "delegates to the context" do
351
+ @context.expects(:first).returns([])
352
+ @criteria.first.should == []
353
+ end
354
+
355
+ end
356
+
357
+ describe "#group" do
358
+
359
+ before do
360
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
361
+ @criteria.instance_variable_set(:@context, @context)
362
+ end
363
+
364
+ it "delegates to the context" do
365
+ @context.expects(:group).returns({})
366
+ @criteria.group.should == {}
367
+ end
368
+
369
+ end
370
+
371
+ describe "#initialize" do
372
+
373
+ let(:criteria) { Humanoid::Criteria.new(Person) }
374
+
375
+ it "sets the selector to an empty hash" do
376
+ criteria.selector.should == {}
377
+ end
378
+
379
+ it "sets the options to an empty hash" do
380
+ criteria.options.should == {}
381
+ end
382
+
383
+ it "sets the documents to an empty array" do
384
+ criteria.documents.should == []
385
+ end
386
+
387
+ it "sets the klass to the given class" do
388
+ criteria.klass.should == Person
389
+ end
390
+ end
391
+
392
+ describe "#last" do
393
+
394
+ before do
395
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
396
+ @criteria.instance_variable_set(:@context, @context)
397
+ end
398
+
399
+ it "delegates to the context" do
400
+ @context.expects(:last).returns([])
401
+ @criteria.last.should == []
402
+ end
403
+
404
+ end
405
+
406
+ describe "#max" do
407
+
408
+ before do
409
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
410
+ @criteria.instance_variable_set(:@context, @context)
411
+ end
412
+
413
+ it "delegates to the context" do
414
+ @context.expects(:max).with(:field).returns(100)
415
+ @criteria.max(:field).should == 100
416
+ end
417
+
418
+ end
419
+
420
+ describe "#merge" do
421
+
422
+ before do
423
+ @criteria.where(:title => "Sir", :age => 30).skip(40).limit(20)
424
+ end
425
+
426
+ context "with another criteria" do
427
+
428
+ context "when the other has a selector and options" do
429
+
430
+ before do
431
+ @other = Humanoid::Criteria.new(Person)
432
+ @other.where(:name => "Chloe").order_by([[:name, :asc]])
433
+ @selector = { :title => "Sir", :age => 30, :name => "Chloe" }
434
+ @options = { :skip => 40, :limit => 20, :sort => [[:name, :asc]] }
435
+ end
436
+
437
+ it "merges the selector and options hashes together" do
438
+ @criteria.merge(@other)
439
+ @criteria.selector.should == @selector
440
+ @criteria.options.should == @options
441
+ end
442
+
443
+ end
444
+
445
+ context "when the other has no selector or options" do
446
+
447
+ before do
448
+ @other = Humanoid::Criteria.new(Person)
449
+ @selector = { :title => "Sir", :age => 30 }
450
+ @options = { :skip => 40, :limit => 20 }
451
+ end
452
+
453
+ it "merges the selector and options hashes together" do
454
+ @criteria.merge(@other)
455
+ @criteria.selector.should == @selector
456
+ @criteria.options.should == @options
457
+ end
458
+
459
+ end
460
+
461
+ context "when the other has a document collection" do
462
+
463
+ before do
464
+ @documents = [ stub ]
465
+ @other = Humanoid::Criteria.new(Person)
466
+ @other.documents = @documents
467
+ end
468
+
469
+ it "merges the documents collection in" do
470
+ @criteria.merge(@other)
471
+ @criteria.documents.should == @documents
472
+ end
473
+
474
+ end
475
+
476
+ end
477
+
478
+ end
479
+
480
+ describe "#method_missing" do
481
+
482
+ before do
483
+ @criteria = Humanoid::Criteria.new(Person)
484
+ @criteria.where(:title => "Sir")
485
+ end
486
+
487
+ it "merges the criteria with the next one" do
488
+ @new_criteria = @criteria.accepted
489
+ @new_criteria.selector.should == { :title => "Sir", :terms => true }
490
+ end
491
+
492
+ context "chaining more than one scope" do
493
+
494
+ before do
495
+ @criteria = Person.accepted.old.knight
496
+ end
497
+
498
+ it "returns the final merged criteria" do
499
+ @criteria.selector.should == { :title => "Sir", :terms => true, :age => { "$gt" => 50 } }
500
+ end
501
+
502
+ end
503
+
504
+ context "when expecting behaviour of an array" do
505
+
506
+ before do
507
+ @array = mock
508
+ @document = mock
509
+ end
510
+
511
+ describe "#[]" do
512
+
513
+ it "collects the criteria and calls []" do
514
+ @criteria.expects(:entries).returns([@document])
515
+ @criteria[0].should == @document
516
+ end
517
+
518
+ end
519
+
520
+ describe "#rand" do
521
+
522
+ it "collects the criteria and call rand" do
523
+ @criteria.expects(:entries).returns(@array)
524
+ @array.expects(:send).with(:rand).returns(@document)
525
+ @criteria.rand
526
+ end
527
+
528
+ end
529
+
530
+ end
531
+
532
+ end
533
+
534
+ describe "#min" do
535
+
536
+ before do
537
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
538
+ @criteria.instance_variable_set(:@context, @context)
539
+ end
540
+
541
+ it "delegates to the context" do
542
+ @context.expects(:min).with(:field).returns(100)
543
+ @criteria.min(:field).should == 100
544
+ end
545
+
546
+ end
547
+
548
+ describe "#offset" do
549
+
550
+ end
551
+
552
+ describe "#one" do
553
+
554
+ before do
555
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
556
+ @criteria.instance_variable_set(:@context, @context)
557
+ end
558
+
559
+ it "delegates to the context" do
560
+ @context.expects(:one)
561
+ @criteria.one
562
+ end
563
+
564
+ end
565
+
566
+ describe "#page" do
567
+
568
+ before do
569
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
570
+ @criteria.instance_variable_set(:@context, @context)
571
+ end
572
+
573
+ it "delegates to the context" do
574
+ @context.expects(:page).returns(1)
575
+ @criteria.page.should == 1
576
+ end
577
+
578
+ end
579
+
580
+ describe "#paginate" do
581
+
582
+ before do
583
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
584
+ @criteria.instance_variable_set(:@context, @context)
585
+ end
586
+
587
+ it "delegates to the context" do
588
+ @context.expects(:paginate).returns([])
589
+ @criteria.paginate.should == []
590
+ end
591
+
592
+ end
593
+
594
+ describe "#per_page" do
595
+
596
+ before do
597
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
598
+ @criteria.instance_variable_set(:@context, @context)
599
+ end
600
+
601
+ it "delegates to the context" do
602
+ @context.expects(:per_page).returns(20)
603
+ @criteria.per_page.should == 20
604
+ end
605
+
606
+ end
607
+
608
+ describe "#scoped" do
609
+
610
+ before do
611
+ @criteria = Person.where(:title => "Sir").skip(20)
612
+ end
613
+
614
+ it "returns the selector plus the options" do
615
+ @criteria.scoped.should == { :where => { :title => "Sir" }, :skip => 20 }
616
+ end
617
+
618
+ end
619
+
620
+ describe "#sum" do
621
+
622
+ before do
623
+ @context = stub.quacks_like(Humanoid::Contexts::Mongo.allocate)
624
+ @criteria.instance_variable_set(:@context, @context)
625
+ end
626
+
627
+ it "delegates to the context" do
628
+ @context.expects(:sum).with(:field).returns(20)
629
+ @criteria.sum(:field).should == 20
630
+ end
631
+
632
+ end
633
+
634
+ describe ".translate" do
635
+
636
+ context "with a single argument" do
637
+
638
+ context "when the arg is a string" do
639
+
640
+ before do
641
+ @id = Mongo::ObjectID.new.to_s
642
+ @document = stub
643
+ @criteria = mock
644
+ Humanoid::Criteria.expects(:new).returns(@criteria)
645
+ end
646
+
647
+ it "delegates to #id_criteria" do
648
+ @criteria.expects(:id_criteria).with(@id).returns(@document)
649
+ Humanoid::Criteria.translate(Person, @id).should == @document
650
+ end
651
+ end
652
+
653
+ context "when the arg is an object id" do
654
+
655
+ before do
656
+ @id = Mongo::ObjectID.new
657
+ @document = stub
658
+ @criteria = mock
659
+ Humanoid::Criteria.expects(:new).returns(@criteria)
660
+ end
661
+
662
+ it "delegates to #id_criteria" do
663
+ @criteria.expects(:id_criteria).with(@id).returns(@document)
664
+ Humanoid::Criteria.translate(Person, @id).should == @document
665
+ end
666
+ end
667
+ end
668
+
669
+ context "multiple arguments" do
670
+
671
+ context "when an array of ids" do
672
+
673
+ before do
674
+ @ids = []
675
+ @documents = []
676
+ 3.times do
677
+ @ids << Mongo::ObjectID.new.to_s
678
+ @documents << stub
679
+ end
680
+ @criteria = mock
681
+ Humanoid::Criteria.expects(:new).returns(@criteria)
682
+ end
683
+
684
+ it "delegates to #id_criteria" do
685
+ @criteria.expects(:id_criteria).with(@ids).returns(@documents)
686
+ Humanoid::Criteria.translate(Person, @ids).should == @documents
687
+ end
688
+
689
+ end
690
+
691
+ context "when Person, :conditions => {}" do
692
+
693
+ before do
694
+ @criteria = Humanoid::Criteria.translate(Person, :conditions => { :title => "Test" })
695
+ end
696
+
697
+ it "returns a criteria with a selector from the conditions" do
698
+ @criteria.selector.should == { :title => "Test" }
699
+ end
700
+
701
+ it "returns a criteria with klass Person" do
702
+ @criteria.klass.should == Person
703
+ end
704
+
705
+ end
706
+
707
+ context "when :all, :conditions => {}" do
708
+
709
+ before do
710
+ @criteria = Humanoid::Criteria.translate(Person, :conditions => { :title => "Test" })
711
+ end
712
+
713
+ it "returns a criteria with a selector from the conditions" do
714
+ @criteria.selector.should == { :title => "Test" }
715
+ end
716
+
717
+ it "returns a criteria with klass Person" do
718
+ @criteria.klass.should == Person
719
+ end
720
+
721
+ end
722
+
723
+ context "when :last, :conditions => {}" do
724
+
725
+ before do
726
+ @criteria = Humanoid::Criteria.translate(Person, :conditions => { :title => "Test" })
727
+ end
728
+
729
+ it "returns a criteria with a selector from the conditions" do
730
+ @criteria.selector.should == { :title => "Test" }
731
+ end
732
+
733
+ it "returns a criteria with klass Person" do
734
+ @criteria.klass.should == Person
735
+ end
736
+ end
737
+
738
+ context "when options are provided" do
739
+
740
+ before do
741
+ @criteria = Humanoid::Criteria.translate(Person, :conditions => { :title => "Test" }, :skip => 10)
742
+ end
743
+
744
+ it "adds the criteria and the options" do
745
+ @criteria.selector.should == { :title => "Test" }
746
+ @criteria.options.should == { :skip => 10 }
747
+ end
748
+
749
+ end
750
+
751
+ end
752
+
753
+ end
754
+
755
+ context "#fuse" do
756
+
757
+ it ":where => {:title => 'Test'} returns a criteria with the correct selector" do
758
+ @result = @criteria.fuse(:where => { :title => 'Test' })
759
+ @result.selector[:title].should == 'Test'
760
+ end
761
+
762
+ it ":where => {:title => 'Test'}, :skip => 10 returns a criteria with the correct selector and options" do
763
+ @result = @criteria.fuse(:where => { :title => 'Test' }, :skip => 10)
764
+ @result.selector[:title].should == 'Test'
765
+ @result.options.should == { :skip => 10 }
766
+ end
767
+ end
768
+
769
+ end