api-pagination 4.0.0 → 4.1.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: 44223a7f3dfd7705282d1c93abc09c04e55b5cd0
4
- data.tar.gz: dbcb589c144ae07bc298b90da5de60eb627b021c
3
+ metadata.gz: 1c24620f87870aa113532b4e5b68ac39d48196a8
4
+ data.tar.gz: c5b6ebc23e6db9248dcedfa86ae6def3ddd91b0f
5
5
  SHA512:
6
- metadata.gz: fe8b5fb947b1bf135a49283f4ca86f3b3c5131b564c2492105f358f5864eabf6f0509f5d54594c94e8dfff9a1f2a02fe4573c23dcd32625d08a2978b7be41bc8
7
- data.tar.gz: df50ee727c766cb82b2f23efeae78f1c91ea63aa790e0459f5645ccaf48c52145904896592ec596990b8c40a4b542530613f4cc0e151b0dbee500254d06a4b72
6
+ metadata.gz: fe232ed61e1987363a1da75f58480f1f5d0485364180c08b9a3ce3ebf5079a1ec34c5448b43e2e94b019b29a72b820539371274cefde5835e96476204c9c3e70
7
+ data.tar.gz: 33bbf495e991be605c548d6f20c804a79339f1a45ec47eca36c469fb544389e6918daa7f9eb9fb9cfb8ef567a6be2945379b1168cfe46d780f499fb7a61e69ac
@@ -1,33 +1,20 @@
1
+ require 'api-pagination/configuration'
1
2
  require 'api-pagination/version'
2
3
 
3
4
  module ApiPagination
4
5
  class << self
5
- attr_reader :paginator
6
-
7
6
  def paginate(collection, options = {})
8
7
  options[:page] = options[:page].to_i
9
8
  options[:page] = 1 if options[:page] <= 0
10
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
- if Kaminari.config.max_per_page && options[:per_page] > Kaminari.config.max_per_page
15
- options[:per_page] = Kaminari.config.max_per_page
16
- elsif options[:per_page] <= 0
17
- options[:per_page] = Kaminari.config.default_per_page
18
- end
19
- collection = Kaminari.paginate_array(collection) if collection.is_a?(Array)
20
- collection.page(options[:page]).per(options[:per_page])
13
+ paginate_with_kaminari(collection, options)
21
14
  when :will_paginate
22
- options[:per_page] = WillPaginate.per_page if options[:per_page] <= 0
23
-
24
- if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
25
- collection.paginate(options[:page], options[:per_page])
26
- else
27
- collection.paginate(:page => options[:page], :per_page => options[:per_page])
28
- end
15
+ paginate_with_will_paginate(collection, options)
29
16
  else
30
- raise StandardError, "Unknown paginator: #{ApiPagination.paginator}"
17
+ raise StandardError, "Unknown paginator: #{ApiPagination.config.paginator}"
31
18
  end
32
19
  end
33
20
 
@@ -46,11 +33,49 @@ module ApiPagination
46
33
  end
47
34
 
48
35
  def total_from(collection)
49
- case ApiPagination.paginator
36
+ case ApiPagination.config.paginator
50
37
  when :kaminari then collection.total_count.to_s
51
38
  when :will_paginate then collection.total_entries.to_s
52
39
  end
53
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
54
79
  end
55
80
  end
56
81
 
@@ -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,77 +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
- # Kaminari and will_paginate conflict with each other, so we should check
23
- # to see if either is already active before attempting to load them.
24
- if defined?(Kaminari) && defined?(WillPaginate::CollectionMethods)
25
- STDERR.puts <<-EOC
26
- Warning: api-pagination relies on either Kaminari or WillPaginate, but these
27
- gems conflict. Please ensure only one of them is active at any given time.
28
-
29
- EOC
30
- return
31
- elsif defined?(Kaminari)
32
- initialize_kaminari! and return
33
- elsif defined?(WillPaginate::CollectionMethods)
34
- initialize_will_paginate! and return
35
- end
36
- # If neither is loaded, we can safely attempt these requires.
37
- unless ApiPagination.paginator == :will_paginate
38
- begin
39
- require 'kaminari'
40
- initialize_kaminari! and return
41
- rescue LoadError
42
- end
43
- end
44
-
45
- begin
46
- require 'will_paginate'
47
- initialize_will_paginate! and return
48
- rescue LoadError
49
- end
50
-
51
- STDERR.puts <<-EOC
52
- Warning: api-pagination relies on either Kaminari or WillPaginate. Please
53
- install either dependency by adding one of the following to your Gemfile:
54
-
55
- gem 'kaminari'
56
- gem 'will_paginate'
57
-
58
- EOC
59
- end
60
-
61
- def self.initialize_kaminari!
62
- require 'kaminari/models/array_extension'
63
- ApiPagination.instance_variable_set(:@paginator, :kaminari)
64
- end
65
-
66
- def self.initialize_will_paginate!
67
- WillPaginate::CollectionMethods.module_eval do
68
- def first_page?() !previous_page end
69
- def last_page?() !next_page end
70
- end
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
71
6
 
72
- ApiPagination.instance_variable_set(:@paginator, :will_paginate)
73
- end
74
- 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)
75
11
  end
76
12
 
77
- 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 = 4
4
- MINOR = 0
4
+ MINOR = 1
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] || route_setting(: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
@@ -29,6 +34,7 @@ module Grape
29
34
  base.class_eval do
30
35
  def self.paginate(options = {})
31
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'
@@ -1,15 +1,17 @@
1
+ require 'support/numbers_controller'
2
+ require 'support/numbers_api'
3
+ require 'api-pagination'
4
+
1
5
  if ENV['PAGINATOR']
2
- ApiPagination.instance_variable_set(:@paginator, ENV['PAGINATOR'].to_sym)
6
+ require ENV['PAGINATOR']
7
+ ApiPagination.config.paginator = ENV['PAGINATOR'].to_sym
3
8
  else
4
9
  warn 'No PAGINATOR set. Defaulting to kaminari. To test against will_paginate, run `PAGINATOR=will_paginate bundle exec rspec`'
5
- ApiPagination.instance_variable_set(:@paginator, :kaminari)
10
+ require 'kaminari'
11
+ ApiPagination.config.paginator = :kaminari
6
12
  end
7
13
 
8
- require 'support/numbers_controller'
9
- require 'support/numbers_api'
10
- require 'api-pagination'
11
-
12
- require 'will_paginate/array' if ApiPagination.paginator == :will_paginate
14
+ require 'will_paginate/array'
13
15
 
14
16
  RSpec.configure do |config|
15
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: 4.0.0
4
+ version: 4.1.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: 2015-01-13 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
@@ -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