Skip to content

Commit f7ae77f

Browse files
committed
Add a max_per_page option for Grape. Closes #22 and #23
Signed-off-by: David Celis <[email protected]>
1 parent 521640b commit f7ae77f

File tree

4 files changed

+19
-8
lines changed

4 files changed

+19
-8
lines changed

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class MoviesController < ApplicationController
4949
end
5050
```
5151

52-
This will pull your collection from the `json` or `xml` option, paginate it for you using `params[:page]` and `params[:per_page]`, render Link headers, and call `ActionController::Base#render` with whatever you passed to `paginate`. This should work well with [ActiveModel::Serializers](https://github.com/rails-api/active_model-serializers). However, if you need more control over what is done with your paginated collection, you can pass the collection directly to `paginate` instead of in a way that mimics `render`:
52+
This will pull your collection from the `json` or `xml` option, paginate it for you using `params[:page]` and `params[:per_page]`, render Link headers, and call `ActionController::Base#render` with whatever you passed to `paginate`. This should work well with [ActiveModel::Serializers](https://github.com/rails-api/active_model-serializers). However, if you need more control over what is done with your paginated collection, you can pass the collection directly to `paginate` to receive a paginated collection and have your headers set. Then, you can pass that paginated collection to a serializer or do whatever you want with it:
5353

5454
```ruby
5555
class MoviesController < ApplicationController
@@ -69,13 +69,11 @@ class MoviesController < ApplicationController
6969
end
7070
```
7171

72-
This will avoid implicitly calling `render` at the end. Instead, `paginate` will simply set up the headers and return your collection so you can do whatever you want with it.
73-
74-
Note that the collection sent to `paginate` _must_ respond to your paginator's methods. For Kaminari, `Kaminari.paginate_array` will be called for you behind-the-scenes. For WillPaginate, you're out of luck unless you call `require 'will_paginate/array'` somewhere. Because this pollutes `Array`, it won't be done for you automatically.
72+
Note that the collection sent to `paginate` _must_ respond to your paginator's methods. This is typically fine unless you're dealing with a stock Array. For Kaminari, `Kaminari.paginate_array` will be called for you behind-the-scenes. For WillPaginate, you're out of luck unless you call `require 'will_paginate/array'` somewhere. Because this pollutes `Array`, it won't be done for you automatically.
7573

7674
## Grape
7775

78-
With Grape, `paginate` is used to declare that your endpoint takes a `:page` and `:per_page` param. Inside your API endpoint, it simply takes your collection:
76+
With Grape, `paginate` is used to declare that your endpoint takes a `:page` and `:per_page` param. You can also directly specify a `:max_per_page` that users aren't allowed to go over. Then, inside your API endpoint, it simply takes your collection:
7977

8078
```ruby
8179
class MoviesAPI < Grape::API
@@ -93,7 +91,8 @@ class MoviesAPI < Grape::API
9391
desc "Return one movie's cast, paginated"
9492
# Override how many Actors get returned. If unspecified,
9593
# params[:per_page] (which defaults to 25) will be used.
96-
paginate per_page: 10
94+
# There is no default for `max_per_page`.
95+
paginate per_page: 10, max_per_page: 200
9796
get :cast do
9897
paginate Movie.find(params[:id]).actors
9998
end

lib/grape/pagination.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ module Pagination
33
def self.included(base)
44
Grape::Endpoint.class_eval do
55
def paginate(collection)
6+
per_page = params[:per_page] || route_setting(:per_page)
67
options = {
78
:page => params[:page],
8-
:per_page => (params[:per_page] || route_setting(:per_page))
9+
:per_page => [per_page, route_setting(:max_per_page)].compact.min
910
}
1011
collection = ApiPagination.paginate(collection, options)
1112

@@ -30,6 +31,7 @@ def paginate(collection)
3031
base.class_eval do
3132
def self.paginate(options = {})
3233
route_setting :per_page, (options[:per_page] || 25)
34+
route_setting :max_per_page, options[:max_per_page]
3335
params do
3436
optional :page, :type => Integer, :default => 1,
3537
:desc => 'Page of results to fetch.'

spec/grape_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@
5555

5656
it_behaves_like 'an endpoint with a middle page'
5757
end
58+
59+
context 'with a max_per_page setting' do
60+
before { get '/numbers', :count => 100, :per_page => 30 }
61+
62+
it 'should not go above the max_per_page_limit' do
63+
body = '[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]'
64+
65+
expect(last_response.body).to eq(body)
66+
end
67+
end
5868
end
5969
end
6070
end

spec/support/numbers_api.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class NumbersAPI < Grape::API
55
format :json
66

77
desc 'Return some paginated set of numbers'
8-
paginate :per_page => 10
8+
paginate :per_page => 10, :max_per_page => 25
99
params do
1010
requires :count, :type => Integer
1111
optional :with_headers, :default => false, :type => Boolean

0 commit comments

Comments
 (0)