paginater 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +127 -0
- data/Gemfile.lock.broken +78 -0
- data/Gemfile.lock.good +127 -0
- data/LICENSE.txt +22 -0
- data/README.md +268 -0
- data/Rakefile +1 -0
- data/lib/paginater.rb +31 -0
- data/lib/paginater/constants.rb +32 -0
- data/lib/paginater/core.rb +54 -0
- data/lib/paginater/formatter.rb +131 -0
- data/lib/paginater/page/base_page.rb +31 -0
- data/lib/paginater/page/page_links.rb +50 -0
- data/lib/paginater/version.rb +3 -0
- data/paginater.gemspec +36 -0
- data/spec/paginater/paginatable_array_spec.rb +122 -0
- data/spec/paginater/paginater_spec.rb +327 -0
- data/spec/paginater/paginater_with_kaminari_spec.rb +165 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/support/active_record_support.rb +53 -0
- data/test/config.ru +2 -0
- data/test/favicon.ico +0 -0
- data/test/rss_helper.rb +32 -0
- data/test/running_test.rb +32 -0
- metadata +233 -0
@@ -0,0 +1,327 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Formatter::Paginater do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Class.new(Grape::API)
|
7
|
+
end
|
8
|
+
|
9
|
+
def app
|
10
|
+
subject
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#header' do
|
14
|
+
it 'is callable from within a block' do
|
15
|
+
subject.get('/hey') do
|
16
|
+
header 'X-Awesome', 'true'
|
17
|
+
"Awesome"
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/hey'
|
21
|
+
last_response.headers['X-Awesome'].should == 'true'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#headers' do
|
26
|
+
before do
|
27
|
+
subject.get('/headers') do
|
28
|
+
headers.to_json
|
29
|
+
end
|
30
|
+
end
|
31
|
+
it 'includes request headers' do
|
32
|
+
get '/headers'
|
33
|
+
JSON.parse(last_response.body).should == {
|
34
|
+
"Host" => "example.org",
|
35
|
+
"Cookie" => ""
|
36
|
+
}
|
37
|
+
end
|
38
|
+
it 'includes additional request headers' do
|
39
|
+
get '/headers', nil, { "HTTP_X_GRAPE_CLIENT" => "1" }
|
40
|
+
JSON.parse(last_response.body)["X-Grape-Client"].should == "1"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "without paginater" do
|
45
|
+
|
46
|
+
before :each do
|
47
|
+
stuff = {:stuff => 'foo bar'}
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sets itself in the env upon call" do
|
51
|
+
subject.get('/') do
|
52
|
+
'Hello world.'
|
53
|
+
end
|
54
|
+
get '/'
|
55
|
+
last_request.env['api.endpoint'].should be_kind_of(Grape::Endpoint)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should work without paginater" do
|
59
|
+
subject.get('/') do
|
60
|
+
'foo bar!'
|
61
|
+
end
|
62
|
+
get '/'
|
63
|
+
last_response.body.should eq 'foo bar!'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not raise error about stuff" do
|
67
|
+
subject.format :json
|
68
|
+
subject.get '/home' do
|
69
|
+
stuff = {:stuff => 'foo bar'}
|
70
|
+
end
|
71
|
+
lambda do
|
72
|
+
get '/home'
|
73
|
+
end.should_not raise_error
|
74
|
+
get '/home'
|
75
|
+
last_response.body.should eq "{\"stuff\":\"foo bar\"}"
|
76
|
+
last_response.body.should include MultiJson.dump stuff
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
context "with the route_option :paginater => :simple" do
|
82
|
+
|
83
|
+
before :each do
|
84
|
+
subject.format :json
|
85
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
86
|
+
stuff = {:stuff => 'foo bar'}
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should respond with proper content-type" do
|
90
|
+
subject.get '/home', :paginater => :simple do
|
91
|
+
stuff = {:stuff => 'foo bar'}
|
92
|
+
end
|
93
|
+
get('/home?page=1')
|
94
|
+
last_response.headers['Content-Type'].should == 'application/json'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'rescues a specific error' do
|
98
|
+
subject.rescue_from Grape::Formatter::Paginater::Constants::PaginaterError do |e|
|
99
|
+
rack_response("rescued from #{e.class.name}", 500)
|
100
|
+
end
|
101
|
+
subject.get '/home', :paginater => :simple do
|
102
|
+
raise Grape::Formatter::Paginater::Constants::PaginaterError unless params[:page]
|
103
|
+
end
|
104
|
+
subject.get '/foo' do
|
105
|
+
raise 'foo_exception'
|
106
|
+
end
|
107
|
+
get '/home'
|
108
|
+
last_response.status.should eql 500
|
109
|
+
last_response.body.should == 'rescued from Grape::Formatter::Paginater::Constants::PaginaterError'
|
110
|
+
lambda do
|
111
|
+
get '/foo'
|
112
|
+
end.should raise_error
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should understand a missing page situation' do
|
116
|
+
subject.get '/home', :paginater => :simple do
|
117
|
+
params[:page]
|
118
|
+
stuff = {:stuff => 'foo bar'}
|
119
|
+
end
|
120
|
+
lambda do
|
121
|
+
get '/home'
|
122
|
+
end.should_not raise_error
|
123
|
+
last_response.status.should eql 500
|
124
|
+
last_response.body.should eq '{"error":"missing param"}'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'defaults the error formatter to format' do
|
128
|
+
subject.format :json
|
129
|
+
subject.rescue_from :all
|
130
|
+
subject.content_type :json, "application/json"
|
131
|
+
subject.content_type :foo, "text/foo"
|
132
|
+
subject.get '/exception' do
|
133
|
+
raise "rain!"
|
134
|
+
end
|
135
|
+
get '/exception.json'
|
136
|
+
last_response.body.should == '{"error":"rain!"}'
|
137
|
+
get '/exception.foo'
|
138
|
+
last_response.body.should == '{"error":"rain!"}'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should work with the params requested' do
|
142
|
+
subject.format :json
|
143
|
+
subject.rescue_from :all
|
144
|
+
subject.get '/stuff', :paginater => :simple do
|
145
|
+
params[:page]
|
146
|
+
stuff = {:stuff => 'foo bar'}
|
147
|
+
end
|
148
|
+
get '/stuff?page=42'
|
149
|
+
last_response.body.should eq '{"error":"invalid page 42"}'
|
150
|
+
last_response.status.should eq 500
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
context "dealing with something that ... count" do
|
156
|
+
|
157
|
+
before :each do
|
158
|
+
subject.format :json
|
159
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
160
|
+
subject.get '/foo', :paginater => :simple do
|
161
|
+
numbers(42)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
it 'consider Array objects as paginable' do
|
165
|
+
get('/foo?page=11')
|
166
|
+
last_response.status.should eq 200
|
167
|
+
last_response.header.should include 'Link'
|
168
|
+
end
|
169
|
+
it 'paginate the body' do
|
170
|
+
get('/foo?page=11')
|
171
|
+
last_response.status.should eq 200
|
172
|
+
last_response.body.should include '10'
|
173
|
+
end
|
174
|
+
it 'paginate the body and understand size' do
|
175
|
+
get('/foo?page=3&size=3')
|
176
|
+
last_response.status.should eq 200
|
177
|
+
last_response.body.should_not include '5'
|
178
|
+
last_response.body.should include '6'
|
179
|
+
last_response.body.should include '8'
|
180
|
+
last_response.body.should_not include '9'
|
181
|
+
last_response.body.should include 6.upto(8).to_a.to_s.gsub(/\s/,'')
|
182
|
+
end
|
183
|
+
it 'set headers for pagination' do
|
184
|
+
get('/foo?page=3&size=3')
|
185
|
+
last_response.status.should eq 200
|
186
|
+
last_response.header.should include 'Link'
|
187
|
+
last_response.header['Link'].should include 'page=1'
|
188
|
+
last_response.header['Link'].should include 'next'
|
189
|
+
last_response.header['Link'].should include 'page=4'
|
190
|
+
last_response.header['Link'].should include 'prev'
|
191
|
+
last_response.header['Link'].should include 'page=2'
|
192
|
+
end
|
193
|
+
it 'do not mess with last pages' do
|
194
|
+
get('/foo?page=5&size=10')
|
195
|
+
last_response.status.should eq 200
|
196
|
+
last_response.header.should include 'Link'
|
197
|
+
last_response.header['Link'].should include 'prev'
|
198
|
+
last_response.header['Link'].should_not include 'next'
|
199
|
+
last_response.body.should_not include '39'
|
200
|
+
last_response.body.should include '40'
|
201
|
+
last_response.body.should include '42'
|
202
|
+
last_response.body.should_not include '43'
|
203
|
+
last_response.body.should include 41.upto(42).to_a.to_s.gsub(/[\[\s\]]/,'')
|
204
|
+
end
|
205
|
+
it 'do not mess with foo pages' do
|
206
|
+
get('/foo?page=42&size=42')
|
207
|
+
last_response.status.should eq 500
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
context "dealing with something that ... count but :txt" do
|
213
|
+
|
214
|
+
before :each do
|
215
|
+
subject.format :txt
|
216
|
+
subject.formatter :txt, Grape::Formatter::Paginater
|
217
|
+
subject.get '/foo', :paginater => :simple do
|
218
|
+
alphabet
|
219
|
+
end
|
220
|
+
end
|
221
|
+
it 'consider Array objects as paginable' do
|
222
|
+
get('/foo?page=11')
|
223
|
+
last_response.status.should eq 200
|
224
|
+
last_response.header['Link'].should include 'first'
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'understand and return a txt' do
|
228
|
+
get('/foo?page=1&size=10')
|
229
|
+
last_response.body.should include "Alpha"
|
230
|
+
last_response.body.should_not include "Kilo"
|
231
|
+
last_response.header['Link'].should include "page=1"
|
232
|
+
last_response.body.split(',').count.should eq 10
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
context "set pagination headers" do
|
238
|
+
|
239
|
+
before :each do
|
240
|
+
subject.format :json
|
241
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
242
|
+
subject.get '/foo', :paginater => :simple do
|
243
|
+
items(9)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
it 'understand the first page' do
|
247
|
+
get('/foo?page=1')
|
248
|
+
last_response.status.should eq 200
|
249
|
+
last_response.header['Link'].should include 'page=1'
|
250
|
+
last_response.header['Link'].should include 'next'
|
251
|
+
last_response.header['Link'].should_not include 'prev'
|
252
|
+
end
|
253
|
+
it 'understand the last page' do
|
254
|
+
get('/foo?page=10')
|
255
|
+
last_response.status.should eq 200
|
256
|
+
last_response.header['Link'].should include 'prev'
|
257
|
+
last_response.header['Link'].should include 'page=9'
|
258
|
+
last_response.header['Link'].should_not include 'next'
|
259
|
+
end
|
260
|
+
it 'understand the last page with size' do
|
261
|
+
get('/foo?page=5&size=2')
|
262
|
+
last_response.status.should eq 200
|
263
|
+
last_response.header['Link'].should include 'prev'
|
264
|
+
last_response.header.should_not include 'next'
|
265
|
+
last_response.body.split(',').count.should eq 2
|
266
|
+
get('/foo?page=4&size=3')
|
267
|
+
last_response.status.should eq 200
|
268
|
+
last_response.header['Link'].should include 'prev'
|
269
|
+
last_response.header['Link'].should include 'page=3'
|
270
|
+
last_response.header.should_not include 'next'
|
271
|
+
last_response.body.split(',').count.should eq 1
|
272
|
+
end
|
273
|
+
it 'paginate the body' do
|
274
|
+
get('/foo?page=1')
|
275
|
+
last_response.status.should eq 200
|
276
|
+
last_response.body.should eq '["item 000"]'
|
277
|
+
get('/foo?page=10')
|
278
|
+
last_response.body.should eq '["item 009"]'
|
279
|
+
get('/foo?page=11')
|
280
|
+
last_response.status.should eq 500
|
281
|
+
get('/foo?page=3&size=3')
|
282
|
+
last_response.body.should eq '["item 006","item 007","item 008"]'
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
context "with the wrapped object" do
|
288
|
+
|
289
|
+
before :each do
|
290
|
+
subject.format :json
|
291
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
292
|
+
subject.get '/foobar', :paginater => :wrapper do
|
293
|
+
items(42)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
it "get something paginated" do
|
297
|
+
get('/foobar?page=1')
|
298
|
+
last_response.status.should eq 200
|
299
|
+
# deprecating....
|
300
|
+
last_response.header['Link'].should include 'page', 'next'
|
301
|
+
last_response.header.should_not include 'prev'
|
302
|
+
last_response.body.should include '["item 000"]'
|
303
|
+
last_response.body.should eq "{\"content\":[\"item 000\"],\"page\":1,\"size\":1,\"links\":null}"
|
304
|
+
get('/foobar?page=3&size=3')
|
305
|
+
last_response.body.should eq "{\"content\":[\"item 006\",\"item 007\",\"item 008\"],\"page\":3,\"size\":3,\"links\":null}"
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
context "put something in the cookies" do
|
311
|
+
|
312
|
+
before :each do
|
313
|
+
subject.format :json
|
314
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
315
|
+
subject.get '/foobar', :paginater => :simple do
|
316
|
+
items(42)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
it "count total_pages and original_count" do
|
321
|
+
get('/foobar?page=7&size=3')
|
322
|
+
last_response.header['Set-Cookie'].should include "7/14/43"
|
323
|
+
end
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
end # Grape::Formatter::Paginater
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# spec/paginater/paginater_with_kaminari_spec.rb
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'support/active_record_support'
|
5
|
+
|
6
|
+
if defined? ActiveRecord
|
7
|
+
|
8
|
+
describe Grape::Formatter::Paginater do
|
9
|
+
|
10
|
+
subject do
|
11
|
+
Class.new(Grape::API)
|
12
|
+
end
|
13
|
+
|
14
|
+
def app
|
15
|
+
subject
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples_for 'the first page' do
|
19
|
+
it { should have(25).users }
|
20
|
+
its('first.name') { should == 'user001' }
|
21
|
+
end
|
22
|
+
|
23
|
+
shared_examples_for 'blank page' do
|
24
|
+
it { should have(0).users }
|
25
|
+
end
|
26
|
+
|
27
|
+
before :each do
|
28
|
+
0.upto(999) {|i| User.create! :name => "user#{'%03d' % i}", :age => (i / 10)}
|
29
|
+
end
|
30
|
+
|
31
|
+
after :each do
|
32
|
+
User.destroy_all
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with the kaminari amazing gem" do
|
36
|
+
|
37
|
+
before :each do
|
38
|
+
subject.format :json
|
39
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
40
|
+
subject.get '/users', :paginater => :kaminari do
|
41
|
+
User.where("age > 0")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "get something paginated" do
|
46
|
+
get('/users?page=42&size=10')
|
47
|
+
last_response.status.should eq 200
|
48
|
+
last_response.header['Link'].should include 'first', 'next'
|
49
|
+
last_response.header.should_not include 'prev'
|
50
|
+
last_response.body.should include '{"user":{"age":42,"id":425,"name":"user424"}}'
|
51
|
+
last_response.header['Link'].should include "{:url=>\"/users?page=1&size=10\", :rel=>\"first\"}"
|
52
|
+
last_response.header['Link'].should include "{:url=>\"/users?page=43&size=10\", :rel=>\"next\"}"
|
53
|
+
last_response.header['Link'].should include "{:url=>\"/users?page=41&size=10\", :rel=>\"prev\"}"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context "some features with kaminari" do
|
59
|
+
|
60
|
+
# let(:all_user_count) { User.all.count }
|
61
|
+
|
62
|
+
before :each do
|
63
|
+
subject.format :json
|
64
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
65
|
+
subject.get '/users', :paginater => :kaminari do
|
66
|
+
User.where("age > -1")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "get something paginated with body, headers and cookies" do
|
71
|
+
get('/users?page=1&size=10')
|
72
|
+
last_response.status.should eq 200
|
73
|
+
last_response.body.scan(/\"user\"/).flatten.count.should == 10
|
74
|
+
last_response.header['Set-Cookie'].should include "1/100/1000" #page/pages/count
|
75
|
+
last_response.header['Link'].should include '/users?page=2&size=10'
|
76
|
+
last_response.header['Link'].should_not include 'prev'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "use the default size of 1" do
|
80
|
+
get('/users?page=1')
|
81
|
+
last_response.status.should eq 200
|
82
|
+
last_response.body.scan(/\"user\"/).flatten.count.should == 1
|
83
|
+
last_response.header['Set-Cookie'].should include "1/1000/1000"
|
84
|
+
last_response.header['Link'].should include '/users?page=2'
|
85
|
+
end
|
86
|
+
|
87
|
+
it "manage an exceeding size as .all" do
|
88
|
+
get('/users?page=1&size=9999')
|
89
|
+
last_response.status.should eq 200
|
90
|
+
last_response.body.scan(/\"user\"/).flatten.count.should == 1000
|
91
|
+
last_response.header['Set-Cookie'].should include "1/1/1000"
|
92
|
+
last_response.header['Link'].should_not include '/users?page=2'
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
context "with kaminari and an array (PaginatableArray)" do
|
98
|
+
|
99
|
+
before :each do
|
100
|
+
subject.format :json
|
101
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
102
|
+
subject.get '/foobar', :paginater => :kaminari do
|
103
|
+
items(99)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
it "get something paginated (with default size)" do
|
107
|
+
get('/foobar?page=1')
|
108
|
+
last_response.status.should eq 200
|
109
|
+
end
|
110
|
+
it "get something paginated" do
|
111
|
+
get('/foobar?page=1&size=10')
|
112
|
+
last_response.status.should eq 200
|
113
|
+
last_response.header['Link'].should include "{:url=>\"/foobar?page=1&size=10\", :rel=>\"first\"}"
|
114
|
+
last_response.header['Link'].should include "{:url=>\"/foobar?page=2&size=10\", :rel=>\"next\"}"
|
115
|
+
last_response.body.should include "item 009"
|
116
|
+
last_response.body.should_not include "item 010"
|
117
|
+
end
|
118
|
+
it "get something paginated with default size = 1" do
|
119
|
+
get('/foobar?page=100')
|
120
|
+
last_response.body.scan(/"(.*?)"/).flatten.count.should == 1
|
121
|
+
last_response.header['Link'].should_not include "next"
|
122
|
+
end
|
123
|
+
it "get something paginated with the size param" do
|
124
|
+
get('/foobar?page=10&size=10')
|
125
|
+
last_response.body.scan(/"(.*?)"/).flatten.count.should == 10
|
126
|
+
last_response.header['Link'].should_not include "next"
|
127
|
+
end
|
128
|
+
it "understand a non existing page" do
|
129
|
+
get('/foobar?page=11&size=10')
|
130
|
+
last_response.body.scan(/"(.*?)"/).flatten.count.should == 0
|
131
|
+
last_response.body.should == '[]'
|
132
|
+
# FIXME?
|
133
|
+
# p last_response.header['Link']
|
134
|
+
# p last_response.header['Set-Cookie']
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with kaminari and an hash" do
|
140
|
+
|
141
|
+
before :each do
|
142
|
+
subject.format :json
|
143
|
+
subject.formatter :json, Grape::Formatter::Paginater
|
144
|
+
subject.get '/foobar', :paginater => :kaminari do
|
145
|
+
[{:foo => 'bar'},{:bar => 'foo'}]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
it "get something paginated" do
|
149
|
+
get('/foobar?page=1')
|
150
|
+
last_response.status.should eq 200
|
151
|
+
last_response.body.should include "[{\"foo\":\"bar\"}]"
|
152
|
+
last_response.header['Link'].should include "{:url=>\"/foobar?page=1\", :rel=>\"first\"}"
|
153
|
+
end
|
154
|
+
it "discern errors and override size with 1 if necessary" do
|
155
|
+
get('/foobar?page=1&size=0')
|
156
|
+
last_response.status.should eq 200
|
157
|
+
last_response.body.should == "[{\"foo\":\"bar\"}]"
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end # Grape::Formatter::Paginater
|
163
|
+
|
164
|
+
end
|
165
|
+
|