Skip to content

Commit 294d6f1

Browse files
committed
Test: APIHelper::Paginatable
1 parent e0b65d1 commit 294d6f1

File tree

8 files changed

+284
-47
lines changed

8 files changed

+284
-47
lines changed

lib/api_helper/includable.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
module APIHelper::Includable
163163
extend ActiveSupport::Concern
164164

165-
# Gets the include parameters, organize them into a +@inclusion+ hash.
165+
# Gets the include parameters, organize them into a +@inclusion+ hash
166166
#
167167
# Params:
168168
#
@@ -226,7 +226,7 @@ def inclusion_for(resource, default: false,
226226
end
227227
end
228228

229-
# Getter for the inclusion data.
229+
# Getter for the inclusion data
230230
#
231231
# This method will act as a traditional getter of the inclusion data and
232232
# returns a hash containing fields for each resource if no parameter is

lib/api_helper/paginatable.rb

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
require 'active_support'
22

3-
# = Helper To Make Resource APIs Paginatable
3+
# = Paginatable
44
#
5-
# Paginating the requested items can avoid returning too much information
6-
# in a single response. API callers can iterate over the results using
7-
# pagination instead of rerteving all the data in one time, ruining the
8-
# database connection or network.
5+
# Paginating the requested items can avoid returning too much data in a single
6+
# response. API clients can iterate over the results with pagination instead of
7+
# rerteving all the data in one time, ruining the database connection or
8+
# network.
99
#
10-
# There are two parameters clients can use: +per_page+ and +page+. The former
11-
# is used for setting how many data will be returned in each page, there will
10+
# There are two available URL parameters: +per_page+ and +page+. The former is
11+
# used for setting how many resources will be returned in each page, there will
1212
# be a maxium limit and default value for each API:
1313
#
1414
# GET /posts?per_page=10
1515
#
16-
# <em>The server will respond 10 items at a time.</em>
16+
# <em>The server will respond 10 resources in a request.</em>
1717
#
18-
# Use the +page+ parameter to specify which to retrieve:
18+
# Use the +page+ parameter to specify which to page get:
1919
#
2020
# GET /posts?page=5
2121
#
@@ -30,6 +30,9 @@
3030
#
3131
# Which follows the proposed RFC 5988 standard.
3232
#
33+
# An aditional header, +X-Items-Count+, will also be set to the total pages
34+
# count.
35+
#
3336
# == Usage
3437
#
3538
# Include this +Concern+ in your Action Controller:
@@ -41,85 +44,104 @@
4144
# or in your Grape API class:
4245
#
4346
# class SampleAPI < Grape::API
44-
# include APIHelper::Paginatable
47+
# helpers APIHelper::Paginatable
4548
# end
4649
#
47-
# then set the options for pagination in the grape method:
50+
# then set the options for pagination in the grape method, as the following as
51+
# an example:
4852
#
4953
# resources :posts do
5054
# get do
51-
# pagination User.count, default_per_page: 25, maxium_per_page: 100
55+
# collection = current_user.posts
56+
# pagination collection.count, default_per_page: 25, maxium_per_page: 100
5257
#
5358
# # ...
5459
# end
5560
# end
5661
#
57-
# Then use the helper methods, like this:
62+
# Then use the helper methods like this:
5863
#
64+
# # this example uses kaminari
5965
# User.page(page).per(per_page)
6066
#
61-
# HTTP Link header will be automatically set.
67+
# HTTP Link header will be automatically set by the way.
6268
module APIHelper::Paginatable
6369
extend ActiveSupport::Concern
6470

65-
def pagination(items_count, default_per_page: 20, maxium_per_page: 100, set_header: true)
71+
# Set pagination for the request
72+
#
73+
# Params:
74+
#
75+
# +items_count+::
76+
# +Symbol+ name of resource to receive the inclusion
77+
#
78+
# +default_per_page+::
79+
# +Integer+ default per_page
80+
#
81+
# +maxium_per_page+::
82+
# +Integer+ maximum results do return on a single page
83+
#
84+
def pagination(items_count, default_per_page: 20,
85+
maxium_per_page: 100,
86+
set_header: true)
6687
items_count = items_count.count if items_count.respond_to? :count
6788

68-
@per_page = (params[:per_page] || default_per_page).to_i
69-
@per_page = maxium_per_page if @per_page > maxium_per_page
70-
@per_page = 1 if @per_page < 1
89+
@pagination_per_page = (params[:per_page] || default_per_page).to_i
90+
@pagination_per_page = maxium_per_page if @pagination_per_page > maxium_per_page
91+
@pagination_per_page = 1 if @pagination_per_page < 1
7192

7293
items_count = 0 if items_count < 0
73-
pages_count = (items_count.to_f / @per_page).ceil
94+
pages_count = (items_count.to_f / @pagination_per_page).ceil
7495
pages_count = 1 if pages_count < 1
7596

76-
@page = (params[:page] || 1).to_i
77-
@page = pages_count if @page > pages_count
78-
@page = 1 if @page < 1
97+
@pagination_page = (params[:page] || 1).to_i
98+
@pagination_page = pages_count if @pagination_page > pages_count
99+
@pagination_page = 1 if @pagination_page < 1
79100

80-
link_headers ||= []
101+
if set_header
102+
link_headers ||= []
81103

82-
if current_page < pages_count
83-
link_headers << "<#{add_or_replace_uri_param(request.url, :page, current_page + 1)}>; rel=\"next\""
84-
link_headers << "<#{add_or_replace_uri_param(request.url, :page, pages_count)}>; rel=\"last\""
85-
end
86-
if current_page > 1
87-
link_headers << "<#{add_or_replace_uri_param(request.url, :page, (current_page > pages_count ? pages_count : current_page - 1))}>; rel=\"prev\""
88-
link_headers << "<#{add_or_replace_uri_param(request.url, :page, 1)}>; rel=\"first\""
89-
end
104+
if current_page > 1
105+
link_headers << "<#{add_or_replace_uri_param(request.url, :page, 1)}>; rel=\"first\""
106+
link_headers << "<#{add_or_replace_uri_param(request.url, :page, (current_page > pages_count ? pages_count : current_page - 1))}>; rel=\"prev\""
107+
end
90108

91-
link_header = link_headers.join(', ')
109+
if current_page < pages_count
110+
link_headers << "<#{add_or_replace_uri_param(request.url, :page, current_page + 1)}>; rel=\"next\""
111+
link_headers << "<#{add_or_replace_uri_param(request.url, :page, pages_count)}>; rel=\"last\""
112+
end
113+
114+
link_header = link_headers.join(', ')
92115

93-
if set_header
94116
if self.respond_to?(:header)
95117
self.header('Link', link_header)
96118
self.header('X-Items-Count', items_count.to_s)
119+
self.header('X-Pages-Count', pages_count.to_s)
97120
end
98121

99122
if defined?(response) && response.respond_to?(:headers)
100123
response.headers['Link'] = link_header
101124
response.headers['X-Items-Count'] = items_count.to_s
125+
response.headers['X-Pages-Count'] = pages_count.to_s
102126
end
103127
end
104-
105-
link_header
106128
end
107129

108130
# Getter for the current page
109-
def page
110-
@page
131+
def pagination_page
132+
@pagination_page
111133
end
112134

113-
alias_method :current_page, :page
135+
alias_method :current_page, :pagination_page
114136

115137
# Getter for per_page
116-
def per_page
117-
@per_page
138+
def pagination_per_page
139+
@pagination_per_page
118140
end
119141

120-
alias_method :page_with, :per_page
142+
alias_method :paginate_with, :pagination_per_page
121143

122-
def add_or_replace_uri_param(url, param_name, param_value)
144+
def add_or_replace_uri_param(url, param_name, param_value) # :nodoc:
123145
uri = URI(url)
124146
params = URI.decode_www_form(uri.query || '')
125147
params.delete_if { |param| param[0].to_s == param_name.to_s }

lib/api_helper/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module APIHelper
2-
VERSION = "0.0.4"
2+
VERSION = "0.0.5"
33
end

spec/api_helper/grape/includable_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'rails_helper'
1+
require 'grape_helper'
22

33
describe APIHelper::Includable do
44
context "used in a Grape app" do

spec/api_helper/grape/multigettable_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'rails_helper'
1+
require 'grape_helper'
22

33
describe APIHelper::Multigettable do
44
context "used in a Grape app" do
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
require 'grape_helper'
2+
3+
describe APIHelper::Paginatable do
4+
context "used in a Grape app" do
5+
include Rack::Test::Methods
6+
7+
class PaginatableAPI < Grape::API
8+
helpers APIHelper::Paginatable
9+
10+
resources :resources do
11+
get do
12+
pagination 1201, default_per_page: 20, maxium_per_page: 100
13+
return []
14+
end
15+
end
16+
end
17+
18+
def app
19+
PaginatableAPI
20+
end
21+
22+
it "sets the correct HTTP Link response header" do
23+
get 'resources.json'
24+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=2>; rel="next", <http://example.org/resources.json?page=61>; rel="last"')
25+
26+
get 'resources.json?page=2'
27+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=1>; rel="first", <http://example.org/resources.json?page=1>; rel="prev", <http://example.org/resources.json?page=3>; rel="next", <http://example.org/resources.json?page=61>; rel="last"')
28+
29+
get 'resources.json?page=3'
30+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=1>; rel="first", <http://example.org/resources.json?page=2>; rel="prev", <http://example.org/resources.json?page=4>; rel="next", <http://example.org/resources.json?page=61>; rel="last"')
31+
32+
get 'resources.json?page=60'
33+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=1>; rel="first", <http://example.org/resources.json?page=59>; rel="prev", <http://example.org/resources.json?page=61>; rel="next", <http://example.org/resources.json?page=61>; rel="last"')
34+
35+
get 'resources.json?page=61'
36+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=1>; rel="first", <http://example.org/resources.json?page=60>; rel="prev"')
37+
38+
get 'resources.json?page=62'
39+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=1>; rel="first", <http://example.org/resources.json?page=60>; rel="prev"')
40+
41+
get 'resources.json?page=0'
42+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?page=2>; rel="next", <http://example.org/resources.json?page=61>; rel="last"')
43+
44+
get 'resources.json?per_page=5&page=3'
45+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?per_page=5&page=1>; rel="first", <http://example.org/resources.json?per_page=5&page=2>; rel="prev", <http://example.org/resources.json?per_page=5&page=4>; rel="next", <http://example.org/resources.json?per_page=5&page=241>; rel="last"')
46+
47+
get 'resources.json?per_page=5000&page=3'
48+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?per_page=5000&page=1>; rel="first", <http://example.org/resources.json?per_page=5000&page=2>; rel="prev", <http://example.org/resources.json?per_page=5000&page=4>; rel="next", <http://example.org/resources.json?per_page=5000&page=13>; rel="last"')
49+
50+
get 'resources.json?per_page=0&page=3'
51+
expect(last_response.header['Link']).to eq('<http://example.org/resources.json?per_page=0&page=1>; rel="first", <http://example.org/resources.json?per_page=0&page=2>; rel="prev", <http://example.org/resources.json?per_page=0&page=4>; rel="next", <http://example.org/resources.json?per_page=0&page=1201>; rel="last"')
52+
end
53+
54+
it "sets the correct HTTP X-Items-Count response header" do
55+
get 'resources.json'
56+
expect(last_response.header['X-Items-Count']).to eq('1201')
57+
end
58+
59+
it "sets the correct HTTP X-Pages-Count response header" do
60+
get 'resources.json'
61+
expect(last_response.header['X-Pages-Count']).to eq('61')
62+
63+
get 'resources.json?per_page=5&page=3'
64+
expect(last_response.header['X-Pages-Count']).to eq('241')
65+
66+
get 'resources.json?per_page=5000&page=3'
67+
expect(last_response.header['X-Pages-Count']).to eq('13')
68+
69+
get 'resources.json?per_page=0&page=3'
70+
expect(last_response.header['X-Pages-Count']).to eq('1201')
71+
end
72+
end
73+
end if defined?(Grape)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require 'spec_helper'
2+
3+
describe APIHelper::Paginatable do
4+
describe ".per_page_param_desc" do
5+
it "returns a string" do
6+
expect(APIHelper::Paginatable.per_page_param_desc).to be_a(String)
7+
end
8+
end
9+
10+
describe ".page_param_desc" do
11+
it "returns a string" do
12+
expect(APIHelper::Paginatable.page_param_desc).to be_a(String)
13+
end
14+
end
15+
end

0 commit comments

Comments
 (0)