plucky 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.bundle/config +4 -0
  2. data/.gitignore +3 -1
  3. data/.travis.yml +2 -2
  4. data/Gemfile +13 -3
  5. data/Guardfile +13 -0
  6. data/README.md +1 -1
  7. data/Rakefile +3 -17
  8. data/examples/query.rb +1 -1
  9. data/lib/plucky.rb +13 -0
  10. data/lib/plucky/criteria_hash.rb +85 -56
  11. data/lib/plucky/normalizers/criteria_hash_key.rb +17 -0
  12. data/lib/plucky/normalizers/criteria_hash_value.rb +83 -0
  13. data/lib/plucky/normalizers/fields_value.rb +26 -0
  14. data/lib/plucky/normalizers/integer.rb +19 -0
  15. data/lib/plucky/normalizers/options_hash_key.rb +23 -0
  16. data/lib/plucky/normalizers/options_hash_value.rb +85 -0
  17. data/lib/plucky/normalizers/sort_value.rb +55 -0
  18. data/lib/plucky/options_hash.rb +56 -85
  19. data/lib/plucky/pagination/decorator.rb +3 -2
  20. data/lib/plucky/pagination/paginator.rb +15 -6
  21. data/lib/plucky/query.rb +93 -51
  22. data/lib/plucky/version.rb +1 -1
  23. data/script/criteria_hash.rb +21 -0
  24. data/{test → spec}/helper.rb +12 -8
  25. data/spec/plucky/criteria_hash_spec.rb +166 -0
  26. data/spec/plucky/normalizers/criteria_hash_key_spec.rb +37 -0
  27. data/spec/plucky/normalizers/criteria_hash_value_spec.rb +193 -0
  28. data/spec/plucky/normalizers/fields_value_spec.rb +45 -0
  29. data/spec/plucky/normalizers/integer_spec.rb +24 -0
  30. data/spec/plucky/normalizers/options_hash_key_spec.rb +23 -0
  31. data/spec/plucky/normalizers/options_hash_value_spec.rb +99 -0
  32. data/spec/plucky/normalizers/sort_value_spec.rb +94 -0
  33. data/spec/plucky/options_hash_spec.rb +64 -0
  34. data/{test/plucky/pagination/test_decorator.rb → spec/plucky/pagination/decorator_spec.rb} +8 -10
  35. data/spec/plucky/pagination/paginator_spec.rb +118 -0
  36. data/spec/plucky/query_spec.rb +839 -0
  37. data/spec/plucky_spec.rb +68 -0
  38. data/{test/test_symbol_operator.rb → spec/symbol_operator_spec.rb} +14 -16
  39. data/spec/symbol_spec.rb +9 -0
  40. metadata +58 -23
  41. data/test/plucky/pagination/test_paginator.rb +0 -120
  42. data/test/plucky/test_criteria_hash.rb +0 -359
  43. data/test/plucky/test_options_hash.rb +0 -302
  44. data/test/plucky/test_query.rb +0 -843
  45. data/test/test_plucky.rb +0 -48
  46. data/test/test_symbol.rb +0 -11
@@ -1,302 +0,0 @@
1
- require 'helper'
2
-
3
- class OptionsHashTest < Test::Unit::TestCase
4
- include Plucky
5
-
6
- context "Plucky::OptionsHash" do
7
- should "delegate missing methods to the source hash" do
8
- hash = {:limit => 1, :skip => 1}
9
- options = OptionsHash.new(hash)
10
- options[:skip].should == 1
11
- options[:limit].should == 1
12
- options.keys.to_set.should == [:limit, :skip].to_set
13
- end
14
-
15
- context "#initialize_copy" do
16
- setup do
17
- @original = OptionsHash.new(:fields => {:name => true}, :sort => :name, :limit => 10)
18
- @cloned = @original.clone
19
- end
20
-
21
- should "duplicate source hash" do
22
- @cloned.source.should_not equal(@original.source)
23
- end
24
-
25
- should "clone duplicable? values" do
26
- @cloned[:fields].should_not equal(@original[:fields])
27
- @cloned[:sort].should_not equal(@original[:sort])
28
- end
29
- end
30
-
31
- context "#fields?" do
32
- should "be true if fields have been selected" do
33
- OptionsHash.new(:fields => :name).fields?.should be(true)
34
- end
35
-
36
- should "be false if no fields have been selected" do
37
- OptionsHash.new.fields?.should be(false)
38
- end
39
- end
40
-
41
- context "#[]=" do
42
- should "convert order to sort" do
43
- options = OptionsHash.new(:order => :foo)
44
- options[:order].should be_nil
45
- options[:sort].should == [['foo', 1]]
46
- end
47
-
48
- should "convert select to fields" do
49
- options = OptionsHash.new(:select => 'foo')
50
- options[:select].should be_nil
51
- options[:fields].should == ['foo']
52
- end
53
-
54
- should "convert offset to skip" do
55
- options = OptionsHash.new(:offset => 1)
56
- options[:offset].should be_nil
57
- options[:skip].should == 1
58
- end
59
-
60
- context ":fields" do
61
- setup { @options = OptionsHash.new }
62
- subject { @options }
63
-
64
- should "default to nil" do
65
- subject[:fields].should be_nil
66
- end
67
-
68
- should "be nil if empty string" do
69
- subject[:fields] = ''
70
- subject[:fields].should be_nil
71
- end
72
-
73
- should "be nil if empty array" do
74
- subject[:fields] = []
75
- subject[:fields].should be_nil
76
- end
77
-
78
- should "work with array" do
79
- subject[:fields] = %w[one two]
80
- subject[:fields].should == %w[one two]
81
- end
82
-
83
- # Ruby 1.9.1 was sending array [{:age => 20}],
84
- # instead of hash.
85
- should "work with array that has one hash" do
86
- subject[:fields] = [{:age => 20}]
87
- subject[:fields].should == {:age => 20}
88
- end
89
-
90
- should "flatten multi-dimensional array" do
91
- subject[:fields] = [[:one, :two]]
92
- subject[:fields].should == [:one, :two]
93
- end
94
-
95
- should "work with symbol" do
96
- subject[:fields] = :one
97
- subject[:fields].should == [:one]
98
- end
99
-
100
- should "work with array of symbols" do
101
- subject[:fields] = [:one, :two]
102
- subject[:fields].should == [:one, :two]
103
- end
104
-
105
- should "work with hash" do
106
- subject[:fields] = {:one => 1, :two => -1}
107
- subject[:fields].should == {:one => 1, :two => -1}
108
- end
109
-
110
- should "convert comma separated list to array" do
111
- subject[:fields] = 'one, two'
112
- subject[:fields].should == %w[one two]
113
- end
114
-
115
- should "convert select" do
116
- subject[:select] = 'one, two'
117
- subject[:select].should be_nil
118
- subject[:fields].should == %w[one two]
119
- end
120
- end
121
-
122
- context ":limit" do
123
- setup { @options = OptionsHash.new }
124
- subject { @options }
125
-
126
- should "default to nil" do
127
- subject[:limit].should be_nil
128
- end
129
-
130
- should "use limit provided" do
131
- subject[:limit] = 1
132
- subject[:limit].should == 1
133
- end
134
-
135
- should "convert string to integer" do
136
- subject[:limit] = '1'
137
- subject[:limit].should == 1
138
- end
139
- end
140
-
141
- context ":skip" do
142
- setup { @options = OptionsHash.new }
143
- subject { @options }
144
-
145
- should "default to nil" do
146
- subject[:skip].should be_nil
147
- end
148
-
149
- should "use limit provided" do
150
- subject[:skip] = 1
151
- subject[:skip].should == 1
152
- end
153
-
154
- should "convert string to integer" do
155
- subject[:skip] = '1'
156
- subject[:skip].should == 1
157
- end
158
-
159
- should "be set from offset" do
160
- subject[:offset] = '1'
161
- subject[:offset].should be_nil
162
- subject[:skip].should == 1
163
- end
164
- end
165
-
166
- context ":sort" do
167
- setup { @options = OptionsHash.new }
168
- subject { @options }
169
-
170
- should "default to nil" do
171
- subject[:sort].should be_nil
172
- end
173
-
174
- should "work with natural order ascending" do
175
- subject[:sort] = {'$natural' => 1}
176
- subject[:sort].should == {'$natural' => 1}
177
- end
178
-
179
- should "work with natural order descending" do
180
- subject[:sort] = {'$natural' => -1}
181
- subject[:sort].should =={'$natural' => -1}
182
- end
183
-
184
- should "convert single ascending field (string)" do
185
- subject[:sort] = 'foo asc'
186
- subject[:sort].should == [['foo', 1]]
187
-
188
- subject[:sort] = 'foo ASC'
189
- subject[:sort].should == [['foo', 1]]
190
- end
191
-
192
- should "convert single descending field (string)" do
193
- subject[:sort] = 'foo desc'
194
- subject[:sort].should == [['foo', -1]]
195
-
196
- subject[:sort] = 'foo DESC'
197
- subject[:sort].should == [['foo', -1]]
198
- end
199
-
200
- should "convert multiple fields (string)" do
201
- subject[:sort] = 'foo desc, bar asc'
202
- subject[:sort].should == [['foo', -1], ['bar', 1]]
203
- end
204
-
205
- should "convert multiple fields and default no direction to ascending (string)" do
206
- subject[:sort] = 'foo desc, bar, baz'
207
- subject[:sort].should == [['foo', -1], ['bar', 1], ['baz', 1]]
208
- end
209
-
210
- should "convert symbol" do
211
- subject[:sort] = :name
212
- subject[:sort] = [['name', 1]]
213
- end
214
-
215
- should "convert operator" do
216
- subject[:sort] = :foo.desc
217
- subject[:sort].should == [['foo', -1]]
218
- end
219
-
220
- should "convert array of operators" do
221
- subject[:sort] = [:foo.desc, :bar.asc]
222
- subject[:sort].should == [['foo', -1], ['bar', 1]]
223
- end
224
-
225
- should "convert array of symbols" do
226
- subject[:sort] = [:first_name, :last_name]
227
- subject[:sort] = [['first_name', 1], ['last_name', 1]]
228
- end
229
-
230
- should "work with array and one string element" do
231
- subject[:sort] = ['foo, bar desc']
232
- subject[:sort].should == [['foo', 1], ['bar', -1]]
233
- end
234
-
235
- should "work with array of single array" do
236
- subject[:sort] = [['foo', -1]]
237
- subject[:sort].should == [['foo', -1]]
238
- end
239
-
240
- should "work with array of multiple arrays" do
241
- subject[:sort] = [['foo', -1], ['bar', 1]]
242
- subject[:sort].should == [['foo', -1], ['bar', 1]]
243
- end
244
-
245
- should "compact nil values in array" do
246
- subject[:sort] = [nil, :foo.desc]
247
- subject[:sort].should == [['foo', -1]]
248
- end
249
-
250
- should "convert array with mix of values" do
251
- subject[:sort] = [:foo.desc, 'bar']
252
- subject[:sort].should == [['foo', -1], ['bar', 1]]
253
- end
254
-
255
- should "convert id to _id" do
256
- subject[:sort] = [:id.asc]
257
- subject[:sort].should == [['_id', 1]]
258
- end
259
-
260
- should "convert string with $natural correctly" do
261
- subject[:sort] = '$natural desc'
262
- subject[:sort].should == [['$natural', -1]]
263
- end
264
- end
265
- end
266
- end
267
-
268
- context "#merge" do
269
- setup do
270
- @o1 = OptionsHash.new(:skip => 5, :sort => :name)
271
- @o2 = OptionsHash.new(:limit => 10, :skip => 15)
272
- @merged = @o1.merge(@o2)
273
- end
274
-
275
- should "override options in first with options in second" do
276
- @merged.should == OptionsHash.new(:limit => 10, :skip => 15, :sort => :name)
277
- end
278
-
279
- should "return new instance and not change either of the merged" do
280
- @o1[:skip].should == 5
281
- @o2[:sort].should be_nil
282
- @merged.should_not equal(@o1)
283
- @merged.should_not equal(@o2)
284
- end
285
- end
286
-
287
- context "#merge!" do
288
- setup do
289
- @o1 = OptionsHash.new(:skip => 5, :sort => :name)
290
- @o2 = OptionsHash.new(:limit => 10, :skip => 15)
291
- @merged = @o1.merge!(@o2)
292
- end
293
-
294
- should "override options in first with options in second" do
295
- @merged.should == OptionsHash.new(:limit => 10, :skip => 15, :sort => :name)
296
- end
297
-
298
- should "just update the first" do
299
- @merged.should equal(@o1)
300
- end
301
- end
302
- end
@@ -1,843 +0,0 @@
1
- require 'helper'
2
-
3
- class QueryTest < Test::Unit::TestCase
4
- context "Query" do
5
- include Plucky
6
-
7
- setup do
8
- @chris = oh(['_id', 'chris'], ['age', 26], ['name', 'Chris'])
9
- @steve = oh(['_id', 'steve'], ['age', 29], ['name', 'Steve'])
10
- @john = oh(['_id', 'john'], ['age', 28], ['name', 'John'])
11
- @collection = DB['users']
12
- @collection.insert(@chris)
13
- @collection.insert(@steve)
14
- @collection.insert(@john)
15
- end
16
-
17
- context "#initialize" do
18
- setup { @query = Query.new(@collection) }
19
- subject { @query }
20
-
21
- should "default options to options hash" do
22
- @query.options.should be_instance_of(OptionsHash)
23
- end
24
-
25
- should "default criteria to criteria hash" do
26
- @query.criteria.should be_instance_of(CriteriaHash)
27
- end
28
- end
29
-
30
- context "#initialize_copy" do
31
- setup do
32
- @original = Query.new(@collection)
33
- @cloned = @original.clone
34
- end
35
-
36
- should "duplicate options" do
37
- @cloned.options.should_not equal(@original.options)
38
- end
39
-
40
- should "duplicate criteria" do
41
- @cloned.criteria.should_not equal(@original.criteria)
42
- end
43
- end
44
-
45
- context "#[]=" do
46
- setup { @query = Query.new(@collection) }
47
- subject { @query }
48
-
49
- should "set key on options for option" do
50
- subject[:skip] = 1
51
- subject[:skip].should == 1
52
- end
53
-
54
- should "set key on criteria for criteria" do
55
- subject[:foo] = 'bar'
56
- subject[:foo].should == 'bar'
57
- end
58
- end
59
-
60
- context "#find_each" do
61
- should "return a cursor" do
62
- cursor = Query.new(@collection).find_each
63
- cursor.should be_instance_of(Mongo::Cursor)
64
- end
65
-
66
- should "work with and normalize criteria" do
67
- cursor = Query.new(@collection).find_each(:id.in => ['john'])
68
- cursor.to_a.should == [@john]
69
- end
70
-
71
- should "work with and normalize options" do
72
- cursor = Query.new(@collection).find_each(:order => :name.asc)
73
- cursor.to_a.should == [@chris, @john, @steve]
74
- end
75
-
76
- should "yield elements to a block if given" do
77
- yielded_elements = Set.new
78
- Query.new(@collection).find_each { |doc| yielded_elements << doc }
79
- yielded_elements.should == [@chris, @john, @steve].to_set
80
- end
81
-
82
- should "be Ruby-like and return a reset cursor if a block is given" do
83
- cursor = Query.new(@collection).find_each {}
84
- cursor.should be_instance_of(Mongo::Cursor)
85
- cursor.next.should be_instance_of(oh.class)
86
- end
87
- end
88
-
89
- context "#find_one" do
90
- should "work with and normalize criteria" do
91
- Query.new(@collection).find_one(:id.in => ['john']).should == @john
92
- end
93
-
94
- should "work with and normalize options" do
95
- Query.new(@collection).find_one(:order => :age.desc).should == @steve
96
- end
97
- end
98
-
99
- context "#find" do
100
- setup do
101
- @query = Query.new(@collection)
102
- end
103
- subject { @query }
104
-
105
- should "work with single id" do
106
- @query.find('chris').should == @chris
107
- end
108
-
109
- should "work with multiple ids" do
110
- @query.find('chris', 'john').should == [@chris, @john]
111
- end
112
-
113
- should "work with array of one id" do
114
- @query.find(['chris']).should == [@chris]
115
- end
116
-
117
- should "work with array of ids" do
118
- @query.find(['chris', 'john']).should == [@chris, @john]
119
- end
120
-
121
- should "ignore those not found" do
122
- @query.find('john', 'frank').should == [@john]
123
- end
124
-
125
- should "return nil for nil" do
126
- @query.find(nil).should be_nil
127
- end
128
-
129
- should "return nil for *nil" do
130
- @query.find(*nil).should be_nil
131
- end
132
-
133
- should "normalize if using object id" do
134
- id = @collection.insert(:name => 'Frank')
135
- @query.object_ids([:_id])
136
- doc = @query.find(id.to_s)
137
- doc['name'].should == 'Frank'
138
- end
139
- end
140
-
141
- context "#per_page" do
142
- should "default to 25" do
143
- Query.new(@collection).per_page.should == 25
144
- end
145
-
146
- should "be changeable and chainable" do
147
- query = Query.new(@collection)
148
- query.per_page(10).per_page.should == 10
149
- end
150
- end
151
-
152
- context "#paginate" do
153
- setup do
154
- @query = Query.new(@collection).sort(:age).per_page(1)
155
- end
156
- subject { @query }
157
-
158
- should "default to page 1" do
159
- subject.paginate.should == [@chris]
160
- end
161
-
162
- should "work with other pages" do
163
- subject.paginate(:page => 2).should == [@john]
164
- subject.paginate(:page => 3).should == [@steve]
165
- end
166
-
167
- should "work with string page number" do
168
- subject.paginate(:page => '2').should == [@john]
169
- end
170
-
171
- should "allow changing per_page" do
172
- subject.paginate(:per_page => 2).should == [@chris, @john]
173
- end
174
-
175
- should "decorate return value" do
176
- docs = subject.paginate
177
- docs.should respond_to(:paginator)
178
- docs.should respond_to(:total_entries)
179
- end
180
-
181
- should "not modify the original query" do
182
- subject.paginate(:name => 'John')
183
- subject[:page].should be_nil
184
- subject[:per_page].should be_nil
185
- subject[:name].should be_nil
186
- end
187
-
188
- context "with options" do
189
- setup do
190
- @result = @query.sort(:age).paginate(:age.gt => 27, :per_page => 10)
191
- end
192
- subject { @result }
193
-
194
- should "only return matching" do
195
- subject.should == [@john, @steve]
196
- end
197
-
198
- should "correctly count matching" do
199
- subject.total_entries.should == 2
200
- end
201
- end
202
- end
203
-
204
- context "#all" do
205
- should "work with no arguments" do
206
- docs = Query.new(@collection).all
207
- docs.size.should == 3
208
- docs.should include(@john)
209
- docs.should include(@steve)
210
- docs.should include(@chris)
211
- end
212
-
213
- should "work with and normalize criteria" do
214
- docs = Query.new(@collection).all(:id.in => ['steve'])
215
- docs.should == [@steve]
216
- end
217
-
218
- should "work with and normalize options" do
219
- docs = Query.new(@collection).all(:order => :name.asc)
220
- docs.should == [@chris, @john, @steve]
221
- end
222
-
223
- should "not modify original query object" do
224
- query = Query.new(@collection)
225
- query.all(:name => 'Steve')
226
- query[:name].should be_nil
227
- end
228
- end
229
-
230
- context "#first" do
231
- should "work with and normalize criteria" do
232
- Query.new(@collection).first(:age.lt => 29).should == @chris
233
- end
234
-
235
- should "work with and normalize options" do
236
- Query.new(@collection).first(:age.lte => 29, :order => :name.desc).should == @steve
237
- end
238
-
239
- should "not modify original query object" do
240
- query = Query.new(@collection)
241
- query.first(:name => 'Steve')
242
- query[:name].should be_nil
243
- end
244
- end
245
-
246
- context "#last" do
247
- should "work with and normalize criteria" do
248
- Query.new(@collection).last(:age.lte => 29, :order => :name.asc).should == @steve
249
- end
250
-
251
- should "work with and normalize options" do
252
- Query.new(@collection).last(:age.lte => 26, :order => :name.desc).should == @chris
253
- end
254
-
255
- should "not modify original query object" do
256
- query = Query.new(@collection)
257
- query.last(:name => 'Steve')
258
- query[:name].should be_nil
259
- end
260
- end
261
-
262
- context "#count" do
263
- should "work with no arguments" do
264
- Query.new(@collection).count.should == 3
265
- end
266
-
267
- should "work with and normalize criteria" do
268
- Query.new(@collection).count(:age.lte => 28).should == 2
269
- end
270
-
271
- should "not modify original query object" do
272
- query = Query.new(@collection)
273
- query.count(:name => 'Steve')
274
- query[:name].should be_nil
275
- end
276
- end
277
-
278
- context "#size" do
279
- should "work just like count without options" do
280
- Query.new(@collection).size.should == 3
281
- end
282
- end
283
-
284
- context "#distinct" do
285
- setup do
286
- # same age as John
287
- @mark = oh(['_id', 'mark'], ['age', 28], ['name', 'Mark'])
288
- @collection.insert(@mark)
289
- end
290
-
291
- should "work with just a key" do
292
- Query.new(@collection).distinct(:age).sort.should == [26, 28, 29]
293
- end
294
-
295
- should "work with criteria" do
296
- Query.new(@collection).distinct(:age, :age.gt => 26).sort.should == [28, 29]
297
- end
298
-
299
- should "not modify the original query object" do
300
- query = Query.new(@collection)
301
- query.distinct(:age, :name => 'Mark').should == [28]
302
- query[:name].should be_nil
303
- end
304
- end
305
-
306
- context "#remove" do
307
- should "work with no arguments" do
308
- lambda { Query.new(@collection).remove }.should change { @collection.count }.by(3)
309
- end
310
-
311
- should "work with and normalize criteria" do
312
- lambda { Query.new(@collection).remove(:age.lte => 28) }.should change { @collection.count }
313
- end
314
-
315
- should "work with options" do
316
- lambda { Query.new(@collection).remove({:age.lte => 28}, :safe => true) }.should change { @collection.count }
317
- end
318
-
319
- should "not modify original query object" do
320
- query = Query.new(@collection)
321
- query.remove(:name => 'Steve')
322
- query[:name].should be_nil
323
- end
324
- end
325
-
326
- context "#update" do
327
- setup do
328
- @query = Query.new(@collection).where('_id' => 'john')
329
- end
330
-
331
- should "work with document" do
332
- @query.update('$set' => {'age' => 29})
333
- doc = @query.first('_id' => 'john')
334
- doc['age'].should be(29)
335
- end
336
-
337
- should "work with document and driver options" do
338
- @query.update({'$set' => {'age' => 30}}, :multi => true)
339
- @query.each do |doc|
340
- doc['age'].should be(30)
341
- end
342
- end
343
- end
344
-
345
- context "#[]" do
346
- should "return value if key in criteria (symbol)" do
347
- Query.new(@collection, :count => 1)[:count].should == 1
348
- end
349
-
350
- should "return value if key in criteria (string)" do
351
- Query.new(@collection, :count => 1)['count'].should == 1
352
- end
353
-
354
- should "return nil if key not in criteria" do
355
- Query.new(@collection)[:count].should be_nil
356
- end
357
- end
358
-
359
- context "#[]=" do
360
- setup { @query = Query.new(@collection) }
361
-
362
- should "set the value of the given criteria key" do
363
- @query[:count] = 1
364
- @query[:count].should == 1
365
- end
366
-
367
- should "overwrite value if key already exists" do
368
- @query[:count] = 1
369
- @query[:count] = 2
370
- @query[:count].should == 2
371
- end
372
-
373
- should "normalize value" do
374
- now = Time.now
375
- @query[:published_at] = now
376
- @query[:published_at].should == now.utc
377
- end
378
- end
379
-
380
- context "#fields" do
381
- setup { @query = Query.new(@collection) }
382
- subject { @query }
383
-
384
- should "work" do
385
- subject.fields(:name).first(:id => 'john').keys.should == ['_id', 'name']
386
- end
387
-
388
- should "return new instance of query" do
389
- new_query = subject.fields(:name)
390
- new_query.should_not equal(subject)
391
- subject[:fields].should be_nil
392
- end
393
-
394
- should "work with hash" do
395
- subject.fields(:name => 0).
396
- first(:id => 'john').keys.sort.
397
- should == ['_id', 'age']
398
- end
399
- end
400
-
401
- context "#ignore" do
402
- setup {@query = Query.new(@collection)}
403
- subject {@query}
404
-
405
- should "include a list of keys to ignore" do
406
- new_query = subject.ignore(:name).first(:id => 'john')
407
- new_query.keys.should == ['_id', 'age']
408
- end
409
- end
410
-
411
- context "#only" do
412
- setup {@query = Query.new(@collection)}
413
- subject {@query}
414
-
415
- should "inclue a list of keys with others excluded" do
416
- new_query = subject.only(:name).first(:id => 'john')
417
- new_query.keys.should == ['_id', 'name']
418
- end
419
-
420
- end
421
-
422
- context "#skip" do
423
- setup { @query = Query.new(@collection) }
424
- subject { @query }
425
-
426
- should "work" do
427
- subject.skip(2).all(:order => :age).should == [@steve]
428
- end
429
-
430
- should "set skip option" do
431
- subject.skip(5).options[:skip].should == 5
432
- end
433
-
434
- should "override existing skip" do
435
- subject.skip(5).skip(10).options[:skip].should == 10
436
- end
437
-
438
- should "return nil for nil" do
439
- subject.skip.options[:skip].should be_nil
440
- end
441
-
442
- should "return new instance of query" do
443
- new_query = subject.skip(2)
444
- new_query.should_not equal(subject)
445
- subject[:skip].should be_nil
446
- end
447
-
448
- should "alias to offset" do
449
- subject.offset(5).options[:skip].should == 5
450
- end
451
- end
452
-
453
- context "#limit" do
454
- setup { @query = Query.new(@collection) }
455
- subject { @query }
456
-
457
- should "work" do
458
- subject.limit(2).all(:order => :age).should == [@chris, @john]
459
- end
460
-
461
- should "set limit option" do
462
- subject.limit(5).options[:limit].should == 5
463
- end
464
-
465
- should "overwrite existing limit" do
466
- subject.limit(5).limit(15).options[:limit].should == 15
467
- end
468
-
469
- should "return new instance of query" do
470
- new_query = subject.limit(2)
471
- new_query.should_not equal(subject)
472
- subject[:limit].should be_nil
473
- end
474
- end
475
-
476
- context "#sort" do
477
- setup { @query = Query.new(@collection) }
478
- subject { @query }
479
-
480
- should "work" do
481
- subject.sort(:age).all.should == [@chris, @john, @steve]
482
- subject.sort(:age.desc).all.should == [@steve, @john, @chris]
483
- end
484
-
485
- should "work with symbol operators" do
486
- subject.sort(:foo.asc, :bar.desc).options[:sort].should == [['foo', 1], ['bar', -1]]
487
- end
488
-
489
- should "work with string" do
490
- subject.sort('foo, bar desc').options[:sort].should == [['foo', 1], ['bar', -1]]
491
- end
492
-
493
- should "work with just a symbol" do
494
- subject.sort(:foo).options[:sort].should == [['foo', 1]]
495
- end
496
-
497
- should "work with symbol descending" do
498
- subject.sort(:foo.desc).options[:sort].should == [['foo', -1]]
499
- end
500
-
501
- should "work with multiple symbols" do
502
- subject.sort(:foo, :bar).options[:sort].should == [['foo', 1], ['bar', 1]]
503
- end
504
-
505
- should "return new instance of query" do
506
- new_query = subject.sort(:name)
507
- new_query.should_not equal(subject)
508
- subject[:sort].should be_nil
509
- end
510
-
511
- should "be aliased to order" do
512
- subject.order(:foo).options[:sort].should == [['foo', 1]]
513
- subject.order(:foo, :bar).options[:sort].should == [['foo', 1], ['bar', 1]]
514
- end
515
- end
516
-
517
- context "#reverse" do
518
- setup { @query = Query.new(@collection) }
519
- subject { @query }
520
-
521
- should "work" do
522
- subject.sort(:age).reverse.all.should == [@steve, @john, @chris]
523
- end
524
-
525
- should "not error if no sort provided" do
526
- assert_nothing_raised do
527
- subject.reverse
528
- end
529
- end
530
-
531
- should "reverse the sort order" do
532
- subject.sort('foo asc, bar desc').
533
- reverse.options[:sort].should == [['foo', -1], ['bar', 1]]
534
- end
535
-
536
- should "return new instance of query" do
537
- sorted_query = subject.sort(:name)
538
- new_query = sorted_query.reverse
539
- new_query.should_not equal(sorted_query)
540
- sorted_query[:sort].should == [['name', 1]]
541
- end
542
- end
543
-
544
- context "#amend" do
545
- should "normalize and update options" do
546
- Query.new(@collection).amend(:order => :age.desc).options[:sort].should == [['age', -1]]
547
- end
548
-
549
- should "work with simple stuff" do
550
- Query.new(@collection).
551
- amend(:foo => 'bar').
552
- amend(:baz => 'wick').
553
- criteria.should == CriteriaHash.new(:foo => 'bar', :baz => 'wick')
554
- end
555
- end
556
-
557
- context "#where" do
558
- setup { @query = Query.new(@collection) }
559
- subject { @query }
560
-
561
- should "work" do
562
- subject.where(:age.lt => 29).where(:name => 'Chris').all.should == [@chris]
563
- end
564
-
565
- should "work with literal regexp" do
566
- subject.where(:name => /^c/i).all.should == [@chris]
567
- end
568
-
569
- should "update criteria" do
570
- subject.
571
- where(:moo => 'cow').
572
- where(:foo => 'bar').
573
- criteria.should == CriteriaHash.new(:foo => 'bar', :moo => 'cow')
574
- end
575
-
576
- should "get normalized" do
577
- subject.
578
- where(:moo => 'cow').
579
- where(:foo.in => ['bar']).
580
- criteria.should == CriteriaHash.new(:moo => 'cow', :foo => {'$in' => ['bar']})
581
- end
582
-
583
- should "normalize merged criteria" do
584
- subject.
585
- where(:foo => 'bar').
586
- where(:foo => 'baz').
587
- criteria.should == CriteriaHash.new(:foo => {'$in' => %w[bar baz]})
588
- end
589
-
590
- should "return new instance of query" do
591
- new_query = subject.where(:name => 'John')
592
- new_query.should_not equal(subject)
593
- subject[:name].should be_nil
594
- end
595
- end
596
-
597
- context "#filter" do
598
- setup { @query = Query.new(@collection) }
599
- subject { @query }
600
-
601
- should "work the same as where" do
602
- subject.filter(:age.lt => 29).filter(:name => 'Chris').all.should == [@chris]
603
- end
604
- end
605
-
606
- context "#empty?" do
607
- should "be true if empty" do
608
- @collection.remove
609
- Query.new(@collection).should be_empty
610
- end
611
-
612
- should "be false if not empty" do
613
- Query.new(@collection).should_not be_empty
614
- end
615
- end
616
-
617
- context "#exists?" do
618
- should "be true if found" do
619
- Query.new(@collection).exists?(:name => 'John').should be(true)
620
- end
621
-
622
- should "be false if not found" do
623
- Query.new(@collection).exists?(:name => 'Billy Bob').should be(false)
624
- end
625
- end
626
-
627
- context "#exist?" do
628
- should "be true if found" do
629
- Query.new(@collection).exist?(:name => 'John').should be(true)
630
- end
631
-
632
- should "be false if not found" do
633
- Query.new(@collection).exist?(:name => 'Billy Bob').should be(false)
634
- end
635
- end
636
-
637
- context "#include?" do
638
- should "be true if included" do
639
- Query.new(@collection).include?(@john).should be(true)
640
- end
641
-
642
- should "be false if not included" do
643
- Query.new(@collection).include?(['_id', 'frankyboy']).should be(false)
644
- end
645
- end
646
-
647
- context "#to_a" do
648
- should "return all documents the query matches" do
649
- Query.new(@collection).sort(:name).to_a.
650
- should == [@chris, @john, @steve]
651
-
652
- Query.new(@collection).where(:name => 'John').sort(:name).to_a.
653
- should == [@john]
654
- end
655
- end
656
-
657
- context "#each" do
658
- should "iterate through matching documents" do
659
- docs = []
660
- Query.new(@collection).sort(:name).each do |doc|
661
- docs << doc
662
- end
663
- docs.should == [@chris, @john, @steve]
664
- end
665
-
666
- should "return a working enumerator" do
667
- query = Query.new(@collection)
668
- query.each.methods.map(&:to_sym).include?(:group_by).should be(true)
669
- query.each.next.class.should == oh.class
670
- end
671
-
672
- should "be fulfilled by #find_each" do
673
- query = Query.new(@collection)
674
- query.expects(:find_each)
675
- query.each
676
- end
677
- end
678
-
679
- context "enumerables" do
680
- should "work" do
681
- query = Query.new(@collection).sort(:name)
682
- query.map { |doc| doc['name'] }.should == %w(Chris John Steve)
683
- query.collect { |doc| doc['name'] }.should == %w(Chris John Steve)
684
- query.detect { |doc| doc['name'] == 'John' }.should == @john
685
- query.min { |a, b| a['age'] <=> b['age'] }.should == @chris
686
- end
687
- end
688
-
689
- context "#object_ids" do
690
- setup { @query = Query.new(@collection) }
691
- subject { @query }
692
-
693
- should "set criteria's object_ids" do
694
- subject.criteria.expects(:object_ids=).with([:foo, :bar])
695
- subject.object_ids(:foo, :bar)
696
- end
697
-
698
- should "return current object ids if keys argument is empty" do
699
- subject.object_ids(:foo, :bar)
700
- subject.object_ids.should == [:foo, :bar]
701
- end
702
- end
703
-
704
- context "#merge" do
705
- should "overwrite options" do
706
- query1 = Query.new(@collection, :skip => 5, :limit => 5)
707
- query2 = Query.new(@collection, :skip => 10, :limit => 10)
708
- new_query = query1.merge(query2)
709
- new_query.options[:skip].should == 10
710
- new_query.options[:limit].should == 10
711
- end
712
-
713
- should "merge criteria" do
714
- query1 = Query.new(@collection, :foo => 'bar')
715
- query2 = Query.new(@collection, :foo => 'baz', :fent => 'wick')
716
- new_query = query1.merge(query2)
717
- new_query.criteria[:fent].should == 'wick'
718
- new_query.criteria[:foo].should == {'$in' => %w[bar baz]}
719
- end
720
-
721
- should "not affect either of the merged queries" do
722
- query1 = Query.new(@collection, :foo => 'bar', :limit => 5)
723
- query2 = Query.new(@collection, :foo => 'baz', :limit => 10)
724
- new_query = query1.merge(query2)
725
- query1[:foo].should == 'bar'
726
- query1[:limit].should == 5
727
- query2[:foo].should == 'baz'
728
- query2[:limit].should == 10
729
- end
730
- end
731
-
732
- context "Criteria/option auto-detection" do
733
- should "know :conditions are criteria" do
734
- query = Query.new(@collection, :conditions => {:foo => 'bar'})
735
- query.criteria.should == CriteriaHash.new(:foo => 'bar')
736
- query.options.keys.should_not include(:conditions)
737
- end
738
-
739
- {
740
- :fields => ['foo'],
741
- :sort => [['foo', 1]],
742
- :hint => '',
743
- :skip => 0,
744
- :limit => 0,
745
- :batch_size => 0,
746
- :timeout => 0,
747
- }.each do |option, value|
748
- should "know #{option} is an option" do
749
- query = Query.new(@collection, option => value)
750
- query.options[option].should == value
751
- query.criteria.keys.should_not include(option)
752
- end
753
- end
754
-
755
- should "know select is an option and remove it from options" do
756
- query = Query.new(@collection, :select => 'foo')
757
- query.options[:fields].should == ['foo']
758
- query.criteria.keys.should_not include(:select)
759
- query.options.keys.should_not include(:select)
760
- end
761
-
762
- should "know order is an option and remove it from options" do
763
- query = Query.new(@collection, :order => 'foo')
764
- query.options[:sort].should == [['foo', 1]]
765
- query.criteria.keys.should_not include(:order)
766
- query.options.keys.should_not include(:order)
767
- end
768
-
769
- should "know offset is an option and remove it from options" do
770
- query = Query.new(@collection, :offset => 0)
771
- query.options[:skip].should == 0
772
- query.criteria.keys.should_not include(:offset)
773
- query.options.keys.should_not include(:offset)
774
- end
775
-
776
- should "work with full range of things" do
777
- query = Query.new(@collection, {
778
- :foo => 'bar',
779
- :baz => true,
780
- :sort => [['foo', 1]],
781
- :fields => ['foo', 'baz'],
782
- :limit => 10,
783
- :skip => 10,
784
- })
785
- query.criteria.should == CriteriaHash.new(:foo => 'bar', :baz => true)
786
- query.options.should == OptionsHash.new({
787
- :sort => [['foo', 1]],
788
- :fields => ['foo', 'baz'],
789
- :limit => 10,
790
- :skip => 10,
791
- })
792
- end
793
- end
794
-
795
- should "inspect pretty" do
796
- inspect = Query.new(@collection, :baz => 'wick', :foo => 'bar').inspect
797
- inspect.should == '#<Plucky::Query baz: "wick", foo: "bar">'
798
- end
799
-
800
- should "delegate simple? to criteria" do
801
- query = Query.new(@collection)
802
- query.criteria.expects(:simple?)
803
- query.simple?
804
- end
805
-
806
- should "delegate fields? to options" do
807
- query = Query.new(@collection)
808
- query.options.expects(:fields?)
809
- query.fields?
810
- end
811
-
812
- context "#explain" do
813
- setup { @query = Query.new(@collection) }
814
- subject { @query }
815
-
816
- should "work" do
817
- explain = subject.where(:age.lt => 28).explain
818
- explain['cursor'].should == 'BasicCursor'
819
- explain['nscanned'].should == 3
820
- end
821
- end
822
-
823
- context "Transforming documents" do
824
- setup do
825
- transformer = lambda { |doc| @user_class.new(doc['_id'], doc['name'], doc['age']) }
826
- @user_class = Struct.new(:id, :name, :age)
827
- @query = Query.new(@collection, :transformer => transformer)
828
- end
829
-
830
- should "work with find_one" do
831
- result = @query.find_one('_id' => 'john')
832
- result.should be_instance_of(@user_class)
833
- end
834
-
835
- should "work with find_each" do
836
- results = @query.find_each
837
- results.each do |result|
838
- result.should be_instance_of(@user_class)
839
- end
840
- end
841
- end
842
- end
843
- end