api_only_pagination 0.1.5 → 0.2.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/.rubocop.yml +1 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +38 -2
- data/README.md +66 -25
- data/lib/api_only_pagination/active_record_paginate.rb +5 -3
- data/lib/api_only_pagination/configuration.rb +2 -1
- data/lib/api_only_pagination/controller.rb +41 -0
- data/lib/api_only_pagination/paginated_data.rb +25 -3
- data/lib/api_only_pagination/version.rb +1 -1
- data/lib/api_only_pagination.rb +6 -0
- metadata +5 -12
- data/CLAUDE.md +0 -40
- data/api_only_pagination.gemspec +0 -39
- data/gemfiles/activerecord_6.1.gemfile +0 -16
- data/gemfiles/activerecord_7.0.gemfile +0 -16
- data/gemfiles/activerecord_7.1.gemfile +0 -9
- data/gemfiles/activerecord_7.2.gemfile +0 -9
- data/gemfiles/activerecord_8.0.gemfile +0 -9
- data/gemfiles/activerecord_8.1.gemfile +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 720784fef8ffa7a27fb958bcc70ae7389a967f99be74da4661e09202fec813ce
|
|
4
|
+
data.tar.gz: 1978db31fad9115663f84853648a158bac002c7f55a99fbfc21037124d026b24
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a445371db801c78c7581c12fcd3078afa5ce3d8aa4794ed15584995acec291681619433adba036afa92809bc07e1217123439792a4da1af258794b40dc543825
|
|
7
|
+
data.tar.gz: b0db20fc6d8e4967e8f566e7768798fa27dbe5e9d2b815181f360cd0222d444c691086d80b6e0b180adb3c70ffa5e8e78061ebf8e720a8b0fc5f8902be232db7
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-10-30
|
|
4
|
+
|
|
5
|
+
- **Breaking:** require Ruby >= 3.2 and ActiveRecord >= 6.1 (the versions CI tests)
|
|
6
|
+
- **Breaking:** remove the `paginate` alias, which clashed with will_paginate; use `do_paginate` or `paginated_response`
|
|
7
|
+
- Add `next_page` and `previous_page` to the response
|
|
8
|
+
- Add `PaginatedData#to_h` and an explicit `#as_json`; serializer options such as `only:` apply to the records in `data`
|
|
9
|
+
- Add `render_paginated` controller helper (ActionController::Base and ::API) that reads `page`/`per_page` from params, with opt-in `X-Total-Count`/`Link` pagination headers (`pagination_headers` setting)
|
|
10
|
+
- Note: Rails 8.1 does not load on Ruby 3.3.0 because of a Ruby parser bug fixed in 3.3.1; use a later 3.3.x
|
|
11
|
+
|
|
3
12
|
## [0.1.5] - 2026-10-30
|
|
4
13
|
|
|
5
14
|
- Fix: negative `page`/`per_page` values produced invalid SQL; any value that isn't a positive integer now falls back to the configured default
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
api_only_pagination (0.
|
|
5
|
-
activerecord (>=
|
|
4
|
+
api_only_pagination (0.2.0)
|
|
5
|
+
activerecord (>= 6.1)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
+
actionpack (6.1.7.3)
|
|
11
|
+
actionview (= 6.1.7.3)
|
|
12
|
+
activesupport (= 6.1.7.3)
|
|
13
|
+
rack (~> 2.0, >= 2.0.9)
|
|
14
|
+
rack-test (>= 0.6.3)
|
|
15
|
+
rails-dom-testing (~> 2.0)
|
|
16
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
17
|
+
actionview (6.1.7.3)
|
|
18
|
+
activesupport (= 6.1.7.3)
|
|
19
|
+
builder (~> 3.1)
|
|
20
|
+
erubi (~> 1.4)
|
|
21
|
+
rails-dom-testing (~> 2.0)
|
|
22
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
10
23
|
activemodel (6.1.7.3)
|
|
11
24
|
activesupport (= 6.1.7.3)
|
|
12
25
|
activerecord (6.1.7.3)
|
|
@@ -19,17 +32,39 @@ GEM
|
|
|
19
32
|
tzinfo (~> 2.0)
|
|
20
33
|
zeitwerk (~> 2.3)
|
|
21
34
|
ast (2.4.2)
|
|
35
|
+
builder (3.3.0)
|
|
22
36
|
concurrent-ruby (1.2.2)
|
|
37
|
+
crass (1.0.8)
|
|
23
38
|
diff-lcs (1.5.0)
|
|
39
|
+
erubi (1.13.1)
|
|
24
40
|
i18n (1.13.0)
|
|
25
41
|
concurrent-ruby (~> 1.0)
|
|
26
42
|
json (2.6.1)
|
|
43
|
+
loofah (2.25.2)
|
|
44
|
+
crass (~> 1.0.2)
|
|
45
|
+
nokogiri (>= 1.12.0)
|
|
27
46
|
minitest (5.18.0)
|
|
47
|
+
nokogiri (1.19.4-arm64-darwin)
|
|
48
|
+
racc (~> 1.4)
|
|
49
|
+
nokogiri (1.19.4-x86_64-darwin)
|
|
50
|
+
racc (~> 1.4)
|
|
51
|
+
nokogiri (1.19.4-x86_64-linux-gnu)
|
|
52
|
+
racc (~> 1.4)
|
|
28
53
|
parallel (1.21.0)
|
|
29
54
|
parser (3.3.13.0)
|
|
30
55
|
ast (~> 2.4.1)
|
|
31
56
|
racc
|
|
32
57
|
racc (1.8.1)
|
|
58
|
+
rack (2.2.25)
|
|
59
|
+
rack-test (2.3.0)
|
|
60
|
+
rack (>= 1.3)
|
|
61
|
+
rails-dom-testing (2.3.0)
|
|
62
|
+
activesupport (>= 5.0.0)
|
|
63
|
+
minitest
|
|
64
|
+
nokogiri (>= 1.6)
|
|
65
|
+
rails-html-sanitizer (1.7.1)
|
|
66
|
+
loofah (~> 2.25, >= 2.25.2)
|
|
67
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
33
68
|
rainbow (3.1.1)
|
|
34
69
|
rake (13.0.6)
|
|
35
70
|
regexp_parser (2.3.0)
|
|
@@ -74,6 +109,7 @@ PLATFORMS
|
|
|
74
109
|
x86_64-linux
|
|
75
110
|
|
|
76
111
|
DEPENDENCIES
|
|
112
|
+
actionpack
|
|
77
113
|
api_only_pagination!
|
|
78
114
|
rake (~> 13.0)
|
|
79
115
|
rspec (~> 3.6)
|
data/README.md
CHANGED
|
@@ -9,45 +9,86 @@ or add it to your Gemfile
|
|
|
9
9
|
```
|
|
10
10
|
gem 'api_only_pagination'
|
|
11
11
|
```
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
Ruby 3.2+ and ActiveRecord 6.1+. Versions 0.1.x support older Rubies and Rails but are no longer maintained.
|
|
15
|
+
|
|
12
16
|
## Usage
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
Optionally configure defaults in `config/initializers/api_only_pagination.rb`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
ApiOnlyPagination.configure do |config|
|
|
22
|
+
config.default_page_size = 20 # per_page when none (or an invalid value) is given
|
|
23
|
+
config.max_per_page = 100 # upper limit for per_page
|
|
24
|
+
config.default_page = 1 # page when none (or an invalid value) is given
|
|
25
|
+
config.pagination_headers = false # see "In controllers"
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Call `do_paginate(page, per_page)` on any model or relation:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
result = User.where(active: true).order(:id).do_paginate(params[:page], params[:per_page])
|
|
33
|
+
render json: result
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`page` and `per_page` can come straight from request params. Strings are converted to integers, any value that isn't a positive integer falls back to the configured default, and `per_page` is capped at `max_per_page`.
|
|
37
|
+
|
|
38
|
+
`do_paginate` returns an `ApiOnlyPagination::PaginatedData` object. It renders as:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"data": [{ "id": 3, "name": "..." }, { "id": 4, "name": "..." }],
|
|
43
|
+
"per_page": 2,
|
|
44
|
+
"current_page": 2,
|
|
45
|
+
"total_pages": 3,
|
|
46
|
+
"total_records": 5,
|
|
47
|
+
"next_page": 3,
|
|
48
|
+
"previous_page": 1
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`next_page` and `previous_page` are `null` on the last and first page. Serializer options apply to the records, so `render json: result, only: [:id, :name]` limits the fields in `data` and keeps the pagination fields. Use `result.to_h` for a symbol-keyed hash, or read the attributes directly (`result.data`, `result.total_pages`, ...).
|
|
53
|
+
|
|
54
|
+
### In controllers
|
|
55
|
+
|
|
56
|
+
`render_paginated` reads `page` and `per_page` from params, paginates, renders the JSON above and returns the result:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class UsersController < ApplicationController
|
|
60
|
+
def index
|
|
61
|
+
render_paginated User.where(active: true).order(:id)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Other options go to `render`, so `render_paginated scope, only: [:id, :name], status: :ok` works. Pass `page:` or `per_page:` to override params.
|
|
67
|
+
|
|
68
|
+
The helper can also set pagination headers: `X-Total-Count`, `X-Total-Pages`, `X-Per-Page`, `X-Page`, and a `Link` header with `first`/`prev`/`next`/`last` URLs that keep the request's other query params. Turn them on for one call with `render_paginated scope, pagination_headers: true`, or for every call:
|
|
23
69
|
|
|
24
|
-
|
|
70
|
+
```ruby
|
|
71
|
+
ApiOnlyPagination.configure do |config|
|
|
72
|
+
config.pagination_headers = true
|
|
73
|
+
end
|
|
25
74
|
```
|
|
26
|
-
response = User.do_paginate(1,20)
|
|
27
|
-
```
|
|
28
75
|
|
|
29
|
-
|
|
76
|
+
If browser clients need to read these headers in a cross-origin setup, add them to `expose` in your CORS config.
|
|
30
77
|
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
"data" => [],
|
|
34
|
-
"per_page" => 3,
|
|
35
|
-
"current_page" => 1,
|
|
36
|
-
"total_pages" => 2,
|
|
37
|
-
"total_records" => 5
|
|
38
|
-
}
|
|
39
|
-
|
|
78
|
+
### Upgrading from 0.1.x
|
|
40
79
|
|
|
80
|
+
- The `paginate` alias was removed because it clashes with will_paginate. Use `do_paginate` (or `paginated_response`).
|
|
81
|
+
- The response has two new fields, `next_page` and `previous_page`.
|
|
41
82
|
|
|
42
83
|
## Development
|
|
43
84
|
|
|
44
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
85
|
+
After checking out the repo, run `bin/setup` to install dependencies, then `bundle exec rake` to run the specs and RuboCop. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
45
86
|
|
|
46
87
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
47
88
|
|
|
48
89
|
## Contributing
|
|
49
90
|
|
|
50
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
91
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ashok1/api_only_pagination. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/ashok1/api_only_pagination/blob/main/CODE_OF_CONDUCT.md).
|
|
51
92
|
|
|
52
93
|
## License
|
|
53
94
|
|
|
@@ -55,4 +96,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
55
96
|
|
|
56
97
|
## Code of Conduct
|
|
57
98
|
|
|
58
|
-
Everyone interacting in the ApiOnlyPagination project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
|
99
|
+
Everyone interacting in the ApiOnlyPagination project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ashok1/api_only_pagination/blob/main/CODE_OF_CONDUCT.md).
|
|
@@ -9,10 +9,10 @@ module ApiOnlyPagination
|
|
|
9
9
|
|
|
10
10
|
# Pagination entry points available on models and relations
|
|
11
11
|
module ClassMethods
|
|
12
|
-
%i[do_paginate
|
|
12
|
+
%i[do_paginate paginated_response].each do |access_method|
|
|
13
13
|
# nil page/per_page fall back to the configured defaults in Paginate
|
|
14
14
|
define_method(access_method) do |page = nil, per_page = nil|
|
|
15
|
-
pagination = ApiOnlyPagination::Pagination.new(all, { page
|
|
15
|
+
pagination = ApiOnlyPagination::Pagination.new(all, { page:, per_page: })
|
|
16
16
|
meta_data = pagination.metadata
|
|
17
17
|
|
|
18
18
|
ApiOnlyPagination::PaginatedData
|
|
@@ -20,7 +20,9 @@ module ApiOnlyPagination
|
|
|
20
20
|
per_page: meta_data.per_page,
|
|
21
21
|
current_page: meta_data.page,
|
|
22
22
|
total_pages: meta_data.total_pages,
|
|
23
|
-
total_records: meta_data.count
|
|
23
|
+
total_records: meta_data.count,
|
|
24
|
+
next_page: meta_data.next_page,
|
|
25
|
+
previous_page: meta_data.previous_page)
|
|
24
26
|
end
|
|
25
27
|
end
|
|
26
28
|
end
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
module ApiOnlyPagination
|
|
4
4
|
# Global settings, changed through ApiOnlyPagination.configure
|
|
5
5
|
class Configuration
|
|
6
|
-
attr_accessor :default_page_size, :max_per_page, :default_page
|
|
6
|
+
attr_accessor :default_page_size, :max_per_page, :default_page, :pagination_headers
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@default_page_size = 20
|
|
10
10
|
@max_per_page = 100
|
|
11
11
|
@default_page = 1
|
|
12
|
+
@pagination_headers = false
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ApiOnlyPagination
|
|
4
|
+
# Controller helper, included into ActionController::Base and ActionController::API on load
|
|
5
|
+
module Controller
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# Paginates scope with page/per_page from params, renders it as JSON and returns the PaginatedData.
|
|
9
|
+
# Remaining options go to render, e.g. render_paginated(User.all, only: [:id, :name], status: :ok)
|
|
10
|
+
def render_paginated(scope, page: params[:page], per_page: params[:per_page],
|
|
11
|
+
pagination_headers: ApiOnlyPagination.configuration.pagination_headers, **render_options)
|
|
12
|
+
result = scope.do_paginate(page, per_page)
|
|
13
|
+
add_pagination_headers(result) if pagination_headers
|
|
14
|
+
render(json: result, **render_options)
|
|
15
|
+
result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_pagination_headers(result)
|
|
19
|
+
{
|
|
20
|
+
"X-Total-Count" => result.total_records,
|
|
21
|
+
"X-Total-Pages" => result.total_pages,
|
|
22
|
+
"X-Per-Page" => result.per_page,
|
|
23
|
+
"X-Page" => result.current_page,
|
|
24
|
+
"Link" => pagination_link_header(result).presence
|
|
25
|
+
}.compact.each { |name, value| response.headers[name] = value.to_s }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pagination_link_header(result)
|
|
29
|
+
return "" if result.total_pages.zero?
|
|
30
|
+
|
|
31
|
+
{ first: 1, prev: result.previous_page, next: result.next_page, last: result.total_pages }
|
|
32
|
+
.filter_map { |rel, page| %(<#{pagination_url(page, result.per_page)}>; rel="#{rel}") if page }
|
|
33
|
+
.join(", ")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def pagination_url(page, per_page)
|
|
37
|
+
query = request.query_parameters.merge("page" => page, "per_page" => per_page).to_query
|
|
38
|
+
"#{request.base_url}#{request.path}?#{query}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -2,20 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
# Main module
|
|
4
4
|
module ApiOnlyPagination
|
|
5
|
-
# Response object
|
|
5
|
+
# Response object returned by do_paginate; serializes to the documented JSON shape
|
|
6
6
|
class PaginatedData
|
|
7
7
|
attr_accessor :data,
|
|
8
8
|
:per_page,
|
|
9
9
|
:current_page,
|
|
10
10
|
:total_pages,
|
|
11
|
-
:total_records
|
|
11
|
+
:total_records,
|
|
12
|
+
:next_page,
|
|
13
|
+
:previous_page
|
|
12
14
|
|
|
13
|
-
def initialize(data:, per_page:, current_page:, total_pages:, total_records:)
|
|
15
|
+
def initialize(data:, per_page:, current_page:, total_pages:, total_records:, next_page: nil, previous_page: nil) # rubocop:disable Metrics/ParameterLists
|
|
14
16
|
@data = data
|
|
15
17
|
@per_page = per_page
|
|
16
18
|
@current_page = current_page
|
|
17
19
|
@total_pages = total_pages
|
|
18
20
|
@total_records = total_records
|
|
21
|
+
@next_page = next_page
|
|
22
|
+
@previous_page = previous_page
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_h
|
|
26
|
+
{
|
|
27
|
+
data:,
|
|
28
|
+
per_page:,
|
|
29
|
+
current_page:,
|
|
30
|
+
total_pages:,
|
|
31
|
+
total_records:,
|
|
32
|
+
next_page:,
|
|
33
|
+
previous_page:
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Serializer options (only:, except:, include:, methods:) apply to the records in data,
|
|
38
|
+
# so `render json: result, only: [:id, :name]` works as it would for the bare relation.
|
|
39
|
+
def as_json(options = nil)
|
|
40
|
+
to_h.merge(data: data.as_json(options)).as_json
|
|
19
41
|
end
|
|
20
42
|
end
|
|
21
43
|
end
|
data/lib/api_only_pagination.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "api_only_pagination/paginate"
|
|
|
7
7
|
require_relative "api_only_pagination/pagination"
|
|
8
8
|
require_relative "api_only_pagination/paginated_data"
|
|
9
9
|
require_relative "api_only_pagination/active_record_paginate"
|
|
10
|
+
require_relative "api_only_pagination/controller"
|
|
10
11
|
|
|
11
12
|
# Offset pagination for ActiveRecord that returns data plus page metadata for API responses
|
|
12
13
|
module ApiOnlyPagination
|
|
@@ -28,3 +29,8 @@ end
|
|
|
28
29
|
ActiveSupport.on_load(:active_record) do
|
|
29
30
|
include ApiOnlyPagination::ActiveRecordPaginate
|
|
30
31
|
end
|
|
32
|
+
|
|
33
|
+
# Fires for both ActionController::Base and ActionController::API; does nothing in apps without ActionPack
|
|
34
|
+
ActiveSupport.on_load(:action_controller) do
|
|
35
|
+
include ApiOnlyPagination::Controller
|
|
36
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_only_pagination
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ashok
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '6.1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '6.1'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rspec
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -48,23 +48,16 @@ files:
|
|
|
48
48
|
- ".rspec"
|
|
49
49
|
- ".rubocop.yml"
|
|
50
50
|
- CHANGELOG.md
|
|
51
|
-
- CLAUDE.md
|
|
52
51
|
- CODE_OF_CONDUCT.md
|
|
53
52
|
- Gemfile
|
|
54
53
|
- Gemfile.lock
|
|
55
54
|
- LICENSE.txt
|
|
56
55
|
- README.md
|
|
57
56
|
- Rakefile
|
|
58
|
-
- api_only_pagination.gemspec
|
|
59
|
-
- gemfiles/activerecord_6.1.gemfile
|
|
60
|
-
- gemfiles/activerecord_7.0.gemfile
|
|
61
|
-
- gemfiles/activerecord_7.1.gemfile
|
|
62
|
-
- gemfiles/activerecord_7.2.gemfile
|
|
63
|
-
- gemfiles/activerecord_8.0.gemfile
|
|
64
|
-
- gemfiles/activerecord_8.1.gemfile
|
|
65
57
|
- lib/api_only_pagination.rb
|
|
66
58
|
- lib/api_only_pagination/active_record_paginate.rb
|
|
67
59
|
- lib/api_only_pagination/configuration.rb
|
|
60
|
+
- lib/api_only_pagination/controller.rb
|
|
68
61
|
- lib/api_only_pagination/paginate.rb
|
|
69
62
|
- lib/api_only_pagination/paginated_data.rb
|
|
70
63
|
- lib/api_only_pagination/pagination.rb
|
|
@@ -84,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
84
77
|
requirements:
|
|
85
78
|
- - ">="
|
|
86
79
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: 2.
|
|
80
|
+
version: 3.2.0
|
|
88
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
82
|
requirements:
|
|
90
83
|
- - ">="
|
data/CLAUDE.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Project
|
|
6
|
-
|
|
7
|
-
`api_only_pagination` is a small Ruby gem that adds offset pagination to ActiveRecord models and relations for API-only apps. It returns a plain response object (data + page metadata) instead of view helpers. Depends on `activerecord >= 5.0`; targets Ruby >= 2.6.
|
|
8
|
-
|
|
9
|
-
## Commands
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
bin/setup # install dependencies
|
|
13
|
-
bin/console # IRB with the gem loaded
|
|
14
|
-
bundle exec rake # specs + RuboCop (what CI runs)
|
|
15
|
-
bundle exec rubocop # lint
|
|
16
|
-
bundle exec rspec # run specs
|
|
17
|
-
bundle exec rspec spec/api_only_pagination_spec.rb:13 # single example
|
|
18
|
-
bundle exec rake install # install gem locally
|
|
19
|
-
bundle exec rake release # tag + push to rubygems (bump lib/api_only_pagination/version.rb first)
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Specs run against an in-memory SQLite database; `spec/spec_helper.rb` defines the schema and a `User` model, and resets records and configuration before each example.
|
|
23
|
-
|
|
24
|
-
CI (`.github/workflows/ci.yml`) runs specs against every Gemfile in `gemfiles/` (one per ActiveRecord version) on several Rubies. To reproduce one locally: `BUNDLE_GEMFILE=gemfiles/activerecord_7.2.gemfile bundle install && BUNDLE_GEMFILE=gemfiles/activerecord_7.2.gemfile bundle exec rspec`. When adding a Rails version, add a gemfile and a matrix entry.
|
|
25
|
-
|
|
26
|
-
## Architecture
|
|
27
|
-
|
|
28
|
-
The call flow spans several small files in `lib/api_only_pagination/`:
|
|
29
|
-
|
|
30
|
-
1. **Hooking into ActiveRecord** (`lib/api_only_pagination.rb`): an `ActiveSupport.on_load(:active_record)` block includes `ActiveRecordPaginate` into `ActiveRecord::Base`. Chained calls like `User.where(...).do_paginate` still work, because a relation passes unknown methods to its model class while applying its own scope, so `all` inside the method returns the scoped relation.
|
|
31
|
-
2. **`ActiveRecordPaginate`**: defines three aliases, `do_paginate`, `paginate`, and `paginated_response`, each taking `(page, per_page)`. They wrap `all` in a `Pagination` and build a `PaginatedData`.
|
|
32
|
-
3. **`Pagination`**: runs `collection.count(:all)` once when it's constructed (for grouped relations it counts the groups), then applies `limit`/`offset` from the metadata in `results`. `results` is still a lazy relation.
|
|
33
|
-
4. **`Paginate`**: the metadata and normalization logic. Any page/per_page value that isn't a positive integer (nil, zero, negative, non-numeric) falls back to the configured default, and `per_page` is capped at `max_per_page`. It also computes `offset`, `total_pages`, and next/previous helpers.
|
|
34
|
-
5. **`PaginatedData`**: a plain object with `data`, `per_page`, `current_page`, `total_pages`, and `total_records`. The README shows the result as a hash, but it's actually this object.
|
|
35
|
-
|
|
36
|
-
**Configuration** (`Configuration` + `ApiOnlyPagination.configure`/`.reset`): `default_page_size` (20), `max_per_page` (100), `default_page` (1). Values are read when each call runs, so changes take effect right away.
|
|
37
|
-
|
|
38
|
-
## Style
|
|
39
|
-
|
|
40
|
-
RuboCop (`.rubocop.yml`): double-quoted strings, max line length 120, `TargetRubyVersion` 2.6. Every file starts with `# frozen_string_literal: true`.
|
data/api_only_pagination.gemspec
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/api_only_pagination/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "api_only_pagination"
|
|
7
|
-
spec.version = ApiOnlyPagination::VERSION
|
|
8
|
-
spec.authors = ["Ashok"]
|
|
9
|
-
spec.email = ["kingkoirala1@gmail.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "pagination with api response only"
|
|
12
|
-
spec.description = "pagination with api response only"
|
|
13
|
-
spec.homepage = "https://github.com/ashok1/api_only_pagination"
|
|
14
|
-
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
-
|
|
17
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
|
18
|
-
|
|
19
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
|
-
|
|
22
|
-
# Specify which files should be added to the gem when it is released.
|
|
23
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
-
spec.files = Dir.chdir(__dir__) do
|
|
25
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
-
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
spec.bindir = "exe"
|
|
30
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
-
spec.require_paths = ["lib"]
|
|
32
|
-
spec.add_dependency "activerecord", ">= 5.0"
|
|
33
|
-
spec.add_development_dependency "rspec", "~> 3.6"
|
|
34
|
-
# Uncomment to register a new dependency of your gem
|
|
35
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
|
36
|
-
|
|
37
|
-
# For more information and examples about making a new gem, check out our
|
|
38
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
|
39
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gemspec path: ".."
|
|
6
|
-
|
|
7
|
-
gem "activerecord", "~> 6.1.0"
|
|
8
|
-
gem "rake", "~> 13.0"
|
|
9
|
-
gem "sqlite3", "~> 1.4"
|
|
10
|
-
|
|
11
|
-
# Rails < 7.1 breaks with concurrent-ruby 1.3.5+ and relies on gems Ruby 3.4 no longer ships by default
|
|
12
|
-
gem "base64"
|
|
13
|
-
gem "bigdecimal"
|
|
14
|
-
gem "concurrent-ruby", "< 1.3.5"
|
|
15
|
-
gem "logger"
|
|
16
|
-
gem "mutex_m"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gemspec path: ".."
|
|
6
|
-
|
|
7
|
-
gem "activerecord", "~> 7.0.0"
|
|
8
|
-
gem "rake", "~> 13.0"
|
|
9
|
-
gem "sqlite3", "~> 1.4"
|
|
10
|
-
|
|
11
|
-
# Rails < 7.1 breaks with concurrent-ruby 1.3.5+ and relies on gems Ruby 3.4 no longer ships by default
|
|
12
|
-
gem "base64"
|
|
13
|
-
gem "bigdecimal"
|
|
14
|
-
gem "concurrent-ruby", "< 1.3.5"
|
|
15
|
-
gem "logger"
|
|
16
|
-
gem "mutex_m"
|