graphql-connections 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1206b9dbdd7d135f43b52b0a62b4d28b4d5d8c93bb5e8a18800ef5abcc5a6157
4
- data.tar.gz: 509ba8323078d1dee9095c18ec227b8416967ad0ce51bb4e3bd2806f09a28a57
3
+ metadata.gz: ed09e36d3d30a8e2cd939a1b9b39341168478d547e4b0e990798e612f6f83e49
4
+ data.tar.gz: bc35f6d38dcebf5a97ce0cf69d6540db2712046bddc15f5c1bca0c685115eac4
5
5
  SHA512:
6
- metadata.gz: 63f191221ff5b1e2133ad84bb8cb377941cad75de324310f18734729f5a519dae01f466a88539e895a155f32e39eccfab60f423271a220b4cf3fcfa33bfdbb3b
7
- data.tar.gz: da4d990bcc8941fbce16049f0a4ea6aa642f73194a34bfb757c1c0080bd16da1736e4466f2ee7f2868dcc5ceebc8677b745a6c3ea43328561af8ad09f9c9e71f
6
+ metadata.gz: 2408beb9fb3346e5ce7e980b462dc34762d5507bb65eb9c1051672d1dc0b8d33c2807d5113b0837a704ad3311bcbc27baeddb5bd4bb1177e8e748d00ca0f2b98
7
+ data.tar.gz: 042f6ee93e8fbc8391122ac5546bf18c3be595b2c45504daeff6dbec16240278141ad3e9a5d145e30a14c84e70d37ef03fb242e282c110f41ffd2605ebca81c4
data/README.md CHANGED
@@ -59,7 +59,9 @@ Also, you can pass the `:desc` option to reverse the relation:
59
59
  GraphQL::Connections::Stable.new(Message.all, keys: %w[name id], desc: true)
60
60
  ```
61
61
 
62
- **NOTE:** `:desc` option is not implemented for stable connections with `:primary_key` passed; if you need it—use keyset pagination or implement `:desc` option for us 🙂.
62
+ ```ruby
63
+ GraphQL::Connections::Stable.new(Message.all, primary_key: :created_at, desc: true)
64
+ ```
63
65
 
64
66
  Also, you can disable opaque cursors by setting `opaque_cursor` param:
65
67
 
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Connections
5
+ # Implements pagination by one field with asc order
6
+ module PrimaryKey
7
+ class Asc < Base
8
+ PAGE_COMPARABLE_METHODS = {
9
+ previous: {query: :lt, cursor: :lteq},
10
+ next: {query: :gt, cursor: :gteq}
11
+ }
12
+
13
+ SLICED_COMPARABLE_METHODS = {
14
+ after: :gt,
15
+ before: :lt
16
+ }.freeze
17
+
18
+ private
19
+
20
+ def first_limited_sorted_table
21
+ arel_table[primary_key].asc
22
+ end
23
+
24
+ def last_limited_sorted_table
25
+ arel_table[primary_key].desc
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Connections
5
+ module PrimaryKey
6
+ # Base class for PrimaryKey pagination implementations
7
+ class Base < ::GraphQL::Connections::Base
8
+ COMPARABLE_METHODS = %i[
9
+ gt lt lteq gteq
10
+ ].freeze
11
+
12
+ def initialize(*args, primary_key: nil, **kwargs)
13
+ @primary_key = primary_key
14
+
15
+ super(*args, **kwargs)
16
+ end
17
+
18
+ def has_previous_page
19
+ if last
20
+ nodes.any? && items_exist?(type: :query, search: nodes.first[primary_key], page_type: :previous)
21
+ elsif after
22
+ items_exist?(type: :cursor, search: after_cursor, page_type: :previous)
23
+ else
24
+ false
25
+ end
26
+ end
27
+
28
+ def has_next_page
29
+ if first
30
+ items_exist?(type: :query, search: nodes.last[primary_key], page_type: :next)
31
+ elsif before
32
+ items_exist?(type: :cursor, search: before_cursor, page_type: :next)
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def cursor_for(item)
39
+ cursor = serialize(item[primary_key])
40
+ cursor = encode(cursor) if opaque_cursor
41
+ cursor
42
+ end
43
+
44
+ private
45
+
46
+ def page_comparable_method(query_type:, page_type:)
47
+ self.class::PAGE_COMPARABLE_METHODS.fetch(page_type).fetch(query_type)
48
+ end
49
+
50
+ def items_exist?(type:, search:, page_type:)
51
+ comparable_method = page_comparable_method(query_type: type, page_type: page_type)
52
+
53
+ if COMPARABLE_METHODS.exclude?(comparable_method)
54
+ raise ArgumentError.new("Unknown #{comparable_method} comparable type. Allowed #{COMPARABLE_METHODS.join(", ")}")
55
+ end
56
+
57
+ items.where(arel_table[primary_key].send(comparable_method, search)).exists?
58
+ end
59
+
60
+ def limited_relation
61
+ scope = sliced_relation
62
+ nodes = []
63
+
64
+ if first
65
+ nodes |=
66
+ scope
67
+ .reorder(first_limited_sorted_table)
68
+ .limit(first)
69
+ .to_a
70
+ end
71
+
72
+ if last
73
+ nodes |=
74
+ scope
75
+ .reorder(last_limited_sorted_table)
76
+ .limit(last)
77
+ .to_a.reverse!
78
+ end
79
+
80
+ nodes
81
+ end
82
+
83
+ def sliced_relation
84
+ items
85
+ .yield_self { |s| after ? sliced_items(items: s, cursor: after_cursor, type: :after) : s }
86
+ .yield_self { |s| before ? sliced_items(items: s, cursor: before_cursor, type: :before) : s }
87
+ end
88
+
89
+ def sliced_items(items:, cursor:, type:)
90
+ items.where(arel_table[primary_key].send(sliced_comparable_method(type), cursor))
91
+ end
92
+
93
+ def sliced_comparable_method(type)
94
+ self.class::SLICED_COMPARABLE_METHODS.fetch(type.to_sym)
95
+ end
96
+
97
+ def first_limited_sorted_table
98
+ raise NotImplementedError.new("method \"#{__method__}\" should be implemented in #{self.class.name} class")
99
+ end
100
+
101
+ def last_limited_sorted_table
102
+ raise NotImplementedError.new("method \"#{__method__}\" should be implemented in #{self.class.name} class")
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Connections
5
+ # Implements pagination by one field with desc order
6
+ module PrimaryKey
7
+ class Desc < Base
8
+ PAGE_COMPARABLE_METHODS = {
9
+ previous: {query: :gt, cursor: :gteq},
10
+ next: {query: :lt, cursor: :lteq}
11
+ }.freeze
12
+
13
+ SLICED_COMPARABLE_METHODS = {
14
+ after: :lt,
15
+ before: :gt
16
+ }.freeze
17
+
18
+ private
19
+
20
+ def first_limited_sorted_table
21
+ arel_table[primary_key].desc
22
+ end
23
+
24
+ def last_limited_sorted_table
25
+ arel_table[primary_key].asc
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -14,9 +14,9 @@ module GraphQL
14
14
  module Stable
15
15
  def self.new(*args, desc: false, keys: nil, **kwargs)
16
16
  if kwargs[:primary_key] || keys.nil?
17
- raise NotImplementedError, "desc connection is not implemented yet" if desc
17
+ cls = desc ? GraphQL::Connections::PrimaryKey::Desc : GraphQL::Connections::PrimaryKey::Asc
18
18
 
19
- return GraphQL::Connections::KeyAsc.new(*args, **kwargs)
19
+ return cls.new(*args, **kwargs)
20
20
  end
21
21
 
22
22
  raise ArgumentError, "keyset for more that 2 keys is not implemented yet" if keys.length > 2
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Paging
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.0"
6
6
  end
7
7
  end
@@ -11,10 +11,13 @@ end
11
11
  require "graphql/connections/stable"
12
12
 
13
13
  require "graphql/connections/base"
14
- require "graphql/connections/key_asc"
15
14
 
16
15
  require "graphql/connections/keyset/base"
17
16
  require "graphql/connections/keyset/asc"
18
17
  require "graphql/connections/keyset/desc"
19
18
 
19
+ require "graphql/connections/primary_key/base"
20
+ require "graphql/connections/primary_key/asc"
21
+ require "graphql/connections/primary_key/desc"
22
+
20
23
  require "graphql/connections/chewy"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-connections
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Misha Merkushin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-04 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -210,10 +210,12 @@ files:
210
210
  - lib/graphql/connections.rb
211
211
  - lib/graphql/connections/base.rb
212
212
  - lib/graphql/connections/chewy.rb
213
- - lib/graphql/connections/key_asc.rb
214
213
  - lib/graphql/connections/keyset/asc.rb
215
214
  - lib/graphql/connections/keyset/base.rb
216
215
  - lib/graphql/connections/keyset/desc.rb
216
+ - lib/graphql/connections/primary_key/asc.rb
217
+ - lib/graphql/connections/primary_key/base.rb
218
+ - lib/graphql/connections/primary_key/desc.rb
217
219
  - lib/graphql/connections/stable.rb
218
220
  - lib/graphql/connections/version.rb
219
221
  homepage: https://github.com/bibendi/graphql-connections
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphQL
4
- module Connections
5
- # Implements pagination by one field with asc order
6
- class KeyAsc < ::GraphQL::Connections::Base
7
- def initialize(*args, primary_key: nil, **kwargs)
8
- @primary_key = primary_key
9
-
10
- super(*args, **kwargs)
11
- end
12
-
13
- def has_previous_page
14
- if last
15
- nodes.any? && items.where(arel_table[primary_key].lt(nodes.first[primary_key])).exists?
16
- elsif after
17
- items.where(arel_table[primary_key].lteq(after_cursor)).exists?
18
- else
19
- false
20
- end
21
- end
22
-
23
- def has_next_page
24
- if first
25
- nodes.any? && items.where(arel_table[primary_key].gt(nodes.last[primary_key])).exists?
26
- elsif before
27
- items.where(arel_table[primary_key].gteq(before_cursor)).exists?
28
- else
29
- false
30
- end
31
- end
32
-
33
- def cursor_for(item)
34
- cursor = serialize(item[primary_key])
35
- cursor = encode(cursor) if opaque_cursor
36
- cursor
37
- end
38
-
39
- private
40
-
41
- def limited_relation
42
- scope = sliced_relation
43
- nodes = []
44
-
45
- if first
46
- nodes |= scope
47
- .reorder(arel_table[primary_key].asc)
48
- .limit(first)
49
- .to_a
50
- end
51
-
52
- if last
53
- nodes |= scope
54
- .reorder(arel_table[primary_key].desc)
55
- .limit(last)
56
- .to_a.reverse!
57
- end
58
-
59
- nodes
60
- end
61
-
62
- def sliced_relation
63
- items
64
- .yield_self { |s| after ? s.where(arel_table[primary_key].gt(after_cursor)) : s }
65
- .yield_self { |s| before ? s.where(arel_table[primary_key].lt(before_cursor)) : s }
66
- end
67
- end
68
- end
69
- end