Skip to content

Commit 196a8e1

Browse files
committed
Add ApiPagination::Configuration
Add a configuration object that handles names of Total and Per-Page headers, as well as allows for the manual setting of the paginator that ApiPagination will use. It's up to the user to make WillPaginate and Kaminari play nicely together if they include both. Closes #33 Signed-off-by: David Celis <[email protected]>
1 parent fd4addb commit 196a8e1

File tree

11 files changed

+190
-139
lines changed

11 files changed

+190
-139
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
1. Fork it
44
2. Create your feature branch (`git checkout -b my-new-feature`)
55
3. Commit your changes and tests (`git commit -am 'Add some feature'`)
6-
4. Run the tests (`PAGINATOR=kaminari bundle exec rspec; PAGINATOR=will_paginate bundle exec rspec`)
6+
4. Run the tests (`PAGINATOR=Kaminari bundle exec rspec; PAGINATOR=WillPaginate bundle exec rspec`)
77
5. Push to the branch (`git push origin my-new-feature`)
88
6. Create a new Pull Request

README.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ gem 'will_paginate'
2525
gem 'api-pagination'
2626
```
2727

28+
## Configuration (optional)
29+
30+
By default, api-pagination will detect whether you're using Kaminari or WillPaginate, and name headers appropriately. If you want to change any of the configurable settings, you may do so:
31+
32+
```ruby
33+
ApiPagination.configure do |config|
34+
# If you have both gems included, you can choose a paginator.
35+
config.paginator = :kaminari # or :will_paginate
36+
37+
# By default, this is set to 'Total'
38+
config.total_header = 'X-Total'
39+
40+
# By default, this is set to 'Per-Page'
41+
config.per_page_header = 'X-Per-Page'
42+
end
43+
```
44+
2845
## Rails
2946

3047
In your controller, provide a pageable collection to the `paginate` method. In its most convenient form, `paginate` simply mimics `render`:
@@ -116,27 +133,6 @@ Per-Page: 10
116133
# ...
117134
```
118135

119-
If you want, you can customize the name of `Total` and `Per-Page` headers.
120-
All you need to do is to set the `total_header` or `per_page_header` on `ApiPagination`.
121-
If nothing is setted it defaults to `Total` and `Per-Page`.
122-
123-
```ruby
124-
ApiPagination.total_header = 'X-Total-Count'
125-
ApiPagination.per_page_header = 'X-Per-Page'
126-
```
127-
128-
```bash
129-
$ curl --include 'https://localhost:3000/movies?page=5'
130-
HTTP/1.1 200 OK
131-
Link: <http://localhost:3000/movies?page=1>; rel="first",
132-
<http://localhost:3000/movies?page=173>; rel="last",
133-
<http://localhost:3000/movies?page=6>; rel="next",
134-
<http://localhost:3000/movies?page=4>; rel="prev"
135-
X-Total-Count: 4321
136-
X-Per-Page: 10
137-
# ...
138-
```
139-
140136
[kaminari]: https://github.com/amatsuda/kaminari
141137
[will_paginate]: https://github.com/mislav/will_paginate
142138

lib/api-pagination.rb

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,20 @@
1+
require 'api-pagination/configuration'
12
require 'api-pagination/version'
23

34
module ApiPagination
45
class << self
5-
attr_reader :paginator
6-
attr_writer :total_header, :per_page_header
7-
86
def paginate(collection, options = {})
97
options[:page] = options[:page].to_i
108
options[:page] = 1 if options[:page] <= 0
119
options[:per_page] = options[:per_page].to_i
1210

13-
case ApiPagination.paginator
11+
case ApiPagination.config.paginator
1412
when :kaminari
15-
if Kaminari.config.max_per_page && options[:per_page] > Kaminari.config.max_per_page
16-
options[:per_page] = Kaminari.config.max_per_page
17-
elsif options[:per_page] <= 0
18-
options[:per_page] = Kaminari.config.default_per_page
19-
end
20-
collection = Kaminari.paginate_array(collection) if collection.is_a?(Array)
21-
collection.page(options[:page]).per(options[:per_page])
13+
paginate_with_kaminari(collection, options)
2214
when :will_paginate
23-
options[:per_page] = WillPaginate.per_page if options[:per_page] <= 0
24-
25-
if defined?(Sequel::Dataset) && collection.kind_of?(Sequel::Dataset)
26-
collection.paginate(options[:page], options[:per_page])
27-
else
28-
collection.paginate(:page => options[:page], :per_page => options[:per_page])
29-
end
15+
paginate_with_will_paginate(collection, options)
3016
else
31-
raise StandardError, "Unknown paginator: #{ApiPagination.paginator}"
17+
raise StandardError, "Unknown paginator: #{ApiPagination.config.paginator}"
3218
end
3319
end
3420

@@ -47,18 +33,48 @@ def pages_from(collection)
4733
end
4834

4935
def total_from(collection)
50-
case ApiPagination.paginator
36+
case ApiPagination.config.paginator
5137
when :kaminari then collection.total_count.to_s
5238
when :will_paginate then collection.total_entries.to_s
5339
end
5440
end
5541

56-
def total_header
57-
@total_header ||= 'Total'
42+
def paginator
43+
warn "[DEPRECATION] ApiPagination.paginator is deprecated. Please use ApiPagination.config.paginator"
44+
config.paginator
5845
end
5946

6047
def per_page_header
61-
@per_page_header ||= 'Per-Page'
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
6278
end
6379
end
6480
end

lib/api-pagination/configuration.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/api-pagination/hooks.rb

Lines changed: 14 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -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
716

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)
7511
end
7612

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

lib/grape/pagination.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def paginate(collection)
2020
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
2121
end
2222

23-
total_header = ApiPagination.total_header
24-
per_page_header = ApiPagination.per_page_header
23+
total_header = ApiPagination.config.total_header
24+
per_page_header = ApiPagination.config.per_page_header
2525

2626
header 'Link', links.join(', ') unless links.empty?
2727
header total_header, ApiPagination.total_from(collection)

lib/rails/pagination.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def _paginate_collection(collection, options={})
3939
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
4040
end
4141

42-
total_header = ApiPagination.total_header
43-
per_page_header = ApiPagination.per_page_header
42+
total_header = ApiPagination.config.total_header
43+
per_page_header = ApiPagination.config.per_page_header
4444

4545
headers['Link'] = links.join(', ') unless links.empty?
4646
headers[total_header] = ApiPagination.total_from(collection)

spec/grape_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@
6969

7070
context 'with custom response headers' do
7171
before do
72-
ApiPagination.total_header = 'X-Total-Count'
73-
ApiPagination.per_page_header = 'X-Per-Page'
72+
ApiPagination.config.total_header = 'X-Total-Count'
73+
ApiPagination.config.per_page_header = 'X-Per-Page'
7474

7575
get '/numbers', count: 10
7676
end
7777

7878
after do
79-
ApiPagination.total_header = nil
80-
ApiPagination.per_page_header = nil
79+
ApiPagination.config.total_header = 'Total'
80+
ApiPagination.config.per_page_header = 'Per-Page'
8181
end
8282

8383
let(:total) { last_response.header['X-Total-Count'].to_i }

0 commit comments

Comments
 (0)