api-pagination 4.7.2 → 5.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/configuration.rb +23 -7
- data/lib/api-pagination/hooks.rb +13 -4
- data/lib/api-pagination/version.rb +3 -3
- data/lib/api-pagination.rb +63 -14
- data/lib/grape/pagination.rb +4 -4
- data/lib/rails/pagination.rb +13 -7
- data/spec/active_record_spec.rb +50 -0
- data/spec/api-pagination_spec.rb +52 -42
- data/spec/grape_spec.rb +10 -1
- data/spec/rails_spec.rb +56 -51
- data/spec/sequel_spec.rb +1 -2
- data/spec/spec_helper.rb +9 -3
- data/spec/support/active_record/foo.rb +3 -0
- data/spec/support/active_record/schema.rb +5 -0
- data/spec/support/shared_examples/first_page.rb +2 -2
- data/spec/support/shared_examples/last_page.rb +2 -2
- metadata +119 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0df72041cd862671500fd9efe94c7fe8e35975877e0e9bbcd1c0b77860e4a4
|
4
|
+
data.tar.gz: debcf49e29dcda50d93cd31a2e9efec2332541dbe38f6bdaa488b450e5bb7d09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4661d9bfa18456e1c6fcfb837ef4c3acbc9e35dd983299201340dc077056e3f5632c39a8f718b285d66b4e3985aec23da68a56c8c278f0fc850f27626352bb0b
|
7
|
+
data.tar.gz: 688fae38d680fb9f1b133a627172d687c7fa6f56186039a88a5bfe238e396c5310359266580644d7719b981d6f3589ac884f6b6d88688c497c8516cbffd122cd
|
@@ -10,6 +10,8 @@ module ApiPagination
|
|
10
10
|
|
11
11
|
attr_accessor :base_url
|
12
12
|
|
13
|
+
attr_accessor :response_formats
|
14
|
+
|
13
15
|
def configure(&block)
|
14
16
|
yield self
|
15
17
|
end
|
@@ -20,6 +22,7 @@ module ApiPagination
|
|
20
22
|
@page_header = nil
|
21
23
|
@include_total = true
|
22
24
|
@base_url = nil
|
25
|
+
@response_formats = [:json, :xml]
|
23
26
|
end
|
24
27
|
|
25
28
|
['page', 'per_page'].each do |param_name|
|
@@ -50,11 +53,17 @@ module ApiPagination
|
|
50
53
|
end
|
51
54
|
|
52
55
|
def paginator
|
53
|
-
|
56
|
+
if instance_variable_defined? :@paginator
|
57
|
+
@paginator
|
58
|
+
else
|
59
|
+
set_paginator
|
60
|
+
end
|
54
61
|
end
|
55
62
|
|
56
63
|
def paginator=(paginator)
|
57
64
|
case paginator.to_sym
|
65
|
+
when :pagy
|
66
|
+
use_pagy
|
58
67
|
when :kaminari
|
59
68
|
use_kaminari
|
60
69
|
when :will_paginate
|
@@ -67,11 +76,12 @@ module ApiPagination
|
|
67
76
|
private
|
68
77
|
|
69
78
|
def set_paginator
|
70
|
-
|
79
|
+
conditions = [defined?(Pagy), defined?(Kaminari), defined?(WillPaginate::CollectionMethods)]
|
80
|
+
if conditions.compact.size > 1
|
71
81
|
Kernel.warn <<-WARNING
|
72
|
-
Warning: api-pagination relies on
|
73
|
-
currently active. If possible, you should remove one or the other. If
|
74
|
-
you _must_ configure api-pagination on your own. For example:
|
82
|
+
Warning: api-pagination relies on Pagy, Kaminari, or WillPaginate, but more than
|
83
|
+
one are currently active. If possible, you should remove one or the other. If
|
84
|
+
you can't, you _must_ configure api-pagination on your own. For example:
|
75
85
|
|
76
86
|
ApiPagination.configure do |config|
|
77
87
|
config.paginator = :kaminari
|
@@ -86,13 +96,19 @@ Kaminari.configure do |config|
|
|
86
96
|
end
|
87
97
|
|
88
98
|
WARNING
|
99
|
+
elsif defined?(Pagy)
|
100
|
+
use_pagy
|
89
101
|
elsif defined?(Kaminari)
|
90
|
-
|
102
|
+
use_kaminari
|
91
103
|
elsif defined?(WillPaginate::CollectionMethods)
|
92
|
-
|
104
|
+
use_will_paginate
|
93
105
|
end
|
94
106
|
end
|
95
107
|
|
108
|
+
def use_pagy
|
109
|
+
@paginator = :pagy
|
110
|
+
end
|
111
|
+
|
96
112
|
def use_kaminari
|
97
113
|
require 'kaminari/models/array_extension'
|
98
114
|
@paginator = :kaminari
|
data/lib/api-pagination/hooks.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
begin; require 'grape'; rescue LoadError; end
|
2
2
|
if defined?(Grape::API)
|
3
3
|
require 'grape/pagination'
|
4
|
-
|
4
|
+
|
5
|
+
klass = if Grape::VERSION >= '1.2.0' || defined?(Grape::API::Instance)
|
6
|
+
Grape::API::Instance
|
7
|
+
else
|
8
|
+
Grape::API
|
9
|
+
end
|
10
|
+
|
11
|
+
klass.send(:include, Grape::Pagination)
|
5
12
|
end
|
6
13
|
|
14
|
+
begin; require 'pagy'; rescue LoadError; end
|
7
15
|
begin; require 'kaminari'; rescue LoadError; end
|
8
16
|
begin; require 'will_paginate'; rescue LoadError; end
|
9
17
|
|
10
|
-
unless defined?(Kaminari) || defined?(WillPaginate::CollectionMethods)
|
18
|
+
unless defined?(Pagy) || defined?(Kaminari) || defined?(WillPaginate::CollectionMethods)
|
11
19
|
Kernel.warn <<-WARNING.gsub(/^\s{4}/, '')
|
12
|
-
Warning: api-pagination relies on either Kaminari or WillPaginate.
|
13
|
-
install
|
20
|
+
Warning: api-pagination relies on either Pagy, Kaminari, or WillPaginate.
|
21
|
+
Please install a paginator by adding one of the following to your Gemfile:
|
14
22
|
|
23
|
+
gem 'pagy'
|
15
24
|
gem 'kaminari'
|
16
25
|
gem 'will_paginate'
|
17
26
|
WARNING
|
data/lib/api-pagination.rb
CHANGED
@@ -9,6 +9,8 @@ module ApiPagination
|
|
9
9
|
options[:per_page] = options[:per_page].to_i
|
10
10
|
|
11
11
|
case ApiPagination.config.paginator
|
12
|
+
when :pagy
|
13
|
+
paginate_with_pagy(collection, options)
|
12
14
|
when :kaminari
|
13
15
|
paginate_with_kaminari(collection, options, options[:paginate_array_options] || {})
|
14
16
|
when :will_paginate
|
@@ -18,7 +20,9 @@ module ApiPagination
|
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def pages_from(collection)
|
23
|
+
def pages_from(collection, options = {})
|
24
|
+
return pagy_pages_from(collection) if ApiPagination.config.paginator == :pagy && collection.is_a?(Pagy)
|
25
|
+
|
22
26
|
{}.tap do |pages|
|
23
27
|
unless collection.first_page?
|
24
28
|
pages[:first] = 1
|
@@ -26,7 +30,7 @@ module ApiPagination
|
|
26
30
|
end
|
27
31
|
|
28
32
|
unless collection.last_page? || (ApiPagination.config.paginator == :kaminari && collection.out_of_range?)
|
29
|
-
pages[:last] = collection.total_pages
|
33
|
+
pages[:last] = collection.total_pages if ApiPagination.config.include_total
|
30
34
|
pages[:next] = collection.current_page + 1
|
31
35
|
end
|
32
36
|
end
|
@@ -34,6 +38,7 @@ module ApiPagination
|
|
34
38
|
|
35
39
|
def total_from(collection)
|
36
40
|
case ApiPagination.config.paginator
|
41
|
+
when :pagy then collection.count.to_s
|
37
42
|
when :kaminari then collection.total_count.to_s
|
38
43
|
when :will_paginate then collection.total_entries.to_s
|
39
44
|
end
|
@@ -41,6 +46,47 @@ module ApiPagination
|
|
41
46
|
|
42
47
|
private
|
43
48
|
|
49
|
+
def paginate_with_pagy(collection, options)
|
50
|
+
if Pagy::DEFAULT[:max_per_page] && options[:per_page] > Pagy::DEFAULT[:max_per_page]
|
51
|
+
options[:per_page] = Pagy::DEFAULT[:max_per_page]
|
52
|
+
elsif options[:per_page] <= 0
|
53
|
+
options[:per_page] = Pagy::DEFAULT[:items]
|
54
|
+
end
|
55
|
+
|
56
|
+
pagy = pagy_from(collection, options)
|
57
|
+
collection = if collection.respond_to?(:offset) && collection.respond_to?(:limit)
|
58
|
+
collection.offset(pagy.offset).limit(pagy.items)
|
59
|
+
else
|
60
|
+
collection[pagy.offset, pagy.items]
|
61
|
+
end
|
62
|
+
|
63
|
+
return [collection, pagy]
|
64
|
+
end
|
65
|
+
|
66
|
+
def pagy_from(collection, options)
|
67
|
+
if options[:count]
|
68
|
+
count = options[:count]
|
69
|
+
else
|
70
|
+
count = collection.is_a?(Array) ? collection.count : collection.count(:all)
|
71
|
+
end
|
72
|
+
|
73
|
+
Pagy.new(count: count, items: options[:per_page], page: options[:page])
|
74
|
+
end
|
75
|
+
|
76
|
+
def pagy_pages_from(pagy)
|
77
|
+
{}.tap do |pages|
|
78
|
+
unless pagy.page == 1
|
79
|
+
pages[:first] = 1
|
80
|
+
pages[:prev] = pagy.prev
|
81
|
+
end
|
82
|
+
|
83
|
+
unless pagy.page == pagy.pages
|
84
|
+
pages[:last] = pagy.pages if ApiPagination.config.include_total
|
85
|
+
pages[:next] = pagy.next
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
44
90
|
def paginate_with_kaminari(collection, options, paginate_array_options = {})
|
45
91
|
if Kaminari.config.max_per_page && options[:per_page] > Kaminari.config.max_per_page
|
46
92
|
options[:per_page] = Kaminari.config.max_per_page
|
@@ -48,8 +94,10 @@ module ApiPagination
|
|
48
94
|
options[:per_page] = get_default_per_page_for_kaminari(collection)
|
49
95
|
end
|
50
96
|
|
51
|
-
collection = Kaminari.paginate_array(collection, paginate_array_options) if collection.is_a?(Array)
|
52
|
-
collection.page(options[:page]).per(options[:per_page])
|
97
|
+
collection = Kaminari.paginate_array(collection, **paginate_array_options) if collection.is_a?(Array)
|
98
|
+
collection = collection.page(options[:page]).per(options[:per_page])
|
99
|
+
collection.without_count if !collection.is_a?(Array) && !ApiPagination.config.include_total
|
100
|
+
[collection, nil]
|
53
101
|
end
|
54
102
|
|
55
103
|
def paginate_with_will_paginate(collection, options)
|
@@ -57,35 +105,36 @@ module ApiPagination
|
|
57
105
|
options[:per_page] = default_per_page_for_will_paginate(collection)
|
58
106
|
end
|
59
107
|
|
60
|
-
if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
|
108
|
+
collection = if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
|
61
109
|
collection.paginate(options[:page], options[:per_page])
|
62
110
|
else
|
63
111
|
supported_options = [:page, :per_page, :total_entries]
|
64
112
|
options = options.dup.keep_if { |k,v| supported_options.include?(k.to_sym) }
|
65
113
|
collection.paginate(options)
|
66
114
|
end
|
115
|
+
|
116
|
+
[collection, nil]
|
67
117
|
end
|
68
118
|
|
69
119
|
def get_default_per_page_for_kaminari(collection)
|
70
120
|
default = Kaminari.config.default_per_page
|
71
|
-
|
72
|
-
rescue
|
73
|
-
default
|
121
|
+
extract_per_page_from_model(collection, :default_per_page) || default
|
74
122
|
end
|
75
123
|
|
76
124
|
def default_per_page_for_will_paginate(collection)
|
77
125
|
default = WillPaginate.per_page
|
78
|
-
|
79
|
-
rescue
|
80
|
-
default
|
126
|
+
extract_per_page_from_model(collection, :per_page) || default
|
81
127
|
end
|
82
128
|
|
83
|
-
def
|
84
|
-
if collection.respond_to?(:
|
85
|
-
collection.
|
129
|
+
def extract_per_page_from_model(collection, accessor)
|
130
|
+
klass = if collection.respond_to?(:klass)
|
131
|
+
collection.klass
|
86
132
|
else
|
87
133
|
collection.first.class
|
88
134
|
end
|
135
|
+
|
136
|
+
return unless klass.respond_to?(accessor)
|
137
|
+
klass.send(accessor)
|
89
138
|
end
|
90
139
|
end
|
91
140
|
end
|
data/lib/grape/pagination.rb
CHANGED
@@ -9,11 +9,11 @@ module Grape
|
|
9
9
|
:page => ApiPagination.config.page_param(params),
|
10
10
|
:per_page => [per_page, route_setting(:max_per_page)].compact.min
|
11
11
|
}
|
12
|
-
collection = ApiPagination.paginate(collection, options)
|
12
|
+
collection, pagy = ApiPagination.paginate(collection, options)
|
13
13
|
|
14
14
|
links = (header['Link'] || "").split(',').map(&:strip)
|
15
15
|
url = request.url.sub(/\?.*$/, '')
|
16
|
-
pages = ApiPagination.pages_from(collection)
|
16
|
+
pages = ApiPagination.pages_from(pagy || collection, options)
|
17
17
|
|
18
18
|
pages.each do |k, v|
|
19
19
|
old_params = Rack::Utils.parse_nested_query(request.query_string)
|
@@ -27,7 +27,7 @@ module Grape
|
|
27
27
|
include_total = ApiPagination.config.include_total
|
28
28
|
|
29
29
|
header 'Link', links.join(', ') unless links.empty?
|
30
|
-
header total_header, ApiPagination.total_from(collection).to_s if include_total
|
30
|
+
header total_header, ApiPagination.total_from(pagy || collection).to_s if include_total
|
31
31
|
header per_page_header, options[:per_page].to_s
|
32
32
|
header page_header, options[:page].to_s unless page_header.nil?
|
33
33
|
|
@@ -46,7 +46,7 @@ module Grape
|
|
46
46
|
params do
|
47
47
|
optional :page, :type => Integer, :default => 1,
|
48
48
|
:desc => 'Page of results to fetch.'
|
49
|
-
optional :per_page, :type => Integer,
|
49
|
+
optional :per_page, :type => Integer, :default => options[:per_page],
|
50
50
|
:desc => 'Number of results to return per page.',
|
51
51
|
:values => per_page_values
|
52
52
|
end
|
data/lib/rails/pagination.rb
CHANGED
@@ -8,11 +8,12 @@ module Rails
|
|
8
8
|
|
9
9
|
return _paginate_collection(collection, options) if collection
|
10
10
|
|
11
|
-
|
11
|
+
response_format = _discover_format(options)
|
12
|
+
|
13
|
+
collection = options[response_format]
|
12
14
|
collection = _paginate_collection(collection, options)
|
13
15
|
|
14
|
-
options[
|
15
|
-
options[:xml] = collection if options[:xml]
|
16
|
+
options[response_format] = collection if options[response_format]
|
16
17
|
|
17
18
|
render options
|
18
19
|
end
|
@@ -23,15 +24,21 @@ module Rails
|
|
23
24
|
|
24
25
|
private
|
25
26
|
|
27
|
+
def _discover_format(options)
|
28
|
+
for response_format in ApiPagination.config.response_formats
|
29
|
+
return response_format if options.key?(response_format)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
26
33
|
def _paginate_collection(collection, options={})
|
27
34
|
options[:page] = ApiPagination.config.page_param(params)
|
28
35
|
options[:per_page] ||= ApiPagination.config.per_page_param(params)
|
29
36
|
|
30
|
-
collection = ApiPagination.paginate(collection, options)
|
37
|
+
collection, pagy = ApiPagination.paginate(collection, options)
|
31
38
|
|
32
39
|
links = (headers['Link'] || '').split(',').map(&:strip)
|
33
40
|
url = base_url + request.path_info
|
34
|
-
pages = ApiPagination.pages_from(collection)
|
41
|
+
pages = ApiPagination.pages_from(pagy || collection, options)
|
35
42
|
|
36
43
|
pages.each do |k, v|
|
37
44
|
new_params = request.query_parameters.merge(:page => v)
|
@@ -46,7 +53,7 @@ module Rails
|
|
46
53
|
headers['Link'] = links.join(', ') unless links.empty?
|
47
54
|
headers[per_page_header] = options[:per_page].to_s
|
48
55
|
headers[page_header] = options[:page].to_s unless page_header.nil?
|
49
|
-
headers[total_header] = total_count(collection, options).to_s if include_total
|
56
|
+
headers[total_header] = total_count(pagy || collection, options).to_s if include_total
|
50
57
|
|
51
58
|
return collection
|
52
59
|
end
|
@@ -62,6 +69,5 @@ module Rails
|
|
62
69
|
def base_url
|
63
70
|
ApiPagination.config.base_url || request.base_url
|
64
71
|
end
|
65
|
-
|
66
72
|
end
|
67
73
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/active_record/foo'
|
3
|
+
require 'nulldb_rspec'
|
4
|
+
|
5
|
+
ActiveRecord::Base.establish_connection(
|
6
|
+
adapter: :nulldb,
|
7
|
+
schema: 'spec/support/active_record/schema.rb'
|
8
|
+
)
|
9
|
+
|
10
|
+
NullDB.configure { |ndb| def ndb.project_root; Dir.pwd; end; }
|
11
|
+
|
12
|
+
shared_examples 'produces_correct_sql' do
|
13
|
+
it 'produces correct sql for first page' do
|
14
|
+
allow(collection).to receive(:count).and_return(collection_size)
|
15
|
+
paginated_sql, _ = ApiPagination.paginate(collection, per_page: per_page)
|
16
|
+
expect(paginated_sql.to_sql).to eql(Foo.limit(per_page).offset(0).to_sql)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'ActiveRecord Support' do
|
21
|
+
let(:collection) { Foo.all }
|
22
|
+
let(:collection_size) { 50 }
|
23
|
+
let(:per_page) { 5 }
|
24
|
+
|
25
|
+
if ApiPagination.config.paginator == :will_paginate
|
26
|
+
require 'will_paginate/active_record'
|
27
|
+
end
|
28
|
+
|
29
|
+
context "pagination with #{ApiPagination.config.paginator}" do
|
30
|
+
include_examples 'produces_correct_sql'
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
if ApiPagination.config.paginator != :pagy
|
35
|
+
context 'reflections' do
|
36
|
+
it 'invokes the correct methods to determine type' do
|
37
|
+
expect(collection).to receive(:klass).at_least(:once)
|
38
|
+
.and_call_original
|
39
|
+
ApiPagination.paginate(collection)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not fail if table name is not snake cased class name' do
|
43
|
+
allow(collection).to receive(:table_name).and_return(SecureRandom.uuid)
|
44
|
+
expect { ApiPagination.paginate(collection) }.to_not raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
data/spec/api-pagination_spec.rb
CHANGED
@@ -2,63 +2,73 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe ApiPagination do
|
4
4
|
let(:collection) {(1..100).to_a}
|
5
|
+
let(:active_record_relation) {double("ActiveRecord_Relation").as_null_object}
|
5
6
|
let(:paginate_array_options) {{ total_count: 1000 }}
|
6
7
|
|
7
8
|
describe "#paginate" do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if ENV['PAGINATOR'].to_sym == :kaminari
|
10
|
+
context 'Using kaminari' do
|
11
|
+
describe '.paginate' do
|
12
|
+
it 'should accept paginate_array_options option' do
|
13
|
+
expect(Kaminari).to receive(:paginate_array)
|
14
|
+
.with(collection, **paginate_array_options)
|
15
|
+
.and_call_original
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
ApiPagination.paginate(
|
18
|
+
collection,
|
19
|
+
{
|
20
|
+
per_page: 30,
|
21
|
+
paginate_array_options: paginate_array_options
|
22
|
+
}
|
23
|
+
)
|
24
|
+
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
.with(collection, paginate_array_options)
|
20
|
-
.and_call_original
|
26
|
+
context 'configured not to include the total' do
|
27
|
+
before { ApiPagination.config.include_total = false }
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
context 'and paginating an array' do
|
30
|
+
it 'should not call without_count on the collection' do
|
31
|
+
expect(collection).to_not receive :without_count
|
32
|
+
ApiPagination.paginate(collection)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context 'and paginating an active record relation' do
|
36
|
+
it 'should call without_count on the relation' do
|
37
|
+
expect(active_record_relation).to receive :without_count
|
38
|
+
ApiPagination.paginate(active_record_relation)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
after { ApiPagination.config.include_total = true }
|
43
|
+
end
|
44
|
+
end
|
30
45
|
|
31
|
-
|
32
|
-
|
46
|
+
describe '.pages_from' do
|
47
|
+
subject { described_class.pages_from(collection) }
|
33
48
|
|
34
|
-
|
35
|
-
|
49
|
+
context 'on empty collection' do
|
50
|
+
let(:collection) { ApiPagination.paginate([], page: 1).first }
|
36
51
|
|
37
|
-
|
52
|
+
it { is_expected.to be_empty }
|
53
|
+
end
|
38
54
|
end
|
39
55
|
end
|
40
56
|
end
|
41
57
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
context 'passing in total_entries in options' do
|
52
|
-
it 'should set total_entries using the passed in value' do
|
53
|
-
paginated_collection = ApiPagination.paginate(collection, total_entries: 3000)
|
54
|
-
expect(paginated_collection.total_entries).to eq(3000)
|
58
|
+
if ENV['PAGINATOR'].to_sym == :will_paginate
|
59
|
+
context 'Using will_paginate' do
|
60
|
+
context 'passing in total_entries in options' do
|
61
|
+
it 'should set total_entries using the passed in value' do
|
62
|
+
paginated_collection = ApiPagination.paginate(collection, total_entries: 3000).first
|
63
|
+
expect(paginated_collection.total_entries).to eq(3000)
|
64
|
+
end
|
55
65
|
end
|
56
|
-
end
|
57
66
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
67
|
+
context 'passing in collection only' do
|
68
|
+
it 'should set total_entries using the size of the collection ' do
|
69
|
+
paginated_collection = ApiPagination.paginate(collection).first
|
70
|
+
expect(paginated_collection.total_entries).to eq(100)
|
71
|
+
end
|
62
72
|
end
|
63
73
|
end
|
64
74
|
end
|
data/spec/grape_spec.rb
CHANGED
@@ -5,8 +5,11 @@ require 'support/shared_examples/middle_page'
|
|
5
5
|
require 'support/shared_examples/last_page'
|
6
6
|
|
7
7
|
describe NumbersAPI do
|
8
|
+
it { is_expected.to be_kind_of(Grape::Pagination) }
|
9
|
+
|
8
10
|
describe 'GET #index' do
|
9
|
-
let(:
|
11
|
+
let(:link) { last_response.headers['Link'] }
|
12
|
+
let(:links) { link.split(', ') }
|
10
13
|
let(:total) { last_response.headers['Total'].to_i }
|
11
14
|
let(:per_page) { last_response.headers['Per-Page'].to_i }
|
12
15
|
|
@@ -139,6 +142,12 @@ describe NumbersAPI do
|
|
139
142
|
expect(last_response.header['Total']).to be_nil
|
140
143
|
end
|
141
144
|
|
145
|
+
it 'should not include a link with rel "last"' do
|
146
|
+
get '/numbers', count: 100
|
147
|
+
|
148
|
+
expect(link).to_not include('rel="last"')
|
149
|
+
end
|
150
|
+
|
142
151
|
after { ApiPagination.config.include_total = true }
|
143
152
|
end
|
144
153
|
|
data/spec/rails_spec.rb
CHANGED
@@ -8,7 +8,8 @@ describe NumbersController, :type => :controller do
|
|
8
8
|
before { request.host = 'example.org' }
|
9
9
|
|
10
10
|
describe 'GET #index' do
|
11
|
-
let(:
|
11
|
+
let(:link) { response.headers['Link'] }
|
12
|
+
let(:links) { link.split(', ') }
|
12
13
|
let(:total) { response.headers['Total'].to_i }
|
13
14
|
let(:per_page) { response.headers['Per-Page'].to_i }
|
14
15
|
|
@@ -134,6 +135,12 @@ describe NumbersController, :type => :controller do
|
|
134
135
|
expect(response.header['Total']).to be_nil
|
135
136
|
end
|
136
137
|
|
138
|
+
it 'should not include a link with rel "last"' do
|
139
|
+
get :index, params: { count: 100 }
|
140
|
+
|
141
|
+
expect(link).to_not include('rel="last"')
|
142
|
+
end
|
143
|
+
|
137
144
|
after { ApiPagination.config.include_total = true }
|
138
145
|
end
|
139
146
|
|
@@ -214,8 +221,11 @@ describe NumbersController, :type => :controller do
|
|
214
221
|
end
|
215
222
|
end
|
216
223
|
|
217
|
-
|
218
|
-
|
224
|
+
if ApiPagination.config.paginator.to_sym == :kaminari
|
225
|
+
context 'paginate array options' do
|
226
|
+
let(:paginate_array_total_count) { 300 }
|
227
|
+
let(:total_header) { 300 }
|
228
|
+
let(:count) { 50 }
|
219
229
|
let(:params) do
|
220
230
|
{
|
221
231
|
paginate_array_total_count: paginate_array_total_count,
|
@@ -223,80 +233,75 @@ describe NumbersController, :type => :controller do
|
|
223
233
|
}
|
224
234
|
end
|
225
235
|
|
226
|
-
|
236
|
+
it 'has a properly set Total header' do
|
227
237
|
get :index_with_paginate_array_options, params: params
|
228
238
|
|
229
239
|
expect(response.header['Total']).to be_kind_of(String)
|
230
240
|
expect(response.header['Total'].to_i).to eq total_header
|
231
241
|
end
|
232
242
|
end
|
243
|
+
end
|
233
244
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
245
|
+
if [:will_paginate, :kaminari].include?(ApiPagination.config.paginator.to_sym)
|
246
|
+
context 'default per page in model' do
|
247
|
+
before do
|
248
|
+
class Fixnum
|
249
|
+
@default_per_page = 6
|
250
|
+
@per_page = 6
|
241
251
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
252
|
+
class << self
|
253
|
+
attr_accessor :default_per_page, :per_page
|
254
|
+
end
|
255
|
+
end
|
246
256
|
end
|
247
|
-
end
|
248
257
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
ApiPagination.config.paginator = paginator
|
258
|
+
after do
|
259
|
+
class Fixnum
|
260
|
+
@default_per_page = 25
|
261
|
+
@per_page = 25
|
262
|
+
end
|
255
263
|
end
|
256
264
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
265
|
+
after :all do
|
266
|
+
class Fixnum
|
267
|
+
class << self
|
268
|
+
undef_method :default_per_page, :per_page
|
269
|
+
end
|
270
|
+
end
|
261
271
|
end
|
262
|
-
end
|
263
|
-
end
|
264
272
|
|
265
|
-
|
266
|
-
|
267
|
-
class Fixnum
|
268
|
-
@default_per_page = 6
|
269
|
-
@per_page = 6
|
273
|
+
it 'should use default per page from model' do
|
274
|
+
get :index_with_no_per_page, params: {count: 100}
|
270
275
|
|
271
|
-
|
272
|
-
attr_accessor :default_per_page, :per_page
|
273
|
-
end
|
276
|
+
expect(response.header['Per-Page']).to eq('6')
|
274
277
|
end
|
275
|
-
end
|
276
278
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
279
|
+
it 'should not fail if the model yields nil for per page' do
|
280
|
+
class Fixnum
|
281
|
+
@default_per_page = nil
|
282
|
+
@per_page = nil
|
283
|
+
end
|
283
284
|
|
284
|
-
|
285
|
-
get :index_with_no_per_page, params: {count: 100}
|
285
|
+
get :index_with_no_per_page, params: {count: 100}
|
286
286
|
|
287
|
-
|
287
|
+
expect(response.header['Per-Page']).to eq(
|
288
|
+
case ApiPagination.config.paginator
|
289
|
+
when :pagy then Pagy::DEFAULT[:items].to_s
|
290
|
+
when :kaminari then Kaminari.config.default_per_page.to_s
|
291
|
+
when :will_paginate then WillPaginate.per_page.to_s
|
292
|
+
end
|
293
|
+
)
|
294
|
+
end
|
288
295
|
end
|
296
|
+
end
|
289
297
|
|
298
|
+
context 'default per page in objects without paginator defaults' do
|
290
299
|
it 'should not fail if model does not respond to per page' do
|
291
|
-
class Fixnum
|
292
|
-
@default_per_page = nil
|
293
|
-
@per_page = nil
|
294
|
-
end
|
295
|
-
|
296
300
|
get :index_with_no_per_page, params: {count: 100}
|
297
301
|
|
298
302
|
expect(response.header['Per-Page']).to eq(
|
299
303
|
case ApiPagination.config.paginator
|
304
|
+
when :pagy then Pagy::DEFAULT[:items].to_s
|
300
305
|
when :kaminari then Kaminari.config.default_per_page.to_s
|
301
306
|
when :will_paginate then WillPaginate.per_page.to_s
|
302
307
|
end
|
data/spec/sequel_spec.rb
CHANGED
@@ -23,9 +23,8 @@ if ApiPagination.config.paginator == :will_paginate
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'returns a Sequel::Dataset' do
|
26
|
-
collection = ApiPagination.paginate(people)
|
26
|
+
collection = ApiPagination.paginate(people).first
|
27
27
|
expect(collection.kind_of?(Sequel::Dataset)).to be_truthy
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
data/spec/spec_helper.rb
CHANGED
@@ -3,14 +3,20 @@ require 'support/numbers_api'
|
|
3
3
|
require 'api-pagination'
|
4
4
|
|
5
5
|
if ENV['PAGINATOR'].nil?
|
6
|
-
warn
|
7
|
-
|
6
|
+
warn <<-WARNING
|
7
|
+
No PAGINATOR set. Defaulting to pagy.
|
8
|
+
|
9
|
+
To test against kaminari, run `PAGINATOR=kaminari bundle exec rspec`
|
10
|
+
To test against will_paginate, run `PAGINATOR=will_paginate bundle exec rspec`
|
11
|
+
WARNING
|
12
|
+
|
13
|
+
ENV['PAGINATOR'] = 'pagy'
|
8
14
|
end
|
9
15
|
|
10
16
|
require ENV['PAGINATOR']
|
11
17
|
ApiPagination.config.paginator = ENV['PAGINATOR'].to_sym
|
12
18
|
|
13
|
-
require 'will_paginate/array'
|
19
|
+
require 'will_paginate/array' if ENV['PAGINATOR'].to_sym == :will_paginate
|
14
20
|
|
15
21
|
RSpec.configure do |config|
|
16
22
|
config.include Rack::Test::Methods
|
@@ -1,10 +1,10 @@
|
|
1
1
|
shared_examples 'an endpoint with a first page' do
|
2
2
|
it 'should not give a link with rel "first"' do
|
3
|
-
expect(
|
3
|
+
expect(link).not_to include('rel="first"')
|
4
4
|
end
|
5
5
|
|
6
6
|
it 'should not give a link with rel "prev"' do
|
7
|
-
expect(
|
7
|
+
expect(link).not_to include('rel="prev"')
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should give a link with rel "last"' do
|
@@ -1,10 +1,10 @@
|
|
1
1
|
shared_examples 'an endpoint with a last page' do
|
2
2
|
it 'should not give a link with rel "last"' do
|
3
|
-
expect(
|
3
|
+
expect(link).not_to include('rel="last"')
|
4
4
|
end
|
5
5
|
|
6
6
|
it 'should not give a link with rel "next"' do
|
7
|
-
expect(
|
7
|
+
expect(link).not_to include('rel="next"')
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'should give a link with rel "first"' do
|
metadata
CHANGED
@@ -1,85 +1,171 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-pagination
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Celis
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: kaminari
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.1
|
20
23
|
type: :development
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '1.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.2.1
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: pagy
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.1'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 5.1.2
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '5.1'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 5.1.2
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: will_paginate
|
29
55
|
requirement: !ruby/object:Gem::Requirement
|
30
56
|
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '3.3'
|
31
60
|
- - ">="
|
32
61
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
62
|
+
version: 3.3.1
|
34
63
|
type: :development
|
35
64
|
prerelease: false
|
36
65
|
version_requirements: !ruby/object:Gem::Requirement
|
37
66
|
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.3'
|
38
70
|
- - ">="
|
39
71
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
72
|
+
version: 3.3.1
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rspec
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '3.10'
|
80
|
+
type: :development
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '3.10'
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: grape
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - "~>"
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.6'
|
94
|
+
type: :development
|
95
|
+
prerelease: false
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '1.6'
|
41
101
|
- !ruby/object:Gem::Dependency
|
42
102
|
name: railties
|
43
103
|
requirement: !ruby/object:Gem::Requirement
|
44
104
|
requirements:
|
105
|
+
- - "~>"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '6.1'
|
45
108
|
- - ">="
|
46
109
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
110
|
+
version: 6.1.4.1
|
48
111
|
type: :development
|
49
112
|
prerelease: false
|
50
113
|
version_requirements: !ruby/object:Gem::Requirement
|
51
114
|
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '6.1'
|
52
118
|
- - ">="
|
53
119
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
120
|
+
version: 6.1.4.1
|
55
121
|
- !ruby/object:Gem::Dependency
|
56
122
|
name: actionpack
|
57
123
|
requirement: !ruby/object:Gem::Requirement
|
58
124
|
requirements:
|
125
|
+
- - "~>"
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '6.1'
|
59
128
|
- - ">="
|
60
129
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
130
|
+
version: 6.1.4.1
|
62
131
|
type: :development
|
63
132
|
prerelease: false
|
64
133
|
version_requirements: !ruby/object:Gem::Requirement
|
65
134
|
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '6.1'
|
66
138
|
- - ">="
|
67
139
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
140
|
+
version: 6.1.4.1
|
69
141
|
- !ruby/object:Gem::Dependency
|
70
142
|
name: sequel
|
71
143
|
requirement: !ruby/object:Gem::Requirement
|
72
144
|
requirements:
|
73
|
-
- - "
|
145
|
+
- - "~>"
|
74
146
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
147
|
+
version: '5.49'
|
76
148
|
type: :development
|
77
149
|
prerelease: false
|
78
150
|
version_requirements: !ruby/object:Gem::Requirement
|
79
151
|
requirements:
|
80
|
-
- - "
|
152
|
+
- - "~>"
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '5.49'
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: activerecord-nulldb-adapter
|
157
|
+
requirement: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - "~>"
|
81
160
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
161
|
+
version: 0.7.0
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - "~>"
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 0.7.0
|
83
169
|
description: Link header pagination for Rails and Grape APIs
|
84
170
|
email:
|
85
171
|
- me@davidcel.is
|
@@ -94,11 +180,14 @@ files:
|
|
94
180
|
- lib/api-pagination/version.rb
|
95
181
|
- lib/grape/pagination.rb
|
96
182
|
- lib/rails/pagination.rb
|
183
|
+
- spec/active_record_spec.rb
|
97
184
|
- spec/api-pagination_spec.rb
|
98
185
|
- spec/grape_spec.rb
|
99
186
|
- spec/rails_spec.rb
|
100
187
|
- spec/sequel_spec.rb
|
101
188
|
- spec/spec_helper.rb
|
189
|
+
- spec/support/active_record/foo.rb
|
190
|
+
- spec/support/active_record/schema.rb
|
102
191
|
- spec/support/numbers_api.rb
|
103
192
|
- spec/support/numbers_controller.rb
|
104
193
|
- spec/support/shared_examples/existing_headers.rb
|
@@ -109,35 +198,37 @@ homepage: https://github.com/davidcelis/api-pagination
|
|
109
198
|
licenses:
|
110
199
|
- MIT
|
111
200
|
metadata: {}
|
112
|
-
post_install_message:
|
201
|
+
post_install_message:
|
113
202
|
rdoc_options: []
|
114
203
|
require_paths:
|
115
204
|
- lib
|
116
205
|
required_ruby_version: !ruby/object:Gem::Requirement
|
117
206
|
requirements:
|
118
|
-
- - "
|
207
|
+
- - ">"
|
119
208
|
- !ruby/object:Gem::Version
|
120
|
-
version: '
|
209
|
+
version: '2.7'
|
121
210
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
211
|
requirements:
|
123
212
|
- - ">="
|
124
213
|
- !ruby/object:Gem::Version
|
125
214
|
version: '0'
|
126
215
|
requirements: []
|
127
|
-
|
128
|
-
|
129
|
-
signing_key:
|
216
|
+
rubygems_version: 3.2.22
|
217
|
+
signing_key:
|
130
218
|
specification_version: 4
|
131
219
|
summary: Link header pagination for Rails and Grape APIs. Don't use the request body.
|
132
220
|
test_files:
|
133
|
-
- spec/
|
221
|
+
- spec/active_record_spec.rb
|
134
222
|
- spec/api-pagination_spec.rb
|
223
|
+
- spec/grape_spec.rb
|
224
|
+
- spec/rails_spec.rb
|
135
225
|
- spec/sequel_spec.rb
|
226
|
+
- spec/spec_helper.rb
|
227
|
+
- spec/support/active_record/foo.rb
|
228
|
+
- spec/support/active_record/schema.rb
|
136
229
|
- spec/support/numbers_api.rb
|
137
230
|
- spec/support/numbers_controller.rb
|
231
|
+
- spec/support/shared_examples/existing_headers.rb
|
138
232
|
- spec/support/shared_examples/first_page.rb
|
139
|
-
- spec/support/shared_examples/middle_page.rb
|
140
233
|
- spec/support/shared_examples/last_page.rb
|
141
|
-
- spec/support/shared_examples/
|
142
|
-
- spec/grape_spec.rb
|
143
|
-
- spec/rails_spec.rb
|
234
|
+
- spec/support/shared_examples/middle_page.rb
|