grumlin 0.5.1 → 0.7.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/.overcommit.yml +0 -3
- data/.tool-versions +1 -0
- data/Gemfile.lock +3 -3
- data/bin/console +1 -1
- data/lib/async/channel.rb +1 -1
- data/lib/grumlin/anonymous_step.rb +6 -6
- data/lib/grumlin/client.rb +45 -16
- data/lib/grumlin/order.rb +7 -8
- data/lib/grumlin/p.rb +1 -1
- data/lib/grumlin/pop.rb +7 -18
- data/lib/grumlin/request_dispatcher.rb +6 -0
- data/lib/grumlin/t.rb +7 -8
- data/lib/grumlin/translator.rb +4 -0
- data/lib/grumlin/transport.rb +50 -35
- data/lib/grumlin/traversal.rb +4 -5
- data/lib/grumlin/typed_value.rb +19 -0
- data/lib/grumlin/u.rb +4 -1
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +2 -2
- 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: 4260297c3d75ec7bc1f987d8c7bad4efd75c81627814fdf984a0aaac0045332b
|
4
|
+
data.tar.gz: 1cb521906a08cf5390f058a28f380492a2999e55ef023c473fc6bae64533b53b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 992d2192402f3fe282203a3fd0eff1e6c886470899cb37f00ca2abf1392e4d946c7391be0eb7d6560c9941f7a17a3552682a8a47f56273f4299c06551c1e9ffc
|
7
|
+
data.tar.gz: d8695e2485915cb7b4d37a755e42d20b6d7f575d906ae5a0aca5f508c01a104c08f7bb51a2285c9dac48a0091771882afeb405fd26e7802c4d48513274e2895f
|
data/.overcommit.yml
CHANGED
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.6.6
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (0.7.0)
|
5
5
|
async-pool (~> 0.3)
|
6
6
|
async-websocket (~> 0.19)
|
7
7
|
|
@@ -70,7 +70,7 @@ GEM
|
|
70
70
|
ast (~> 2.4.1)
|
71
71
|
protocol-hpack (1.4.2)
|
72
72
|
protocol-http (0.22.5)
|
73
|
-
protocol-http1 (0.14.
|
73
|
+
protocol-http1 (0.14.2)
|
74
74
|
protocol-http (~> 0.22)
|
75
75
|
protocol-http2 (0.14.2)
|
76
76
|
protocol-hpack (~> 1.4)
|
@@ -166,4 +166,4 @@ DEPENDENCIES
|
|
166
166
|
solargraph
|
167
167
|
|
168
168
|
BUNDLED WITH
|
169
|
-
2.2.
|
169
|
+
2.2.26
|
data/bin/console
CHANGED
data/lib/async/channel.rb
CHANGED
@@ -4,23 +4,23 @@ module Grumlin
|
|
4
4
|
class AnonymousStep
|
5
5
|
attr_reader :name, :args
|
6
6
|
|
7
|
+
# TODO: add other steps
|
8
|
+
SUPPORTED_STEPS = %w[E V addE addV as by coalesce count dedup drop elementMap emit fold from group groupCount has
|
9
|
+
hasId hasLabel hasNot in inV label limit not order out outE path project property repeat select
|
10
|
+
to unfold valueMap values where].freeze
|
11
|
+
|
7
12
|
def initialize(name, *args, previous_steps: [])
|
8
13
|
@name = name
|
9
14
|
@previous_steps = previous_steps
|
10
15
|
@args = args
|
11
16
|
end
|
12
17
|
|
13
|
-
|
14
|
-
not outE groupCount label group in out fold unfold inV path dedup project coalesce repeat emit
|
15
|
-
elementMap where].each do |step|
|
18
|
+
SUPPORTED_STEPS.each do |step|
|
16
19
|
define_method step do |*args|
|
17
20
|
add_step(step, args, previous_steps: steps)
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
|
-
alias addVertex addV
|
22
|
-
alias addEdge addE
|
23
|
-
|
24
24
|
def inspect
|
25
25
|
@inspect ||= to_bytecode.to_s
|
26
26
|
end
|
data/lib/grumlin/client.rb
CHANGED
@@ -13,6 +13,7 @@ module Grumlin
|
|
13
13
|
def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
|
14
14
|
super(concurrency)
|
15
15
|
@client = client_factory.call(url, parent).tap(&:connect)
|
16
|
+
@parent = parent
|
16
17
|
end
|
17
18
|
|
18
19
|
def closed?
|
@@ -25,34 +26,67 @@ module Grumlin
|
|
25
26
|
|
26
27
|
def write(*args)
|
27
28
|
@client.write(*args)
|
29
|
+
ensure
|
30
|
+
@count += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def viable?
|
34
|
+
!closed?
|
35
|
+
end
|
36
|
+
|
37
|
+
def reusable?
|
38
|
+
!closed?
|
28
39
|
end
|
29
40
|
end
|
30
41
|
|
42
|
+
include Console
|
43
|
+
|
44
|
+
# Client is not reusable. Once closed should be recreated.
|
31
45
|
def initialize(url, parent: Async::Task.current, **client_options)
|
32
46
|
@url = url
|
33
47
|
@client_options = client_options
|
34
48
|
@parent = parent
|
35
|
-
|
49
|
+
@request_dispatcher = nil
|
50
|
+
@transport = nil
|
36
51
|
end
|
37
52
|
|
38
53
|
def connect
|
54
|
+
raise "ClientClosed" if @closed
|
55
|
+
|
39
56
|
@transport = build_transport
|
40
57
|
response_channel = @transport.connect
|
41
58
|
@request_dispatcher = RequestDispatcher.new
|
42
|
-
@parent.async do
|
59
|
+
@response_task = @parent.async do
|
43
60
|
response_channel.each do |response|
|
44
61
|
@request_dispatcher.add_response(response)
|
45
62
|
end
|
46
|
-
rescue StandardError
|
47
|
-
close
|
63
|
+
rescue Async::Stop, Async::TimeoutError, StandardError
|
64
|
+
close(check_requests: false)
|
48
65
|
end
|
66
|
+
logger.debug(self, "Connected")
|
49
67
|
end
|
50
68
|
|
51
|
-
|
52
|
-
|
53
|
-
|
69
|
+
# Before calling close the user must ensure that:
|
70
|
+
# 1) There are no ongoing requests
|
71
|
+
# 2) There will be no new writes after
|
72
|
+
def close(check_requests: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
73
|
+
return if @closed
|
74
|
+
|
75
|
+
@closed = true
|
54
76
|
|
55
|
-
|
77
|
+
@transport&.close
|
78
|
+
@transport&.wait
|
79
|
+
|
80
|
+
@response_task&.stop
|
81
|
+
@response_task&.wait
|
82
|
+
|
83
|
+
return if @request_dispatcher&.requests&.empty?
|
84
|
+
|
85
|
+
@request_dispatcher.clear unless check_requests
|
86
|
+
|
87
|
+
raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}" if check_requests
|
88
|
+
ensure
|
89
|
+
logger.debug(self, "Closed")
|
56
90
|
end
|
57
91
|
|
58
92
|
def connected?
|
@@ -70,9 +104,9 @@ module Grumlin
|
|
70
104
|
|
71
105
|
begin
|
72
106
|
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
73
|
-
rescue Async::Stop
|
74
|
-
|
75
|
-
raise
|
107
|
+
rescue Async::Stop, Async::TimeoutError
|
108
|
+
close(check_requests: false)
|
109
|
+
raise
|
76
110
|
end
|
77
111
|
end
|
78
112
|
|
@@ -96,11 +130,6 @@ module Grumlin
|
|
96
130
|
}
|
97
131
|
end
|
98
132
|
|
99
|
-
def reset!
|
100
|
-
@request_dispatcher = nil
|
101
|
-
@transport = nil
|
102
|
-
end
|
103
|
-
|
104
133
|
def build_transport
|
105
134
|
Transport.new(@url, parent: @parent, **@client_options)
|
106
135
|
end
|
data/lib/grumlin/order.rb
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module Order
|
5
|
+
# TODO: share the code?
|
5
6
|
class << self
|
6
|
-
|
7
|
-
|
7
|
+
%i[asc desc].each do |step|
|
8
|
+
define_method step do
|
9
|
+
name = "@#{step}"
|
10
|
+
return instance_variable_get(name) if instance_variable_defined?(name)
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def desc
|
14
|
-
DESC
|
12
|
+
instance_variable_set(name, TypedValue.new("Order", step))
|
13
|
+
end
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
data/lib/grumlin/p.rb
CHANGED
data/lib/grumlin/pop.rb
CHANGED
@@ -2,26 +2,15 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module Pop
|
5
|
+
# TODO: share the code?
|
5
6
|
class << self
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
%i[first last all mixed].each do |step|
|
8
|
+
define_method step do
|
9
|
+
name = "@#{step}"
|
10
|
+
return instance_variable_get(name) if instance_variable_defined?(name)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def last
|
16
|
-
LAST
|
17
|
-
end
|
18
|
-
|
19
|
-
def all
|
20
|
-
ALL
|
21
|
-
end
|
22
|
-
|
23
|
-
def mixed
|
24
|
-
MIXED
|
12
|
+
instance_variable_set(name, TypedValue.new("Pop", step))
|
13
|
+
end
|
25
14
|
end
|
26
15
|
end
|
27
16
|
end
|
@@ -22,6 +22,8 @@ module Grumlin
|
|
22
22
|
498 => ClientSideError
|
23
23
|
}.freeze
|
24
24
|
|
25
|
+
include Console
|
26
|
+
|
25
27
|
def initialize
|
26
28
|
@requests = {}
|
27
29
|
end
|
@@ -69,6 +71,10 @@ module Grumlin
|
|
69
71
|
@requests.key?(request_id)
|
70
72
|
end
|
71
73
|
|
74
|
+
def clear
|
75
|
+
@requests.clear
|
76
|
+
end
|
77
|
+
|
72
78
|
private
|
73
79
|
|
74
80
|
def check_errors!(status)
|
data/lib/grumlin/t.rb
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module T
|
5
|
+
# TODO: share the code?
|
5
6
|
class << self
|
6
|
-
|
7
|
-
|
7
|
+
%i[id label].each do |step|
|
8
|
+
define_method step do
|
9
|
+
name = "@#{step}"
|
10
|
+
return instance_variable_get(name) if instance_variable_defined?(name)
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def label
|
14
|
-
T_LABEL
|
12
|
+
instance_variable_set(name, TypedValue.new("T", step))
|
13
|
+
end
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
data/lib/grumlin/translator.rb
CHANGED
@@ -20,6 +20,7 @@ module Grumlin
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def arg_to_bytecode(arg)
|
23
|
+
return arg.to_bytecode if arg.is_a?(TypedValue)
|
23
24
|
return arg unless arg.is_a?(AnonymousStep)
|
24
25
|
|
25
26
|
args = arg.args.flatten.map do |a|
|
@@ -30,8 +31,11 @@ module Grumlin
|
|
30
31
|
|
31
32
|
def arg_to_query_bytecode(arg)
|
32
33
|
return ["none"] if arg.nil?
|
34
|
+
return arg.to_bytecode if arg.is_a?(TypedValue)
|
33
35
|
return arg unless arg.is_a?(AnonymousStep)
|
34
36
|
|
37
|
+
# return arg.to_bytecode if arg.is_a?(TypedValue)
|
38
|
+
|
35
39
|
args = arg.args.flatten.map do |a|
|
36
40
|
a.instance_of?(AnonymousStep) ? Typing.to_bytecode(to_bytecode(a.steps)) : arg_to_query_bytecode(a)
|
37
41
|
end
|
data/lib/grumlin/transport.rb
CHANGED
@@ -5,47 +5,33 @@ module Grumlin
|
|
5
5
|
# A transport based on https://github.com/socketry/async
|
6
6
|
# and https://github.com/socketry/async-websocket
|
7
7
|
|
8
|
+
include Console
|
9
|
+
|
8
10
|
attr_reader :url
|
9
11
|
|
12
|
+
# Transport is not reusable. Once closed should be recreated.
|
10
13
|
def initialize(url, parent: Async::Task.current, **client_options)
|
11
14
|
@url = url
|
12
15
|
@parent = parent
|
13
16
|
@client_options = client_options
|
14
17
|
@request_channel = Async::Channel.new
|
15
18
|
@response_channel = Async::Channel.new
|
16
|
-
reset!
|
17
19
|
end
|
18
20
|
|
19
21
|
def connected?
|
20
|
-
|
22
|
+
!@connection.nil?
|
21
23
|
end
|
22
24
|
|
23
|
-
def connect
|
25
|
+
def connect
|
26
|
+
raise "ClientClosed" if @closed
|
24
27
|
raise AlreadyConnectedError if connected?
|
25
28
|
|
26
29
|
@connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
|
30
|
+
logger.debug(self) { "Connected to #{@url}." }
|
27
31
|
|
28
|
-
@response_task = @parent.async
|
29
|
-
loop do
|
30
|
-
data = @connection.read
|
31
|
-
@response_channel << data
|
32
|
-
end
|
33
|
-
rescue Async::Stop
|
34
|
-
@response_channel.close
|
35
|
-
rescue StandardError => e
|
36
|
-
@response_channel.exception(e)
|
37
|
-
end
|
32
|
+
@response_task = @parent.async { run_response_task }
|
38
33
|
|
39
|
-
@request_task = @parent.async
|
40
|
-
@request_channel.each do |message|
|
41
|
-
@connection.write(message)
|
42
|
-
@connection.flush
|
43
|
-
end
|
44
|
-
rescue StandardError => e
|
45
|
-
@response_channel.exception(e)
|
46
|
-
end
|
47
|
-
|
48
|
-
@connected = true
|
34
|
+
@request_task = @parent.async { run_request_task }
|
49
35
|
|
50
36
|
@response_channel
|
51
37
|
end
|
@@ -57,30 +43,59 @@ module Grumlin
|
|
57
43
|
end
|
58
44
|
|
59
45
|
def close
|
60
|
-
return
|
46
|
+
return if @closed
|
61
47
|
|
62
|
-
@
|
63
|
-
@request_task.wait
|
48
|
+
@closed = true
|
64
49
|
|
65
|
-
@
|
66
|
-
@
|
50
|
+
@request_channel.close
|
51
|
+
@response_channel.close
|
67
52
|
|
68
53
|
begin
|
69
54
|
@connection.close
|
70
|
-
rescue
|
55
|
+
rescue StandardError
|
71
56
|
nil
|
72
57
|
end
|
58
|
+
@connection = nil
|
59
|
+
|
60
|
+
@request_task&.stop(true)
|
61
|
+
@response_task&.stop(true)
|
62
|
+
end
|
73
63
|
|
74
|
-
|
64
|
+
def wait
|
65
|
+
@request_task.wait
|
66
|
+
@response_task.wait
|
75
67
|
end
|
76
68
|
|
77
69
|
private
|
78
70
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
71
|
+
def run_response_task
|
72
|
+
with_guard do
|
73
|
+
loop do
|
74
|
+
data = @connection.read
|
75
|
+
@response_channel << data
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_request_task
|
81
|
+
with_guard do
|
82
|
+
@request_channel.each do |message|
|
83
|
+
@connection.write(message)
|
84
|
+
@connection.flush
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def with_guard
|
90
|
+
yield
|
91
|
+
rescue Async::Stop, Async::TimeoutError, StandardError => e
|
92
|
+
logger.debug(self) { "Guard error, closing." }
|
93
|
+
begin
|
94
|
+
@response_channel.exception(e)
|
95
|
+
rescue Async::Channel::ChannelClosedError
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
close
|
84
99
|
end
|
85
100
|
end
|
86
101
|
end
|
data/lib/grumlin/traversal.rb
CHANGED
@@ -4,18 +4,17 @@ module Grumlin
|
|
4
4
|
class Traversal
|
5
5
|
attr_reader :connection
|
6
6
|
|
7
|
+
# TODO: add other start steps
|
8
|
+
SUPPORTED_START_STEPS = %w[E V addE addV].freeze
|
9
|
+
|
7
10
|
def initialize(pool = Grumlin.config.default_pool)
|
8
11
|
@pool = pool
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
%w[addV addE V E].each do |step|
|
14
|
+
SUPPORTED_START_STEPS.each do |step|
|
13
15
|
define_method step do |*args|
|
14
16
|
Step.new(@pool, step, *args)
|
15
17
|
end
|
16
18
|
end
|
17
|
-
|
18
|
-
alias addVertex addV
|
19
|
-
alias addEdge addE
|
20
19
|
end
|
21
20
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
# TODO: find a better name
|
5
|
+
class TypedValue
|
6
|
+
def initialize(type, value)
|
7
|
+
@type = type
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect(*)
|
12
|
+
"#{@type}.#{@value}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_bytecode
|
16
|
+
@to_bytecode ||= { "@type": "g:#{@type}", "@value": @value }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/grumlin/u.rb
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module U
|
5
|
+
# TODO: add other start steps
|
6
|
+
SUPPORTED_START_STEPS = %w[V addV count has out unfold values].freeze
|
7
|
+
|
5
8
|
class << self
|
6
|
-
|
9
|
+
SUPPORTED_START_STEPS.each do |step|
|
7
10
|
define_method step do |*args|
|
8
11
|
AnonymousStep.new(step, *args)
|
9
12
|
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
@@ -19,6 +19,7 @@ require_relative "grumlin/exceptions"
|
|
19
19
|
|
20
20
|
require_relative "grumlin/transport"
|
21
21
|
require_relative "grumlin/client"
|
22
|
+
require_relative "grumlin/typed_value"
|
22
23
|
|
23
24
|
require_relative "grumlin/vertex"
|
24
25
|
require_relative "grumlin/edge"
|
@@ -42,10 +43,9 @@ module Grumlin
|
|
42
43
|
class Config
|
43
44
|
attr_accessor :url, :pool_size, :client_concurrency, :client_factory
|
44
45
|
|
45
|
-
# For some reason, client_concurrency must be greater than pool_size
|
46
46
|
def initialize
|
47
47
|
@pool_size = 10
|
48
|
-
@client_concurrency =
|
48
|
+
@client_concurrency = 5
|
49
49
|
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
50
50
|
end
|
51
51
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grumlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- ".overcommit.yml"
|
51
51
|
- ".rspec"
|
52
52
|
- ".rubocop.yml"
|
53
|
+
- ".tool-versions"
|
53
54
|
- CHANGELOG.md
|
54
55
|
- CODE_OF_CONDUCT.md
|
55
56
|
- Gemfile
|
@@ -82,6 +83,7 @@ files:
|
|
82
83
|
- lib/grumlin/translator.rb
|
83
84
|
- lib/grumlin/transport.rb
|
84
85
|
- lib/grumlin/traversal.rb
|
86
|
+
- lib/grumlin/typed_value.rb
|
85
87
|
- lib/grumlin/typing.rb
|
86
88
|
- lib/grumlin/u.rb
|
87
89
|
- lib/grumlin/version.rb
|