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.
@@ -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