Skip to content

Commit 5eee138

Browse files
Allow configuration overrides from request options
1 parent df05199 commit 5eee138

File tree

8 files changed

+131
-41
lines changed

8 files changed

+131
-41
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ class MoviesController < ApplicationController
9797
# params[:per_page] (which defaults to 25) will be used.
9898
paginate json: actors, per_page: 10
9999
end
100+
101+
# GET /movies/:id/reviews
102+
def reviews
103+
reviews = Movie.find(params[:id]).reviews
104+
105+
# Override any configuration setting on request basis.
106+
# For example you may want to disable the total count since the count query is slow.
107+
paginate json: actors, total_count: false
108+
end
100109
end
101110
```
102111

@@ -117,6 +126,13 @@ class MoviesController < ApplicationController
117126

118127
render json: ActorsSerializer.new(actors)
119128
end
129+
130+
# GET /movies/:id/reviews
131+
def reviews
132+
reviews = paginate Movie.find(params[:id]).reviews, total_count: false
133+
134+
render json: ReviewSerializer.new(reviews)
135+
end
120136
end
121137
```
122138

lib/api-pagination.rb

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,43 @@
44
module ApiPagination
55
class << self
66
def paginate(collection, options = {})
7-
options[:page] = options[:page].to_i
8-
options[:page] = 1 if options[:page] <= 0
9-
options[:per_page] = options[:per_page].to_i
7+
options[:page] = options[:page].to_i
8+
options[:page] = 1 if options[:page] <= 0
9+
options[:per_page] = options[:per_page].to_i
10+
options[:paginator] ||= ApiPagination.config.paginator
1011

11-
case ApiPagination.config.paginator
12+
case options[:paginator]
1213
when :pagy
1314
paginate_with_pagy(collection, options)
1415
when :kaminari
1516
paginate_with_kaminari(collection, options, options[:paginate_array_options] || {})
1617
when :will_paginate
1718
paginate_with_will_paginate(collection, options)
1819
else
19-
raise StandardError, "Unknown paginator: #{ApiPagination.config.paginator}"
20+
raise StandardError, "Unknown paginator: #{options[:paginator]}"
2021
end
2122
end
2223

2324
def pages_from(collection, options = {})
24-
return pagy_pages_from(collection) if ApiPagination.config.paginator == :pagy && collection.is_a?(Pagy)
25+
options[:paginator] ||= ApiPagination.config.paginator
26+
return pagy_pages_from(collection, options) if options[:paginator] == :pagy && collection.is_a?(Pagy)
2527

2628
{}.tap do |pages|
2729
unless collection.first_page?
2830
pages[:first] = 1
2931
pages[:prev] = collection.current_page - 1
3032
end
3133

32-
unless collection.last_page? || (ApiPagination.config.paginator == :kaminari && collection.out_of_range?)
33-
pages[:last] = collection.total_pages if ApiPagination.config.include_total
34+
unless collection.last_page? || (options[:paginator] == :kaminari && collection.out_of_range?)
35+
pages[:last] = collection.total_pages if options[:include_total]
3436
pages[:next] = collection.current_page + 1
3537
end
3638
end
3739
end
3840

39-
def total_from(collection)
40-
case ApiPagination.config.paginator
41+
def total_from(collection, options)
42+
options[:paginator] ||= ApiPagination.config.paginator
43+
case options[:paginator]
4144
when :pagy then collection.count.to_s
4245
when :kaminari then collection.total_count.to_s
4346
when :will_paginate then collection.total_entries.to_s
@@ -69,19 +72,19 @@ def pagy_from(collection, options)
6972
else
7073
count = collection.is_a?(Array) ? collection.count : collection.count(:all)
7174
end
72-
75+
7376
Pagy.new(count: count, items: options[:per_page], page: options[:page])
7477
end
7578

76-
def pagy_pages_from(pagy)
79+
def pagy_pages_from(pagy, options)
7780
{}.tap do |pages|
7881
unless pagy.page == 1
7982
pages[:first] = 1
8083
pages[:prev] = pagy.prev
8184
end
8285

8386
unless pagy.page == pagy.pages
84-
pages[:last] = pagy.pages if ApiPagination.config.include_total
87+
pages[:last] = pagy.pages if options[:include_total]
8588
pages[:next] = pagy.next
8689
end
8790
end
@@ -96,7 +99,7 @@ def paginate_with_kaminari(collection, options, paginate_array_options = {})
9699

97100
collection = Kaminari.paginate_array(collection, paginate_array_options) if collection.is_a?(Array)
98101
collection = collection.page(options[:page]).per(options[:per_page])
99-
collection.without_count if !collection.is_a?(Array) && !ApiPagination.config.include_total
102+
collection.without_count if !collection.is_a?(Array) && !options[:include_total]
100103
[collection, nil]
101104
end
102105

lib/grape/pagination.rb

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@ module Grape
22
module Pagination
33
def self.included(base)
44
Grape::Endpoint.class_eval do
5-
def paginate(collection)
6-
per_page = ApiPagination.config.per_page_param(params) || route_setting(:per_page)
7-
8-
options = {
9-
:page => ApiPagination.config.page_param(params),
10-
:per_page => [per_page, route_setting(:max_per_page)].compact.min
5+
def paginate(collection, options = {})
6+
per_page = ApiPagination.config.per_page_param(params) || route_setting(:per_page)
7+
options[:per_page] = [per_page, route_setting(:max_per_page)].compact.min
8+
options[:page] = ApiPagination.config.page_param(params)
9+
10+
default_options = {
11+
:total_header => ApiPagination.config.total_header,
12+
:per_page_header => ApiPagination.config.per_page_header,
13+
:page_header => ApiPagination.config.page_header,
14+
:include_total => ApiPagination.config.include_total,
15+
:paginator => ApiPagination.config.paginator
1116
}
17+
options.reverse_merge!(default_options)
18+
1219
collection, pagy = ApiPagination.paginate(collection, options)
1320

1421
links = (header['Link'] || "").split(',').map(&:strip)
@@ -21,15 +28,10 @@ def paginate(collection)
2128
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
2229
end
2330

24-
total_header = ApiPagination.config.total_header
25-
per_page_header = ApiPagination.config.per_page_header
26-
page_header = ApiPagination.config.page_header
27-
include_total = ApiPagination.config.include_total
28-
29-
header 'Link', links.join(', ') unless links.empty?
30-
header total_header, ApiPagination.total_from(pagy || collection).to_s if include_total
31-
header per_page_header, options[:per_page].to_s
32-
header page_header, options[:page].to_s unless page_header.nil?
31+
header 'Link', links.join(', ') unless links.empty?
32+
header options[:total_header], ApiPagination.total_from(pagy || collection, options).to_s if options[:include_total]
33+
header options[:per_page_header], options[:per_page].to_s
34+
header options[:page_header], options[:page].to_s unless options[:page_header].nil?
3335

3436
return collection
3537
end

lib/rails/pagination.rb

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,15 @@ def _discover_format(options)
3232

3333
def _paginate_collection(collection, options={})
3434
options[:page] = ApiPagination.config.page_param(params)
35-
options[:per_page] ||= ApiPagination.config.per_page_param(params)
35+
default_options = {
36+
:per_page => ApiPagination.config.per_page_param(params),
37+
:total_header => ApiPagination.config.total_header,
38+
:per_page_header => ApiPagination.config.per_page_header,
39+
:page_header => ApiPagination.config.page_header,
40+
:include_total => ApiPagination.config.include_total,
41+
:paginator => ApiPagination.config.paginator
42+
}
43+
options.reverse_merge!(default_options)
3644

3745
collection, pagy = ApiPagination.paginate(collection, options)
3846

@@ -45,25 +53,20 @@ def _paginate_collection(collection, options={})
4553
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
4654
end
4755

48-
total_header = ApiPagination.config.total_header
49-
per_page_header = ApiPagination.config.per_page_header
50-
page_header = ApiPagination.config.page_header
51-
include_total = ApiPagination.config.include_total
52-
5356
headers['Link'] = links.join(', ') unless links.empty?
54-
headers[per_page_header] = options[:per_page].to_s
55-
headers[page_header] = options[:page].to_s unless page_header.nil?
56-
headers[total_header] = total_count(pagy || collection, options).to_s if include_total
57+
headers[options[:per_page_header]] = options[:per_page].to_s
58+
headers[options[:page_header]] = options[:page].to_s unless options[:page_header].nil?
59+
headers[options[:total_header]] = total_count(pagy || collection, options).to_s if options[:include_total]
5760

5861
return collection
5962
end
6063

6164
def total_count(collection, options)
62-
total_count = if ApiPagination.config.paginator == :kaminari
65+
total_count = if options[:paginator] == :kaminari
6366
paginate_array_options = options[:paginate_array_options]
6467
paginate_array_options[:total_count] if paginate_array_options
6568
end
66-
total_count || ApiPagination.total_from(collection)
69+
total_count || ApiPagination.total_from(collection, options)
6770
end
6871

6972
def base_url

spec/grape_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,28 @@
161161
expect(links).to include('<http://example.org/numbers?count=100&page=2&parity%5B%5D=odd&parity%5B%5D=even>; rel="next"')
162162
end
163163
end
164+
165+
context 'request option to not include the total' do
166+
it 'should not include a Total header' do
167+
get '/numbers_with_inline_options', count: 10
168+
169+
expect(last_response.header['Total']).to be_nil
170+
end
171+
172+
it 'should not include a link with rel "last"' do
173+
get '/numbers_with_inline_options', count: 100
174+
175+
expect(link).to_not include('rel="last"')
176+
end
177+
end
178+
179+
context 'request option to change page_header' do
180+
it 'should give a X-Page header' do
181+
get '/numbers_with_inline_options', count: 10
182+
183+
expect(last_response.headers.keys).to include('X-Page')
184+
expect(last_response.headers['X-Page'].to_i).to eq(1)
185+
end
186+
end
164187
end
165188
end

spec/rails_spec.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,29 @@
219219
expect(response.header['Per-Page']).to eq('2')
220220
end
221221
end
222+
223+
context 'request option to not include the total' do
224+
it 'should not include a Total header' do
225+
get :index_with_inline_options, params: {count: 10}
226+
227+
expect(response.header['Total']).to be_nil
228+
end
229+
230+
it 'should not include a link with rel "last"' do
231+
get :index_with_inline_options, params: { count: 100 }
232+
233+
expect(link).to_not include('rel="last"')
234+
end
235+
end
236+
237+
context 'request option to change page_header' do
238+
it 'should give a X-Page header' do
239+
get :index_with_inline_options, params: {count: 10}
240+
241+
expect(response.headers.keys).to include('X-Page')
242+
expect(response.headers['X-Page'].to_i).to eq(1)
243+
end
244+
end
222245
end
223246

224247
if ApiPagination.config.paginator.to_sym == :kaminari
@@ -309,4 +332,4 @@ class Fixnum
309332
end
310333
end
311334
end
312-
end
335+
end

spec/support/numbers_api.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ class NumbersAPI < Grape::API
3838
get :numbers_with_enforced_max_per_page do
3939
paginate (1..params[:count]).to_a
4040
end
41+
42+
desc 'Return some paginated set of numbers with inline options'
43+
paginate :per_page => 10
44+
params do
45+
requires :count, :type => Integer
46+
end
47+
get :numbers_with_inline_options do
48+
paginate (1..params[:count]).to_a, include_total: false, page_header: 'X-Page'
49+
end
4150
end

spec/support/numbers_controller.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def teardown(*methods)
4545
get :index_with_custom_render
4646
get :index_with_no_per_page
4747
get :index_with_paginate_array_options
48+
get :index_with_inline_options
4849
end
4950
end
5051
end
@@ -98,6 +99,16 @@ def index_with_paginate_array_options
9899

99100
render json: NumbersSerializer.new(numbers)
100101
end
102+
103+
def index_with_inline_options
104+
total = params.fetch(:count).to_i
105+
paginate(
106+
:json => (1..total).to_a,
107+
:per_page => 10,
108+
:include_total => false,
109+
:page_header => 'X-Page'
110+
)
111+
end
101112
end
102113

103114
ApiPagination::Railtie.initializers.each(&:run)

0 commit comments

Comments
 (0)