Skip to content

Commit 9498645

Browse files
committed
Release api-pagination 1.0.0
Signed-off-by: David Celis <[email protected]>
0 parents  commit 9498645

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+618
-0
lines changed

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
*.gem
2+
*.rbc
3+
.bundle
4+
.config
5+
.yardoc
6+
Gemfile.lock
7+
InstalledFiles
8+
_yardoc
9+
coverage
10+
doc/
11+
lib/bundler/man
12+
pkg
13+
rdoc
14+
spec/reports
15+
test/tmp
16+
test/version_tmp
17+
tmp

.rspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--color
2+
--format progress

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: ruby
2+
rvm:
3+
- 1.9.3
4+
- 2.0.0
5+
script: bundle exec rspec

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in api_pagination.gemspec
4+
gemspec

LICENSE.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright © 2013 David Celis
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# api-pagination [![Build Status](https://travis-ci.org/davidcelis/api-pagination.png)](https://travis-ci.org/davidcelis/api-pagination)
2+
3+
Put pagination info for your Rails API in your Link headers, not your response body.
4+
5+
## Installation
6+
7+
In your `Gemfile`:
8+
9+
```ruby
10+
# Requires 'rails', '>= 3.0.0' and is compatible with 'rails-api'
11+
gem 'api-pagination'
12+
```
13+
14+
## Usage
15+
16+
In your controllers:
17+
18+
```ruby
19+
class MoviesController < ApplicationController
20+
after_filter only: [:index] { paginate(:movies) }
21+
after_filter only: [:cast] { paginate(:actors) }
22+
23+
# GET /movies
24+
def index
25+
@movies = Movie.page(params[:page])
26+
27+
render json: @movies
28+
end
29+
30+
# GET /movies/:id/cast
31+
def cast
32+
@movie = Movie.find(params[:id])
33+
@actors = @movie.actors.page(params[:page])
34+
35+
render json: @actors
36+
end
37+
end
38+
```
39+
40+
Then `curl --include` to see your Link header pagination in action:
41+
42+
```bash
43+
$ curl --include 'https://localhost:3000/movies?page=5'
44+
HTTP/1.1 200 OK
45+
Link: <http://localhost:3000/movies?page=1>; rel="first">,
46+
<http://localhost:3000/movies?page=173>; rel="last">,
47+
<http://localhost:3000/movies?page=6>; rel="next">,
48+
<http://localhost:3000/movies?page=4>; rel="prev">
49+
# ...
50+
```
51+
52+
api-pagination uses [Kaminari][kaminari] under the hood for paginating your ActiveRecord relations. See Kaminari's [documentation][kaminari-docs] for more information on its usage.
53+
54+
## Contributing
55+
56+
1. Fork it
57+
2. Create your feature branch (`git checkout -b my-new-feature`)
58+
3. Commit your changes (`git commit -am 'Add some feature'`)
59+
4. Push to the branch (`git push origin my-new-feature`)
60+
5. Create a new Pull Request
61+
62+
[kaminari]: https://github.com/amatsuda/kaminari
63+
[kaminari-docs]: http://rubydoc.info/github/amatsuda/kaminari/frames

api-pagination.gemspec

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# encoding: utf-8
2+
$:.unshift(File.expand_path('../lib', __FILE__))
3+
require 'api-pagination/version'
4+
5+
Gem::Specification.new do |s|
6+
s.name = 'api-pagination'
7+
s.version = ApiPagination::VERSION
8+
s.authors = ['David Celis']
9+
s.email = ['[email protected]']
10+
s.description = 'Link header pagination for Rails APIs'
11+
s.summary = "Link header pagination for Rails APIs. Don't use the request body."
12+
s.homepage = 'https://github.com/davidcelis/api-pagination'
13+
s.license = 'MIT'
14+
15+
s.files = Dir['lib/**/*']
16+
s.test_files = Dir['spec/**/*']
17+
s.require_paths = ['lib']
18+
19+
s.add_dependency 'rails', '>= 3.0.0'
20+
s.add_dependency 'kaminari', '>= 0.13.0'
21+
22+
s.add_development_dependency 'bundler', '~> 1.3'
23+
s.add_development_dependency 'rspec-rails'
24+
end

lib/api-pagination.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'api-pagination/version'
2+
require 'kaminari'
3+
4+
module ApiPagination
5+
protected
6+
def paginate(scope)
7+
query_params = request.query_parameters
8+
scope = instance_variable_get(:"@#{scope}")
9+
url = request.original_url.sub(/\?.*$/, '')
10+
pages = {}
11+
links = []
12+
13+
unless scope.first_page?
14+
pages[:first] = 1
15+
pages[:prev] = scope.current_page - 1
16+
end
17+
18+
unless scope.last_page?
19+
pages[:last] = scope.total_pages
20+
pages[:next] = scope.current_page + 1
21+
end
22+
23+
pages.each do |k, v|
24+
new_params = query_params.merge({ :page => v })
25+
links << %(<#{url}?#{new_params.to_param}>; rel="#{k}")
26+
end
27+
28+
headers['Link'] = links.join(', ')
29+
end
30+
end
31+
32+
ActionController::Base.send(:include, ApiPagination) if defined?(ActionController::Base)
33+
ActionController::API.send(:include, ApiPagination) if defined?(ActionController::API)

lib/api-pagination/version.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module ApiPagination
2+
MAJOR = 1
3+
MINOR = 0
4+
PATCH = 0
5+
6+
VERSION = [MAJOR, MINOR, PATCH].join('.')
7+
end
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
require 'spec_helper'
2+
3+
describe NumbersController do
4+
describe 'GET #index' do
5+
context 'without enough items to give more than one page' do
6+
it 'should not paginate' do
7+
get :index, count: 20
8+
response.headers['Link'].should be_blank
9+
end
10+
end
11+
12+
context 'with enough items to paginate' do
13+
context 'when on the first page' do
14+
before(:each) do
15+
get :index, count: 100
16+
17+
@links = response.headers['Link'].split(', ')
18+
end
19+
20+
it 'should not give a link with rel "first"' do
21+
@links.should_not include('rel="first"')
22+
end
23+
24+
it 'should not give a link with rel "prev"' do
25+
@links.should_not include('rel="prev"')
26+
end
27+
28+
it 'should give a link with rel "last"' do
29+
@links.should include('<http://test.host/numbers?count=100&page=4>; rel="last"')
30+
end
31+
32+
it 'should give a link with rel "next"' do
33+
@links.should include('<http://test.host/numbers?count=100&page=2>; rel="next"')
34+
end
35+
end
36+
37+
context 'when on the last page' do
38+
before(:each) do
39+
get :index, count: 100, page: 4
40+
41+
@links = response.headers['Link'].split(', ')
42+
end
43+
44+
it 'should not give a link with rel "last"' do
45+
@links.should_not include('rel="last"')
46+
end
47+
48+
it 'should not give a link with rel "next"' do
49+
@links.should_not include('rel="next"')
50+
end
51+
52+
it 'should give a link with rel "first"' do
53+
@links.should include('<http://test.host/numbers?count=100&page=1>; rel="first"')
54+
end
55+
56+
it 'should give a link with rel "prev"' do
57+
@links.should include('<http://test.host/numbers?count=100&page=3>; rel="prev"')
58+
end
59+
end
60+
61+
context 'when somewhere comfortably in the middle' do
62+
it 'should give all pagination links' do
63+
get :index, count: 100, page: 2
64+
65+
links = response.headers['Link'].split(', ')
66+
67+
links.should include('<http://test.host/numbers?count=100&page=1>; rel="first"')
68+
links.should include('<http://test.host/numbers?count=100&page=4>; rel="last"')
69+
links.should include('<http://test.host/numbers?count=100&page=3>; rel="next"')
70+
links.should include('<http://test.host/numbers?count=100&page=1>; rel="prev"')
71+
end
72+
end
73+
end
74+
end
75+
end
76+
77+
78+
79+
80+

0 commit comments

Comments
 (0)