api-pagination 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c576834fa7e045c8227f8ac9eb178dedc241533
4
- data.tar.gz: 6c620fe958d4b4c87e8df6d28faf4a24c500e2e4
3
+ metadata.gz: c9b48cddb3e2509595aa30dd7c836cde57a4a690
4
+ data.tar.gz: 3fc62e5440d43d007d4b54d415be8e6f0011dd3d
5
5
  SHA512:
6
- metadata.gz: b22e56913a84c875f177f1e3acb401bcb4decf175ed31023efe6cfdc33782d7d67e1b84e85cf8f0a9454181aa2284047d24c0f8c43b9b7328266c8e28500beaf
7
- data.tar.gz: c23680b6346a5dab04bbecfd6a18ec4d54190f2907b488936743108eaf4657b66d02724ab64feab558f731ca80e1287784c3fa2c7e7634df163e44b167a3c38a
6
+ metadata.gz: 5d74a30037b8433a10c00e127cdded4ff3c60164a1beb45ea5c66d8532cb2ed43971e73f6a32d5e886f09fa0a10d6f8013d79a1e067a44acf5746a57fcdd7744
7
+ data.tar.gz: da3767bee2071943af66c7bc424d0a4b31504fc03d8d8109b08968adc8e7c6389a6f6f0f26de874a62961c1cb473aad3c5a24dcfab997f2fef1b42cc9981ebc1
@@ -1,24 +1,20 @@
1
- require 'api-pagination/hooks'
1
+ require 'api-pagination/configuration'
2
2
  require 'api-pagination/version'
3
3
 
4
4
  module ApiPagination
5
5
  class << self
6
- attr_reader :paginator
7
-
8
6
  def paginate(collection, options = {})
9
- options[:page] ||= 1
10
- options[:per_page] = (options[:per_page].to_i <= 0 ? 25 : options[:per_page])
7
+ options[:page] = options[:page].to_i
8
+ options[:page] = 1 if options[:page] <= 0
9
+ options[:per_page] = options[:per_page].to_i
11
10
 
12
- case ApiPagination.paginator
11
+ case ApiPagination.config.paginator
13
12
  when :kaminari
14
- collection = Kaminari.paginate_array(collection) if collection.is_a?(Array)
15
- collection.page(options[:page]).per(options[:per_page])
13
+ paginate_with_kaminari(collection, options)
16
14
  when :will_paginate
17
- if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
18
- collection.paginate(options[:page], options[:per_page])
19
- else
20
- collection.paginate(:page => options[:page], :per_page => options[:per_page])
21
- end
15
+ paginate_with_will_paginate(collection, options)
16
+ else
17
+ raise StandardError, "Unknown paginator: #{ApiPagination.config.paginator}"
22
18
  end
23
19
  end
24
20
 
@@ -37,10 +33,50 @@ module ApiPagination
37
33
  end
38
34
 
39
35
  def total_from(collection)
40
- case ApiPagination.paginator
36
+ case ApiPagination.config.paginator
41
37
  when :kaminari then collection.total_count.to_s
42
38
  when :will_paginate then collection.total_entries.to_s
43
39
  end
44
40
  end
41
+
42
+ def paginator
43
+ warn "[DEPRECATION] ApiPagination.paginator is deprecated. Please use ApiPagination.config.paginator"
44
+ config.paginator
45
+ end
46
+
47
+ def per_page_header
48
+ warn "[DEPRECATION] ApiPagination.paginator is deprecated. Please use ApiPagination.config.per_page_header"
49
+ config.per_page_header
50
+ end
51
+
52
+ def total_header
53
+ warn "[DEPRECATION] ApiPagination.paginator is deprecated. Please use ApiPagination.config.total_header"
54
+ config.total_header
55
+ end
56
+
57
+ private
58
+
59
+ def paginate_with_kaminari(collection, options)
60
+ if Kaminari.config.max_per_page && options[:per_page] > Kaminari.config.max_per_page
61
+ options[:per_page] = Kaminari.config.max_per_page
62
+ elsif options[:per_page] <= 0
63
+ options[:per_page] = Kaminari.config.default_per_page
64
+ end
65
+
66
+ collection = Kaminari.paginate_array(collection) if collection.is_a?(Array)
67
+ collection.page(options[:page]).per(options[:per_page])
68
+ end
69
+
70
+ def paginate_with_will_paginate(collection, options)
71
+ options[:per_page] = WillPaginate.per_page if options[:per_page] <= 0
72
+
73
+ if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
74
+ collection.paginate(options[:page], options[:per_page])
75
+ else
76
+ collection.paginate(:page => options[:page], :per_page => options[:per_page])
77
+ end
78
+ end
45
79
  end
46
80
  end
81
+
82
+ require 'api-pagination/hooks'
@@ -0,0 +1,97 @@
1
+ module ApiPagination
2
+ class Configuration
3
+ attr_reader :paginator
4
+
5
+ attr_accessor :total_header
6
+
7
+ attr_accessor :per_page_header
8
+
9
+ def configure(&block)
10
+ yield self
11
+ end
12
+
13
+ def initialize
14
+ @total_header = 'Total'
15
+ @per_page_header = 'Per-Page'
16
+ set_paginator
17
+ end
18
+
19
+ def paginator=(paginator)
20
+ case paginator
21
+ when :kaminari
22
+ use_kaminari
23
+ when :will_paginate
24
+ use_will_paginate
25
+ else
26
+ raise StandardError, "Unknown paginator: #{paginator}"
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def set_paginator
33
+ if defined?(Kaminari) && defined?(WillPaginate::CollectionMethods)
34
+ Kernel.warn <<-WARNING
35
+ Warning: api-pagination relies on either Kaminari or WillPaginate, but both are
36
+ currently active. If possible, you should remove one or the other. If you can't,
37
+ you must configure api-pagination on your own. For example:
38
+
39
+ ApiPagination.configure do |config|
40
+ config.paginator = Kaminari
41
+ end
42
+
43
+ WARNING
44
+ elsif defined?(Kaminari)
45
+ use_kaminari and return
46
+ elsif defined?(WillPaginate::CollectionMethods)
47
+ use_will_paginate and return
48
+ end
49
+
50
+ begin
51
+ require 'kaminari'
52
+ use_kaminari and return
53
+ rescue LoadError
54
+ end
55
+
56
+ begin
57
+ require 'will_paginate'
58
+ use_will_paginate and return
59
+ rescue LoadError
60
+ end
61
+
62
+ Kernel.warn <<-WARNING
63
+ Warning: api-pagination relies on either Kaminari or WillPaginate. Please
64
+ install either dependency by adding one of the following to your Gemfile:
65
+
66
+ gem 'kaminari'
67
+ gem 'will_paginate'
68
+
69
+ WARNING
70
+ end
71
+
72
+ def use_kaminari
73
+ require 'kaminari/models/array_extension'
74
+ @paginator = :kaminari
75
+ end
76
+
77
+ def use_will_paginate
78
+ WillPaginate::CollectionMethods.module_eval do
79
+ def first_page?() !previous_page end
80
+ def last_page?() !next_page end
81
+ end
82
+
83
+ @paginator = :will_paginate
84
+ end
85
+ end
86
+
87
+ class << self
88
+ def configure
89
+ yield config
90
+ end
91
+
92
+ def config
93
+ @config ||= Configuration.new
94
+ end
95
+ alias :configuration :config
96
+ end
97
+ end
@@ -1,49 +1,17 @@
1
- module ApiPagination
2
- class Hooks
3
- def self.init
4
- begin; require 'rails'; rescue LoadError; end
5
- if defined?(ActionController::Base)
6
- require 'rails/pagination'
7
- ActionController::Base.send(:include, Rails::Pagination)
8
- end
9
-
10
- begin; require 'rails-api'; rescue LoadError; end
11
- if defined?(ActionController::API)
12
- require 'rails/pagination'
13
- ActionController::API.send(:include, Rails::Pagination)
14
- end
15
-
16
- begin; require 'grape'; rescue LoadError; end
17
- if defined?(Grape::API)
18
- require 'grape/pagination'
19
- Grape::API.send(:include, Grape::Pagination)
20
- end
21
-
22
- begin; require 'will_paginate'; rescue LoadError; end
23
- if defined?(WillPaginate::CollectionMethods)
24
- WillPaginate::CollectionMethods.module_eval do
25
- def first_page?() !previous_page end
26
- def last_page?() !next_page end
27
- end
28
-
29
- ApiPagination.instance_variable_set(:@paginator, :will_paginate)
30
- end
31
-
32
- begin; require 'kaminari'; rescue LoadError; end
33
- if defined?(Kaminari)
34
- ApiPagination.instance_variable_set(:@paginator, :kaminari)
35
- end
36
-
37
- STDERR.puts <<-EOC unless defined?(Kaminari) || defined?(WillPaginate)
38
- Warning: api-pagination relies on either Kaminari or WillPaginate. Please
39
- install either dependency by adding one of the following to your Gemfile:
40
-
41
- gem 'kaminari'
42
- gem 'will_paginate'
1
+ begin; require 'rails'; rescue LoadError; end
2
+ if defined?(ActionController::Base)
3
+ require 'rails/pagination'
4
+ ActionController::Base.send(:include, Rails::Pagination)
5
+ end
43
6
 
44
- EOC
45
- end
46
- end
7
+ begin; require 'rails-api'; rescue LoadError; end
8
+ if defined?(ActionController::API)
9
+ require 'rails/pagination'
10
+ ActionController::API.send(:include, Rails::Pagination)
47
11
  end
48
12
 
49
- ApiPagination::Hooks.init
13
+ begin; require 'grape'; rescue LoadError; end
14
+ if defined?(Grape::API)
15
+ require 'grape/pagination'
16
+ Grape::API.send(:include, Grape::Pagination)
17
+ end
@@ -1,7 +1,7 @@
1
1
  module ApiPagination
2
2
  class Version
3
3
  MAJOR = 3
4
- MINOR = 1
4
+ MINOR = 2
5
5
  PATCH = 0
6
6
 
7
7
  def self.to_s
@@ -3,9 +3,10 @@ module Grape
3
3
  def self.included(base)
4
4
  Grape::Endpoint.class_eval do
5
5
  def paginate(collection)
6
+ per_page = params[:per_page] || route_setting(:per_page)
6
7
  options = {
7
8
  :page => params[:page],
8
- :per_page => (params[:per_page] || settings[:per_page])
9
+ :per_page => [per_page, route_setting(:max_per_page)].compact.min
9
10
  }
10
11
  collection = ApiPagination.paginate(collection, options)
11
12
 
@@ -19,8 +20,12 @@ module Grape
19
20
  links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
20
21
  end
21
22
 
22
- header 'Link', links.join(', ') unless links.empty?
23
- header 'Total', ApiPagination.total_from(collection)
23
+ total_header = ApiPagination.config.total_header
24
+ per_page_header = ApiPagination.config.per_page_header
25
+
26
+ header 'Link', links.join(', ') unless links.empty?
27
+ header total_header, ApiPagination.total_from(collection)
28
+ header per_page_header, options[:per_page].to_s
24
29
 
25
30
  return collection
26
31
  end
@@ -28,7 +33,8 @@ module Grape
28
33
 
29
34
  base.class_eval do
30
35
  def self.paginate(options = {})
31
- set :per_page, (options[:per_page] || 25)
36
+ route_setting :per_page, (options[:per_page] || 25)
37
+ route_setting :max_per_page, options[:max_per_page]
32
38
  params do
33
39
  optional :page, :type => Integer, :default => 1,
34
40
  :desc => 'Page of results to fetch.'
@@ -39,11 +39,14 @@ module Rails
39
39
  links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
40
40
  end
41
41
 
42
- headers['Link'] = links.join(', ') unless links.empty?
43
- headers['Total'] = ApiPagination.total_from(collection)
42
+ total_header = ApiPagination.config.total_header
43
+ per_page_header = ApiPagination.config.per_page_header
44
+
45
+ headers['Link'] = links.join(', ') unless links.empty?
46
+ headers[total_header] = ApiPagination.total_from(collection)
47
+ headers[per_page_header] = options[:per_page].to_s
44
48
 
45
49
  return collection
46
50
  end
47
51
  end
48
52
  end
49
-
@@ -8,9 +8,10 @@ describe NumbersAPI do
8
8
  describe 'GET #index' do
9
9
  let(:links) { last_response.headers['Link'].split(', ') }
10
10
  let(:total) { last_response.headers['Total'].to_i }
11
+ let(:per_page) { last_response.headers['Per-Page'].to_i }
11
12
 
12
13
  context 'without enough items to give more than one page' do
13
- before { get :numbers, :count => 10 }
14
+ before { get '/numbers', :count => 10 }
14
15
 
15
16
  it 'should not paginate' do
16
17
  expect(last_response.headers.keys).not_to include('Link')
@@ -20,6 +21,10 @@ describe NumbersAPI do
20
21
  expect(total).to eq(10)
21
22
  end
22
23
 
24
+ it 'should give a Per-Page header' do
25
+ expect(per_page).to eq(10)
26
+ end
27
+
23
28
  it 'should list all numbers in the response body' do
24
29
  body = '[1,2,3,4,5,6,7,8,9,10]'
25
30
  expect(last_response.body).to eq(body)
@@ -27,29 +32,72 @@ describe NumbersAPI do
27
32
  end
28
33
 
29
34
  context 'with existing Link headers' do
30
- before { get :numbers, :count => 30, :with_headers => true }
35
+ before { get '/numbers', :count => 30, :with_headers => true }
31
36
 
32
37
  it_behaves_like 'an endpoint with existing Link headers'
33
38
  end
34
39
 
35
40
  context 'with enough items to paginate' do
36
41
  context 'when on the first page' do
37
- before { get :numbers, :count => 100 }
42
+ before { get '/numbers', :count => 100 }
38
43
 
39
44
  it_behaves_like 'an endpoint with a first page'
40
45
  end
41
46
 
42
47
  context 'when on the last page' do
43
- before { get :numbers, :count => 100, :page => 10 }
48
+ before { get '/numbers', :count => 100, :page => 10 }
44
49
 
45
50
  it_behaves_like 'an endpoint with a last page'
46
51
  end
47
52
 
48
53
  context 'when somewhere comfortably in the middle' do
49
- before { get :numbers, :count => 100, :page => 2 }
54
+ before { get '/numbers', :count => 100, :page => 2 }
50
55
 
51
56
  it_behaves_like 'an endpoint with a middle page'
52
57
  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
68
+ end
69
+
70
+ context 'with custom response headers' do
71
+ before do
72
+ ApiPagination.config.total_header = 'X-Total-Count'
73
+ ApiPagination.config.per_page_header = 'X-Per-Page'
74
+
75
+ get '/numbers', count: 10
76
+ end
77
+
78
+ after do
79
+ ApiPagination.config.total_header = 'Total'
80
+ ApiPagination.config.per_page_header = 'Per-Page'
81
+ end
82
+
83
+ let(:total) { last_response.header['X-Total-Count'].to_i }
84
+ let(:per_page) { last_response.header['X-Per-Page'].to_i }
85
+
86
+ it 'should give a X-Total-Count header' do
87
+ headers_keys = last_response.headers.keys
88
+
89
+ expect(headers_keys).not_to include('Total')
90
+ expect(headers_keys).to include('X-Total-Count')
91
+ expect(total).to eq(10)
92
+ end
93
+
94
+ it 'should give a X-Per-Page header' do
95
+ headers_keys = last_response.headers.keys
96
+
97
+ expect(headers_keys).not_to include('Per-Page')
98
+ expect(headers_keys).to include('X-Per-Page')
99
+ expect(per_page).to eq(10)
100
+ end
53
101
  end
54
102
  end
55
103
  end
@@ -10,6 +10,7 @@ describe NumbersController, :type => :controller do
10
10
  describe 'GET #index' do
11
11
  let(:links) { response.headers['Link'].split(', ') }
12
12
  let(:total) { response.headers['Total'].to_i }
13
+ let(:per_page) { response.headers['Per-Page'].to_i }
13
14
 
14
15
  context 'without enough items to give more than one page' do
15
16
  before { get :index, :count => 10 }
@@ -22,6 +23,10 @@ describe NumbersController, :type => :controller do
22
23
  expect(total).to eq(10)
23
24
  end
24
25
 
26
+ it 'should give a Per-Page header' do
27
+ expect(per_page).to eq(10)
28
+ end
29
+
25
30
  it 'should list all numbers in the response body' do
26
31
  body = '[1,2,3,4,5,6,7,8,9,10]'
27
32
  expect(response.body).to eq(body)
@@ -63,10 +68,38 @@ describe NumbersController, :type => :controller do
63
68
  expect(response.body).to eq(json)
64
69
  end
65
70
  end
66
- end
67
- end
68
71
 
72
+ context 'with custom response headers' do
73
+ before do
74
+ ApiPagination.config.total_header = 'X-Total-Count'
75
+ ApiPagination.config.per_page_header = 'X-Per-Page'
69
76
 
77
+ get :index, count: 10
78
+ end
79
+
80
+ after do
81
+ ApiPagination.config.total_header = 'Total'
82
+ ApiPagination.config.per_page_header = 'Per-Page'
83
+ end
70
84
 
85
+ let(:total) { response.header['X-Total-Count'].to_i }
86
+ let(:per_page) { response.header['X-Per-Page'].to_i }
71
87
 
88
+ it 'should give a X-Total-Count header' do
89
+ headers_keys = response.headers.keys
72
90
 
91
+ expect(headers_keys).not_to include('Total')
92
+ expect(headers_keys).to include('X-Total-Count')
93
+ expect(total).to eq(10)
94
+ end
95
+
96
+ it 'should give a X-Per-Page header' do
97
+ headers_keys = response.headers.keys
98
+
99
+ expect(headers_keys).not_to include('Per-Page')
100
+ expect(headers_keys).to include('X-Per-Page')
101
+ expect(per_page).to eq(10)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- if ApiPagination.paginator == :will_paginate
3
+ if ApiPagination.config.paginator == :will_paginate
4
4
  require 'sqlite3'
5
5
  require 'sequel'
6
6
  require 'will_paginate/sequel'
@@ -3,17 +3,15 @@ require 'support/numbers_api'
3
3
  require 'api-pagination'
4
4
 
5
5
  if ENV['PAGINATOR']
6
- ApiPagination.instance_variable_set(:@paginator, ENV['PAGINATOR'].to_sym)
6
+ require ENV['PAGINATOR']
7
+ ApiPagination.config.paginator = ENV['PAGINATOR'].to_sym
7
8
  else
8
9
  warn 'No PAGINATOR set. Defaulting to kaminari. To test against will_paginate, run `PAGINATOR=will_paginate bundle exec rspec`'
9
- ApiPagination.instance_variable_set(:@paginator, :kaminari)
10
+ require 'kaminari'
11
+ ApiPagination.config.paginator = :kaminari
10
12
  end
11
13
 
12
- if ApiPagination.paginator == :kaminari
13
- Kaminari::Hooks.init
14
- elsif ApiPagination.paginator == :will_paginate
15
- require 'will_paginate/array'
16
- end
14
+ require 'will_paginate/array'
17
15
 
18
16
  RSpec.configure do |config|
19
17
  config.include Rack::Test::Methods
@@ -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 => 10
8
+ paginate :per_page => 10, :max_per_page => 25
9
9
  params do
10
10
  requires :count, :type => Integer
11
11
  optional :with_headers, :default => false, :type => Boolean
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: 3.1.0
4
+ version: 3.2.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-09-21 00:00:00.000000000 Z
11
+ date: 2015-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.10.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.10.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: railties
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +88,7 @@ extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - lib/api-pagination.rb
91
+ - lib/api-pagination/configuration.rb
91
92
  - lib/api-pagination/hooks.rb
92
93
  - lib/api-pagination/version.rb
93
94
  - lib/grape/pagination.rb
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  version: '0'
123
124
  requirements: []
124
125
  rubyforge_project:
125
- rubygems_version: 2.2.2
126
+ rubygems_version: 2.4.5
126
127
  signing_key:
127
128
  specification_version: 4
128
129
  summary: Link header pagination for Rails and Grape APIs. Don't use the request body.
@@ -137,3 +138,4 @@ test_files:
137
138
  - spec/support/shared_examples/first_page.rb
138
139
  - spec/support/shared_examples/last_page.rb
139
140
  - spec/support/shared_examples/middle_page.rb
141
+ has_rdoc: