Skip to content

Commit f2d3dac

Browse files
Allow configuration overrides from request options
1 parent c9578ad commit f2d3dac

File tree

8 files changed

+131
-41
lines changed

8 files changed

+131
-41
lines changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ class MoviesController < ApplicationController
9494
# params[:per_page] (which defaults to 25) will be used.
9595
paginate json: actors, per_page: 10
9696
end
97+
98+
# GET /movies/:id/reviews
99+
def reviews
100+
reviews = Movie.find(params[:id]).reviews
101+
102+
# Override any configuration setting on request basis.
103+
# For example you may want to disable the total count since the count query is slow.
104+
paginate json: actors, total_count: false
105+
end
97106
end
98107
```
99108

@@ -114,6 +123,13 @@ class MoviesController < ApplicationController
114123

115124
render json: ActorsSerializer.new(actors)
116125
end
126+
127+
# GET /movies/:id/reviews
128+
def reviews
129+
reviews = paginate Movie.find(params[:id]).reviews, total_count: false
130+
131+
render json: ReviewSerializer.new(reviews)
132+
end
117133
end
118134
```
119135

lib/api-pagination.rb

+17-14
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

+17-15
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

+14-11
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ def paginate_with(collection)
2525

2626
def _paginate_collection(collection, options={})
2727
options[:page] = ApiPagination.config.page_param(params)
28-
options[:per_page] ||= ApiPagination.config.per_page_param(params)
28+
default_options = {
29+
:per_page => ApiPagination.config.per_page_param(params),
30+
:total_header => ApiPagination.config.total_header,
31+
:per_page_header => ApiPagination.config.per_page_header,
32+
:page_header => ApiPagination.config.page_header,
33+
:include_total => ApiPagination.config.include_total,
34+
:paginator => ApiPagination.config.paginator
35+
}
36+
options.reverse_merge!(default_options)
2937

3038
collection, pagy = ApiPagination.paginate(collection, options)
3139

@@ -38,25 +46,20 @@ def _paginate_collection(collection, options={})
3846
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
3947
end
4048

41-
total_header = ApiPagination.config.total_header
42-
per_page_header = ApiPagination.config.per_page_header
43-
page_header = ApiPagination.config.page_header
44-
include_total = ApiPagination.config.include_total
45-
4649
headers['Link'] = links.join(', ') unless links.empty?
47-
headers[per_page_header] = options[:per_page].to_s
48-
headers[page_header] = options[:page].to_s unless page_header.nil?
49-
headers[total_header] = total_count(pagy || collection, options).to_s if include_total
50+
headers[options[:per_page_header]] = options[:per_page].to_s
51+
headers[options[:page_header]] = options[:page].to_s unless options[:page_header].nil?
52+
headers[options[:total_header]] = total_count(pagy || collection, options).to_s if options[:include_total]
5053

5154
return collection
5255
end
5356

5457
def total_count(collection, options)
55-
total_count = if ApiPagination.config.paginator == :kaminari
58+
total_count = if options[:paginator] == :kaminari
5659
paginate_array_options = options[:paginate_array_options]
5760
paginate_array_options[:total_count] if paginate_array_options
5861
end
59-
total_count || ApiPagination.total_from(collection)
62+
total_count || ApiPagination.total_from(collection, options)
6063
end
6164

6265
def base_url

spec/grape_spec.rb

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

spec/rails_spec.rb

+24-1
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

+9
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

+11
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)