grumlin 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e532964db5d5afd11d89978a132a5f4f7d1a4d98c740f639acadac260c93be89
4
- data.tar.gz: 9015cb71bbab17895d8809dc7ff514b90e9d1ceb81ed6a9eec446a0db7ec0994
3
+ metadata.gz: a847514be85565126dc453b6f088ad5c9a388464e74f6744e34427f09a07ec9c
4
+ data.tar.gz: ea6b1241c611222aa7866b8c3a7b4b798d5a68ba09f79f030196758d4f2dd7f0
5
5
  SHA512:
6
- metadata.gz: a4295b9e6041726c36c05cc0aed94a645e8d7103c99351dfadef7077f14037b81daf87a4ffb1fd48105d8c1fa888e96ce173aa3be42c9b35ec58de3e289c01ed
7
- data.tar.gz: 51824ca159be63be91fc798b40777a25d55f0ce423b07a9ce4ce9880002e94b4e4196bc0d92b4ca84bee86356f17fd90f3cc2dd9daa594f906fc2e93f11be422
6
+ metadata.gz: 2aa5546c4b0139b1fd81cf2c2d5b32886a0c54ee958ddf3d9684ff9a38a19c8401fb2ef89a6767840f2dc0a12fbc86ea0b4821477f0721d767fc4c4f3a6cd7c1
7
+ data.tar.gz: 38da748bde5febd21a8d94a8ae00768fa8b25bb544dd9b9895a9dd544136faa489a8591d01386afcbb8ed103e5f909c7c4dfff2af9a69f2c42a77fa963be7438
@@ -1,6 +1,6 @@
1
1
  name: Ruby
2
2
 
3
- on: [push, pull_request]
3
+ on: [push]
4
4
 
5
5
  jobs:
6
6
  lint:
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.3.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.7)
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.1)
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.15
169
+ 2.2.26
data/bin/console CHANGED
@@ -10,7 +10,7 @@ Grumlin.configure do |config|
10
10
  end
11
11
 
12
12
  Async do
13
- g = Grumlin::Traversal.new
13
+ include Grumlin::Sugar
14
14
 
15
15
  IRB.setup(nil)
16
16
  workspace = IRB::WorkSpace.new(binding)
@@ -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
- %w[addV addE V E limit count drop property valueMap select from to as order by has hasLabel values hasNot
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
@@ -2,45 +2,49 @@
2
2
 
3
3
  module Grumlin
4
4
  class Client
5
- class PoolResource < self
6
- attr :concurrency, :count
5
+ class PoolResource < Async::Pool::Resource
6
+ attr_reader :client
7
7
 
8
8
  def self.call
9
- new(Grumlin.config.url, concurrency: Grumlin.config.client_concurrency).tap(&:connect)
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(url, parent: parent)
14
- @concurrency = concurrency
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 viable?
19
- connected?
18
+ def closed?
19
+ !@client.connected?
20
20
  end
21
21
 
22
- def closed?
23
- connected?
22
+ def close
23
+ @client.close
24
24
  end
25
25
 
26
- def reusable?
27
- true
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
- response_queue = @transport.connect
39
+ @transport = build_transport
40
+ response_channel = @transport.connect
39
41
  @request_dispatcher = RequestDispatcher.new
40
42
  @parent.async do
41
- response_queue.each do |response|
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.connected?
59
+ @transport&.connected? || false
56
60
  end
57
61
 
58
62
  # TODO: support yielding
59
- def write(*args) # rubocop:disable Metrics/MethodLength
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
- queue = @request_dispatcher.add_request(request)
68
+ channel = @request_dispatcher.add_request(request)
63
69
  @transport.write(request)
64
70
 
65
71
  begin
66
- msg, response = queue.dequeue
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 UnknownRequestStopped, "#{request_id} is not in the ongoing requests list"
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=#{@transport.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:) # rubocop:disable Metrics/ParameterLists
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
- module Order
6
- DESC = { "@type": "g:Order", "@value": "desc" }.freeze
7
- ASC = { "@type": "g:Order", "@value": "desc" }.freeze
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
- extend self # rubocop:disable Style/ModuleFunction
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
@@ -5,7 +5,7 @@ module Grumlin
5
5
  module P
6
6
  %w[within].each do |step|
7
7
  define_method step do |*args|
8
- { # TODO: replace with a class?
8
+ { # TODO: replace with a TypedValue?
9
9
  "@type": "g:P",
10
10
  "@value": { predicate: "within", value: { "@type": "g:List", "@value": args } }
11
11
  }
data/lib/grumlin/pop.rb CHANGED
@@ -2,31 +2,16 @@
2
2
 
3
3
  module Grumlin
4
4
  module Pop
5
- module Pop
6
- extend self # rubocop:disable Style/ModuleFunction
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
- FIRST = { "@type": "g:Pop", "@value": "first" }.freeze
9
- LAST = { "@type": "g:Pop", "@value": "last" }.freeze
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::Queue.new.tap do |queue|
33
- @requests[request[:requestId]] = { request: request, result: [], queue: queue }
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 queue
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, Metrics/MethodLength
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[:queue] << [:result, request[:result] + [response.dig(:result, :data)]]
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[:queue] << [:result, []]
53
+ request[:channel] << []
54
54
  close_request(request_id)
55
55
  end
56
56
  rescue StandardError => e
57
- request[:queue] << [:error, e]
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[:queue] << nil
65
+ request[:channel].close
66
66
  end
67
67
 
68
68
  def ongoing_request?(request_id)
data/lib/grumlin/step.rb CHANGED
@@ -14,7 +14,7 @@ module Grumlin
14
14
  @enum.next
15
15
  end
16
16
 
17
- def toList # rubocop:disable Naming/MethodName
17
+ def toList
18
18
  @pool.acquire do |client|
19
19
  client.write(*steps)
20
20
  end
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
- base.extend ClassMethods
16
- end
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
- module T
6
- T_ID = { :@type => "g:T", :@value => "id" }.freeze # TODO: replace with a class?
7
- T_LABEL = { :@type => "g:T", :@value => "label" }.freeze # TODO: replace with a class?
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
- extend self # rubocop:disable Style/ModuleFunction
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
- let(:g) { Grumlin::Traversal.new }
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
@@ -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
@@ -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
- def initialize(url, parent: Async::Task.current)
8
- @endpoint = Async::HTTP::Endpoint.parse(url)
7
+
8
+ attr_reader :url
9
+
10
+ def initialize(url, parent: Async::Task.current, **client_options)
11
+ @url = url
9
12
  @parent = parent
10
- @request_queue = Async::Queue.new
11
- @response_queue = Async::Queue.new
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(@endpoint)
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
- @response_queue << data
31
+ @response_channel << data
32
32
  end
33
33
  rescue Async::Stop
34
- @response_queue << nil
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
- @request_queue.each do |message|
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
- @response_queue
50
+ @response_channel
47
51
  end
48
52
 
49
53
  def write(message)
50
54
  raise NotConnectedError unless connected?
51
55
 
52
- @request_queue << message
56
+ @request_channel << message
53
57
  end
54
58
 
55
59
  def close
56
- raise NotConnectedError unless connected?
60
+ return unless connected?
57
61
 
58
- @request_queue << nil
62
+ @request_channel.close
59
63
  @request_task.wait
60
64
 
61
65
  @response_task.stop
62
66
  @response_task.wait
63
67
 
64
- @connection.close
68
+ begin
69
+ @connection.close
70
+ rescue Errno::EPIPE
71
+ nil
72
+ end
65
73
 
66
74
  reset!
67
75
  end
@@ -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
- # TODO: add other start steps
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
- module U
6
- extend self # rubocop:disable Style/ModuleFunction
5
+ # TODO: add other start steps
6
+ SUPPORTED_START_STEPS = %w[V addV count has out unfold values].freeze
7
7
 
8
- %w[addV V has count out values unfold].each do |step|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.3.0"
4
+ VERSION = "0.6.0"
5
5
  end
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 greather pool_size
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.3.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-07-29 00:00:00.000000000 Z
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