api-pagination 2.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/api-pagination.rb +5 -6
- data/lib/api-pagination/hooks.rb +2 -0
- data/lib/api-pagination/version.rb +3 -3
- data/lib/grape/pagination.rb +18 -14
- data/lib/rails/pagination.rb +33 -14
- data/spec/grape_spec.rb +8 -3
- data/spec/rails_spec.rb +9 -3
- data/spec/spec_helper.rb +9 -26
- data/spec/support/numbers_api.rb +2 -2
- data/spec/support/numbers_controller.rb +1 -4
- data/spec/support/shared_examples/existing_headers.rb +1 -1
- data/spec/support/shared_examples/first_page.rb +10 -1
- data/spec/support/shared_examples/last_page.rb +11 -1
- data/spec/support/shared_examples/middle_page.rb +11 -1
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83a3cdfe943a03cd590d6a3c9e80aa11d7d5e684
|
4
|
+
data.tar.gz: df6e1401a570e87fad204d6f249e7e31b09ea159
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 139197be0f405c0e4954250f4ffa56f5bdc02246e49b674d1ecaab262a2c412d8b088ac6eaa54cf00cda4a75e30e74a746c8b3fb122c4ea7dc9954c5ebc67d82
|
7
|
+
data.tar.gz: c8e15989e239fa9b3d77f8968b4612c8ce93ca78000c39f1c04063d32b24cb1dbe20664e8b1a4ab677fefdb153c20f184ba6319686e126980440ae4b9a6891f2
|
data/lib/api-pagination.rb
CHANGED
@@ -5,15 +5,16 @@ module ApiPagination
|
|
5
5
|
class << self
|
6
6
|
attr_reader :paginator
|
7
7
|
|
8
|
-
def paginate(collection, options = {}
|
8
|
+
def paginate(collection, options = {})
|
9
9
|
options[:page] ||= 1
|
10
|
-
options[:per_page] ||=
|
10
|
+
options[:per_page] ||= 25
|
11
11
|
|
12
12
|
case ApiPagination.paginator
|
13
13
|
when :kaminari
|
14
|
-
collection.
|
14
|
+
collection = Kaminari.paginate_array(collection) if collection.is_a?(Array)
|
15
|
+
collection.page(options[:page]).per(options[:per_page])
|
15
16
|
when :will_paginate
|
16
|
-
collection.paginate(:page => options[:page], :per_page => options[:per_page])
|
17
|
+
collection.paginate(:page => options[:page], :per_page => options[:per_page])
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -39,5 +40,3 @@ module ApiPagination
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
42
|
-
|
43
|
-
ApiPagination::Hooks.init
|
data/lib/api-pagination/hooks.rb
CHANGED
data/lib/grape/pagination.rb
CHANGED
@@ -3,32 +3,36 @@ module Grape
|
|
3
3
|
def self.included(base)
|
4
4
|
Grape::Endpoint.class_eval do
|
5
5
|
def paginate(collection)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
options = {
|
7
|
+
:page => params[:page],
|
8
|
+
:per_page => (settings[:per_page] || params[:per_page])
|
9
|
+
}
|
10
|
+
collection = ApiPagination.paginate(collection, options)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
|
15
|
-
end
|
12
|
+
links = (header['Link'] || "").split(',').map(&:strip)
|
13
|
+
url = request.url.sub(/\?.*$/, '')
|
14
|
+
pages = ApiPagination.pages_from(collection)
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
pages.each do |k, v|
|
17
|
+
old_params = Rack::Utils.parse_query(request.query_string)
|
18
|
+
new_params = old_params.merge('page' => v)
|
19
|
+
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
+
header 'Link', links.join(', ') unless links.empty?
|
23
|
+
header 'Total', ApiPagination.total_from(collection)
|
24
|
+
|
25
|
+
return collection
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
29
|
base.class_eval do
|
26
30
|
def self.paginate(options = {})
|
27
|
-
options
|
31
|
+
set :per_page, options[:per_page]
|
28
32
|
params do
|
29
33
|
optional :page, :type => Integer, :default => 1,
|
30
34
|
:desc => 'Page of results to fetch.'
|
31
|
-
optional :per_page, :type => Integer,
|
35
|
+
optional :per_page, :type => Integer,
|
32
36
|
:desc => 'Number of results to return per page.'
|
33
37
|
end
|
34
38
|
end
|
data/lib/rails/pagination.rb
CHANGED
@@ -2,24 +2,43 @@ module Rails
|
|
2
2
|
module Pagination
|
3
3
|
protected
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
def paginate(options)
|
6
|
+
collection = options[:json] || options[:xml]
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
pages = ApiPagination.pages_from(collection)
|
8
|
+
collection = _paginate_collection(collection, options)
|
9
|
+
options[:json] = collection if options[:json]
|
10
|
+
options[:xml] = collection if options[:xml]
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
|
16
|
-
end
|
12
|
+
render options
|
13
|
+
end
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
def paginate_with(collection)
|
16
|
+
respond_with _paginate_collection(collection)
|
17
|
+
end
|
21
18
|
|
22
|
-
|
19
|
+
private
|
20
|
+
|
21
|
+
def _paginate_collection(collection, options)
|
22
|
+
options = {
|
23
|
+
:page => params[:page],
|
24
|
+
:per_page => (options.delete(:per_page) || params[:per_page])
|
25
|
+
}
|
26
|
+
collection = ApiPagination.paginate(collection, options)
|
27
|
+
|
28
|
+
links = (headers['Link'] || "").split(',').map(&:strip)
|
29
|
+
url = request.original_url.sub(/\?.*$/, '')
|
30
|
+
pages = ApiPagination.pages_from(collection)
|
31
|
+
|
32
|
+
pages.each do |k, v|
|
33
|
+
new_params = request.query_parameters.merge(:page => v)
|
34
|
+
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
|
23
35
|
end
|
36
|
+
|
37
|
+
headers['Link'] = links.join(', ') unless links.empty?
|
38
|
+
headers['Total'] = ApiPagination.total_from(collection)
|
39
|
+
|
40
|
+
return collection
|
41
|
+
end
|
24
42
|
end
|
25
43
|
end
|
44
|
+
|
data/spec/grape_spec.rb
CHANGED
@@ -10,14 +10,19 @@ describe NumbersAPI do
|
|
10
10
|
let(:total) { last_response.headers['Total'].to_i }
|
11
11
|
|
12
12
|
context 'without enough items to give more than one page' do
|
13
|
-
before { get :numbers, :count =>
|
13
|
+
before { get :numbers, :count => 10 }
|
14
14
|
|
15
15
|
it 'should not paginate' do
|
16
16
|
expect(last_response.headers.keys).not_to include('Link')
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'should give a Total header' do
|
20
|
-
expect(total).to eq(
|
20
|
+
expect(total).to eq(10)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should list all numbers in the response body' do
|
24
|
+
body = '[1,2,3,4,5,6,7,8,9,10]'
|
25
|
+
expect(last_response.body).to eq(body)
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
@@ -35,7 +40,7 @@ describe NumbersAPI do
|
|
35
40
|
end
|
36
41
|
|
37
42
|
context 'when on the last page' do
|
38
|
-
before { get :numbers, :count => 100, :page =>
|
43
|
+
before { get :numbers, :count => 100, :page => 10 }
|
39
44
|
|
40
45
|
it_behaves_like 'an endpoint with a last page'
|
41
46
|
end
|
data/spec/rails_spec.rb
CHANGED
@@ -12,13 +12,19 @@ describe NumbersController, :type => :controller do
|
|
12
12
|
let(:total) { response.headers['Total'].to_i }
|
13
13
|
|
14
14
|
context 'without enough items to give more than one page' do
|
15
|
-
before { get :index, :count =>
|
15
|
+
before { get :index, :count => 10 }
|
16
|
+
|
16
17
|
it 'should not paginate' do
|
17
18
|
expect(response.headers.keys).not_to include('Link')
|
18
19
|
end
|
19
20
|
|
20
21
|
it 'should give a Total header' do
|
21
|
-
expect(total).to eq(
|
22
|
+
expect(total).to eq(10)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should list all numbers in the response body' do
|
26
|
+
body = '[1,2,3,4,5,6,7,8,9,10]'
|
27
|
+
expect(response.body).to eq(body)
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
@@ -36,7 +42,7 @@ describe NumbersController, :type => :controller do
|
|
36
42
|
end
|
37
43
|
|
38
44
|
context 'when on the last page' do
|
39
|
-
before { get :index, :count => 100, :page =>
|
45
|
+
before { get :index, :count => 100, :page => 10 }
|
40
46
|
|
41
47
|
it_behaves_like 'an endpoint with a last page'
|
42
48
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,33 +1,10 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear_merged!
|
3
|
+
|
1
4
|
require 'support/numbers_controller'
|
2
5
|
require 'support/numbers_api'
|
3
6
|
require 'api-pagination'
|
4
7
|
|
5
|
-
# Quacks like Kaminari and will_paginate
|
6
|
-
PaginatedSet = Struct.new(:current_page, :per_page, :total_count) do
|
7
|
-
def total_pages
|
8
|
-
total_count.zero? ? 1 : (total_count.to_f / per_page).ceil
|
9
|
-
end
|
10
|
-
|
11
|
-
def first_page?() current_page == 1 end
|
12
|
-
def last_page?() current_page == total_pages end
|
13
|
-
|
14
|
-
def page(page)
|
15
|
-
current_page = page
|
16
|
-
self
|
17
|
-
end
|
18
|
-
|
19
|
-
def per(per)
|
20
|
-
per_page = per
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def paginate(options = {})
|
25
|
-
page(options[:page]).per(options[:per_page])
|
26
|
-
end
|
27
|
-
|
28
|
-
alias :total_entries :total_count
|
29
|
-
end
|
30
|
-
|
31
8
|
if ENV['PAGINATOR']
|
32
9
|
ApiPagination.instance_variable_set(:@paginator, ENV['PAGINATOR'].to_sym)
|
33
10
|
else
|
@@ -35,6 +12,12 @@ else
|
|
35
12
|
ApiPagination.instance_variable_set(:@paginator, :kaminari)
|
36
13
|
end
|
37
14
|
|
15
|
+
if ApiPagination.paginator == :kaminari
|
16
|
+
Kaminari::Hooks.init
|
17
|
+
elsif ApiPagination.paginator == :will_paginate
|
18
|
+
require 'will_paginate/array'
|
19
|
+
end
|
20
|
+
|
38
21
|
RSpec.configure do |config|
|
39
22
|
config.include Rack::Test::Methods
|
40
23
|
config.include ControllerExampleGroup, :type => :controller
|
data/spec/support/numbers_api.rb
CHANGED
@@ -5,7 +5,7 @@ class NumbersAPI < Grape::API
|
|
5
5
|
format :json
|
6
6
|
|
7
7
|
desc 'Return some paginated set of numbers'
|
8
|
-
paginate :per_page =>
|
8
|
+
paginate :per_page => 10
|
9
9
|
params do
|
10
10
|
requires :count, :type => Integer
|
11
11
|
optional :with_headers, :default => false, :type => Boolean
|
@@ -18,6 +18,6 @@ class NumbersAPI < Grape::API
|
|
18
18
|
header 'Link', %(<#{url}?#{query.to_query}>; rel="without")
|
19
19
|
end
|
20
20
|
|
21
|
-
paginate
|
21
|
+
paginate (1..params[:count]).to_a
|
22
22
|
end
|
23
23
|
end
|
@@ -45,8 +45,6 @@ end
|
|
45
45
|
class NumbersController < ActionController::Base
|
46
46
|
include Rails.application.routes.url_helpers
|
47
47
|
|
48
|
-
after_filter :only => [:index] { paginate(:numbers) }
|
49
|
-
|
50
48
|
def index
|
51
49
|
page = params.fetch(:page, 1).to_i
|
52
50
|
total = params.fetch(:count).to_i
|
@@ -57,7 +55,6 @@ class NumbersController < ActionController::Base
|
|
57
55
|
headers['Link'] = %(<#{numbers_url}?#{query.to_param}>; rel="without")
|
58
56
|
end
|
59
57
|
|
60
|
-
|
61
|
-
render :json => @numbers
|
58
|
+
paginate :json => (1..total).to_a, :per_page => 10
|
62
59
|
end
|
63
60
|
end
|
@@ -5,7 +5,7 @@ shared_examples 'an endpoint with existing Link headers' do
|
|
5
5
|
|
6
6
|
it 'should contain pagination Links' do
|
7
7
|
expect(links).to include('<http://example.org/numbers?count=30&page=2&with_headers=true>; rel="next"')
|
8
|
-
expect(links).to include('<http://example.org/numbers?count=30&page=
|
8
|
+
expect(links).to include('<http://example.org/numbers?count=30&page=3&with_headers=true>; rel="last"')
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'should give a Total header' do
|
@@ -8,7 +8,7 @@ shared_examples 'an endpoint with a first page' do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should give a link with rel "last"' do
|
11
|
-
expect(links).to include('<http://example.org/numbers?count=100&page=
|
11
|
+
expect(links).to include('<http://example.org/numbers?count=100&page=10>; rel="last"')
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'should give a link with rel "next"' do
|
@@ -18,4 +18,13 @@ shared_examples 'an endpoint with a first page' do
|
|
18
18
|
it 'should give a Total header' do
|
19
19
|
expect(total).to eq(100)
|
20
20
|
end
|
21
|
+
|
22
|
+
it 'should list the first page of numbers in the response body' do
|
23
|
+
body = '[1,2,3,4,5,6,7,8,9,10]'
|
24
|
+
if defined?(response)
|
25
|
+
expect(response.body).to eq(body)
|
26
|
+
else
|
27
|
+
expect(last_response.body).to eq(body)
|
28
|
+
end
|
29
|
+
end
|
21
30
|
end
|
@@ -12,10 +12,20 @@ shared_examples 'an endpoint with a last page' do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'should give a link with rel "prev"' do
|
15
|
-
expect(links).to include('<http://example.org/numbers?count=100&page=
|
15
|
+
expect(links).to include('<http://example.org/numbers?count=100&page=9>; rel="prev"')
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should give a Total header' do
|
19
19
|
expect(total).to eq(100)
|
20
20
|
end
|
21
|
+
|
22
|
+
it 'should list the last page of numbers in the response body' do
|
23
|
+
body = '[91,92,93,94,95,96,97,98,99,100]'
|
24
|
+
|
25
|
+
if defined?(response)
|
26
|
+
expect(response.body).to eq(body)
|
27
|
+
else
|
28
|
+
expect(last_response.body).to eq(body)
|
29
|
+
end
|
30
|
+
end
|
21
31
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
shared_examples 'an endpoint with a middle page' do
|
2
2
|
it 'should give all pagination links' do
|
3
3
|
expect(links).to include('<http://example.org/numbers?count=100&page=1>; rel="first"')
|
4
|
-
expect(links).to include('<http://example.org/numbers?count=100&page=
|
4
|
+
expect(links).to include('<http://example.org/numbers?count=100&page=10>; rel="last"')
|
5
5
|
expect(links).to include('<http://example.org/numbers?count=100&page=3>; rel="next"')
|
6
6
|
expect(links).to include('<http://example.org/numbers?count=100&page=1>; rel="prev"')
|
7
7
|
end
|
@@ -9,4 +9,14 @@ shared_examples 'an endpoint with a middle page' do
|
|
9
9
|
it 'should give a Total header' do
|
10
10
|
expect(total).to eq(100)
|
11
11
|
end
|
12
|
+
|
13
|
+
it 'should list a middle page of numbers in the response body' do
|
14
|
+
body = '[11,12,13,14,15,16,17,18,19,20]'
|
15
|
+
|
16
|
+
if defined?(response)
|
17
|
+
expect(response.body).to eq(body)
|
18
|
+
else
|
19
|
+
expect(last_response.body).to eq(body)
|
20
|
+
end
|
21
|
+
end
|
12
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-pagination
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: railties
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.0.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.0.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: actionpack
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,7 +66,7 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: 3.0.0
|
55
|
-
description: Link header pagination for Rails APIs
|
69
|
+
description: Link header pagination for Rails and Grape APIs
|
56
70
|
email:
|
57
71
|
- me@davidcel.is
|
58
72
|
executables: []
|
@@ -93,10 +107,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
107
|
version: '0'
|
94
108
|
requirements: []
|
95
109
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.2.
|
110
|
+
rubygems_version: 2.2.2
|
97
111
|
signing_key:
|
98
112
|
specification_version: 4
|
99
|
-
summary: Link header pagination for Rails APIs. Don't use the request body.
|
113
|
+
summary: Link header pagination for Rails and Grape APIs. Don't use the request body.
|
100
114
|
test_files:
|
101
115
|
- spec/grape_spec.rb
|
102
116
|
- spec/rails_spec.rb
|
@@ -107,3 +121,4 @@ test_files:
|
|
107
121
|
- spec/support/shared_examples/first_page.rb
|
108
122
|
- spec/support/shared_examples/last_page.rb
|
109
123
|
- spec/support/shared_examples/middle_page.rb
|
124
|
+
has_rdoc:
|