grumlin 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|