geared_pagination 0.2 → 1.0.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 +5 -5
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +123 -38
- data/README.md +47 -2
- data/Rakefile +1 -0
- data/bin/rails +16 -0
- data/geared_pagination.gemspec +3 -3
- data/lib/geared_pagination.rb +1 -1
- data/lib/geared_pagination/controller.rb +5 -5
- data/lib/geared_pagination/cursor.rb +40 -0
- data/lib/geared_pagination/{railtie.rb → engine.rb} +1 -1
- data/lib/geared_pagination/headers.rb +16 -9
- data/lib/geared_pagination/order.rb +44 -0
- data/lib/geared_pagination/page.rb +13 -7
- data/lib/geared_pagination/portions.rb +2 -0
- data/lib/geared_pagination/portions/portion_at_cursor.rb +134 -0
- data/lib/geared_pagination/{portion.rb → portions/portion_at_offset.rb} +6 -2
- data/lib/geared_pagination/recordset.rb +25 -5
- data/test/controller_test.rb +2 -24
- data/test/cursor_test.rb +34 -0
- data/test/dummy/Rakefile +3 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/cable.js +13 -0
- data/test/dummy/app/assets/javascripts/channels/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/recordings_controller.rb +11 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/recording.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +21 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +61 -0
- data/test/dummy/config/environments/production.rb +94 -0
- data/test/dummy/config/environments/test.rb +46 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/db/migrate/20200504213548_create_recordings.rb +7 -0
- data/test/dummy/db/schema.rb +19 -0
- data/test/dummy/db/seeds.rb +7 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/lib/tasks/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/robots.txt +1 -0
- data/test/dummy/test/application_system_test_case.rb +5 -0
- data/test/dummy/test/controllers/.keep +0 -0
- data/test/dummy/test/fixtures/.keep +0 -0
- data/test/dummy/test/fixtures/files/.keep +0 -0
- data/test/dummy/test/helpers/.keep +0 -0
- data/test/dummy/test/integration/.keep +0 -0
- data/test/dummy/test/mailers/.keep +0 -0
- data/test/dummy/test/models/.keep +0 -0
- data/test/dummy/test/system/.keep +0 -0
- data/test/dummy/test/test_helper.rb +10 -0
- data/test/dummy/vendor/.keep +0 -0
- data/test/headers_test.rb +5 -3
- data/test/order_test.rb +34 -0
- data/test/page_test.rb +17 -3
- data/test/portion_at_cursor_test.rb +84 -0
- data/test/portion_at_offset_test.rb +33 -0
- data/test/recordset_test.rb +41 -3
- data/test/test_helper.rb +12 -4
- metadata +158 -10
- data/test/portion_test.rb +0 -27
- data/test/recording_stubs.rb +0 -62
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6442a3bf0d55e8a7959814c6fd7d7e02da254952dbd829493aaf99c01bdd85f4
|
|
4
|
+
data.tar.gz: d064b59363360f871c6cc91adcce01d4a863d6379282fabb354fbeea0f402cfb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: becae20eab1dd38355befb8a1440dcd432c0743f94d59f27f7323a7a2386849736605c96ddb344aa45d039569d4a5b406d0732fec2576cdbe093a602084e4fbc
|
|
7
|
+
data.tar.gz: 066f7da6d2db1875602aeb1517bf0cb19be41fc142dd3104cd68e0e204b77c3f196cac3ca19604ef75132424a7c6d988352c2e5d5ee1ed651047b4680d5c0c6c
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
tests:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
|
|
7
|
+
steps:
|
|
8
|
+
- uses: actions/checkout@v1
|
|
9
|
+
|
|
10
|
+
- name: Set up Ruby 2.6
|
|
11
|
+
uses: actions/setup-ruby@v1
|
|
12
|
+
with:
|
|
13
|
+
ruby-version: 2.6.x
|
|
14
|
+
|
|
15
|
+
- name: Cache gem dependencies
|
|
16
|
+
uses: actions/cache@v1
|
|
17
|
+
with:
|
|
18
|
+
path: vendor/bundle
|
|
19
|
+
key: ${{ runner.os }}-bundler-${{ hashFiles('**/Gemfile.lock') }}
|
|
20
|
+
restore-keys: ${{ runner.os }}-bundler-
|
|
21
|
+
|
|
22
|
+
- name: Install system dependencies
|
|
23
|
+
run: sudo apt-get update && sudo apt-get install libsqlite3-dev
|
|
24
|
+
|
|
25
|
+
- name: Install gem dependencies
|
|
26
|
+
run: gem install bundler && bundle install --jobs 4 --retry 3 --path vendor/bundle
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: bundle exec rake test
|
data/.gitignore
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,67 +1,152 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
geared_pagination (0.
|
|
4
|
+
geared_pagination (0.2)
|
|
5
5
|
activesupport (>= 5.0)
|
|
6
6
|
addressable (>= 2.5.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
actioncable (6.0.3)
|
|
12
|
+
actionpack (= 6.0.3)
|
|
13
|
+
nio4r (~> 2.0)
|
|
14
|
+
websocket-driver (>= 0.6.1)
|
|
15
|
+
actionmailbox (6.0.3)
|
|
16
|
+
actionpack (= 6.0.3)
|
|
17
|
+
activejob (= 6.0.3)
|
|
18
|
+
activerecord (= 6.0.3)
|
|
19
|
+
activestorage (= 6.0.3)
|
|
20
|
+
activesupport (= 6.0.3)
|
|
21
|
+
mail (>= 2.7.1)
|
|
22
|
+
actionmailer (6.0.3)
|
|
23
|
+
actionpack (= 6.0.3)
|
|
24
|
+
actionview (= 6.0.3)
|
|
25
|
+
activejob (= 6.0.3)
|
|
26
|
+
mail (~> 2.5, >= 2.5.4)
|
|
16
27
|
rails-dom-testing (~> 2.0)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
activesupport (=
|
|
28
|
+
actionpack (6.0.3)
|
|
29
|
+
actionview (= 6.0.3)
|
|
30
|
+
activesupport (= 6.0.3)
|
|
31
|
+
rack (~> 2.0, >= 2.0.8)
|
|
32
|
+
rack-test (>= 0.6.3)
|
|
33
|
+
rails-dom-testing (~> 2.0)
|
|
34
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
35
|
+
actiontext (6.0.3)
|
|
36
|
+
actionpack (= 6.0.3)
|
|
37
|
+
activerecord (= 6.0.3)
|
|
38
|
+
activestorage (= 6.0.3)
|
|
39
|
+
activesupport (= 6.0.3)
|
|
40
|
+
nokogiri (>= 1.8.5)
|
|
41
|
+
actionview (6.0.3)
|
|
42
|
+
activesupport (= 6.0.3)
|
|
20
43
|
builder (~> 3.1)
|
|
21
|
-
|
|
44
|
+
erubi (~> 1.4)
|
|
22
45
|
rails-dom-testing (~> 2.0)
|
|
23
|
-
rails-html-sanitizer (~> 1.
|
|
24
|
-
|
|
46
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
47
|
+
activejob (6.0.3)
|
|
48
|
+
activesupport (= 6.0.3)
|
|
49
|
+
globalid (>= 0.3.6)
|
|
50
|
+
activemodel (6.0.3)
|
|
51
|
+
activesupport (= 6.0.3)
|
|
52
|
+
activerecord (6.0.3)
|
|
53
|
+
activemodel (= 6.0.3)
|
|
54
|
+
activesupport (= 6.0.3)
|
|
55
|
+
activestorage (6.0.3)
|
|
56
|
+
actionpack (= 6.0.3)
|
|
57
|
+
activejob (= 6.0.3)
|
|
58
|
+
activerecord (= 6.0.3)
|
|
59
|
+
marcel (~> 0.3.1)
|
|
60
|
+
activesupport (6.0.3)
|
|
25
61
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
26
|
-
i18n (
|
|
62
|
+
i18n (>= 0.7, < 2)
|
|
27
63
|
minitest (~> 5.1)
|
|
28
64
|
tzinfo (~> 1.1)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
65
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
66
|
+
addressable (2.5.2)
|
|
67
|
+
public_suffix (>= 2.0.2, < 4.0)
|
|
68
|
+
builder (3.2.4)
|
|
69
|
+
byebug (10.0.2)
|
|
70
|
+
concurrent-ruby (1.1.6)
|
|
71
|
+
crass (1.0.6)
|
|
72
|
+
erubi (1.9.0)
|
|
73
|
+
globalid (0.4.2)
|
|
74
|
+
activesupport (>= 4.2.0)
|
|
75
|
+
i18n (1.8.2)
|
|
76
|
+
concurrent-ruby (~> 1.0)
|
|
77
|
+
loofah (2.5.0)
|
|
78
|
+
crass (~> 1.0.2)
|
|
37
79
|
nokogiri (>= 1.5.9)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
80
|
+
mail (2.7.1)
|
|
81
|
+
mini_mime (>= 0.1.1)
|
|
82
|
+
marcel (0.3.3)
|
|
83
|
+
mimemagic (~> 0.3.2)
|
|
84
|
+
method_source (1.0.0)
|
|
85
|
+
mimemagic (0.3.5)
|
|
86
|
+
mini_mime (1.0.2)
|
|
87
|
+
mini_portile2 (2.4.0)
|
|
88
|
+
minitest (5.14.0)
|
|
89
|
+
nio4r (2.5.2)
|
|
90
|
+
nokogiri (1.10.9)
|
|
91
|
+
mini_portile2 (~> 2.4.0)
|
|
92
|
+
public_suffix (3.0.3)
|
|
93
|
+
rack (2.2.2)
|
|
94
|
+
rack-test (1.1.0)
|
|
95
|
+
rack (>= 1.0, < 3)
|
|
96
|
+
rails (6.0.3)
|
|
97
|
+
actioncable (= 6.0.3)
|
|
98
|
+
actionmailbox (= 6.0.3)
|
|
99
|
+
actionmailer (= 6.0.3)
|
|
100
|
+
actionpack (= 6.0.3)
|
|
101
|
+
actiontext (= 6.0.3)
|
|
102
|
+
actionview (= 6.0.3)
|
|
103
|
+
activejob (= 6.0.3)
|
|
104
|
+
activemodel (= 6.0.3)
|
|
105
|
+
activerecord (= 6.0.3)
|
|
106
|
+
activestorage (= 6.0.3)
|
|
107
|
+
activesupport (= 6.0.3)
|
|
108
|
+
bundler (>= 1.3.0)
|
|
109
|
+
railties (= 6.0.3)
|
|
110
|
+
sprockets-rails (>= 2.0.0)
|
|
111
|
+
rails-dom-testing (2.0.3)
|
|
112
|
+
activesupport (>= 4.2.0)
|
|
113
|
+
nokogiri (>= 1.6)
|
|
114
|
+
rails-html-sanitizer (1.3.0)
|
|
115
|
+
loofah (~> 2.3)
|
|
116
|
+
railties (6.0.3)
|
|
117
|
+
actionpack (= 6.0.3)
|
|
118
|
+
activesupport (= 6.0.3)
|
|
119
|
+
method_source
|
|
120
|
+
rake (>= 0.8.7)
|
|
121
|
+
thor (>= 0.20.3, < 2.0)
|
|
122
|
+
rake (13.0.1)
|
|
123
|
+
sprockets (4.0.0)
|
|
124
|
+
concurrent-ruby (~> 1.0)
|
|
125
|
+
rack (> 1, < 3)
|
|
126
|
+
sprockets-rails (3.2.1)
|
|
127
|
+
actionpack (>= 4.0)
|
|
128
|
+
activesupport (>= 4.0)
|
|
129
|
+
sprockets (>= 3.0.0)
|
|
130
|
+
sqlite3 (1.4.2)
|
|
131
|
+
thor (1.0.1)
|
|
52
132
|
thread_safe (0.3.6)
|
|
53
|
-
tzinfo (1.2.
|
|
133
|
+
tzinfo (1.2.7)
|
|
54
134
|
thread_safe (~> 0.1)
|
|
135
|
+
websocket-driver (0.7.1)
|
|
136
|
+
websocket-extensions (>= 0.1.0)
|
|
137
|
+
websocket-extensions (0.1.4)
|
|
138
|
+
zeitwerk (2.3.0)
|
|
55
139
|
|
|
56
140
|
PLATFORMS
|
|
57
141
|
ruby
|
|
58
142
|
|
|
59
143
|
DEPENDENCIES
|
|
60
|
-
actionpack (>= 5)
|
|
61
144
|
bundler (~> 1.12)
|
|
62
145
|
byebug
|
|
63
146
|
geared_pagination!
|
|
147
|
+
rails (>= 5.0)
|
|
64
148
|
rake
|
|
149
|
+
sqlite3
|
|
65
150
|
|
|
66
151
|
BUNDLED WITH
|
|
67
|
-
1.
|
|
152
|
+
1.17.2
|
data/README.md
CHANGED
|
@@ -30,12 +30,57 @@ Showing page <%= @page.number %> of <%= @page.recordset.page_count %> (<%= @page
|
|
|
30
30
|
|
|
31
31
|
<% if @page.last? %>
|
|
32
32
|
No more pages!
|
|
33
|
-
<% else %>
|
|
34
|
-
<%= link_to "Next page", messages_path(page: @page.
|
|
33
|
+
<% else %>
|
|
34
|
+
<%= link_to "Next page", messages_path(page: @page.next_param) %>
|
|
35
35
|
<% end %>
|
|
36
36
|
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Cursor-based pagination
|
|
40
|
+
|
|
41
|
+
By default, Geared Pagination uses *offset-based pagination*: the `page` query parameter contains the page number. Each page’s records are located using a query with an `OFFSET` clause, like so:
|
|
42
|
+
|
|
43
|
+
```sql
|
|
44
|
+
SELECT *
|
|
45
|
+
FROM messages
|
|
46
|
+
ORDER BY created_at DESC
|
|
47
|
+
LIMIT 30
|
|
48
|
+
OFFSET 15
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You may prefer to use *cursor-based pagination* instead. In cursor-based pagination, the `page` parameter contains a “cursor” describing the last row of the previous page. Each page’s records are located using a query with conditions that only match records after the previous page. For example, if the last record on the previous page had a `created_at` value of `2019-01-24T12:35:26.381Z` and an ID of `7354857`, the current page’s records would be found with a query like this one:
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
SELECT *
|
|
55
|
+
FROM messages
|
|
56
|
+
WHERE (created_at = '2019-01-24T12:35:26.381Z' AND id < 7354857)
|
|
57
|
+
OR created_at < '2019-01-24T12:35:26.381Z'
|
|
58
|
+
ORDER BY created_at DESC, id DESC
|
|
59
|
+
LIMIT 30
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Geared Pagination supports cursor-based pagination. To use it, pass the `:order_by` option to `set_page_and_extract_portion_from` in your controllers. Provide the orders to apply to the paginated relation:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
set_page_and_extract_portion_from Message.all, ordered_by: { created_at: :desc, id: :desc }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Geared Pagination uses the ordered attributes (in the above example, `created_at` and `id`) to generate cursors:
|
|
69
|
+
|
|
70
|
+
```erb
|
|
71
|
+
<%= link_to "Next page", messages_path(page: @page.next_param) %>
|
|
72
|
+
<!-- <a href="/messages?page=eyJwYWdlX251...">Next page</a> -->
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Cursors encode the information Geared Pagination needs to query for the corresponding page’s records: the page number for choosing a page size, and the values of each of the ordered attributes (`created_at` and `id`).
|
|
76
|
+
|
|
77
|
+
### When should I use cursor-based pagination?
|
|
78
|
+
|
|
79
|
+
Cursor-based pagination can outperform offset-based pagination when paginating deeply into a large number of records. DBs commonly execute queries with `OFFSET` clauses by counting past `OFFSET` records one at a time, so each page in offset-based pagination takes slightly longer to load than the last. With cursor-based pagination and an appropriate index, the DB can jump directly to the beginning of each page without scanning.
|
|
80
|
+
|
|
81
|
+
The tradeoff is that Geared Pagination only supports cursor-based pagination on simple relations with simple, column-only orders. Cursor-based pagination also won’t perform better than offset-based pagination without an ordered index. Stick with offset-based pagination if:
|
|
82
|
+
* You need complex ordering on a complex relation
|
|
83
|
+
* You’re paginating a small and/or bounded number of records
|
|
39
84
|
|
|
40
85
|
## Caching
|
|
41
86
|
|
data/Rakefile
CHANGED
data/bin/rails
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
|
3
|
+
# installed from the root of your application.
|
|
4
|
+
|
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/geared_pagination/engine', __dir__)
|
|
7
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
|
8
|
+
|
|
9
|
+
# Set up gems listed in the Gemfile.
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
|
11
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
12
|
+
|
|
13
|
+
require 'rails'
|
|
14
|
+
require 'action_controller/railtie'
|
|
15
|
+
require 'rails/test_unit/railtie'
|
|
16
|
+
require 'rails/engine/commands'
|
data/geared_pagination.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'geared_pagination'
|
|
3
|
-
s.version = '0.
|
|
3
|
+
s.version = '1.0.0'
|
|
4
4
|
s.authors = 'David Heinemeier Hansson'
|
|
5
5
|
s.email = 'david@basecamp.com'
|
|
6
6
|
s.summary = 'Paginate Active Record sets at variable speeds'
|
|
@@ -14,6 +14,6 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
|
|
15
15
|
s.add_development_dependency 'bundler', '~> 1.12'
|
|
16
16
|
|
|
17
|
-
s.files
|
|
18
|
-
s.test_files
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
|
19
19
|
end
|
data/lib/geared_pagination.rb
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
require 'geared_pagination/recordset'
|
|
2
|
-
require 'geared_pagination/
|
|
2
|
+
require 'geared_pagination/engine' if defined?(Rails)
|
|
@@ -11,13 +11,13 @@ module GearedPagination
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
private
|
|
14
|
-
def set_page_and_extract_portion_from(records, per_page: nil)
|
|
15
|
-
@page = current_page_from(records, per_page: per_page)
|
|
14
|
+
def set_page_and_extract_portion_from(records, ordered_by: nil, per_page: nil)
|
|
15
|
+
@page = current_page_from(records, ordered_by: ordered_by, per_page: per_page)
|
|
16
16
|
@page.records
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def current_page_from(records, per_page: nil)
|
|
20
|
-
GearedPagination::Recordset.new(records, per_page: per_page).page(current_page_param)
|
|
19
|
+
def current_page_from(records, ordered_by: nil, per_page: nil)
|
|
20
|
+
GearedPagination::Recordset.new(records, ordered_by: ordered_by, per_page: per_page).page(current_page_param)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def set_paginated_headers
|
|
@@ -29,7 +29,7 @@ module GearedPagination
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def current_page_param
|
|
32
|
-
params[:page]
|
|
32
|
+
params[:page]
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'active_support/json'
|
|
3
|
+
|
|
4
|
+
module GearedPagination
|
|
5
|
+
class Cursor
|
|
6
|
+
class << self
|
|
7
|
+
def from_param(key)
|
|
8
|
+
key.present? ? decode(key) : new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def decode(key)
|
|
12
|
+
if attributes = ActiveSupport::JSON.decode(Base64.urlsafe_decode64(key))
|
|
13
|
+
new **attributes.deep_symbolize_keys
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def encode(page_number: 1, values: {})
|
|
18
|
+
Base64.urlsafe_encode64 ActiveSupport::JSON.encode(page_number: page_number, values: values)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :values
|
|
23
|
+
|
|
24
|
+
def initialize(page_number: 1, values: {})
|
|
25
|
+
@page_number, @values = page_number, values
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def page_number
|
|
29
|
+
@page_number > 0 ? @page_number : 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fetch(attribute)
|
|
33
|
+
values.fetch(attribute)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def include?(attribute)
|
|
37
|
+
values.include?(attribute)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -7,29 +7,36 @@ module GearedPagination
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def apply
|
|
10
|
-
|
|
10
|
+
controller.headers.update(headers) if applicable?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
private
|
|
14
|
+
attr_reader :page, :controller
|
|
15
|
+
delegate :request, to: :controller
|
|
16
|
+
|
|
14
17
|
def headers
|
|
15
18
|
Hash.new.tap do |h|
|
|
16
|
-
h["X-Total-Count"] =
|
|
17
|
-
h["Link"] = next_page_link_header unless
|
|
19
|
+
h["X-Total-Count"] = page.recordset.records_count.to_s
|
|
20
|
+
h["Link"] = next_page_link_header unless page.last?
|
|
18
21
|
end
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def applicable?
|
|
22
|
-
|
|
25
|
+
request.format&.json?
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
def next_page_link_header
|
|
26
|
-
link_header(rel: :next,
|
|
29
|
+
link_header(rel: :next, page: page.next_param).to_s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def link_header(rel:, page:)
|
|
33
|
+
%{<#{uri(page: page)}>; rel="#{rel}"}
|
|
27
34
|
end
|
|
28
35
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
def uri(page:)
|
|
37
|
+
Addressable::URI.parse(request.url).tap do |uri|
|
|
38
|
+
uri.query_values = (uri.query_values || {}).merge("page" => page)
|
|
39
|
+
end.to_s
|
|
33
40
|
end
|
|
34
41
|
end
|
|
35
42
|
end
|