rb_pager 0.1.1 → 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/.github/workflows/ruby.yml +16 -2
- data/Gemfile.lock +3 -9
- data/README.md +18 -5
- data/Rakefile +12 -0
- data/lib/rb_pager.rb +0 -1
- data/lib/rb_pager/orm/pager_active_record.rb +86 -61
- data/lib/rb_pager/version.rb +1 -1
- data/rb_pager.gemspec +1 -2
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdd82410767649de7ec56d84ba2c311a08555eb4e53ca47e0974f8e54540cf02
|
4
|
+
data.tar.gz: fcc26daca9475701f37e4e5f259e8921edb2fd961ce64e874df1cf3cca84e33a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 335ff86d22e237f2f14826edaf9357b220e0b48019dc9f0534b7f611370ae7f15fdff6956da2a552c25a9fe316de7b5215079a690485c95a08171dd6d633a9d3
|
7
|
+
data.tar.gz: 12bc348e3abd3f82bfaee15fbd52a8ea15ca4ab9aca9da92cc0e3ba9e3345cb205bda27e4e1b9b05ae44fae84c330a77e978d2e3273027dec25e5eed4089720c
|
data/.github/workflows/ruby.yml
CHANGED
@@ -18,6 +18,16 @@ jobs:
|
|
18
18
|
|
19
19
|
runs-on: ubuntu-latest
|
20
20
|
|
21
|
+
services:
|
22
|
+
db:
|
23
|
+
image: postgres:11.6-alpine
|
24
|
+
env:
|
25
|
+
POSTGRES_PASSWORD: password
|
26
|
+
POSTGRES_DB: rb_pager_test
|
27
|
+
ports:
|
28
|
+
- 5432:5432
|
29
|
+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
30
|
+
|
21
31
|
steps:
|
22
32
|
- uses: actions/checkout@v2
|
23
33
|
- name: Set up Ruby
|
@@ -29,8 +39,12 @@ jobs:
|
|
29
39
|
ruby-version: 2.6
|
30
40
|
- name: Setup System
|
31
41
|
run: |
|
32
|
-
sudo apt-get install
|
42
|
+
sudo apt-get -y install libpq-dev
|
33
43
|
- name: Install dependencies
|
34
44
|
run: bundle install
|
35
45
|
- name: Run tests
|
36
|
-
|
46
|
+
env:
|
47
|
+
DATABASE_URL: postgres://postgres:password@localhost:5432/rb_pager_test
|
48
|
+
run: |
|
49
|
+
bundle exec rake db:create
|
50
|
+
bundle exec rspec
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rb_pager (0.
|
4
|
+
rb_pager (0.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -17,16 +17,12 @@ GEM
|
|
17
17
|
minitest (~> 5.1)
|
18
18
|
tzinfo (~> 1.1)
|
19
19
|
zeitwerk (~> 2.2, >= 2.2.2)
|
20
|
-
coderay (1.1.3)
|
21
20
|
concurrent-ruby (1.1.6)
|
22
21
|
diff-lcs (1.4.4)
|
23
22
|
i18n (1.8.3)
|
24
23
|
concurrent-ruby (~> 1.0)
|
25
|
-
method_source (1.0.0)
|
26
24
|
minitest (5.14.1)
|
27
|
-
|
28
|
-
coderay (~> 1.1)
|
29
|
-
method_source (~> 1.0)
|
25
|
+
pg (1.2.3)
|
30
26
|
rake (13.0.1)
|
31
27
|
rspec (3.9.0)
|
32
28
|
rspec-core (~> 3.9.0)
|
@@ -41,7 +37,6 @@ GEM
|
|
41
37
|
diff-lcs (>= 1.2.0, < 2.0)
|
42
38
|
rspec-support (~> 3.9.0)
|
43
39
|
rspec-support (3.9.3)
|
44
|
-
sqlite3 (1.4.2)
|
45
40
|
thread_safe (0.3.6)
|
46
41
|
tzinfo (1.2.7)
|
47
42
|
thread_safe (~> 0.1)
|
@@ -52,11 +47,10 @@ PLATFORMS
|
|
52
47
|
|
53
48
|
DEPENDENCIES
|
54
49
|
activerecord (>= 5.2)
|
55
|
-
|
50
|
+
pg
|
56
51
|
rake (~> 13.0)
|
57
52
|
rb_pager!
|
58
53
|
rspec (~> 3.0)
|
59
|
-
sqlite3
|
60
54
|
|
61
55
|
BUNDLED WITH
|
62
56
|
2.1.4
|
data/README.md
CHANGED
@@ -38,24 +38,37 @@ The system use `Base64.strict_decode64` and `Base64.strict_encode64` for cursor
|
|
38
38
|
|
39
39
|
```ruby
|
40
40
|
# return first 20 employee records, by default limit set to 20
|
41
|
-
Employee.pager
|
41
|
+
Employee.pager
|
42
42
|
|
43
43
|
# return first 15 employee records
|
44
|
-
Employee.pager(
|
44
|
+
Employee.pager(limit: 15)
|
45
45
|
|
46
46
|
# return first 10 employee records order by created_at asc
|
47
47
|
# /employee?limit=10&sort=created_at
|
48
|
-
Employee.pager(after:
|
48
|
+
Employee.pager(after: 'w33t44==', limit: 10, sort: 'created_at')
|
49
49
|
|
50
50
|
# return first 10 employee records order by created_at desc
|
51
51
|
# /employee?limit=10&sort=-created_at
|
52
|
-
Employee.pager(after:
|
52
|
+
Employee.pager(after: 'w33t44==', limit: 10, sort: '-created_at')
|
53
53
|
|
54
54
|
# [order by non uniq column](https://engineering.shopify.com/blogs/engineering/pagination-relative-cursors)
|
55
55
|
# /employee?limit=10&sort=name,id
|
56
|
-
Employee.pager(after:
|
56
|
+
Employee.pager(after: 'w33t44==', limit: 10, sort: 'name,id')
|
57
|
+
|
58
|
+
# on EmployeesController
|
59
|
+
records, meta = Employee.pager(limit: 15)
|
60
|
+
# meta => { next_cursor => 'uahOI==' }
|
61
|
+
render json: EmployeeSerializer.new(records, meta: meta).serialized_json, status: :ok
|
57
62
|
```
|
58
63
|
|
64
|
+
## On progress
|
65
|
+
|
66
|
+
- [ ] implement `before` cursor
|
67
|
+
- [ ] meta url for first, last, next and previous page, collection size
|
68
|
+
- [ ] [error-cases](https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#auto-id-error-cases)
|
69
|
+
- [ ] custom encoder
|
70
|
+
- [ ] ui helper
|
71
|
+
|
59
72
|
## Development
|
60
73
|
|
61
74
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
@@ -4,3 +4,15 @@ require "rspec/core/rake_task"
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
6
|
task :default => :spec
|
7
|
+
|
8
|
+
namespace :db do
|
9
|
+
desc "Create the test DB"
|
10
|
+
task :create do
|
11
|
+
`createdb rb_pager_test`
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Drop the test DB"
|
15
|
+
task :drop do
|
16
|
+
`dropdb rb_pager_test`
|
17
|
+
end
|
18
|
+
end
|
data/lib/rb_pager.rb
CHANGED
@@ -4,99 +4,124 @@ module RbPager
|
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
AR_ORDER = { '+' => :asc, '-' => :desc }
|
7
|
-
AREL_ORDER = { asc: :gt, desc: :lt }
|
8
|
-
|
9
|
-
def pager(after: nil, limit: nil, sort: nil)
|
10
|
-
page_limit = limit || RbPager.configuration.limit
|
11
|
-
sort_params = sort
|
12
7
|
|
8
|
+
def pager(after: nil, before: nil, limit: nil, sort: nil)
|
13
9
|
raise InvalidLimitValueError if limit && limit < 1
|
10
|
+
instance_variable_set(:@sorted_columns, nil)
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
page_limit = limit || RbPager.configuration.limit
|
13
|
+
@sort = sort
|
14
|
+
@after = decode(after)
|
15
|
+
@before = decode(before)
|
16
|
+
@direction = :next
|
17
|
+
|
18
|
+
collection = where(apply_after)
|
19
|
+
collection = collection.where(apply_before)
|
20
|
+
collection = collection.order(sorted_columns)
|
21
|
+
.extending(ActiveRecordRelationMethods)
|
22
|
+
.limit(page_limit)
|
23
|
+
|
24
|
+
create_paginate_meta(collection)
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
27
28
|
|
28
|
-
def
|
29
|
-
|
30
|
-
return arel_table[primary_key].gt(decode_cursor_params[primary_key]) if sorted_columns.blank?
|
29
|
+
def decode(cursor)
|
30
|
+
return nil if cursor.nil?
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
decode = Base64.strict_decode64(cursor)
|
33
|
+
Hash[
|
34
|
+
decode.split(',').map do |pair|
|
35
|
+
k, v = pair.split(':', 2)
|
36
|
+
end
|
37
|
+
]
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
def apply_after
|
41
|
+
return nil if @after.nil?
|
42
|
+
|
43
|
+
if sorted_columns.values.all? :asc
|
44
|
+
Arel::Nodes::GreaterThan.new(
|
45
|
+
Arel::Nodes::Grouping.new(@after.keys.map{ |col| arel_table[col] }),
|
46
|
+
Arel::Nodes::Grouping.new(@after.values.map{ |col| Arel::Nodes.build_quoted(col) })
|
47
|
+
)
|
48
|
+
else
|
49
|
+
@direction = :prev
|
50
|
+
Arel::Nodes::LessThan.new(
|
51
|
+
Arel::Nodes::Grouping.new(@after.keys.map{|col| arel_table[col]}),
|
52
|
+
Arel::Nodes::Grouping.new(@after.values.map{ |col| Arel::Nodes.build_quoted(col) })
|
53
|
+
)
|
46
54
|
end
|
47
|
-
|
48
|
-
result
|
49
55
|
end
|
50
56
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
def apply_before
|
58
|
+
return nil if @before.nil?
|
59
|
+
|
60
|
+
if sorted_columns.values.all? :asc
|
61
|
+
@direction = :prev
|
62
|
+
Arel::Nodes::LessThan.new(
|
63
|
+
Arel::Nodes::Grouping.new(@before.keys.map{ |col| arel_table[col] }),
|
64
|
+
Arel::Nodes::Grouping.new(@before.values.map{ |col| Arel::Nodes.build_quoted(col) })
|
65
|
+
)
|
66
|
+
else
|
67
|
+
Arel::Nodes::GreaterThan.new(
|
68
|
+
Arel::Nodes::Grouping.new(@before.keys.map{|col| arel_table[col]}),
|
69
|
+
Arel::Nodes::Grouping.new(@before.values.map{ |col| Arel::Nodes.build_quoted(col) })
|
70
|
+
)
|
60
71
|
end
|
61
|
-
|
62
|
-
result
|
63
72
|
end
|
64
73
|
|
65
|
-
def
|
66
|
-
|
74
|
+
def sorted_columns
|
75
|
+
@sorted_columns ||= begin
|
76
|
+
sorted_columns = {} if @sort.nil?
|
77
|
+
sorted_columns ||= construct_sorted_columns
|
78
|
+
end
|
79
|
+
end
|
67
80
|
|
68
|
-
|
81
|
+
def construct_sorted_columns
|
69
82
|
sorted_params = {}
|
70
|
-
|
71
|
-
|
72
|
-
sort_params.split(',').each do |field|
|
73
|
-
next unless attribute_names.include?(field)
|
83
|
+
fields = @sort.split(',')
|
74
84
|
|
85
|
+
fields.each do |field|
|
75
86
|
sort_sign = field =~ /\A[+-]/ ? field.slice!(0) : '+'
|
76
|
-
|
77
|
-
sorted_params[field] = AR_ORDER[sort_sign]
|
87
|
+
sorted_params[field] = AR_ORDER[sort_sign] if attribute_names.include?(field)
|
78
88
|
end
|
79
89
|
|
80
|
-
|
90
|
+
sorted_params
|
81
91
|
end
|
82
92
|
|
83
|
-
def create_paginate_meta(collection
|
84
|
-
|
93
|
+
def create_paginate_meta(collection)
|
94
|
+
cursor = cursor(collection)
|
85
95
|
|
86
|
-
meta = {
|
96
|
+
meta = { prev_cursor: cursor.first, next_cursor: cursor.last }
|
87
97
|
[collection, meta]
|
88
98
|
end
|
89
99
|
|
90
|
-
def
|
91
|
-
return ''
|
100
|
+
def cursor(collection)
|
101
|
+
return ['', ''] if collection.total_size.zero?
|
92
102
|
|
93
|
-
next_cursor =
|
103
|
+
prev_cursor, next_cursor = [], []
|
94
104
|
|
95
|
-
sorted_columns.
|
96
|
-
|
105
|
+
if sorted_columns.blank?
|
106
|
+
prev_cursor = ["#{primary_key}:#{collection.first.send(primary_key)}"]
|
107
|
+
next_cursor = ["#{primary_key}:#{collection.last.send(primary_key)}"]
|
108
|
+
else
|
109
|
+
sorted_columns.each do |key, _value|
|
110
|
+
if type_for_attribute(key).type.eql? :datetime
|
111
|
+
prev_cursor << "#{key}:#{collection.first.send(key).rfc3339(9)}"
|
112
|
+
next_cursor << "#{key}:#{collection.last.send(key).rfc3339(9)}"
|
113
|
+
next
|
114
|
+
end
|
115
|
+
|
116
|
+
prev_cursor << "#{key}:#{collection.first.send(key)}"
|
117
|
+
next_cursor << "#{key}:#{collection.last.send(key)}"
|
118
|
+
end
|
97
119
|
end
|
98
120
|
|
99
|
-
Base64.strict_encode64(next_cursor.
|
121
|
+
return ['', Base64.strict_encode64(next_cursor.join(','))] if (@after.nil? && @before.nil?) || @direction.eql?(:prev) && !collection.left_over?
|
122
|
+
return [Base64.strict_encode64(prev_cursor.join(',')), ''] if @direction.eql?(:next) && !collection.left_over?
|
123
|
+
|
124
|
+
[Base64.strict_encode64(prev_cursor.join(',')), Base64.strict_encode64(next_cursor.join(','))]
|
100
125
|
end
|
101
126
|
end
|
102
127
|
|
data/lib/rb_pager/version.rb
CHANGED
data/rb_pager.gemspec
CHANGED
@@ -24,8 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "activerecord", ">= 5.2"
|
25
25
|
spec.add_development_dependency "rake", "~> 13.0"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
-
spec.add_development_dependency "
|
28
|
-
spec.add_development_dependency "pry"
|
27
|
+
spec.add_development_dependency "pg"
|
29
28
|
|
30
29
|
# Specify which files should be added to the gem when it is released.
|
31
30
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rb_pager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BambangSinaga
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -53,21 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry
|
56
|
+
name: pg
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - ">="
|