graphql-page_cursors 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/lib/graphql/page_cursors.rb +11 -102
- data/lib/page_cursor.rb +40 -0
- data/lib/page_cursor_resolver.rb +111 -0
- data/lib/page_cursors/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86e6f9c53e954f6da661bd84e583650bf1238c9c20708e7d8b271670d9132917
|
4
|
+
data.tar.gz: b145ce2ebc6d45729da5adfe14901cd992f6dec597ca3c84537015b361cc0389
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd4075765ff52c8d216824e21b62f265522498f196fcd07eb297c344382823ffd797e2856b07dfa9454847193fa108f67815acfe4afe41ef18dae41d06afc6a9
|
7
|
+
data.tar.gz: 957064f909047bb83a7761e8d75f21f31ee4bba38d47812ec6c3380f6a7b4a5ed6ed06e3f0a0becb2c02b42e76a1a128674f6a5ebf176b4bd3ae2beaaf99e821
|
data/.rubocop.yml
CHANGED
data/lib/graphql/page_cursors.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'graphql'
|
4
4
|
|
5
|
+
require 'page_cursor_resolver'
|
6
|
+
|
5
7
|
module GraphQL
|
6
8
|
class PageCursor < GraphQL::Schema::Object
|
7
9
|
field :cursor, String, 'first cursor on the page', null: false
|
@@ -21,116 +23,23 @@ module GraphQL
|
|
21
23
|
field :total_pages, Int, null: true
|
22
24
|
field :total_count, Int, null: true
|
23
25
|
|
24
|
-
|
26
|
+
attr_reader :resolver
|
25
27
|
|
26
|
-
def
|
27
|
-
|
28
|
+
def initialize(object, context)
|
29
|
+
super(object, context)
|
30
|
+
@resolver = PageCursorResolver.new(object, context)
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
first: first_cursor,
|
32
|
-
last: last_cursor,
|
33
|
-
previous: previous_cursor
|
34
|
-
}.compact
|
33
|
+
def page_cursors
|
34
|
+
resolver.page_cursors
|
35
35
|
end
|
36
36
|
|
37
37
|
def total_pages
|
38
|
-
|
39
|
-
return 1 if nodes_per_page.nil?
|
40
|
-
|
41
|
-
(object.items.size.to_f / nodes_per_page).ceil
|
38
|
+
resolver.total_pages
|
42
39
|
end
|
43
40
|
|
44
41
|
def total_count
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def around_cursors
|
51
|
-
around_page_numbers.map { |page_num| page_cursor(page_num) }
|
52
|
-
end
|
53
|
-
|
54
|
-
def first_cursor
|
55
|
-
exceeds_max_cursor_count = total_pages > MAX_CURSOR_COUNT
|
56
|
-
include_first_cursor =
|
57
|
-
exceeds_max_cursor_count && around_page_numbers.exclude?(1)
|
58
|
-
return unless include_first_cursor
|
59
|
-
|
60
|
-
page_cursor(1)
|
61
|
-
end
|
62
|
-
|
63
|
-
def last_cursor
|
64
|
-
exceeds_max_cursor_count = total_pages > MAX_CURSOR_COUNT
|
65
|
-
include_last_cursor =
|
66
|
-
exceeds_max_cursor_count && around_page_numbers.exclude?(total_pages)
|
67
|
-
return unless include_last_cursor
|
68
|
-
|
69
|
-
page_cursor(total_pages)
|
70
|
-
end
|
71
|
-
|
72
|
-
def previous_cursor
|
73
|
-
include_previous_cursor = current_page > 1
|
74
|
-
return unless include_previous_cursor
|
75
|
-
|
76
|
-
page_cursor(current_page - 1)
|
77
|
-
end
|
78
|
-
|
79
|
-
def page_cursor(page_num)
|
80
|
-
{
|
81
|
-
cursor: cursor_for_page(page_num),
|
82
|
-
is_current: current_page == page_num,
|
83
|
-
page: page_num
|
84
|
-
}
|
85
|
-
end
|
86
|
-
|
87
|
-
def cursor_for_page(page_num)
|
88
|
-
return '' if page_num == 1
|
89
|
-
|
90
|
-
after_cursor = (page_num - 1) * nodes_per_page
|
91
|
-
encode(after_cursor.to_s)
|
92
|
-
end
|
93
|
-
|
94
|
-
def current_page
|
95
|
-
nodes_before / nodes_per_page + 1
|
96
|
-
end
|
97
|
-
|
98
|
-
def around_page_numbers # rubocop:disable Metrics/AbcSize
|
99
|
-
if total_pages <= MAX_CURSOR_COUNT
|
100
|
-
(1..total_pages).to_a
|
101
|
-
elsif current_page <= 3
|
102
|
-
(1..4).to_a
|
103
|
-
elsif current_page >= total_pages - 2
|
104
|
-
((total_pages - 3)..total_pages).to_a
|
105
|
-
else
|
106
|
-
[current_page - 1, current_page, current_page + 1]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def nodes_before
|
111
|
-
node_offset(object.edge_nodes.first) - 1
|
112
|
-
end
|
113
|
-
|
114
|
-
def nodes_after
|
115
|
-
node_offset(object.edge_nodes.last)
|
116
|
-
end
|
117
|
-
|
118
|
-
def node_offset(node)
|
119
|
-
# this was previously accomplished by calling a private method:
|
120
|
-
# object.send(:offset_from_cursor, object.cursor_from_node(object.edge_nodes.first))
|
121
|
-
object.items.index(node) + 1
|
122
|
-
end
|
123
|
-
|
124
|
-
def nodes_per_page
|
125
|
-
object.first || object.last
|
126
|
-
end
|
127
|
-
|
128
|
-
# borrowed from https://graphql-ruby.org/api-doc/1.10.6/Base64Bp.html
|
129
|
-
def encode(value)
|
130
|
-
str = Base64.strict_encode64(value)
|
131
|
-
str.tr!('+/', '-_')
|
132
|
-
str.delete!('=')
|
133
|
-
str
|
42
|
+
resolver.total_count
|
134
43
|
end
|
135
44
|
end
|
136
45
|
end
|
data/lib/page_cursor.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class PageCursor
|
6
|
+
def self.as_hash(current_page, page_number, per_page)
|
7
|
+
new(current_page, page_number, per_page).to_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :current_page, :page_number, :per_page
|
11
|
+
|
12
|
+
def initialize(current_page, page_number, per_page)
|
13
|
+
@current_page = current_page
|
14
|
+
@page_number = page_number
|
15
|
+
@per_page = per_page
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_hash
|
19
|
+
{
|
20
|
+
cursor: encoded_cursor,
|
21
|
+
is_current: current?,
|
22
|
+
page: page_number
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def current?
|
29
|
+
current_page == page_number
|
30
|
+
end
|
31
|
+
|
32
|
+
def encoded_cursor
|
33
|
+
previous_page = page_number - 1
|
34
|
+
return '' if previous_page.zero?
|
35
|
+
|
36
|
+
after_cursor = previous_page * per_page
|
37
|
+
encoded = Base64.strict_encode64(after_cursor.to_s)
|
38
|
+
encoded.tr('+/', '-_').delete('=')
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'page_cursor'
|
4
|
+
|
5
|
+
class PageCursorResolver
|
6
|
+
MAX_CURSOR_COUNT = 5
|
7
|
+
|
8
|
+
attr_reader :object, :context
|
9
|
+
|
10
|
+
def initialize(object, context)
|
11
|
+
@object = object
|
12
|
+
@context = context
|
13
|
+
end
|
14
|
+
|
15
|
+
def page_cursors
|
16
|
+
return if total_pages <= 1
|
17
|
+
|
18
|
+
{
|
19
|
+
around: around_cursors,
|
20
|
+
first: first_cursor,
|
21
|
+
last: last_cursor,
|
22
|
+
previous: previous_cursor
|
23
|
+
}.compact
|
24
|
+
end
|
25
|
+
|
26
|
+
def total_pages
|
27
|
+
return 0 if object_items.size.zero?
|
28
|
+
return 1 if nodes_per_page.nil?
|
29
|
+
|
30
|
+
(object_items.size.to_f / nodes_per_page).ceil
|
31
|
+
end
|
32
|
+
|
33
|
+
def total_count
|
34
|
+
object_items.size
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def object_items
|
40
|
+
return object.nodes unless object.respond_to?(:items)
|
41
|
+
|
42
|
+
object.items
|
43
|
+
end
|
44
|
+
|
45
|
+
def around_cursors
|
46
|
+
around_page_numbers.map { |page_num| page_cursor(page_num) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def first_cursor
|
50
|
+
exceeds_max_cursor_count = total_pages > MAX_CURSOR_COUNT
|
51
|
+
include_first_cursor =
|
52
|
+
exceeds_max_cursor_count && !around_page_numbers.include?(1)
|
53
|
+
return unless include_first_cursor
|
54
|
+
|
55
|
+
page_cursor(1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def last_cursor
|
59
|
+
exceeds_max_cursor_count = total_pages > MAX_CURSOR_COUNT
|
60
|
+
include_last_cursor =
|
61
|
+
exceeds_max_cursor_count && !around_page_numbers.include?(total_pages)
|
62
|
+
return unless include_last_cursor
|
63
|
+
|
64
|
+
page_cursor(total_pages)
|
65
|
+
end
|
66
|
+
|
67
|
+
def previous_cursor
|
68
|
+
include_previous_cursor = current_page > 1
|
69
|
+
return unless include_previous_cursor
|
70
|
+
|
71
|
+
page_cursor(current_page - 1)
|
72
|
+
end
|
73
|
+
|
74
|
+
def page_cursor(page_number)
|
75
|
+
PageCursor.as_hash(current_page, page_number, nodes_per_page)
|
76
|
+
end
|
77
|
+
|
78
|
+
def current_page
|
79
|
+
nodes_before / nodes_per_page + 1
|
80
|
+
end
|
81
|
+
|
82
|
+
def around_page_numbers # rubocop:disable Metrics/AbcSize
|
83
|
+
if total_pages <= MAX_CURSOR_COUNT
|
84
|
+
(1..total_pages).to_a
|
85
|
+
elsif current_page <= 3
|
86
|
+
(1..4).to_a
|
87
|
+
elsif current_page >= total_pages - 2
|
88
|
+
((total_pages - 3)..total_pages).to_a
|
89
|
+
else
|
90
|
+
[current_page - 1, current_page, current_page + 1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def nodes_before
|
95
|
+
node_offset(object.edge_nodes.first) - 1
|
96
|
+
end
|
97
|
+
|
98
|
+
def nodes_after
|
99
|
+
node_offset(object.edge_nodes.last)
|
100
|
+
end
|
101
|
+
|
102
|
+
def node_offset(node)
|
103
|
+
# this was previously accomplished by calling a private method:
|
104
|
+
# object.send(:offset_from_cursor, object.cursor_from_node(object.edge_nodes.first))
|
105
|
+
object_items.index(node) + 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def nodes_per_page
|
109
|
+
object.first || object.last
|
110
|
+
end
|
111
|
+
end
|
data/lib/page_cursors/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-page_cursors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Allured
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -104,6 +104,8 @@ files:
|
|
104
104
|
- bin/setup
|
105
105
|
- graphql-page_cursors.gemspec
|
106
106
|
- lib/graphql/page_cursors.rb
|
107
|
+
- lib/page_cursor.rb
|
108
|
+
- lib/page_cursor_resolver.rb
|
107
109
|
- lib/page_cursors/version.rb
|
108
110
|
homepage: https://github.com/artsy/graphql-page_cursors
|
109
111
|
licenses:
|