Skip to content

Commit 58da3b5

Browse files
committed
Extract shared code
Signed-off-by: David Celis <[email protected]>
1 parent 8139f64 commit 58da3b5

File tree

6 files changed

+95
-41
lines changed

6 files changed

+95
-41
lines changed

README.md

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ Put pagination info in a Link header, not the response body.
77
In your `Gemfile`:
88

99
```ruby
10-
# Requires Rails and is compatible with Rails-API.
10+
# Requires Rails (Rails-API is also supported) or Grape.
1111
gem 'rails', '>= 3.0.0'
12-
# gem 'rails-api'
12+
gem 'rails-api'
13+
gem 'grape'
1314

1415
# Then choose your preferred paginator from the following:
1516
gem 'kaminari'
@@ -19,33 +20,57 @@ gem 'will_paginate'
1920
gem 'api-pagination'
2021
```
2122

22-
## Usage
23+
## Rails
2324

24-
In your controllers:
25+
In your controller:
2526

2627
```ruby
2728
class MoviesController < ApplicationController
28-
# Uses the @movies and @actors variables set below
29+
# Uses the @movies and @actors variables set below.
30+
# This method must take an ActiveRecord::Relation
31+
# or some equivalent pageable set.
2932
after_filter only: [:index] { paginate(:movies) }
3033
after_filter only: [:cast] { paginate(:actors) }
3134

3235
# GET /movies
3336
def index
34-
@movies = Movie.page(params[:page])
37+
@movies = Movie.scoped
3538

3639
render json: @movies
3740
end
3841

3942
# GET /movies/:id/cast
4043
def cast
4144
@movie = Movie.find(params[:id])
42-
@actors = @movie.actors.page(params[:page])
45+
@actors = @movie.actors
46+
47+
# Override how many Actors get returned.
48+
params[:per_page] = 10
4349

4450
render json: @actors
4551
end
4652
end
4753
```
4854

55+
## Grape
56+
57+
In your API endpoint:
58+
59+
```ruby
60+
class MoviesAPI < Grape::API
61+
format :json
62+
63+
desc 'Return a paginated set of movies'
64+
paginate per_page: 25
65+
get :numbers do
66+
movies = Movie.scoped
67+
68+
# This method must take an ActiveRecord::Relation
69+
# or some equivalent pageable set.
70+
paginate movies
71+
end
72+
end
73+
4974
Then `curl --include` to see your Link header pagination in action:
5075

5176
```bash

lib/api-pagination.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
11
require 'api-pagination/hooks'
22
require 'api-pagination/version'
33

4+
module ApiPagination
5+
class << self
6+
attr_writer :kaminari
7+
attr_writer :will_paginate
8+
9+
def kaminari?() !!@kaminari end
10+
def will_paginate?() !!@will_paginate end
11+
12+
def paginate(collection, options = {}, &block)
13+
options[:page] ||= 1
14+
options[:per_page] ||= 10
15+
16+
if ApiPagination.kaminari?
17+
collection.page(options[:page]).per(options[:per_page]).tap(&block)
18+
elsif ApiPagination.will_paginate?
19+
collection.paginate(:page => options[:page], :per_page => options[:per_page]).tap(&block)
20+
end
21+
end
22+
23+
def pages_from(collection)
24+
{}.tap do |pages|
25+
unless collection.first_page?
26+
pages[:first] = 1
27+
pages[:prev] = collection.current_page - 1
28+
end
29+
30+
unless collection.last_page?
31+
pages[:last] = collection.total_pages
32+
pages[:next] = collection.current_page + 1
33+
end
34+
end
35+
end
36+
end
37+
end
38+
439
ApiPagination::Hooks.init

lib/api-pagination/hooks.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ def self.init
2525
def first_page?() !previous_page end
2626
def last_page?() !next_page end
2727
end
28+
29+
ApiPagination.will_paginate = true
2830
end
2931

3032
begin; require 'kaminari'; rescue LoadError; end
33+
if defined?(Kaminari)
34+
ApiPagination.kaminari = true
35+
end
3136

3237
STDERR.puts <<-EOC unless defined?(Kaminari) || defined?(WillPaginate)
3338
Warning: api-pagination relies on either Kaminari or WillPaginate. Please

lib/grape/pagination.rb

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,10 @@ module Pagination
33
def self.included(base)
44
Grape::Endpoint.class_eval do
55
def paginate(collection)
6-
collection.page(params[:page]).per(params[:per_page]).tap do |scope|
6+
block = Proc.new do |collection|
77
links = (header['Link'] || "").split(',').map(&:strip)
8-
98
url = request.url.sub(/\?.*$/, '')
10-
pages = {}
11-
12-
unless scope.first_page?
13-
pages[:first] = 1
14-
pages[:prev] = scope.current_page - 1
15-
end
16-
17-
unless scope.last_page?
18-
pages[:last] = scope.total_pages
19-
pages[:next] = scope.current_page + 1
20-
end
9+
pages = ApiPagination.pages_from(collection)
2110

2211
pages.each do |k, v|
2312
old_params = Rack::Utils.parse_query(request.query_string)
@@ -27,6 +16,8 @@ def paginate(collection)
2716

2817
header 'Link', links.join(', ') unless links.empty?
2918
end
19+
20+
ApiPagination.paginate(collection, params, &block)
3021
end
3122
end
3223

lib/rails/pagination.rb

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@ module Rails
22
module Pagination
33
protected
44

5-
def paginate(scope)
6-
links = (headers['Link'] || "").split(',').map(&:strip)
5+
def paginate(collection)
6+
collection = instance_variable_get(:"@#{collection}")
77

8-
scope = instance_variable_get(:"@#{scope}")
9-
url = request.original_url.sub(/\?.*$/, '')
10-
pages = {}
8+
block = Proc.new do |collection|
9+
links = (headers['Link'] || "").split(',').map(&:strip)
10+
url = request.original_url.sub(/\?.*$/, '')
11+
pages = ApiPagination.pages_from(collection)
1112

12-
unless scope.first_page?
13-
pages[:first] = 1
14-
pages[:prev] = scope.current_page - 1
15-
end
16-
17-
unless scope.last_page?
18-
pages[:last] = scope.total_pages
19-
pages[:next] = scope.current_page + 1
20-
end
13+
pages.each do |k, v|
14+
new_params = request.query_parameters.merge(:page => v)
15+
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
16+
end
2117

22-
pages.each do |k, v|
23-
new_params = request.query_parameters.merge(:page => v)
24-
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
18+
headers['Link'] = links.join(', ') unless links.empty?
2519
end
2620

27-
headers['Link'] = links.join(', ') unless links.empty?
28-
29-
return scope
21+
ApiPagination.paginate(collection, params, &block)
3022
end
3123
end
3224
end

spec/spec_helper.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
require 'api-pagination'
44
require 'support/numbers_api'
55

6-
# Quacks like Kaminari
6+
ApiPagination.kaminari = true
7+
8+
# Quacks like Kaminari and will_paginate
79
PaginatedSet = Struct.new(:current_page, :per_page, :total_count) do
810
def total_pages
911
total_count.zero? ? 1 : (total_count.to_f / per_page).ceil
@@ -21,6 +23,10 @@ def per(per)
2123
per_page = per
2224
self
2325
end
26+
27+
def paginate(options = {})
28+
page(options[:page]).per(options[:per_page])
29+
end
2430
end
2531

2632
RSpec.configure do |config|

0 commit comments

Comments
 (0)