grumlin 0.7.0 → 0.8.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: 4260297c3d75ec7bc1f987d8c7bad4efd75c81627814fdf984a0aaac0045332b
4
- data.tar.gz: 1cb521906a08cf5390f058a28f380492a2999e55ef023c473fc6bae64533b53b
3
+ metadata.gz: 22dad640b5ac86f86381b9bc9fab03989e7b8c42c4b19234efc6b3a223d07055
4
+ data.tar.gz: c0cea0f26f1e5a5a9ef1c6ab96335bf24e7f642968030b2b1f1c0a74210898d7
5
5
  SHA512:
6
- metadata.gz: 992d2192402f3fe282203a3fd0eff1e6c886470899cb37f00ca2abf1392e4d946c7391be0eb7d6560c9941f7a17a3552682a8a47f56273f4299c06551c1e9ffc
7
- data.tar.gz: d8695e2485915cb7b4d37a755e42d20b6d7f575d906ae5a0aca5f508c01a104c08f7bb51a2285c9dac48a0091771882afeb405fd26e7802c4d48513274e2895f
6
+ metadata.gz: 71b7e970733edc7db87736c449008389b47431d41c12382c426b0db348e106047343d279e3d8901d76dd44175478279b4c2e22cd896b690c9d63eedbf605b6ec
7
+ data.tar.gz: 82f71e74fb61435de9d6a86fa8cceb4cea32cd8a87f91c6a941bbfbd622a21b67b434f3ccbe298f94265edb9999faa33ea4f80148a56a1e39c6491a575df1ae6
data/Gemfile.lock CHANGED
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.7.0)
4
+ grumlin (0.8.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
@@ -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
10
  to unfold 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
@@ -24,8 +24,8 @@ module Grumlin
24
24
  @client.close
25
25
  end
26
26
 
27
- def write(*args)
28
- @client.write(*args)
27
+ def write(bytecode)
28
+ @client.write(bytecode)
29
29
  ensure
30
30
  @count += 1
31
31
  end
@@ -94,11 +94,10 @@ module Grumlin
94
94
  end
95
95
 
96
96
  # TODO: support yielding
97
- def write(*args)
97
+ def write(bytecode)
98
98
  raise NotConnectedError unless connected?
99
99
 
100
- request_id = SecureRandom.uuid
101
- request = to_query(request_id, args)
100
+ request = bytecode.to_query
102
101
  channel = @request_dispatcher.add_request(request)
103
102
  @transport.write(request)
104
103
 
@@ -118,18 +117,6 @@ module Grumlin
118
117
 
119
118
  private
120
119
 
121
- def to_query(request_id, message)
122
- {
123
- requestId: request_id,
124
- op: "bytecode",
125
- processor: "traversal",
126
- args: {
127
- gremlin: Typing.to_bytecode(Translator.to_bytecode_query(message)),
128
- aliases: { g: :g }
129
- }
130
- }
131
- end
132
-
133
120
  def build_transport
134
121
  Transport.new(@url, parent: @parent, **@client_options)
135
122
  end
@@ -48,7 +48,7 @@ module Grumlin
48
48
 
49
49
  case SUCCESS[response.dig(:status, :code)]
50
50
  when :success
51
- request[:channel] << request[:result] + [response.dig(:result, :data)]
51
+ request[:channel] << [*request[:result], response.dig(:result, :data)]
52
52
  close_request(request_id)
53
53
  when :partial_content then request[:result] << response.dig(:result, :data)
54
54
  when :no_content
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
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.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,34 +15,86 @@ 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
61
+
62
+ def initialize(status)
63
+ super(status[:message])
64
+ @status = status
65
+ end
66
+ end
67
+
68
+ class ClientSideError < StatusError; end
69
+
70
+ class ServerSideError < StatusError; end
71
+
72
+ class ScriptEvaluationError < ServerSideError; end
73
+
74
+ class InvalidRequestArgumentsError < ServerSideError; end
75
+
76
+ class ServerError < ServerSideError; end
77
+
78
+ class ServerSerializationError < ServerSideError; end
79
+
80
+ class ServerTimeoutError < ServerSideError; end
81
+
82
+ class InternalClientError < Error; end
83
+
84
+ class UnknownRequestStoppedError < InternalClientError; end
85
+
86
+ class ResourceLeakError < InternalClientError; end
87
+
88
+ class UnknownMapKey < InternalClientError
89
+ attr_reader :key, :map
90
+
91
+ def initialize(key, map)
92
+ @key = key
93
+ @map = map
94
+ super("Cannot cast key #{key} in map #{map}")
95
+ end
96
+ end
41
97
 
42
- module Grumlin
43
98
  class Config
44
99
  attr_accessor :url, :pool_size, :client_concurrency, :client_factory
45
100
 
@@ -66,5 +121,19 @@ module Grumlin
66
121
  def config
67
122
  @config ||= Config.new
68
123
  end
124
+
125
+ def default_pool
126
+ config.default_pool
127
+ end
128
+
129
+ def close
130
+ default_pool.wait while default_pool.busy?
131
+
132
+ default_pool.close
133
+ config.reset!
134
+ end
69
135
  end
70
136
  end
137
+
138
+ loader.setup
139
+ 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.7.0
4
+ version: 0.8.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-06 00:00:00.000000000 Z
11
+ date: 2021-09-07 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