grumlin 0.6.2 → 0.10.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/Gemfile.lock +4 -1
- data/grumlin.gemspec +2 -0
- data/lib/async/channel.rb +1 -1
- data/lib/grumlin/anonymous_step.rb +11 -15
- data/lib/grumlin/bytecode.rb +76 -0
- data/lib/grumlin/client.rb +40 -34
- data/lib/grumlin/request_dispatcher.rb +10 -4
- data/lib/grumlin/step.rb +6 -6
- data/lib/grumlin/test/rspec/gremlin_context.rb +1 -2
- data/lib/grumlin/transport.rb +50 -35
- data/lib/grumlin/traversal.rb +1 -1
- data/lib/grumlin/typed_value.rb +6 -4
- data/lib/grumlin/typing.rb +1 -10
- data/lib/grumlin/u.rb +1 -1
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +94 -24
- metadata +31 -4
- data/lib/grumlin/exceptions.rb +0 -71
- data/lib/grumlin/translator.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 360fd18e170020dfe02e879e9e20e29a958de425e20dd3217c70c1317ae87850
|
4
|
+
data.tar.gz: ec7c3b496802689d0bd5cd82b9eba303ec4b5f60deb993f0fe48bf446d69004a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79e60622d3c58154b13939880a394062f96fdade9eb5eafbe2ed13b040ad2d4749d134e4cc2ad5ba18c48872ed04d71d3ef833525e16765b774fb489cfb24f95
|
7
|
+
data.tar.gz: 4a00526ab10dd4c90a1c83f5fb94a572152b14bd5884be096605d6a1acf8b80c8143ab24a766b9ad25b5d2b93b78f848bce94784c13bd3b1afbdfa6567a24070
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (0.10.0)
|
5
5
|
async-pool (~> 0.3)
|
6
6
|
async-websocket (~> 0.19)
|
7
|
+
oj (~> 3.12)
|
8
|
+
zeitwerk (~> 2.4)
|
7
9
|
|
8
10
|
GEM
|
9
11
|
remote: https://rubygems.org/
|
@@ -62,6 +64,7 @@ GEM
|
|
62
64
|
nio4r (2.5.8)
|
63
65
|
nokogiri (1.11.7-x86_64-linux)
|
64
66
|
racc (~> 1.4)
|
67
|
+
oj (3.13.4)
|
65
68
|
overcommit (0.57.0)
|
66
69
|
childprocess (>= 0.6.3, < 5)
|
67
70
|
iniparse (~> 1.4)
|
data/grumlin.gemspec
CHANGED
data/lib/async/channel.rb
CHANGED
@@ -2,43 +2,39 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
class AnonymousStep
|
5
|
-
attr_reader :name, :args
|
5
|
+
attr_reader :name, :args, :previous_step
|
6
6
|
|
7
7
|
# TODO: add other steps
|
8
8
|
SUPPORTED_STEPS = %w[E V addE addV as by coalesce count dedup drop elementMap emit fold from group groupCount has
|
9
9
|
hasId hasLabel hasNot in inV label limit not order out outE path project property repeat select
|
10
|
-
to unfold valueMap values where].freeze
|
10
|
+
to unfold union valueMap values where].freeze
|
11
11
|
|
12
|
-
def initialize(name, *args,
|
12
|
+
def initialize(name, *args, previous_step: nil)
|
13
13
|
@name = name
|
14
|
-
@
|
14
|
+
@previous_step = previous_step
|
15
15
|
@args = args
|
16
16
|
end
|
17
17
|
|
18
18
|
SUPPORTED_STEPS.each do |step|
|
19
|
-
define_method
|
20
|
-
add_step(step, args
|
19
|
+
define_method(step) do |*args|
|
20
|
+
add_step(step, args)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def inspect
|
25
|
-
|
25
|
+
bytecode.inspect
|
26
26
|
end
|
27
27
|
|
28
28
|
alias to_s inspect
|
29
29
|
|
30
|
-
def
|
31
|
-
@
|
32
|
-
end
|
33
|
-
|
34
|
-
def steps
|
35
|
-
(@previous_steps + [self])
|
30
|
+
def bytecode(no_return: false)
|
31
|
+
@bytecode ||= Bytecode.new(self, no_return: no_return)
|
36
32
|
end
|
37
33
|
|
38
34
|
private
|
39
35
|
|
40
|
-
def add_step(step_name, args
|
41
|
-
self.class.new(step_name, *args,
|
36
|
+
def add_step(step_name, args)
|
37
|
+
self.class.new(step_name, *args, previous_step: self)
|
42
38
|
end
|
43
39
|
end
|
44
40
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
# Incapsulates logic of converting step chains and step arguments to queries that can be sent to the server
|
5
|
+
# and to human readable strings.
|
6
|
+
class Bytecode
|
7
|
+
class NoneStep
|
8
|
+
def to_bytecode
|
9
|
+
["none"]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
NONE_STEP = NoneStep.new
|
14
|
+
|
15
|
+
def initialize(step, no_return: false)
|
16
|
+
@step = step
|
17
|
+
@no_return = no_return
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
to_readable_bytecode.to_s
|
22
|
+
end
|
23
|
+
alias to_s inspect
|
24
|
+
|
25
|
+
def to_query
|
26
|
+
{
|
27
|
+
requestId: SecureRandom.uuid,
|
28
|
+
op: "bytecode",
|
29
|
+
processor: "traversal",
|
30
|
+
args: {
|
31
|
+
gremlin: to_bytecode,
|
32
|
+
aliases: { g: :g }
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_readable_bytecode
|
38
|
+
@to_readable_bytecode ||= steps.map { |s| serialize_arg(s, serialization_method: :to_readable_bytecode) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_bytecode
|
42
|
+
@to_bytecode ||= {
|
43
|
+
"@type": "g:Bytecode",
|
44
|
+
"@value": { step: (steps + (@no_return ? [NONE_STEP] : [])).map { |s| serialize_arg(s) } }
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Serializes step or a step argument to either an executable query or a human readable string representation
|
51
|
+
# depending on the `serialization_method` parameter. I should be either `:to_readable_bytecode` for human readable
|
52
|
+
# representation or `:to_bytecode` for query.
|
53
|
+
def serialize_arg(arg, serialization_method: :to_bytecode)
|
54
|
+
return arg.send(serialization_method) if arg.respond_to?(:to_bytecode)
|
55
|
+
return arg unless arg.is_a?(AnonymousStep)
|
56
|
+
|
57
|
+
arg.args.flatten.each.with_object([arg.name]) do |a, res|
|
58
|
+
res << if a.instance_of?(AnonymousStep)
|
59
|
+
a.bytecode.send(serialization_method)
|
60
|
+
else
|
61
|
+
serialize_arg(a, serialization_method: serialization_method)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def steps
|
67
|
+
@steps ||= [].tap do |result|
|
68
|
+
step = @step
|
69
|
+
until step.nil?
|
70
|
+
result.unshift(step)
|
71
|
+
step = step.previous_step
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
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?
|
@@ -23,8 +24,10 @@ module Grumlin
|
|
23
24
|
@client.close
|
24
25
|
end
|
25
26
|
|
26
|
-
def write(
|
27
|
-
@client.write(
|
27
|
+
def write(bytecode)
|
28
|
+
@client.write(bytecode)
|
29
|
+
ensure
|
30
|
+
@count += 1
|
28
31
|
end
|
29
32
|
|
30
33
|
def viable?
|
@@ -36,33 +39,54 @@ module Grumlin
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
42
|
+
include Console
|
43
|
+
|
44
|
+
# Client is not reusable. Once closed should be recreated.
|
39
45
|
def initialize(url, parent: Async::Task.current, **client_options)
|
40
46
|
@url = url
|
41
47
|
@client_options = client_options
|
42
48
|
@parent = parent
|
43
|
-
|
49
|
+
@request_dispatcher = nil
|
50
|
+
@transport = nil
|
44
51
|
end
|
45
52
|
|
46
53
|
def connect
|
54
|
+
raise "ClientClosed" if @closed
|
55
|
+
|
47
56
|
@transport = build_transport
|
48
57
|
response_channel = @transport.connect
|
49
58
|
@request_dispatcher = RequestDispatcher.new
|
50
|
-
@parent.async do
|
59
|
+
@response_task = @parent.async do
|
51
60
|
response_channel.each do |response|
|
52
61
|
@request_dispatcher.add_response(response)
|
53
62
|
end
|
54
|
-
rescue StandardError
|
55
|
-
close
|
63
|
+
rescue Async::Stop, Async::TimeoutError, StandardError
|
64
|
+
close(check_requests: false)
|
56
65
|
end
|
66
|
+
logger.debug(self, "Connected")
|
57
67
|
end
|
58
68
|
|
59
|
-
|
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
|
76
|
+
|
60
77
|
@transport&.close
|
61
|
-
|
62
|
-
|
63
|
-
|
78
|
+
@transport&.wait
|
79
|
+
|
80
|
+
@response_task&.stop
|
81
|
+
@response_task&.wait
|
64
82
|
|
65
|
-
|
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")
|
66
90
|
end
|
67
91
|
|
68
92
|
def connected?
|
@@ -70,19 +94,18 @@ module Grumlin
|
|
70
94
|
end
|
71
95
|
|
72
96
|
# TODO: support yielding
|
73
|
-
def write(
|
97
|
+
def write(bytecode)
|
74
98
|
raise NotConnectedError unless connected?
|
75
99
|
|
76
|
-
|
77
|
-
request = to_query(request_id, args)
|
100
|
+
request = bytecode.to_query
|
78
101
|
channel = @request_dispatcher.add_request(request)
|
79
102
|
@transport.write(request)
|
80
103
|
|
81
104
|
begin
|
82
105
|
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
83
|
-
rescue Async::Stop
|
84
|
-
|
85
|
-
raise
|
106
|
+
rescue Async::Stop, Async::TimeoutError
|
107
|
+
close(check_requests: false)
|
108
|
+
raise
|
86
109
|
end
|
87
110
|
end
|
88
111
|
|
@@ -94,23 +117,6 @@ module Grumlin
|
|
94
117
|
|
95
118
|
private
|
96
119
|
|
97
|
-
def to_query(request_id, message)
|
98
|
-
{
|
99
|
-
requestId: request_id,
|
100
|
-
op: "bytecode",
|
101
|
-
processor: "traversal",
|
102
|
-
args: {
|
103
|
-
gremlin: Typing.to_bytecode(Translator.to_bytecode_query(message)),
|
104
|
-
aliases: { g: :g }
|
105
|
-
}
|
106
|
-
}
|
107
|
-
end
|
108
|
-
|
109
|
-
def reset!
|
110
|
-
@request_dispatcher = nil
|
111
|
-
@transport = nil
|
112
|
-
end
|
113
|
-
|
114
120
|
def build_transport
|
115
121
|
Transport.new(@url, parent: @parent, **@client_options)
|
116
122
|
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
|
@@ -42,11 +44,11 @@ module Grumlin
|
|
42
44
|
|
43
45
|
request = @requests[request_id]
|
44
46
|
|
45
|
-
check_errors!(response[:status])
|
47
|
+
check_errors!(response[:status], request[:request])
|
46
48
|
|
47
49
|
case SUCCESS[response.dig(:status, :code)]
|
48
50
|
when :success
|
49
|
-
request[:channel] << request[:result]
|
51
|
+
request[:channel] << [*request[:result], response.dig(:result, :data)]
|
50
52
|
close_request(request_id)
|
51
53
|
when :partial_content then request[:result] << response.dig(:result, :data)
|
52
54
|
when :no_content
|
@@ -69,11 +71,15 @@ 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
|
-
def check_errors!(status)
|
80
|
+
def check_errors!(status, query)
|
75
81
|
if (error = ERRORS[status[:code]])
|
76
|
-
raise(
|
82
|
+
raise error.new(status, query)
|
77
83
|
end
|
78
84
|
|
79
85
|
return unless SUCCESS[status[:code]].nil?
|
data/lib/grumlin/step.rb
CHANGED
@@ -4,8 +4,8 @@ module Grumlin
|
|
4
4
|
class Step < AnonymousStep
|
5
5
|
attr_reader :client
|
6
6
|
|
7
|
-
def initialize(pool, name, *args,
|
8
|
-
super(name, *args,
|
7
|
+
def initialize(pool, name, *args, previous_step: nil)
|
8
|
+
super(name, *args, previous_step: previous_step)
|
9
9
|
@pool = pool
|
10
10
|
end
|
11
11
|
|
@@ -16,20 +16,20 @@ module Grumlin
|
|
16
16
|
|
17
17
|
def toList
|
18
18
|
@pool.acquire do |client|
|
19
|
-
client.write(
|
19
|
+
client.write(bytecode)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def iterate
|
24
24
|
@pool.acquire do |client|
|
25
|
-
client.write(
|
25
|
+
client.write(bytecode(no_return: true))
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
-
def add_step(step_name, args
|
32
|
-
self.class.new(@pool, step_name, *args,
|
31
|
+
def add_step(step_name, args)
|
32
|
+
self.class.new(@pool, step_name, *args, previous_step: self)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
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
data/lib/grumlin/typed_value.rb
CHANGED
@@ -8,12 +8,14 @@ module Grumlin
|
|
8
8
|
@value = value
|
9
9
|
end
|
10
10
|
|
11
|
-
def inspect(*)
|
12
|
-
"#{@type}.#{@value}"
|
13
|
-
end
|
14
|
-
|
15
11
|
def to_bytecode
|
16
12
|
@to_bytecode ||= { "@type": "g:#{@type}", "@value": @value }
|
17
13
|
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"<#{@type}.#{@value}>"
|
17
|
+
end
|
18
|
+
alias to_s inspect
|
19
|
+
alias to_readable_bytecode inspect
|
18
20
|
end
|
19
21
|
end
|
data/lib/grumlin/typing.rb
CHANGED
@@ -31,19 +31,10 @@ module Grumlin
|
|
31
31
|
type.call(value[:@value])
|
32
32
|
end
|
33
33
|
|
34
|
-
def to_bytecode(step)
|
35
|
-
{
|
36
|
-
"@type": "g:Bytecode",
|
37
|
-
"@value": { step: step }
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
34
|
private
|
42
35
|
|
43
|
-
def castable_type?(value); end
|
44
|
-
|
45
36
|
def verify_type!(value)
|
46
|
-
raise TypeError, "#{value.inspect} cannot be casted" unless CASTABLE_TYPES.
|
37
|
+
raise TypeError, "#{value.inspect} cannot be casted" unless CASTABLE_TYPES.include?(value.class)
|
47
38
|
end
|
48
39
|
|
49
40
|
def verify_castable_hash!(value, type)
|
data/lib/grumlin/u.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Grumlin
|
4
4
|
module U
|
5
5
|
# TODO: add other start steps
|
6
|
-
SUPPORTED_START_STEPS = %w[V addV count has out unfold values].freeze
|
6
|
+
SUPPORTED_START_STEPS = %w[V addV count fold has out repeat unfold values].freeze
|
7
7
|
|
8
8
|
class << self
|
9
9
|
SUPPORTED_START_STEPS.each do |step|
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "securerandom"
|
4
|
-
require "
|
4
|
+
require "oj"
|
5
|
+
|
6
|
+
Oj.mimic_JSON
|
7
|
+
Oj.add_to_json
|
5
8
|
|
6
9
|
require "async"
|
7
10
|
require "async/pool"
|
@@ -12,40 +15,93 @@ require "async/barrier"
|
|
12
15
|
require "async/http/endpoint"
|
13
16
|
require "async/websocket/client"
|
14
17
|
|
15
|
-
|
18
|
+
require "zeitwerk"
|
16
19
|
|
17
|
-
|
18
|
-
|
20
|
+
loader = Zeitwerk::Loader.for_gem
|
21
|
+
loader.inflector.inflect(
|
22
|
+
"rspec" => "RSpec",
|
23
|
+
"db_cleaner_context" => "DBCleanerContext"
|
24
|
+
)
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
require_relative "grumlin/typed_value"
|
26
|
+
db_adapters = "#{__dir__}/grumlin/test"
|
27
|
+
loader.do_not_eager_load(db_adapters)
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
require_relative "grumlin/path"
|
27
|
-
require_relative "grumlin/typing"
|
28
|
-
require_relative "grumlin/traversal"
|
29
|
-
require_relative "grumlin/request_dispatcher"
|
30
|
-
require_relative "grumlin/translator"
|
29
|
+
module Grumlin
|
30
|
+
class Error < StandardError; end
|
31
31
|
|
32
|
-
|
33
|
-
require_relative "grumlin/step"
|
32
|
+
class UnknownError < Error; end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
class ConnectionError < Error; end
|
35
|
+
|
36
|
+
class CannotConnectError < ConnectionError; end
|
37
|
+
|
38
|
+
class DisconnectError < ConnectionError; end
|
39
|
+
|
40
|
+
class ConnectionStatusError < Error; end
|
41
|
+
|
42
|
+
class NotConnectedError < ConnectionStatusError; end
|
43
|
+
|
44
|
+
class AlreadyConnectedError < ConnectionStatusError; end
|
45
|
+
|
46
|
+
class ProtocolError < Error; end
|
47
|
+
|
48
|
+
class UnknownResponseStatus < ProtocolError
|
49
|
+
attr_reader :status
|
50
|
+
|
51
|
+
def initialize(status)
|
52
|
+
super("unknown response status code #{status[:code]}")
|
53
|
+
@status = status
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class UnknownTypeError < ProtocolError; end
|
58
|
+
|
59
|
+
class StatusError < Error
|
60
|
+
attr_reader :status, :query
|
61
|
+
|
62
|
+
def initialize(status, query)
|
63
|
+
super(status[:message])
|
64
|
+
@status = status
|
65
|
+
@query = query
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class ClientSideError < StatusError; end
|
70
|
+
|
71
|
+
class ServerSideError < StatusError; end
|
72
|
+
|
73
|
+
class ScriptEvaluationError < ServerSideError; end
|
74
|
+
|
75
|
+
class InvalidRequestArgumentsError < ServerSideError; end
|
76
|
+
|
77
|
+
class ServerError < ServerSideError; end
|
78
|
+
|
79
|
+
class ServerSerializationError < ServerSideError; end
|
80
|
+
|
81
|
+
class ServerTimeoutError < ServerSideError; end
|
82
|
+
|
83
|
+
class InternalClientError < Error; end
|
84
|
+
|
85
|
+
class UnknownRequestStoppedError < InternalClientError; end
|
86
|
+
|
87
|
+
class ResourceLeakError < InternalClientError; end
|
88
|
+
|
89
|
+
class UnknownMapKey < InternalClientError
|
90
|
+
attr_reader :key, :map
|
91
|
+
|
92
|
+
def initialize(key, map)
|
93
|
+
@key = key
|
94
|
+
@map = map
|
95
|
+
super("Cannot cast key #{key} in map #{map}")
|
96
|
+
end
|
97
|
+
end
|
41
98
|
|
42
|
-
module Grumlin
|
43
99
|
class Config
|
44
100
|
attr_accessor :url, :pool_size, :client_concurrency, :client_factory
|
45
101
|
|
46
102
|
def initialize
|
47
103
|
@pool_size = 10
|
48
|
-
@client_concurrency =
|
104
|
+
@client_concurrency = 5
|
49
105
|
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
50
106
|
end
|
51
107
|
|
@@ -66,5 +122,19 @@ module Grumlin
|
|
66
122
|
def config
|
67
123
|
@config ||= Config.new
|
68
124
|
end
|
125
|
+
|
126
|
+
def default_pool
|
127
|
+
config.default_pool
|
128
|
+
end
|
129
|
+
|
130
|
+
def close
|
131
|
+
default_pool.wait while default_pool.busy?
|
132
|
+
|
133
|
+
default_pool.close
|
134
|
+
config.reset!
|
135
|
+
end
|
69
136
|
end
|
70
137
|
end
|
138
|
+
|
139
|
+
loader.setup
|
140
|
+
loader.eager_load
|
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.10.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-09-
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.19'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oj
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.12'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: zeitwerk
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.4'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.4'
|
41
69
|
description: Gremlin query language DSL for Ruby.
|
42
70
|
email:
|
43
71
|
- zhulik.gleb@gmail.com
|
@@ -66,9 +94,9 @@ files:
|
|
66
94
|
- lib/async/channel.rb
|
67
95
|
- lib/grumlin.rb
|
68
96
|
- lib/grumlin/anonymous_step.rb
|
97
|
+
- lib/grumlin/bytecode.rb
|
69
98
|
- lib/grumlin/client.rb
|
70
99
|
- lib/grumlin/edge.rb
|
71
|
-
- lib/grumlin/exceptions.rb
|
72
100
|
- lib/grumlin/order.rb
|
73
101
|
- lib/grumlin/p.rb
|
74
102
|
- lib/grumlin/path.rb
|
@@ -80,7 +108,6 @@ files:
|
|
80
108
|
- lib/grumlin/test/rspec.rb
|
81
109
|
- lib/grumlin/test/rspec/db_cleaner_context.rb
|
82
110
|
- lib/grumlin/test/rspec/gremlin_context.rb
|
83
|
-
- lib/grumlin/translator.rb
|
84
111
|
- lib/grumlin/transport.rb
|
85
112
|
- lib/grumlin/traversal.rb
|
86
113
|
- lib/grumlin/typed_value.rb
|
data/lib/grumlin/exceptions.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Grumlin
|
4
|
-
class Error < StandardError; end
|
5
|
-
|
6
|
-
class UnknownError < Error; end
|
7
|
-
|
8
|
-
class ConnectionError < Error; end
|
9
|
-
|
10
|
-
class CannotConnectError < ConnectionError; end
|
11
|
-
|
12
|
-
class DisconnectError < ConnectionError; end
|
13
|
-
|
14
|
-
class ConnectionStatusError < Error; end
|
15
|
-
|
16
|
-
class NotConnectedError < ConnectionStatusError; end
|
17
|
-
|
18
|
-
class AlreadyConnectedError < ConnectionStatusError; end
|
19
|
-
|
20
|
-
class ProtocolError < Error; end
|
21
|
-
|
22
|
-
class UnknownResponseStatus < ProtocolError
|
23
|
-
attr_reader :status
|
24
|
-
|
25
|
-
def initialize(status)
|
26
|
-
super("unknown response status code #{status[:code]}")
|
27
|
-
@status = status
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class UnknownTypeError < ProtocolError; end
|
32
|
-
|
33
|
-
class StatusError < Error
|
34
|
-
attr_reader :status
|
35
|
-
|
36
|
-
def initialize(status)
|
37
|
-
super(status[:message])
|
38
|
-
@status = status
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
class ClientSideError < StatusError; end
|
43
|
-
|
44
|
-
class ServerSideError < StatusError; end
|
45
|
-
|
46
|
-
class ScriptEvaluationError < ServerSideError; end
|
47
|
-
|
48
|
-
class InvalidRequestArgumentsError < ServerSideError; end
|
49
|
-
|
50
|
-
class ServerError < ServerSideError; end
|
51
|
-
|
52
|
-
class ServerSerializationError < ServerSideError; end
|
53
|
-
|
54
|
-
class ServerTimeoutError < ServerSideError; end
|
55
|
-
|
56
|
-
class InternalClientError < Error; end
|
57
|
-
|
58
|
-
class UnknownRequestStoppedError < InternalClientError; end
|
59
|
-
|
60
|
-
class ResourceLeakError < InternalClientError; end
|
61
|
-
|
62
|
-
class UnknownMapKey < InternalClientError
|
63
|
-
attr_reader :key, :map
|
64
|
-
|
65
|
-
def initialize(key, map)
|
66
|
-
@key = key
|
67
|
-
@map = map
|
68
|
-
super("Cannot cast key #{key} in map #{map}")
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/lib/grumlin/translator.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Grumlin
|
4
|
-
module Translator
|
5
|
-
class << self
|
6
|
-
def to_bytecode(steps)
|
7
|
-
return arg_to_bytecode(steps) if steps.is_a?(AnonymousStep)
|
8
|
-
|
9
|
-
steps.map do |step|
|
10
|
-
arg_to_bytecode(step)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_bytecode_query(steps)
|
15
|
-
steps.map do |step|
|
16
|
-
arg_to_query_bytecode(step)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def arg_to_bytecode(arg)
|
23
|
-
return arg.to_bytecode if arg.is_a?(TypedValue)
|
24
|
-
return arg unless arg.is_a?(AnonymousStep)
|
25
|
-
|
26
|
-
args = arg.args.flatten.map do |a|
|
27
|
-
a.instance_of?(AnonymousStep) ? to_bytecode(a.steps) : arg_to_bytecode(a)
|
28
|
-
end
|
29
|
-
[arg.name, *args]
|
30
|
-
end
|
31
|
-
|
32
|
-
def arg_to_query_bytecode(arg)
|
33
|
-
return ["none"] if arg.nil?
|
34
|
-
return arg.to_bytecode if arg.is_a?(TypedValue)
|
35
|
-
return arg unless arg.is_a?(AnonymousStep)
|
36
|
-
|
37
|
-
# return arg.to_bytecode if arg.is_a?(TypedValue)
|
38
|
-
|
39
|
-
args = arg.args.flatten.map do |a|
|
40
|
-
a.instance_of?(AnonymousStep) ? Typing.to_bytecode(to_bytecode(a.steps)) : arg_to_query_bytecode(a)
|
41
|
-
end
|
42
|
-
[arg.name, *args]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|