grumlin 0.1.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -4,21 +4,14 @@ module Grumlin
4
4
  class Traversal
5
5
  attr_reader :connection
6
6
 
7
- def initialize(client_or_url, &block)
8
- @client = if client_or_url.is_a?(String)
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(@client, step, *args)
14
+ Step.new(@pool, step, *args)
22
15
  end
23
16
  end
24
17
 
@@ -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:Traverser" => ->(value) { cast(value[:value]) } # TODO: wtf is bulk?
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(&:to_sym).transform_values { |v| cast(v) }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.1.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -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
- "<V #{@label}(#{@id})>"
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.1.1
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-05-27 00:00:00.000000000 Z
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.18'
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.18'
27
- description: A ruby client for Gremlin query language.
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.15
111
+ rubygems_version: 3.2.22
85
112
  signing_key:
86
113
  specification_version: 4
87
- summary: A ruby client for Gremlin query language.
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
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- class TraversingContext
5
- ID = { :@type => "g:T", :@value => "id" }.freeze
6
-
7
- attr_reader :g
8
-
9
- def initialize(traversal)
10
- @g = traversal
11
- end
12
-
13
- def id
14
- ID
15
- end
16
- end
17
- end