grumlin 0.1.1 → 0.4.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/.overcommit.yml +8 -0
- data/.rubocop.yml +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +31 -22
- data/bin/console +6 -3
- data/bin/setup +1 -0
- data/grumlin.gemspec +4 -3
- data/lib/async/channel.rb +64 -0
- data/lib/grumlin.rb +49 -3
- data/lib/grumlin/anonymous_step.rb +44 -0
- data/lib/grumlin/client.rb +68 -132
- data/lib/grumlin/edge.rb +2 -2
- data/lib/grumlin/exceptions.rb +22 -2
- data/lib/grumlin/order.rb +22 -0
- data/lib/grumlin/p.rb +18 -0
- data/lib/grumlin/path.rb +15 -0
- data/lib/grumlin/pop.rb +32 -0
- data/lib/grumlin/request_dispatcher.rb +84 -0
- data/lib/grumlin/step.rb +18 -29
- data/lib/grumlin/sugar.rb +42 -0
- data/lib/grumlin/t.rb +22 -0
- data/lib/grumlin/test/rspec.rb +11 -0
- data/lib/grumlin/test/rspec/db_cleaner_context.rb +18 -0
- data/lib/grumlin/test/rspec/gremlin_context.rb +21 -0
- data/lib/grumlin/translator.rb +22 -21
- data/lib/grumlin/transport.rb +86 -0
- data/lib/grumlin/traversal.rb +4 -11
- data/lib/grumlin/typing.rb +25 -5
- data/lib/grumlin/u.rb +18 -0
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin/vertex.rb +2 -2
- metadata +36 -9
- data/bin/stress +0 -51
- data/lib/grumlin/traversing_context.rb +0 -17
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class Transport
|
5
|
+
# A transport based on https://github.com/socketry/async
|
6
|
+
# and https://github.com/socketry/async-websocket
|
7
|
+
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
def initialize(url, parent: Async::Task.current, **client_options)
|
11
|
+
@url = url
|
12
|
+
@parent = parent
|
13
|
+
@client_options = client_options
|
14
|
+
@request_channel = Async::Channel.new
|
15
|
+
@response_channel = Async::Channel.new
|
16
|
+
reset!
|
17
|
+
end
|
18
|
+
|
19
|
+
def connected?
|
20
|
+
@connected
|
21
|
+
end
|
22
|
+
|
23
|
+
def connect # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
24
|
+
raise AlreadyConnectedError if connected?
|
25
|
+
|
26
|
+
@connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
|
27
|
+
|
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
|
38
|
+
|
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
|
49
|
+
|
50
|
+
@response_channel
|
51
|
+
end
|
52
|
+
|
53
|
+
def write(message)
|
54
|
+
raise NotConnectedError unless connected?
|
55
|
+
|
56
|
+
@request_channel << message
|
57
|
+
end
|
58
|
+
|
59
|
+
def close # rubocop:disable Metrics/MethodLength
|
60
|
+
return unless connected?
|
61
|
+
|
62
|
+
@request_channel.close
|
63
|
+
@request_task.wait
|
64
|
+
|
65
|
+
@response_task.stop
|
66
|
+
@response_task.wait
|
67
|
+
|
68
|
+
begin
|
69
|
+
@connection.close
|
70
|
+
rescue Errno::EPIPE
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
reset!
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def reset!
|
80
|
+
@connected = false
|
81
|
+
@connection = nil
|
82
|
+
@response_task = nil
|
83
|
+
@request_task = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/grumlin/traversal.rb
CHANGED
@@ -4,21 +4,14 @@ module Grumlin
|
|
4
4
|
class Traversal
|
5
5
|
attr_reader :connection
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
Grumlin::Client.new(client_or_url)
|
10
|
-
else
|
11
|
-
client_or_url
|
12
|
-
end
|
13
|
-
|
14
|
-
return if block.nil?
|
15
|
-
|
16
|
-
TraversingContext.new(self).instance_exec(&block)
|
7
|
+
def initialize(pool = Grumlin.config.default_pool)
|
8
|
+
@pool = pool
|
17
9
|
end
|
18
10
|
|
11
|
+
# TODO: add other start steps
|
19
12
|
%w[addV addE V E].each do |step|
|
20
13
|
define_method step do |*args|
|
21
|
-
Step.new(@
|
14
|
+
Step.new(@pool, step, *args)
|
22
15
|
end
|
23
16
|
end
|
24
17
|
|
data/lib/grumlin/typing.rb
CHANGED
@@ -4,15 +4,19 @@ module Grumlin
|
|
4
4
|
module Typing
|
5
5
|
TYPES = {
|
6
6
|
"g:List" => ->(value) { value.map { |item| cast(item) } },
|
7
|
+
"g:Set" => ->(value) { Set.new(value.map { |item| cast(item) }) },
|
7
8
|
"g:Map" => ->(value) { cast_map(value) },
|
8
9
|
"g:Vertex" => ->(value) { cast_entity(Grumlin::Vertex, value) },
|
9
10
|
"g:Edge" => ->(value) { cast_entity(Grumlin::Edge, value) },
|
11
|
+
"g:Path" => ->(value) { cast_entity(Grumlin::Path, value) },
|
10
12
|
"g:Int64" => ->(value) { cast_int(value) },
|
11
13
|
"g:Int32" => ->(value) { cast_int(value) },
|
12
|
-
"g:
|
14
|
+
"g:Double" => ->(value) { cast_double(value) },
|
15
|
+
"g:Traverser" => ->(value) { cast(value[:value]) }, # TODO: wtf is bulk?
|
16
|
+
"g:T" => ->(value) { value.to_sym }
|
13
17
|
}.freeze
|
14
18
|
|
15
|
-
CASTABLE_TYPES = [Hash, String, Integer].freeze
|
19
|
+
CASTABLE_TYPES = [Hash, String, Integer, TrueClass, FalseClass].freeze
|
16
20
|
|
17
21
|
class << self
|
18
22
|
def cast(value)
|
@@ -27,6 +31,13 @@ module Grumlin
|
|
27
31
|
type.call(value[:@value])
|
28
32
|
end
|
29
33
|
|
34
|
+
def to_bytecode(step)
|
35
|
+
{
|
36
|
+
"@type": "g:Bytecode",
|
37
|
+
"@value": { step: step }
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
30
41
|
private
|
31
42
|
|
32
43
|
def castable_type?(value); end
|
@@ -47,6 +58,12 @@ module Grumlin
|
|
47
58
|
value
|
48
59
|
end
|
49
60
|
|
61
|
+
def cast_double(value)
|
62
|
+
raise TypeError, "#{value} is not a Double" unless value.is_a?(Float)
|
63
|
+
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
50
67
|
def cast_entity(entity, value)
|
51
68
|
entity.new(**value)
|
52
69
|
rescue ArgumentError, TypeError
|
@@ -54,12 +71,15 @@ module Grumlin
|
|
54
71
|
end
|
55
72
|
|
56
73
|
def cast_map(value)
|
57
|
-
Hash[*value].transform_keys
|
74
|
+
Hash[*value].transform_keys do |key|
|
75
|
+
next key.to_sym if key.respond_to?(:to_sym)
|
76
|
+
next cast(key) if key[:@type]
|
77
|
+
|
78
|
+
raise UnknownMapKey, key, value
|
79
|
+
end.transform_values { |v| cast(v) }
|
58
80
|
rescue ArgumentError
|
59
81
|
raise TypeError, "#{value} cannot be casted to Hash"
|
60
82
|
end
|
61
|
-
|
62
|
-
def cast_traverser(value); end
|
63
83
|
end
|
64
84
|
end
|
65
85
|
end
|
data/lib/grumlin/u.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module U
|
5
|
+
module U
|
6
|
+
extend self # rubocop:disable Style/ModuleFunction
|
7
|
+
|
8
|
+
%w[addV V has count out values unfold].each do |step|
|
9
|
+
define_method step do |*args|
|
10
|
+
AnonymousStep.new(step, *args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: add alias __
|
16
|
+
extend U
|
17
|
+
end
|
18
|
+
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin/vertex.rb
CHANGED
@@ -10,11 +10,11 @@ module Grumlin
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def ==(other)
|
13
|
-
@label == other.label && @id == other.id
|
13
|
+
self.class == other.class && @label == other.label && @id == other.id
|
14
14
|
end
|
15
15
|
|
16
16
|
def inspect
|
17
|
-
"
|
17
|
+
"v[#{@id}]"
|
18
18
|
end
|
19
19
|
alias to_s inspect
|
20
20
|
end
|
metadata
CHANGED
@@ -1,30 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grumlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: async-pool
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: async-websocket
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
33
|
+
version: '0.19'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
27
|
-
description:
|
40
|
+
version: '0.19'
|
41
|
+
description: Gremlin query language DSL for Ruby.
|
28
42
|
email:
|
29
43
|
- zhulik.gleb@gmail.com
|
30
44
|
executables: []
|
@@ -33,6 +47,7 @@ extra_rdoc_files: []
|
|
33
47
|
files:
|
34
48
|
- ".github/workflows/main.yml"
|
35
49
|
- ".gitignore"
|
50
|
+
- ".overcommit.yml"
|
36
51
|
- ".rspec"
|
37
52
|
- ".rubocop.yml"
|
38
53
|
- CHANGELOG.md
|
@@ -43,20 +58,32 @@ files:
|
|
43
58
|
- README.md
|
44
59
|
- bin/console
|
45
60
|
- bin/setup
|
46
|
-
- bin/stress
|
47
61
|
- docker-compose.yml
|
48
62
|
- gremlin_server/Dockerfile
|
49
63
|
- gremlin_server/tinkergraph-empty.properties
|
50
64
|
- grumlin.gemspec
|
65
|
+
- lib/async/channel.rb
|
51
66
|
- lib/grumlin.rb
|
67
|
+
- lib/grumlin/anonymous_step.rb
|
52
68
|
- lib/grumlin/client.rb
|
53
69
|
- lib/grumlin/edge.rb
|
54
70
|
- lib/grumlin/exceptions.rb
|
71
|
+
- lib/grumlin/order.rb
|
72
|
+
- lib/grumlin/p.rb
|
73
|
+
- lib/grumlin/path.rb
|
74
|
+
- lib/grumlin/pop.rb
|
75
|
+
- lib/grumlin/request_dispatcher.rb
|
55
76
|
- lib/grumlin/step.rb
|
77
|
+
- lib/grumlin/sugar.rb
|
78
|
+
- lib/grumlin/t.rb
|
79
|
+
- lib/grumlin/test/rspec.rb
|
80
|
+
- lib/grumlin/test/rspec/db_cleaner_context.rb
|
81
|
+
- lib/grumlin/test/rspec/gremlin_context.rb
|
56
82
|
- lib/grumlin/translator.rb
|
83
|
+
- lib/grumlin/transport.rb
|
57
84
|
- lib/grumlin/traversal.rb
|
58
|
-
- lib/grumlin/traversing_context.rb
|
59
85
|
- lib/grumlin/typing.rb
|
86
|
+
- lib/grumlin/u.rb
|
60
87
|
- lib/grumlin/version.rb
|
61
88
|
- lib/grumlin/vertex.rb
|
62
89
|
homepage: https://github.com/zhulik/grumlin
|
@@ -81,8 +108,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
108
|
- !ruby/object:Gem::Version
|
82
109
|
version: '0'
|
83
110
|
requirements: []
|
84
|
-
rubygems_version: 3.2.
|
111
|
+
rubygems_version: 3.2.22
|
85
112
|
signing_key:
|
86
113
|
specification_version: 4
|
87
|
-
summary:
|
114
|
+
summary: Gremlin query language DSL for Ruby.
|
88
115
|
test_files: []
|
data/bin/stress
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
require "grumlin"
|
6
|
-
require "irb"
|
7
|
-
|
8
|
-
def queries(client, uuids)
|
9
|
-
total = 0
|
10
|
-
g = Grumlin::Traversal.new(client)
|
11
|
-
|
12
|
-
loop do
|
13
|
-
uuid = uuids.sample
|
14
|
-
result = g.V(uuid).toList[0]
|
15
|
-
raise "!!!" if result.id != uuid
|
16
|
-
|
17
|
-
total += 1
|
18
|
-
end
|
19
|
-
rescue Async::Stop
|
20
|
-
total
|
21
|
-
end
|
22
|
-
|
23
|
-
def prepare_dataset(client)
|
24
|
-
uuids = Array.new(1000) { SecureRandom.uuid }
|
25
|
-
|
26
|
-
Grumlin::Traversal.new(client) do
|
27
|
-
g.V().drop
|
28
|
-
|
29
|
-
uuids.each do |uuid|
|
30
|
-
g.addV("test_vertex").property(id, uuid).toList
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
uuids
|
35
|
-
end
|
36
|
-
|
37
|
-
Async do |task|
|
38
|
-
client = Grumlin::Client.new("ws://localhost:8182/gremlin", mode: :bytecode)
|
39
|
-
|
40
|
-
uuids = prepare_dataset(client)
|
41
|
-
tasks = Array.new(20) { task.async { queries(client, uuids) } }
|
42
|
-
|
43
|
-
task.sleep(60)
|
44
|
-
|
45
|
-
tasks.each(&:stop)
|
46
|
-
total = tasks.sum(&:wait)
|
47
|
-
|
48
|
-
p("#{total} requests performed")
|
49
|
-
ensure
|
50
|
-
client.disconnect
|
51
|
-
end
|