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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6633f34f12f74200b56d3447e7b1cd5c2c4185cd4013a27157d809b2f97f01b2
4
- data.tar.gz: a5b0c1b0f4a8ef813a48144b79b17a71c6385802ff77b46ef0fa703643790ac1
3
+ metadata.gz: 360fd18e170020dfe02e879e9e20e29a958de425e20dd3217c70c1317ae87850
4
+ data.tar.gz: ec7c3b496802689d0bd5cd82b9eba303ec4b5f60deb993f0fe48bf446d69004a
5
5
  SHA512:
6
- metadata.gz: 6824448759e26af95f8753a1461fac6b0b66677e1cab723b8e79c76333c4e791b5364da81f87844b624796238a3e02b13065a7afb64d7a5ef7ca83a6e9c3e8be
7
- data.tar.gz: d7ce1a01b2b50f90a317d270ad005561220fcb0f69da5036b97d20c88b2458e5dd7ea285c384cef5d5e9521ff077f2d43650985c1e928942e463cfc29bbdd247
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.6.2)
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
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_dependency "async-pool", "~> 0.3"
27
27
  spec.add_dependency "async-websocket", "~> 0.19"
28
+ spec.add_dependency "oj", "~> 3.12"
29
+ spec.add_dependency "zeitwerk", "~> 2.4"
28
30
  end
data/lib/async/channel.rb CHANGED
@@ -32,7 +32,7 @@ module Async
32
32
  end
33
33
 
34
34
  def close
35
- raise(ChannelClosedError, "Cannot close a closed channel") if closed?
35
+ return if closed?
36
36
 
37
37
  @queue << [:close]
38
38
  @closed = true
@@ -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, previous_steps: [])
12
+ def initialize(name, *args, previous_step: nil)
13
13
  @name = name
14
- @previous_steps = previous_steps
14
+ @previous_step = previous_step
15
15
  @args = args
16
16
  end
17
17
 
18
18
  SUPPORTED_STEPS.each do |step|
19
- define_method step do |*args|
20
- add_step(step, args, previous_steps: steps)
19
+ define_method(step) do |*args|
20
+ add_step(step, args)
21
21
  end
22
22
  end
23
23
 
24
24
  def inspect
25
- @inspect ||= to_bytecode.to_s
25
+ bytecode.inspect
26
26
  end
27
27
 
28
28
  alias to_s inspect
29
29
 
30
- def to_bytecode
31
- @to_bytecode ||= (@previous_steps.last&.to_bytecode || []) + [Translator.to_bytecode(self)]
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, previous_steps:)
41
- self.class.new(step_name, *args, previous_steps: previous_steps)
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
@@ -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(*args)
27
- @client.write(*args)
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
- reset!
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
- def close
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
- if @request_dispatcher&.requests&.any?
62
- raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}"
63
- end
78
+ @transport&.wait
79
+
80
+ @response_task&.stop
81
+ @response_task&.wait
64
82
 
65
- reset!
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(*args)
97
+ def write(bytecode)
74
98
  raise NotConnectedError unless connected?
75
99
 
76
- request_id = SecureRandom.uuid
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
- retry if @request_dispatcher.ongoing_request?(request_id)
85
- raise Grumlin::UnknownRequestStoppedError, "#{request_id} is not in the ongoing requests list"
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] + [response.dig(:result, :data)]
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(error, status)
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, previous_steps: [])
8
- super(name, *args, previous_steps: previous_steps)
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(*steps)
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(*(steps + [nil]))
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, previous_steps:)
32
- self.class.new(@pool, step_name, *args, previous_steps: previous_steps)
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
@@ -18,8 +18,7 @@ module Grumlin
18
18
  end
19
19
 
20
20
  after do
21
- Grumlin.config.default_pool.close
22
- Grumlin.config.reset!
21
+ Grumlin.close
23
22
  end
24
23
  end
25
24
  end
@@ -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
- @connected
22
+ !@connection.nil?
21
23
  end
22
24
 
23
- def connect # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
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 do
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 do
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 unless connected?
46
+ return if @closed
61
47
 
62
- @request_channel.close
63
- @request_task.wait
48
+ @closed = true
64
49
 
65
- @response_task.stop
66
- @response_task.wait
50
+ @request_channel.close
51
+ @response_channel.close
67
52
 
68
53
  begin
69
54
  @connection.close
70
- rescue Errno::EPIPE
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
- reset!
64
+ def wait
65
+ @request_task.wait
66
+ @response_task.wait
75
67
  end
76
68
 
77
69
  private
78
70
 
79
- def reset!
80
- @connected = false
81
- @connection = nil
82
- @response_task = nil
83
- @request_task = nil
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
@@ -7,7 +7,7 @@ module Grumlin
7
7
  # TODO: add other start steps
8
8
  SUPPORTED_START_STEPS = %w[E V addE addV].freeze
9
9
 
10
- def initialize(pool = Grumlin.config.default_pool)
10
+ def initialize(pool = Grumlin.default_pool)
11
11
  @pool = pool
12
12
  end
13
13
 
@@ -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
@@ -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.any? { |t| value.is_a?(t) }
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|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.6.2"
4
+ VERSION = "0.10.0"
5
5
  end
data/lib/grumlin.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "securerandom"
4
- require "json"
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
- require_relative "async/channel"
18
+ require "zeitwerk"
16
19
 
17
- require_relative "grumlin/version"
18
- require_relative "grumlin/exceptions"
20
+ loader = Zeitwerk::Loader.for_gem
21
+ loader.inflector.inflect(
22
+ "rspec" => "RSpec",
23
+ "db_cleaner_context" => "DBCleanerContext"
24
+ )
19
25
 
20
- require_relative "grumlin/transport"
21
- require_relative "grumlin/client"
22
- require_relative "grumlin/typed_value"
26
+ db_adapters = "#{__dir__}/grumlin/test"
27
+ loader.do_not_eager_load(db_adapters)
23
28
 
24
- require_relative "grumlin/vertex"
25
- require_relative "grumlin/edge"
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
- require_relative "grumlin/anonymous_step"
33
- require_relative "grumlin/step"
32
+ class UnknownError < Error; end
34
33
 
35
- require_relative "grumlin/t"
36
- require_relative "grumlin/order"
37
- require_relative "grumlin/u"
38
- require_relative "grumlin/p"
39
- require_relative "grumlin/pop"
40
- require_relative "grumlin/sugar"
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 = 2
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.6.2
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-03 00:00:00.000000000 Z
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
@@ -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
@@ -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