grumlin 0.7.0 → 0.8.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: 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