plucky 0.5.2 → 0.6.0

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 (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