couchrest_model 1.0.0 → 1.1.0.beta
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.
- data/.gitignore +1 -1
- data/Gemfile.lock +19 -20
- data/README.md +145 -20
- data/VERSION +1 -1
- data/couchrest_model.gemspec +2 -3
- data/history.txt +14 -0
- data/lib/couchrest/model/associations.rb +4 -4
- data/lib/couchrest/model/base.rb +5 -0
- data/lib/couchrest/model/callbacks.rb +1 -2
- data/lib/couchrest/model/collection.rb +1 -1
- data/lib/couchrest/model/designs/view.rb +486 -0
- data/lib/couchrest/model/designs.rb +81 -0
- data/lib/couchrest/model/document_queries.rb +1 -1
- data/lib/couchrest/model/persistence.rb +25 -16
- data/lib/couchrest/model/properties.rb +5 -1
- data/lib/couchrest/model/property.rb +2 -2
- data/lib/couchrest/model/proxyable.rb +152 -0
- data/lib/couchrest/model/typecast.rb +1 -1
- data/lib/couchrest/model/validations/casted_model.rb +3 -1
- data/lib/couchrest/model/validations/locale/en.yml +1 -1
- data/lib/couchrest/model/validations/uniqueness.rb +6 -7
- data/lib/couchrest/model/validations.rb +1 -0
- data/lib/couchrest/model/views.rb +11 -9
- data/lib/couchrest_model.rb +3 -0
- data/spec/couchrest/assocations_spec.rb +2 -2
- data/spec/couchrest/base_spec.rb +15 -1
- data/spec/couchrest/casted_model_spec.rb +30 -12
- data/spec/couchrest/class_proxy_spec.rb +2 -2
- data/spec/couchrest/collection_spec.rb +89 -0
- data/spec/couchrest/designs/view_spec.rb +766 -0
- data/spec/couchrest/designs_spec.rb +110 -0
- data/spec/couchrest/persistence_spec.rb +36 -7
- data/spec/couchrest/property_spec.rb +15 -0
- data/spec/couchrest/proxyable_spec.rb +329 -0
- data/spec/couchrest/{validations.rb → validations_spec.rb} +1 -3
- data/spec/couchrest/view_spec.rb +19 -91
- data/spec/fixtures/base.rb +8 -6
- data/spec/fixtures/more/article.rb +1 -1
- data/spec/fixtures/more/course.rb +4 -2
- metadata +21 -76
- data/lib/couchrest/model/view.rb +0 -190
@@ -0,0 +1,766 @@
|
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
class DesignViewModel < CouchRest::Model::Base
|
4
|
+
use_database DB
|
5
|
+
property :name
|
6
|
+
property :title
|
7
|
+
|
8
|
+
design do
|
9
|
+
view :by_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Design View" do
|
14
|
+
|
15
|
+
describe "(unit tests)" do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
@klass = CouchRest::Model::Designs::View
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ".new" do
|
22
|
+
|
23
|
+
describe "with invalid parent model" do
|
24
|
+
it "should burn" do
|
25
|
+
lambda { @klass.new(String) }.should raise_exception
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "with CouchRest Model" do
|
30
|
+
|
31
|
+
it "should setup attributes" do
|
32
|
+
@obj = @klass.new(DesignViewModel, {}, 'test_view')
|
33
|
+
@obj.model.should eql(DesignViewModel)
|
34
|
+
@obj.name.should eql('test_view')
|
35
|
+
@obj.query.should eql({:reduce => false})
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should complain if there is no name" do
|
39
|
+
lambda { @klass.new(DesignViewModel, {}, nil) }.should raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "with previous view instance" do
|
45
|
+
|
46
|
+
before :each do
|
47
|
+
first = @klass.new(DesignViewModel, {}, 'test_view')
|
48
|
+
@obj = @klass.new(first, {:foo => :bar})
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should copy attributes" do
|
52
|
+
@obj.model.should eql(DesignViewModel)
|
53
|
+
@obj.name.should eql('test_view')
|
54
|
+
@obj.query.should eql({:reduce => false, :foo => :bar})
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ".create" do
|
62
|
+
|
63
|
+
before :each do
|
64
|
+
@design_doc = {}
|
65
|
+
DesignViewModel.stub!(:design_doc).and_return(@design_doc)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should add a basic view" do
|
69
|
+
@klass.create(DesignViewModel, 'test_view', :map => 'foo')
|
70
|
+
@design_doc['views']['test_view'].should_not be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should auto generate mapping from name" do
|
74
|
+
lambda { @klass.create(DesignViewModel, 'by_title') }.should_not raise_error
|
75
|
+
str = @design_doc['views']['by_title']['map']
|
76
|
+
str.should include("((doc['couchrest-type'] == 'DesignViewModel') && (doc['title'] != null))")
|
77
|
+
str.should include("emit(doc['title'], 1);")
|
78
|
+
str = @design_doc['views']['by_title']['reduce']
|
79
|
+
str.should include("return sum(values);")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should auto generate mapping from name with and" do
|
83
|
+
@klass.create(DesignViewModel, 'by_title_and_name')
|
84
|
+
str = @design_doc['views']['by_title_and_name']['map']
|
85
|
+
str.should include("(doc['title'] != null) && (doc['name'] != null)")
|
86
|
+
str.should include("emit([doc['title'], doc['name']], 1);")
|
87
|
+
str = @design_doc['views']['by_title_and_name']['reduce']
|
88
|
+
str.should include("return sum(values);")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "instance methods" do
|
94
|
+
|
95
|
+
before :each do
|
96
|
+
@obj = @klass.new(DesignViewModel, {}, 'test_view')
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#rows" do
|
100
|
+
it "should execute query" do
|
101
|
+
@obj.should_receive(:execute).and_return(true)
|
102
|
+
@obj.should_receive(:result).twice.and_return({'rows' => []})
|
103
|
+
@obj.rows.should be_empty
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should wrap rows in ViewRow class" do
|
107
|
+
@obj.should_receive(:execute).and_return(true)
|
108
|
+
@obj.should_receive(:result).twice.and_return({'rows' => [{:foo => :bar}]})
|
109
|
+
CouchRest::Model::Designs::ViewRow.should_receive(:new).with({:foo => :bar}, @obj.model)
|
110
|
+
@obj.rows
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#all" do
|
115
|
+
it "should ensure docs included and call docs" do
|
116
|
+
@obj.should_receive(:include_docs!)
|
117
|
+
@obj.should_receive(:docs)
|
118
|
+
@obj.all
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#docs" do
|
123
|
+
it "should provide docs from rows" do
|
124
|
+
@obj.should_receive(:rows).and_return([])
|
125
|
+
@obj.docs
|
126
|
+
end
|
127
|
+
it "should cache the results" do
|
128
|
+
@obj.should_receive(:rows).once.and_return([])
|
129
|
+
@obj.docs
|
130
|
+
@obj.docs
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#first" do
|
135
|
+
it "should provide the first result of loaded query" do
|
136
|
+
@obj.should_receive(:result).and_return(true)
|
137
|
+
@obj.should_receive(:all).and_return([:foo])
|
138
|
+
@obj.first.should eql(:foo)
|
139
|
+
end
|
140
|
+
it "should perform a query if no results cached" do
|
141
|
+
view = mock('SubView')
|
142
|
+
@obj.should_receive(:result).and_return(nil)
|
143
|
+
@obj.should_receive(:limit).with(1).and_return(view)
|
144
|
+
view.should_receive(:all).and_return([:foo])
|
145
|
+
@obj.first.should eql(:foo)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#last" do
|
150
|
+
it "should provide the last result of loaded query" do
|
151
|
+
@obj.should_receive(:result).and_return(true)
|
152
|
+
@obj.should_receive(:all).and_return([:foo, :bar])
|
153
|
+
@obj.first.should eql(:foo)
|
154
|
+
end
|
155
|
+
it "should perform a query if no results cached" do
|
156
|
+
view = mock('SubView')
|
157
|
+
@obj.should_receive(:result).and_return(nil)
|
158
|
+
@obj.should_receive(:limit).with(1).and_return(view)
|
159
|
+
view.should_receive(:descending).and_return(view)
|
160
|
+
view.should_receive(:all).and_return([:foo, :bar])
|
161
|
+
@obj.last.should eql(:bar)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#count" do
|
166
|
+
it "should raise an error if view prepared for group" do
|
167
|
+
@obj.should_receive(:query).and_return({:group => true})
|
168
|
+
lambda { @obj.count }.should raise_error
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return first row value if reduce possible" do
|
172
|
+
view = mock("SubView")
|
173
|
+
row = mock("Row")
|
174
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
175
|
+
@obj.should_receive(:reduce).and_return(view)
|
176
|
+
view.should_receive(:rows).and_return([row])
|
177
|
+
row.should_receive(:value).and_return(2)
|
178
|
+
@obj.count.should eql(2)
|
179
|
+
end
|
180
|
+
it "should return 0 if no rows and reduce possible" do
|
181
|
+
view = mock("SubView")
|
182
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
183
|
+
@obj.should_receive(:reduce).and_return(view)
|
184
|
+
view.should_receive(:rows).and_return([])
|
185
|
+
@obj.count.should eql(0)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should perform limit request for total_rows" do
|
189
|
+
view = mock("SubView")
|
190
|
+
@obj.should_receive(:limit).with(0).and_return(view)
|
191
|
+
view.should_receive(:total_rows).and_return(4)
|
192
|
+
@obj.should_receive(:can_reduce?).and_return(false)
|
193
|
+
@obj.count.should eql(4)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "#empty?" do
|
198
|
+
it "should check the #all method for any results" do
|
199
|
+
all = mock("All")
|
200
|
+
all.should_receive(:empty?).and_return('win')
|
201
|
+
@obj.should_receive(:all).and_return(all)
|
202
|
+
@obj.empty?.should eql('win')
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "#each" do
|
207
|
+
it "should call each method on all" do
|
208
|
+
@obj.should_receive(:all).and_return([])
|
209
|
+
@obj.each
|
210
|
+
end
|
211
|
+
it "should call each and pass block" do
|
212
|
+
set = [:foo, :bar]
|
213
|
+
@obj.should_receive(:all).and_return(set)
|
214
|
+
result = []
|
215
|
+
@obj.each do |s|
|
216
|
+
result << s
|
217
|
+
end
|
218
|
+
result.should eql(set)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe "#offset" do
|
223
|
+
it "should excute" do
|
224
|
+
@obj.should_receive(:execute).and_return({'offset' => 3})
|
225
|
+
@obj.offset.should eql(3)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#total_rows" do
|
230
|
+
it "should excute" do
|
231
|
+
@obj.should_receive(:execute).and_return({'total_rows' => 3})
|
232
|
+
@obj.total_rows.should eql(3)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "#keys" do
|
237
|
+
it "should request each row and provide key value" do
|
238
|
+
row = mock("Row")
|
239
|
+
row.should_receive(:key).twice.and_return('foo')
|
240
|
+
@obj.should_receive(:rows).and_return([row, row])
|
241
|
+
@obj.keys.should eql(['foo', 'foo'])
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "#values" do
|
246
|
+
it "should request each row and provide value" do
|
247
|
+
row = mock("Row")
|
248
|
+
row.should_receive(:value).twice.and_return('foo')
|
249
|
+
@obj.should_receive(:rows).and_return([row, row])
|
250
|
+
@obj.values.should eql(['foo', 'foo'])
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "#[]" do
|
255
|
+
it "should execute and provide requested field" do
|
256
|
+
@obj.should_receive(:execute).and_return({'total_rows' => 2})
|
257
|
+
@obj['total_rows'].should eql(2)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "#info" do
|
262
|
+
it "should raise error" do
|
263
|
+
lambda { @obj.info }.should raise_error
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "#database" do
|
268
|
+
it "should update query with value" do
|
269
|
+
@obj.should_receive(:update_query).with({:database => 'foo'})
|
270
|
+
@obj.database('foo')
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "#key" do
|
275
|
+
it "should update query with value" do
|
276
|
+
@obj.should_receive(:update_query).with({:key => 'foo'})
|
277
|
+
@obj.key('foo')
|
278
|
+
end
|
279
|
+
it "should raise and error if startkey set" do
|
280
|
+
@obj.query[:startkey] = 'bar'
|
281
|
+
lambda { @obj.key('foo') }.should raise_error
|
282
|
+
end
|
283
|
+
it "should raise and error if endkey set" do
|
284
|
+
@obj.query[:endkey] = 'bar'
|
285
|
+
lambda { @obj.key('foo') }.should raise_error
|
286
|
+
end
|
287
|
+
it "should raise and error if both startkey and endkey set" do
|
288
|
+
@obj.query[:startkey] = 'bar'
|
289
|
+
@obj.query[:endkey] = 'bar'
|
290
|
+
lambda { @obj.key('foo') }.should raise_error
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe "#startkey" do
|
295
|
+
it "should update query with value" do
|
296
|
+
@obj.should_receive(:update_query).with({:startkey => 'foo'})
|
297
|
+
@obj.startkey('foo')
|
298
|
+
end
|
299
|
+
it "should raise and error if key set" do
|
300
|
+
@obj.query[:key] = 'bar'
|
301
|
+
lambda { @obj.startkey('foo') }.should raise_error
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#startkey_doc" do
|
306
|
+
it "should update query with value" do
|
307
|
+
@obj.should_receive(:update_query).with({:startkey_docid => 'foo'})
|
308
|
+
@obj.startkey_doc('foo')
|
309
|
+
end
|
310
|
+
it "should update query with object id if available" do
|
311
|
+
doc = mock("Document")
|
312
|
+
doc.should_receive(:id).and_return(44)
|
313
|
+
@obj.should_receive(:update_query).with({:startkey_docid => 44})
|
314
|
+
@obj.startkey_doc(doc)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "#endkey" do
|
319
|
+
it "should update query with value" do
|
320
|
+
@obj.should_receive(:update_query).with({:endkey => 'foo'})
|
321
|
+
@obj.endkey('foo')
|
322
|
+
end
|
323
|
+
it "should raise and error if key set" do
|
324
|
+
@obj.query[:key] = 'bar'
|
325
|
+
lambda { @obj.endkey('foo') }.should raise_error
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "#endkey_doc" do
|
330
|
+
it "should update query with value" do
|
331
|
+
@obj.should_receive(:update_query).with({:endkey_docid => 'foo'})
|
332
|
+
@obj.endkey_doc('foo')
|
333
|
+
end
|
334
|
+
it "should update query with object id if available" do
|
335
|
+
doc = mock("Document")
|
336
|
+
doc.should_receive(:id).and_return(44)
|
337
|
+
@obj.should_receive(:update_query).with({:endkey_docid => 44})
|
338
|
+
@obj.endkey_doc(doc)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe "#descending" do
|
343
|
+
it "should update query" do
|
344
|
+
@obj.should_receive(:update_query).with({:descending => true})
|
345
|
+
@obj.descending
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe "#limit" do
|
350
|
+
it "should update query with value" do
|
351
|
+
@obj.should_receive(:update_query).with({:limit => 3})
|
352
|
+
@obj.limit(3)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "#skip" do
|
357
|
+
it "should update query with value" do
|
358
|
+
@obj.should_receive(:update_query).with({:skip => 3})
|
359
|
+
@obj.skip(3)
|
360
|
+
end
|
361
|
+
it "should update query with default value" do
|
362
|
+
@obj.should_receive(:update_query).with({:skip => 0})
|
363
|
+
@obj.skip
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe "#reduce" do
|
368
|
+
it "should update query" do
|
369
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
370
|
+
@obj.should_receive(:update_query).with({:reduce => true, :include_docs => nil})
|
371
|
+
@obj.reduce
|
372
|
+
end
|
373
|
+
it "should raise error if query cannot be reduced" do
|
374
|
+
@obj.should_receive(:can_reduce?).and_return(false)
|
375
|
+
lambda { @obj.reduce }.should raise_error
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe "#group" do
|
380
|
+
it "should update query" do
|
381
|
+
@obj.should_receive(:query).and_return({:reduce => true})
|
382
|
+
@obj.should_receive(:update_query).with({:group => true})
|
383
|
+
@obj.group
|
384
|
+
end
|
385
|
+
it "should raise error if query not prepared for reduce" do
|
386
|
+
@obj.should_receive(:query).and_return({:reduce => false})
|
387
|
+
lambda { @obj.group }.should raise_error
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "#group" do
|
392
|
+
it "should update query" do
|
393
|
+
@obj.should_receive(:query).and_return({:reduce => true})
|
394
|
+
@obj.should_receive(:update_query).with({:group => true})
|
395
|
+
@obj.group
|
396
|
+
end
|
397
|
+
it "should raise error if query not prepared for reduce" do
|
398
|
+
@obj.should_receive(:query).and_return({:reduce => false})
|
399
|
+
lambda { @obj.group }.should raise_error
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe "#group_level" do
|
404
|
+
it "should update query" do
|
405
|
+
@obj.should_receive(:group).and_return(@obj)
|
406
|
+
@obj.should_receive(:update_query).with({:group_level => 3})
|
407
|
+
@obj.group_level(3)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe "#include_docs" do
|
412
|
+
it "should call include_docs! on new view" do
|
413
|
+
@obj.should_receive(:update_query).and_return(@obj)
|
414
|
+
@obj.should_receive(:include_docs!)
|
415
|
+
@obj.include_docs
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
describe "#reset!" do
|
420
|
+
it "should empty all cached data" do
|
421
|
+
@obj.should_receive(:result=).with(nil)
|
422
|
+
@obj.instance_exec { @rows = 'foo'; @docs = 'foo' }
|
423
|
+
@obj.reset!
|
424
|
+
@obj.instance_exec { @rows }.should be_nil
|
425
|
+
@obj.instance_exec { @docs }.should be_nil
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
#### PROTECTED METHODS
|
430
|
+
|
431
|
+
describe "#include_docs!" do
|
432
|
+
it "should set query value" do
|
433
|
+
@obj.should_receive(:result).and_return(false)
|
434
|
+
@obj.should_not_receive(:reset!)
|
435
|
+
@obj.send(:include_docs!)
|
436
|
+
@obj.query[:include_docs].should be_true
|
437
|
+
end
|
438
|
+
it "should reset if result and no docs" do
|
439
|
+
@obj.should_receive(:result).and_return(true)
|
440
|
+
@obj.should_receive(:include_docs?).and_return(false)
|
441
|
+
@obj.should_receive(:reset!)
|
442
|
+
@obj.send(:include_docs!)
|
443
|
+
@obj.query[:include_docs].should be_true
|
444
|
+
end
|
445
|
+
it "should raise an error if view is reduced" do
|
446
|
+
@obj.query[:reduce] = true
|
447
|
+
lambda { @obj.send(:include_docs!) }.should raise_error
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
describe "#include_docs?" do
|
452
|
+
it "should return true if set" do
|
453
|
+
@obj.should_receive(:query).and_return({:include_docs => true})
|
454
|
+
@obj.send(:include_docs?).should be_true
|
455
|
+
end
|
456
|
+
it "should return false if not set" do
|
457
|
+
@obj.should_receive(:query).and_return({})
|
458
|
+
@obj.send(:include_docs?).should be_false
|
459
|
+
@obj.should_receive(:query).and_return({:include_docs => false})
|
460
|
+
@obj.send(:include_docs?).should be_false
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe "#update_query" do
|
465
|
+
it "returns a new instance of view" do
|
466
|
+
@obj.send(:update_query).object_id.should_not eql(@obj.object_id)
|
467
|
+
end
|
468
|
+
|
469
|
+
it "returns a new instance of view with extra parameters" do
|
470
|
+
new_obj = @obj.send(:update_query, {:foo => :bar})
|
471
|
+
new_obj.query[:foo].should eql(:bar)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
describe "#design_doc" do
|
476
|
+
it "should call design_doc on model" do
|
477
|
+
@obj.model.should_receive(:design_doc)
|
478
|
+
@obj.send(:design_doc)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
describe "#can_reduce?" do
|
483
|
+
it "should check and prove true" do
|
484
|
+
@obj.should_receive(:name).and_return('test_view')
|
485
|
+
@obj.should_receive(:design_doc).and_return({'views' => {'test_view' => {'reduce' => 'foo'}}})
|
486
|
+
@obj.send(:can_reduce?).should be_true
|
487
|
+
end
|
488
|
+
it "should check and prove false" do
|
489
|
+
@obj.should_receive(:name).and_return('test_view')
|
490
|
+
@obj.should_receive(:design_doc).and_return({'views' => {'test_view' => {'reduce' => nil}}})
|
491
|
+
@obj.send(:can_reduce?).should be_false
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
describe "#execute" do
|
496
|
+
before :each do
|
497
|
+
# disable real execution!
|
498
|
+
@design_doc = mock("DesignDoc")
|
499
|
+
@design_doc.stub!(:view_on)
|
500
|
+
@obj.model.stub!(:design_doc).and_return(@design_doc)
|
501
|
+
end
|
502
|
+
|
503
|
+
it "should return previous result if set" do
|
504
|
+
@obj.result = "foo"
|
505
|
+
@obj.send(:execute).should eql('foo')
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should raise issue if no database" do
|
509
|
+
@obj.should_receive(:query).and_return({:database => nil})
|
510
|
+
model = mock("SomeModel")
|
511
|
+
model.should_receive(:database).and_return(nil)
|
512
|
+
@obj.should_receive(:model).and_return(model)
|
513
|
+
lambda { @obj.send(:execute) }.should raise_error
|
514
|
+
end
|
515
|
+
|
516
|
+
it "should delete the reduce option if not going to be used" do
|
517
|
+
@obj.should_receive(:can_reduce?).and_return(false)
|
518
|
+
@obj.query.should_receive(:delete).with(:reduce)
|
519
|
+
@obj.send(:execute)
|
520
|
+
end
|
521
|
+
|
522
|
+
it "should populate the results" do
|
523
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
524
|
+
@design_doc.should_receive(:view_on).and_return('foos')
|
525
|
+
@obj.send(:execute)
|
526
|
+
@obj.result.should eql('foos')
|
527
|
+
end
|
528
|
+
|
529
|
+
it "should retry once on a resource not found error" do
|
530
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
531
|
+
@obj.model.should_receive(:save_design_doc)
|
532
|
+
@design_doc.should_receive(:view_on).ordered.and_raise(RestClient::ResourceNotFound)
|
533
|
+
@design_doc.should_receive(:view_on).ordered.and_return('foos')
|
534
|
+
@obj.send(:execute)
|
535
|
+
@obj.result.should eql('foos')
|
536
|
+
end
|
537
|
+
|
538
|
+
it "should retry twice and fail on a resource not found error" do
|
539
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
540
|
+
@obj.model.should_receive(:save_design_doc)
|
541
|
+
@design_doc.should_receive(:view_on).twice.and_raise(RestClient::ResourceNotFound)
|
542
|
+
lambda { @obj.send(:execute) }.should raise_error(RestClient::ResourceNotFound)
|
543
|
+
end
|
544
|
+
|
545
|
+
it "should remove nil values from query" do
|
546
|
+
@obj.should_receive(:can_reduce?).and_return(true)
|
547
|
+
@obj.stub!(:use_database).and_return('database')
|
548
|
+
@obj.query = {:reduce => true, :limit => nil, :skip => nil}
|
549
|
+
@design_doc.should_receive(:view_on).with('database', 'test_view', {:reduce => true})
|
550
|
+
@obj.send(:execute)
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
end
|
555
|
+
|
556
|
+
describe "pagination methods" do
|
557
|
+
|
558
|
+
describe "#page" do
|
559
|
+
it "should call limit and skip" do
|
560
|
+
@obj.should_receive(:limit).with(25).and_return(@obj)
|
561
|
+
@obj.should_receive(:skip).with(25).and_return(@obj)
|
562
|
+
@obj.page(2)
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
describe "#per" do
|
567
|
+
it "should raise an error if page not called before hand" do
|
568
|
+
lambda { @obj.per(12) }.should raise_error
|
569
|
+
end
|
570
|
+
it "should not do anything if number less than or eql 0" do
|
571
|
+
view = @obj.page(1)
|
572
|
+
view.per(0).should eql(view)
|
573
|
+
end
|
574
|
+
it "should set limit and update skip" do
|
575
|
+
view = @obj.page(2).per(10)
|
576
|
+
view.query[:skip].should eql(10)
|
577
|
+
view.query[:limit].should eql(10)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
describe "#total_count" do
|
582
|
+
it "set limit and skip to nill and perform count" do
|
583
|
+
@obj.should_receive(:limit).with(nil).and_return(@obj)
|
584
|
+
@obj.should_receive(:skip).with(nil).and_return(@obj)
|
585
|
+
@obj.should_receive(:count).and_return(5)
|
586
|
+
@obj.total_count.should eql(5)
|
587
|
+
@obj.total_count.should eql(5) # Second to test caching
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
describe "#num_pages" do
|
592
|
+
it "should use total_count and limit_value" do
|
593
|
+
@obj.should_receive(:total_count).and_return(200)
|
594
|
+
@obj.should_receive(:limit_value).and_return(25)
|
595
|
+
@obj.num_pages.should eql(8)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe "#current_page" do
|
600
|
+
it "should use offset and limit" do
|
601
|
+
@obj.should_receive(:offset_value).and_return(25)
|
602
|
+
@obj.should_receive(:limit_value).and_return(25)
|
603
|
+
@obj.current_page.should eql(2)
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
describe "ViewRow" do
|
611
|
+
|
612
|
+
before :all do
|
613
|
+
@klass = CouchRest::Model::Designs::ViewRow
|
614
|
+
end
|
615
|
+
|
616
|
+
describe "intialize" do
|
617
|
+
it "should store reference to model" do
|
618
|
+
obj = @klass.new({}, "model")
|
619
|
+
obj.model.should eql('model')
|
620
|
+
end
|
621
|
+
it "should copy details from hash" do
|
622
|
+
obj = @klass.new({:foo => :bar, :test => :example}, "")
|
623
|
+
obj[:foo].should eql(:bar)
|
624
|
+
obj[:test].should eql(:example)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
describe "running" do
|
629
|
+
before :each do
|
630
|
+
end
|
631
|
+
|
632
|
+
it "should provide id" do
|
633
|
+
obj = @klass.new({'id' => '123456'}, 'model')
|
634
|
+
obj.id.should eql('123456')
|
635
|
+
end
|
636
|
+
|
637
|
+
it "should provide key" do
|
638
|
+
obj = @klass.new({'key' => 'thekey'}, 'model')
|
639
|
+
obj.key.should eql('thekey')
|
640
|
+
end
|
641
|
+
|
642
|
+
it "should provide the value" do
|
643
|
+
obj = @klass.new({'value' => 'thevalue'}, 'model')
|
644
|
+
obj.value.should eql('thevalue')
|
645
|
+
end
|
646
|
+
|
647
|
+
it "should provide the raw document" do
|
648
|
+
obj = @klass.new({'doc' => 'thedoc'}, 'model')
|
649
|
+
obj.raw_doc.should eql('thedoc')
|
650
|
+
end
|
651
|
+
|
652
|
+
it "should instantiate a new document" do
|
653
|
+
hash = {'doc' => {'_id' => '12345', 'name' => 'sam'}}
|
654
|
+
obj = @klass.new(hash, DesignViewModel)
|
655
|
+
doc = mock('DesignViewModel')
|
656
|
+
obj.model.should_receive(:build_from_database).with(hash['doc']).and_return(doc)
|
657
|
+
obj.doc.should eql(doc)
|
658
|
+
end
|
659
|
+
|
660
|
+
it "should try to load from id if no document" do
|
661
|
+
hash = {'id' => '12345', 'value' => 5}
|
662
|
+
obj = @klass.new(hash, DesignViewModel)
|
663
|
+
doc = mock('DesignViewModel')
|
664
|
+
obj.model.should_receive(:get).with('12345').and_return(doc)
|
665
|
+
obj.doc.should eql(doc)
|
666
|
+
end
|
667
|
+
|
668
|
+
it "should try to load linked document if available" do
|
669
|
+
hash = {'id' => '12345', 'value' => {'_id' => '54321'}}
|
670
|
+
obj = @klass.new(hash, DesignViewModel)
|
671
|
+
doc = mock('DesignViewModel')
|
672
|
+
obj.model.should_receive(:get).with('54321').and_return(doc)
|
673
|
+
obj.doc.should eql(doc)
|
674
|
+
end
|
675
|
+
|
676
|
+
it "should try to return nil for document if none available" do
|
677
|
+
hash = {'value' => 23} # simulate reduce
|
678
|
+
obj = @klass.new(hash, DesignViewModel)
|
679
|
+
doc = mock('DesignViewModel')
|
680
|
+
obj.model.should_not_receive(:get)
|
681
|
+
obj.doc.should be_nil
|
682
|
+
end
|
683
|
+
|
684
|
+
|
685
|
+
end
|
686
|
+
|
687
|
+
end
|
688
|
+
|
689
|
+
|
690
|
+
describe "scenarios" do
|
691
|
+
|
692
|
+
before :all do
|
693
|
+
@objs = [
|
694
|
+
{:name => "Judith"},
|
695
|
+
{:name => "Lorena"},
|
696
|
+
{:name => "Peter"},
|
697
|
+
{:name => "Sam"},
|
698
|
+
{:name => "Vilma"}
|
699
|
+
].map{|h| DesignViewModel.create(h)}
|
700
|
+
end
|
701
|
+
|
702
|
+
describe "loading documents" do
|
703
|
+
|
704
|
+
it "should return first" do
|
705
|
+
DesignViewModel.by_name.first.name.should eql("Judith")
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should return last" do
|
709
|
+
DesignViewModel.by_name.last.name.should eql("Vilma")
|
710
|
+
end
|
711
|
+
|
712
|
+
it "should allow multiple results" do
|
713
|
+
view = DesignViewModel.by_name.limit(3)
|
714
|
+
view.total_rows.should eql(5)
|
715
|
+
view.last.name.should eql("Peter")
|
716
|
+
view.all.length.should eql(3)
|
717
|
+
end
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
describe "index information" do
|
722
|
+
it "should provide total_rows" do
|
723
|
+
DesignViewModel.by_name.total_rows.should eql(5)
|
724
|
+
end
|
725
|
+
it "should provide total_rows" do
|
726
|
+
DesignViewModel.by_name.total_rows.should eql(5)
|
727
|
+
end
|
728
|
+
it "should provide an offset" do
|
729
|
+
DesignViewModel.by_name.offset.should eql(0)
|
730
|
+
end
|
731
|
+
it "should provide a set of keys" do
|
732
|
+
DesignViewModel.by_name.limit(2).keys.should eql(["Judith", "Lorena"])
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
describe "pagination" do
|
737
|
+
before :all do
|
738
|
+
DesignViewModel.paginates_per 3
|
739
|
+
end
|
740
|
+
before :each do
|
741
|
+
@view = DesignViewModel.by_name.page(1)
|
742
|
+
end
|
743
|
+
|
744
|
+
it "should calculate number of pages" do
|
745
|
+
@view.num_pages.should eql(2)
|
746
|
+
end
|
747
|
+
it "should return results from first page" do
|
748
|
+
@view.all.first.name.should eql('Judith')
|
749
|
+
@view.all.last.name.should eql('Peter')
|
750
|
+
end
|
751
|
+
it "should return results from second page" do
|
752
|
+
@view.page(2).all.first.name.should eql('Sam')
|
753
|
+
@view.page(2).all.last.name.should eql('Vilma')
|
754
|
+
end
|
755
|
+
|
756
|
+
it "should allow overriding per page count" do
|
757
|
+
@view = @view.per(10)
|
758
|
+
@view.num_pages.should eql(1)
|
759
|
+
@view.all.last.name.should eql('Vilma')
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
end
|
764
|
+
|
765
|
+
|
766
|
+
end
|