grumlin 0.3.0 → 0.6.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/.github/workflows/main.yml +1 -1
- data/.rubocop.yml +53 -12
- data/.tool-versions +1 -0
- data/Gemfile.lock +4 -4
- data/bin/console +1 -1
- data/lib/async/channel.rb +64 -0
- data/lib/grumlin/anonymous_step.rb +6 -6
- data/lib/grumlin/client.rb +34 -28
- data/lib/grumlin/edge.rb +1 -3
- data/lib/grumlin/order.rb +8 -13
- data/lib/grumlin/p.rb +1 -1
- data/lib/grumlin/pop.rb +8 -23
- data/lib/grumlin/request_dispatcher.rb +8 -8
- data/lib/grumlin/step.rb +1 -1
- data/lib/grumlin/sugar.rb +3 -17
- data/lib/grumlin/t.rb +8 -13
- data/lib/grumlin/test/rspec/gremlin_context.rb +7 -1
- data/lib/grumlin/translator.rb +4 -0
- data/lib/grumlin/transport.rb +26 -18
- data/lib/grumlin/traversal.rb +4 -5
- data/lib/grumlin/typed_value.rb +19 -0
- data/lib/grumlin/u.rb +4 -6
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +6 -2
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a847514be85565126dc453b6f088ad5c9a388464e74f6744e34427f09a07ec9c
|
|
4
|
+
data.tar.gz: ea6b1241c611222aa7866b8c3a7b4b798d5a68ba09f79f030196758d4f2dd7f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2aa5546c4b0139b1fd81cf2c2d5b32886a0c54ee958ddf3d9684ff9a38a19c8401fb2ef89a6767840f2dc0a12fbc86ea0b4821477f0721d767fc4c4f3a6cd7c1
|
|
7
|
+
data.tar.gz: 38da748bde5febd21a8d94a8ae00768fa8b25bb544dd9b9895a9dd544136faa489a8591d01386afcbb8ed103e5f909c7c4dfff2af9a69f2c42a77fa963be7438
|
data/.github/workflows/main.yml
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -7,24 +7,44 @@ require:
|
|
|
7
7
|
- rubocop-performance
|
|
8
8
|
- rubocop-rspec
|
|
9
9
|
|
|
10
|
-
Style/StringLiterals:
|
|
11
|
-
Enabled: true
|
|
12
|
-
EnforcedStyle: double_quotes
|
|
13
|
-
|
|
14
|
-
Style/StringLiteralsInInterpolation:
|
|
15
|
-
Enabled: true
|
|
16
|
-
EnforcedStyle: double_quotes
|
|
17
|
-
|
|
18
|
-
Style/Documentation:
|
|
19
|
-
Enabled: false
|
|
20
|
-
|
|
21
10
|
Layout/LineLength:
|
|
22
11
|
Max: 120
|
|
12
|
+
Exclude:
|
|
13
|
+
- spec/**/*_spec.rb
|
|
23
14
|
|
|
24
15
|
Metrics/BlockLength:
|
|
25
16
|
Exclude:
|
|
26
17
|
- spec/**/*_spec.rb
|
|
27
18
|
|
|
19
|
+
Metrics/MethodLength:
|
|
20
|
+
Max: 20
|
|
21
|
+
|
|
22
|
+
Metrics/ParameterLists:
|
|
23
|
+
Max: 6
|
|
24
|
+
|
|
25
|
+
Naming/MethodName:
|
|
26
|
+
IgnoredPatterns:
|
|
27
|
+
- toList
|
|
28
|
+
- inVLabel
|
|
29
|
+
- outVLabel
|
|
30
|
+
- inV
|
|
31
|
+
- outV
|
|
32
|
+
|
|
33
|
+
Naming/VariableName:
|
|
34
|
+
AllowedIdentifiers:
|
|
35
|
+
- inV
|
|
36
|
+
- outV
|
|
37
|
+
- inVLabel
|
|
38
|
+
- outVLabel
|
|
39
|
+
|
|
40
|
+
Naming/MethodParameterName:
|
|
41
|
+
AllowedNames:
|
|
42
|
+
- id
|
|
43
|
+
- inV
|
|
44
|
+
- outV
|
|
45
|
+
- inVLabel
|
|
46
|
+
- outVLabel
|
|
47
|
+
|
|
28
48
|
RSpec/NamedSubject:
|
|
29
49
|
Enabled: false
|
|
30
50
|
|
|
@@ -34,5 +54,26 @@ RSpec/NestedGroups:
|
|
|
34
54
|
RSpec/ExampleLength:
|
|
35
55
|
Enabled: false
|
|
36
56
|
|
|
57
|
+
RSpec/MultipleExpectations:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
RSpec/DescribeClass:
|
|
61
|
+
Enabled: false
|
|
62
|
+
|
|
63
|
+
Style/WordArray:
|
|
64
|
+
Exclude:
|
|
65
|
+
- spec/**/*_spec.rb
|
|
66
|
+
|
|
67
|
+
Style/StringLiterals:
|
|
68
|
+
Enabled: true
|
|
69
|
+
EnforcedStyle: double_quotes
|
|
70
|
+
|
|
71
|
+
Style/StringLiteralsInInterpolation:
|
|
72
|
+
Enabled: true
|
|
73
|
+
EnforcedStyle: double_quotes
|
|
74
|
+
|
|
75
|
+
Style/Documentation:
|
|
76
|
+
Enabled: false
|
|
77
|
+
|
|
37
78
|
Style/MultilineBlockChain:
|
|
38
|
-
Enabled: false
|
|
79
|
+
Enabled: false
|
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.6.0)
|
|
5
5
|
async-pool (~> 0.3)
|
|
6
6
|
async-websocket (~> 0.19)
|
|
7
7
|
|
|
@@ -59,7 +59,7 @@ GEM
|
|
|
59
59
|
kramdown-parser-gfm (1.1.0)
|
|
60
60
|
kramdown (~> 2.0)
|
|
61
61
|
minitest (5.14.4)
|
|
62
|
-
nio4r (2.5.
|
|
62
|
+
nio4r (2.5.8)
|
|
63
63
|
nokogiri (1.11.7-x86_64-linux)
|
|
64
64
|
racc (~> 1.4)
|
|
65
65
|
overcommit (0.57.0)
|
|
@@ -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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
# Channel is a wrapper around Async::Queue that provides
|
|
5
|
+
# a protocol and handy tools for passing data, exceptions and closing.
|
|
6
|
+
# It is designed to be used with only one publisher and one subscriber
|
|
7
|
+
class Channel
|
|
8
|
+
class ChannelError < StandardError; end
|
|
9
|
+
|
|
10
|
+
class ChannelClosedError < ChannelError; end
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@queue = Async::Queue.new
|
|
14
|
+
@closed = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def closed?
|
|
18
|
+
@closed
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Methods for a publisher
|
|
22
|
+
def <<(payload)
|
|
23
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if @closed
|
|
24
|
+
|
|
25
|
+
@queue << [:payload, payload]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def exception(exception)
|
|
29
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if closed?
|
|
30
|
+
|
|
31
|
+
@queue << [:exception, exception]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def close
|
|
35
|
+
raise(ChannelClosedError, "Cannot close a closed channel") if closed?
|
|
36
|
+
|
|
37
|
+
@queue << [:close]
|
|
38
|
+
@closed = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Methods for a subscriber
|
|
42
|
+
def dequeue
|
|
43
|
+
each do |payload| # rubocop:disable Lint/UnreachableLoop this is intended
|
|
44
|
+
return payload
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def each
|
|
49
|
+
raise(ChannelClosedError, "Cannot receive from a closed channel") if closed?
|
|
50
|
+
|
|
51
|
+
@queue.each do |type, payload|
|
|
52
|
+
case type
|
|
53
|
+
when :exception
|
|
54
|
+
payload.set_backtrace(caller + (payload.backtrace || [])) # A hack to preserve full backtrace
|
|
55
|
+
raise payload
|
|
56
|
+
when :payload
|
|
57
|
+
yield payload
|
|
58
|
+
when :close
|
|
59
|
+
break
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -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
|
+
hasLabel hasNot in inV label limit not order out outE path project property repeat select to
|
|
10
|
+
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
|
@@ -2,45 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
class Client
|
|
5
|
-
class PoolResource <
|
|
6
|
-
|
|
5
|
+
class PoolResource < Async::Pool::Resource
|
|
6
|
+
attr_reader :client
|
|
7
7
|
|
|
8
8
|
def self.call
|
|
9
|
-
|
|
9
|
+
config = Grumlin.config
|
|
10
|
+
new(config.url, client_factory: config.client_factory, concurrency: config.client_concurrency)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def initialize(url, concurrency: 1, parent: Async::Task.current)
|
|
13
|
-
super(
|
|
14
|
-
@
|
|
15
|
-
@count = 0
|
|
13
|
+
def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
|
|
14
|
+
super(concurrency)
|
|
15
|
+
@client = client_factory.call(url, parent).tap(&:connect)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
19
|
-
connected?
|
|
18
|
+
def closed?
|
|
19
|
+
!@client.connected?
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
|
|
22
|
+
def close
|
|
23
|
+
@client.close
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def
|
|
27
|
-
|
|
26
|
+
def write(*args)
|
|
27
|
+
@client.write(*args)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def initialize(url, parent: Async::Task.current)
|
|
31
|
+
def initialize(url, parent: Async::Task.current, **client_options)
|
|
32
|
+
@url = url
|
|
33
|
+
@client_options = client_options
|
|
32
34
|
@parent = parent
|
|
33
|
-
@transport = Transport.new(url)
|
|
34
35
|
reset!
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def connect
|
|
38
|
-
|
|
39
|
+
@transport = build_transport
|
|
40
|
+
response_channel = @transport.connect
|
|
39
41
|
@request_dispatcher = RequestDispatcher.new
|
|
40
42
|
@parent.async do
|
|
41
|
-
|
|
43
|
+
response_channel.each do |response|
|
|
42
44
|
@request_dispatcher.add_response(response)
|
|
43
45
|
end
|
|
46
|
+
rescue StandardError
|
|
47
|
+
close
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
|
|
@@ -52,31 +56,28 @@ module Grumlin
|
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
def connected?
|
|
55
|
-
@transport
|
|
59
|
+
@transport&.connected? || false
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
# TODO: support yielding
|
|
59
|
-
def write(*args)
|
|
63
|
+
def write(*args)
|
|
64
|
+
raise NotConnectedError unless connected?
|
|
65
|
+
|
|
60
66
|
request_id = SecureRandom.uuid
|
|
61
67
|
request = to_query(request_id, args)
|
|
62
|
-
|
|
68
|
+
channel = @request_dispatcher.add_request(request)
|
|
63
69
|
@transport.write(request)
|
|
64
70
|
|
|
65
71
|
begin
|
|
66
|
-
|
|
67
|
-
raise response if msg == :error
|
|
68
|
-
|
|
69
|
-
return response.flat_map { |item| Typing.cast(item) } if msg == :result
|
|
70
|
-
|
|
71
|
-
raise "ERROR"
|
|
72
|
+
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
|
72
73
|
rescue Async::Stop
|
|
73
74
|
retry if @request_dispatcher.ongoing_request?(request_id)
|
|
74
|
-
raise
|
|
75
|
+
raise Grumlin::UnknownRequestStoppedError, "#{request_id} is not in the ongoing requests list"
|
|
75
76
|
end
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
def inspect
|
|
79
|
-
"<#{self.class} url=#{@
|
|
80
|
+
"<#{self.class} url=#{@url} connected=#{connected?}>"
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
alias to_s inspect
|
|
@@ -97,6 +98,11 @@ module Grumlin
|
|
|
97
98
|
|
|
98
99
|
def reset!
|
|
99
100
|
@request_dispatcher = nil
|
|
101
|
+
@transport = nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def build_transport
|
|
105
|
+
Transport.new(@url, parent: @parent, **@client_options)
|
|
100
106
|
end
|
|
101
107
|
end
|
|
102
108
|
end
|
data/lib/grumlin/edge.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Naming/VariableName,Naming/MethodParameterName,Naming/MethodName
|
|
4
3
|
module Grumlin
|
|
5
4
|
class Edge
|
|
6
5
|
attr_reader :label, :id, :inVLabel, :outVLabel, :inV, :outV
|
|
7
6
|
|
|
8
|
-
def initialize(label:, id:, inVLabel:, outVLabel:, inV:, outV:)
|
|
7
|
+
def initialize(label:, id:, inVLabel:, outVLabel:, inV:, outV:)
|
|
9
8
|
@label = label
|
|
10
9
|
@id = Typing.cast(id)
|
|
11
10
|
@inVLabel = inVLabel
|
|
@@ -24,4 +23,3 @@ module Grumlin
|
|
|
24
23
|
alias to_s inspect
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
|
-
# rubocop:enable Naming/MethodParameterName,Naming/VariableName,Naming/MethodName
|
data/lib/grumlin/order.rb
CHANGED
|
@@ -2,21 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
module Order
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
# TODO: share the code?
|
|
6
|
+
class << self
|
|
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
|
-
def asc
|
|
12
|
-
ASC
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def desc
|
|
16
|
-
DESC
|
|
12
|
+
instance_variable_set(name, TypedValue.new("Order", step))
|
|
13
|
+
end
|
|
17
14
|
end
|
|
18
15
|
end
|
|
19
|
-
|
|
20
|
-
extend Order
|
|
21
16
|
end
|
|
22
17
|
end
|
data/lib/grumlin/p.rb
CHANGED
data/lib/grumlin/pop.rb
CHANGED
|
@@ -2,31 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
module Pop
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
# TODO: share the code?
|
|
6
|
+
class << self
|
|
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)
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ALL = { "@type": "g:Pop", "@value": "all" }.freeze
|
|
11
|
-
MIXED = { "@type": "g:Pop", "@value": "mixed" }.freeze
|
|
12
|
-
|
|
13
|
-
def first
|
|
14
|
-
FIRST
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def last
|
|
18
|
-
LAST
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def all
|
|
22
|
-
ALL
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def mixed
|
|
26
|
-
MIXED
|
|
12
|
+
instance_variable_set(name, TypedValue.new("Pop", step))
|
|
13
|
+
end
|
|
27
14
|
end
|
|
28
15
|
end
|
|
29
|
-
|
|
30
|
-
extend Pop
|
|
31
16
|
end
|
|
32
17
|
end
|
|
@@ -29,14 +29,14 @@ module Grumlin
|
|
|
29
29
|
def add_request(request)
|
|
30
30
|
raise "ERROR" if @requests.key?(request[:requestId])
|
|
31
31
|
|
|
32
|
-
Async::
|
|
33
|
-
@requests[request[:requestId]] = { request: request, result: [],
|
|
32
|
+
Async::Channel.new.tap do |channel|
|
|
33
|
+
@requests[request[:requestId]] = { request: request, result: [], channel: channel }
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# builds a response object, when it's ready sends it to the client via a
|
|
37
|
+
# builds a response object, when it's ready sends it to the client via a channel
|
|
38
38
|
# TODO: sometimes response does not include requestID, no idea how to handle it so far.
|
|
39
|
-
def add_response(response) # rubocop:disable Metrics/AbcSize
|
|
39
|
+
def add_response(response) # rubocop:disable Metrics/AbcSize
|
|
40
40
|
request_id = response[:requestId]
|
|
41
41
|
raise "ERROR" unless ongoing_request?(request_id)
|
|
42
42
|
|
|
@@ -46,15 +46,15 @@ module Grumlin
|
|
|
46
46
|
|
|
47
47
|
case SUCCESS[response.dig(:status, :code)]
|
|
48
48
|
when :success
|
|
49
|
-
request[:
|
|
49
|
+
request[:channel] << request[:result] + [response.dig(:result, :data)]
|
|
50
50
|
close_request(request_id)
|
|
51
51
|
when :partial_content then request[:result] << response.dig(:result, :data)
|
|
52
52
|
when :no_content
|
|
53
|
-
request[:
|
|
53
|
+
request[:channel] << []
|
|
54
54
|
close_request(request_id)
|
|
55
55
|
end
|
|
56
56
|
rescue StandardError => e
|
|
57
|
-
request[:
|
|
57
|
+
request[:channel].exception(e)
|
|
58
58
|
close_request(request_id)
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -62,7 +62,7 @@ module Grumlin
|
|
|
62
62
|
raise "ERROR" unless ongoing_request?(request_id)
|
|
63
63
|
|
|
64
64
|
request = @requests.delete(request_id)
|
|
65
|
-
request[:
|
|
65
|
+
request[:channel].close
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def ongoing_request?(request_id)
|
data/lib/grumlin/step.rb
CHANGED
data/lib/grumlin/sugar.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
module Sugar
|
|
5
|
-
# TODO: how to use it in specs?
|
|
6
5
|
HELPERS = [
|
|
7
6
|
Grumlin::U,
|
|
8
7
|
Grumlin::T,
|
|
@@ -12,25 +11,12 @@ module Grumlin
|
|
|
12
11
|
].freeze
|
|
13
12
|
|
|
14
13
|
def self.included(base)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
module ClassMethods
|
|
19
|
-
def const_missing(name)
|
|
20
|
-
helper = HELPERS.find { |h| h.const_defined?(name) }
|
|
21
|
-
super if helper.nil?
|
|
22
|
-
|
|
23
|
-
const_set(name, helper)
|
|
14
|
+
HELPERS.each do |helper|
|
|
15
|
+
name = helper.name.split("::").last
|
|
16
|
+
base.const_set(name, helper)
|
|
24
17
|
end
|
|
25
18
|
end
|
|
26
19
|
|
|
27
|
-
def const_missing(name)
|
|
28
|
-
helper = HELPERS.find { |h| h.const_defined?(name) }
|
|
29
|
-
super if helper.nil?
|
|
30
|
-
|
|
31
|
-
const_set(name, helper)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
20
|
def __
|
|
35
21
|
Grumlin::U
|
|
36
22
|
end
|
data/lib/grumlin/t.rb
CHANGED
|
@@ -2,21 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
module T
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
# TODO: share the code?
|
|
6
|
+
class << self
|
|
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
|
-
def id
|
|
12
|
-
T_ID
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def label
|
|
16
|
-
T_LABEL
|
|
12
|
+
instance_variable_set(name, TypedValue.new("T", step))
|
|
13
|
+
end
|
|
17
14
|
end
|
|
18
15
|
end
|
|
19
|
-
|
|
20
|
-
extend T
|
|
21
16
|
end
|
|
22
17
|
end
|
|
@@ -8,8 +8,14 @@ module Grumlin
|
|
|
8
8
|
|
|
9
9
|
::RSpec.shared_context GremlinContext do
|
|
10
10
|
include GremlinContext
|
|
11
|
+
include Grumlin::Sugar
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
before do
|
|
14
|
+
Grumlin::Sugar::HELPERS.each do |helper|
|
|
15
|
+
name = helper.name.split("::").last
|
|
16
|
+
stub_const(name, helper)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
13
19
|
|
|
14
20
|
after do
|
|
15
21
|
Grumlin.config.default_pool.close
|
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
|
@@ -4,64 +4,72 @@ module Grumlin
|
|
|
4
4
|
class Transport
|
|
5
5
|
# A transport based on https://github.com/socketry/async
|
|
6
6
|
# and https://github.com/socketry/async-websocket
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
attr_reader :url
|
|
9
|
+
|
|
10
|
+
def initialize(url, parent: Async::Task.current, **client_options)
|
|
11
|
+
@url = url
|
|
9
12
|
@parent = parent
|
|
10
|
-
@
|
|
11
|
-
@
|
|
13
|
+
@client_options = client_options
|
|
14
|
+
@request_channel = Async::Channel.new
|
|
15
|
+
@response_channel = Async::Channel.new
|
|
12
16
|
reset!
|
|
13
17
|
end
|
|
14
18
|
|
|
15
|
-
def url
|
|
16
|
-
@endpoint.url
|
|
17
|
-
end
|
|
18
|
-
|
|
19
19
|
def connected?
|
|
20
20
|
@connected
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def connect # rubocop:disable Metrics/MethodLength
|
|
23
|
+
def connect # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
24
24
|
raise AlreadyConnectedError if connected?
|
|
25
25
|
|
|
26
|
-
@connection = Async::WebSocket::Client.connect(@
|
|
26
|
+
@connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
|
|
27
27
|
|
|
28
28
|
@response_task = @parent.async do
|
|
29
29
|
loop do
|
|
30
30
|
data = @connection.read
|
|
31
|
-
@
|
|
31
|
+
@response_channel << data
|
|
32
32
|
end
|
|
33
33
|
rescue Async::Stop
|
|
34
|
-
@
|
|
34
|
+
@response_channel.close
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
@response_channel.exception(e)
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
@request_task = @parent.async do
|
|
38
|
-
@
|
|
40
|
+
@request_channel.each do |message|
|
|
39
41
|
@connection.write(message)
|
|
40
42
|
@connection.flush
|
|
41
43
|
end
|
|
44
|
+
rescue StandardError => e
|
|
45
|
+
@response_channel.exception(e)
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
@connected = true
|
|
45
49
|
|
|
46
|
-
@
|
|
50
|
+
@response_channel
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
def write(message)
|
|
50
54
|
raise NotConnectedError unless connected?
|
|
51
55
|
|
|
52
|
-
@
|
|
56
|
+
@request_channel << message
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
def close
|
|
56
|
-
|
|
60
|
+
return unless connected?
|
|
57
61
|
|
|
58
|
-
@
|
|
62
|
+
@request_channel.close
|
|
59
63
|
@request_task.wait
|
|
60
64
|
|
|
61
65
|
@response_task.stop
|
|
62
66
|
@response_task.wait
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
begin
|
|
69
|
+
@connection.close
|
|
70
|
+
rescue Errno::EPIPE
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
65
73
|
|
|
66
74
|
reset!
|
|
67
75
|
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,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Grumlin
|
|
4
4
|
module U
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
# TODO: add other start steps
|
|
6
|
+
SUPPORTED_START_STEPS = %w[V addV count has out unfold values].freeze
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
class << self
|
|
9
|
+
SUPPORTED_START_STEPS.each do |step|
|
|
9
10
|
define_method step do |*args|
|
|
10
11
|
AnonymousStep.new(step, *args)
|
|
11
12
|
end
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
|
-
|
|
15
|
-
# TODO: add alias __
|
|
16
|
-
extend U
|
|
17
15
|
end
|
|
18
16
|
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
|
@@ -12,11 +12,14 @@ require "async/barrier"
|
|
|
12
12
|
require "async/http/endpoint"
|
|
13
13
|
require "async/websocket/client"
|
|
14
14
|
|
|
15
|
+
require_relative "async/channel"
|
|
16
|
+
|
|
15
17
|
require_relative "grumlin/version"
|
|
16
18
|
require_relative "grumlin/exceptions"
|
|
17
19
|
|
|
18
20
|
require_relative "grumlin/transport"
|
|
19
21
|
require_relative "grumlin/client"
|
|
22
|
+
require_relative "grumlin/typed_value"
|
|
20
23
|
|
|
21
24
|
require_relative "grumlin/vertex"
|
|
22
25
|
require_relative "grumlin/edge"
|
|
@@ -38,12 +41,13 @@ require_relative "grumlin/sugar"
|
|
|
38
41
|
|
|
39
42
|
module Grumlin
|
|
40
43
|
class Config
|
|
41
|
-
attr_accessor :url, :pool_size, :client_concurrency
|
|
44
|
+
attr_accessor :url, :pool_size, :client_concurrency, :client_factory
|
|
42
45
|
|
|
43
|
-
# For some reason, client_concurrency must be
|
|
46
|
+
# For some reason, client_concurrency must be greater than pool_size
|
|
44
47
|
def initialize
|
|
45
48
|
@pool_size = 10
|
|
46
49
|
@client_concurrency = 20
|
|
50
|
+
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
def default_pool
|
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.6.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-08-30 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
|
|
@@ -62,6 +63,7 @@ files:
|
|
|
62
63
|
- gremlin_server/Dockerfile
|
|
63
64
|
- gremlin_server/tinkergraph-empty.properties
|
|
64
65
|
- grumlin.gemspec
|
|
66
|
+
- lib/async/channel.rb
|
|
65
67
|
- lib/grumlin.rb
|
|
66
68
|
- lib/grumlin/anonymous_step.rb
|
|
67
69
|
- lib/grumlin/client.rb
|
|
@@ -81,6 +83,7 @@ files:
|
|
|
81
83
|
- lib/grumlin/translator.rb
|
|
82
84
|
- lib/grumlin/transport.rb
|
|
83
85
|
- lib/grumlin/traversal.rb
|
|
86
|
+
- lib/grumlin/typed_value.rb
|
|
84
87
|
- lib/grumlin/typing.rb
|
|
85
88
|
- lib/grumlin/u.rb
|
|
86
89
|
- lib/grumlin/version.rb
|