grumlin 0.21.0 → 0.21.1

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: bb67bbe2eff3a7fd02cce19c6052fa70e3f54f3ec2cb06102667f69e43d07e8d
4
- data.tar.gz: 1ecf0285f60fab6fd93bd0b36bfe58c39b94eebf0ead055f6f3cc4f2e9f8b19d
3
+ metadata.gz: 95963424bb38728bb21c2d3746a93be60daf303b2c2897b24d99684fd1fb7bff
4
+ data.tar.gz: 809b4b589b2bc5fd8687f0f63a6f73f17aab82060990b3a5942164c2ac92ef83
5
5
  SHA512:
6
- metadata.gz: '05263993fc271810fd493ae3d5fdd8d84b6b698a8d9a4e4a4c336b09858571b156971b6f3def4bdb2d9c3743b07bebeb40eb1c426e4c316a8aa4a87b264f15b5'
7
- data.tar.gz: 78fc706c4694146ff2b5fedc75b1d0186dca152fc9f2731c1b6936ea906e6410077c95871fba8b4c302f375cc8b28afffa37ccb59097a88470e266c4f26e6c51
6
+ metadata.gz: b095f3958ebf4b70577e607a0b10e27fc073055fe08d6fe55787c71fd104e77cf78cdcd0a54d06e7881c9183cab479ef6e92e6117a1c1b418945d1ebb417655c
7
+ data.tar.gz: 0477a8ce8ccfc5ea1cc3b95b7b48501b05387207918478adc4eecb8e286e8495910b6bd718e1d5c00ab63e61e02ca6e89212913ec35d8df49b267cb75281e9eb
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.21.0)
4
+ grumlin (0.21.1)
5
5
  async-pool (~> 0.3)
6
6
  async-websocket (~> 0.19)
7
7
  oj (~> 3.13)
@@ -21,11 +21,11 @@ GEM
21
21
  console (~> 1.10)
22
22
  nio4r (~> 2.3)
23
23
  timers (~> 4.1)
24
- async-http (0.56.6)
24
+ async-http (0.57.0)
25
25
  async (>= 1.25)
26
26
  async-io (>= 1.28)
27
27
  async-pool (>= 0.2)
28
- protocol-http (~> 0.22.0)
28
+ protocol-http (~> 0.23.1)
29
29
  protocol-http1 (~> 0.14.0)
30
30
  protocol-http2 (~> 0.14.0)
31
31
  traces (~> 0.4.0)
@@ -37,7 +37,7 @@ GEM
37
37
  rspec (~> 3.0)
38
38
  rspec-files (~> 1.0)
39
39
  rspec-memory (~> 1.0)
40
- async-websocket (0.19.0)
40
+ async-websocket (0.19.2)
41
41
  async-http (~> 0.54)
42
42
  async-io (~> 1.23)
43
43
  protocol-websocket (~> 0.7.0)
@@ -72,7 +72,7 @@ GEM
72
72
  racc (~> 1.4)
73
73
  nokogiri (1.13.6-x86_64-linux)
74
74
  racc (~> 1.4)
75
- oj (3.13.18)
75
+ oj (3.13.20)
76
76
  overcommit (0.59.1)
77
77
  childprocess (>= 0.6.3, < 5)
78
78
  iniparse (~> 1.4)
@@ -81,7 +81,7 @@ GEM
81
81
  parser (3.1.2.0)
82
82
  ast (~> 2.4.1)
83
83
  protocol-hpack (1.4.2)
84
- protocol-http (0.22.6)
84
+ protocol-http (0.23.1)
85
85
  protocol-http1 (0.14.4)
86
86
  protocol-http (~> 0.22)
87
87
  protocol-http2 (0.14.2)
data/README.md CHANGED
@@ -46,9 +46,33 @@ Or install it yourself as:
46
46
  ```ruby
47
47
  Grumlin.configure do |config|
48
48
  config.url = "ws://localhost:8182/gremlin"
49
+
50
+ # make sure you select right provider for better compatibility
51
+ config.provider = :tinkergraph
49
52
  end
50
53
  ```
51
54
 
55
+ #### Providers
56
+
57
+ Currently `Grumlin` supports 2 providers:
58
+ - tinkergraph (default)
59
+ - neptune
60
+
61
+ As different providers may have or may have not support for specific features it's recommended to
62
+ explicitly specify the provider you use.
63
+
64
+ #### Provider features
65
+
66
+ Every provider is described by a set of features. In the future `Grumlin` may decide to disable or enable
67
+ some parts of it's functionality to comply provider's supported features. Currently there is no difference
68
+ in behaviour when working with different providers.
69
+
70
+ To check current providers supported features use
71
+
72
+ ```ruby
73
+ Grumlin.features
74
+ ```
75
+
52
76
  ### Traversing graphs
53
77
 
54
78
  **Warning**: Not all steps and expressions defined in the reference documentation are supported.
@@ -4,14 +4,15 @@ module Grumlin
4
4
  class Action < Steppable
5
5
  attr_reader :name, :args, :params, :next_step, :configuration_steps, :previous_step, :shortcut
6
6
 
7
- def initialize(name, args: [], params: {}, previous_step: nil, pool: nil)
8
- super()
7
+ # client is only used when a traversal is a part of transaction
8
+ def initialize(name, args: [], params: {}, previous_step: nil, pool: Grumlin.default_pool, session_id: nil)
9
+ super(pool: pool, session_id: session_id)
10
+
9
11
  @name = name.to_sym
10
12
  @args = args # TODO: add recursive validation: only json types or Action
11
13
  @params = params # TODO: add recursive validation: only json types
12
14
  @previous_step = previous_step
13
15
  @shortcut = shortcuts[@name]
14
- @pool = pool || Grumlin.default_pool
15
16
  end
16
17
 
17
18
  def configuration_step?
@@ -73,14 +74,18 @@ module Grumlin
73
74
  end
74
75
 
75
76
  def toList
76
- @pool.acquire do |client|
77
- client.write(bytecode)
78
- end
77
+ client_write(bytecode)
79
78
  end
80
79
 
81
80
  def iterate
81
+ client_write(bytecode(no_return: true))
82
+ end
83
+
84
+ private
85
+
86
+ def client_write(payload)
82
87
  @pool.acquire do |client|
83
- client.write(bytecode(no_return: true))
88
+ client.write(payload, session_id: @session_id)
84
89
  end
85
90
  end
86
91
  end
@@ -24,8 +24,14 @@ module Grumlin
24
24
  @client.close
25
25
  end
26
26
 
27
- def write(bytecode)
28
- @client.write(bytecode)
27
+ def write(bytecode, session_id: nil)
28
+ @client.write(bytecode, session_id: session_id)
29
+ ensure
30
+ @count += 1
31
+ end
32
+
33
+ def finalize_tx(action, session_id)
34
+ @client.finalize_tx(action, session_id)
29
35
  ensure
30
36
  @count += 1
31
37
  end
@@ -94,19 +100,19 @@ module Grumlin
94
100
  end
95
101
 
96
102
  # TODO: support yielding
97
- def write(bytecode)
103
+ def write(bytecode, session_id: nil)
98
104
  raise NotConnectedError unless connected?
99
105
 
100
- request = to_query(bytecode)
101
- channel = @request_dispatcher.add_request(request)
102
- @transport.write(request)
106
+ request = to_query(bytecode, session_id: session_id)
107
+ submit_request(request)
108
+ end
103
109
 
104
- begin
105
- channel.dequeue.flat_map { |item| Typing.cast(item) }
106
- rescue Async::Stop, Async::TimeoutError
107
- close(check_requests: false)
108
- raise
109
- end
110
+ def finalize_tx(action, session_id)
111
+ raise NotConnectedError unless connected?
112
+ raise ArgumentError, "session_id cannot be nil" if session_id.nil?
113
+
114
+ request = finalize_tx_query(action, session_id)
115
+ submit_request(request)
110
116
  end
111
117
 
112
118
  def inspect
@@ -119,20 +125,52 @@ module Grumlin
119
125
 
120
126
  private
121
127
 
128
+ def submit_request(request)
129
+ channel = @request_dispatcher.add_request(request)
130
+ @transport.write(request)
131
+
132
+ begin
133
+ channel.dequeue.flat_map { |item| Typing.cast(item) }
134
+ rescue Async::Stop, Async::TimeoutError
135
+ close(check_requests: false)
136
+ raise
137
+ end
138
+ end
139
+
122
140
  # This might be overridden in successors
123
141
  def build_transport
124
142
  Transport.new(@url, parent: @parent, **@client_options)
125
143
  end
126
144
 
127
- def to_query(bytecode)
145
+ def to_query(bytecode, session_id:)
146
+ {
147
+ requestId: SecureRandom.uuid,
148
+ op: :bytecode,
149
+ processor: session_id ? :session : :traversal,
150
+ args: {
151
+ gremlin: {
152
+ :@type => "g:Bytecode",
153
+ :@value => bytecode.serialize
154
+ },
155
+ aliases: { g: :g },
156
+ session: session_id
157
+ }.compact
158
+ }
159
+ end
160
+
161
+ def finalize_tx_query(action, session_id)
128
162
  {
129
163
  requestId: SecureRandom.uuid,
130
- op: "bytecode",
131
- processor: "traversal",
164
+ op: :bytecode,
165
+ processor: session_id ? :session : :traversal,
132
166
  args: {
133
- gremlin: { :@type => "g:Bytecode", :@value => bytecode.serialize },
134
- aliases: { g: :g }
135
- }
167
+ gremlin: {
168
+ :@type => "g:Bytecode",
169
+ :@value => { source: [[:tx, action]] }
170
+ },
171
+ aliases: { g: :g },
172
+ session: session_id
173
+ }.compact
136
174
  }
137
175
  end
138
176
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class Config
5
+ attr_accessor :url, :pool_size, :client_concurrency, :client_factory, :provider
6
+
7
+ SUPPORTED_PROVIDERS = %i[neptune tinkergraph].freeze
8
+
9
+ class ConfigurationError < Grumlin::Error; end
10
+
11
+ class UnknownProviderError < ConfigurationError; end
12
+
13
+ def initialize
14
+ @pool_size = 10
15
+ @client_concurrency = 5
16
+ @provider = :tinkergraph
17
+ @client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
18
+ end
19
+
20
+ def validate!
21
+ return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
22
+
23
+ raise UnknownProviderError, "provider '#{provider}' is unknown. Supported providers: #{SUPPORTED_PROVIDERS}"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Features
5
+ class FeatureList
6
+ def user_supplied_ids?
7
+ raise(NotImplementedError) if @user_supplied_ids.nil?
8
+
9
+ @user_supplied_ids
10
+ end
11
+
12
+ def supports_transactions?
13
+ raise(NotImplementedError) if @supports_transactions.nil?
14
+
15
+ @supports_transactions
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Features
5
+ class NeptuneFeatures < FeatureList
6
+ def initialize
7
+ super
8
+ @user_supplied_ids = true
9
+ @supports_transactions = true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Features
5
+ class TinkergraphFeatures < FeatureList
6
+ def initialize
7
+ super
8
+ @user_supplied_ids = true
9
+ @supports_transactions = false
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Features
5
+ class << self
6
+ FEATURES = {
7
+ neptune: NeptuneFeatures.new,
8
+ tinkergraph: TinkergraphFeatures.new
9
+ }.freeze
10
+
11
+ def for(provider)
12
+ FEATURES[provider]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -8,7 +8,7 @@ module Grumlin
8
8
  extend Forwardable
9
9
 
10
10
  UPSERT_RETRY_PARAMS = {
11
- on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentInsertFailedError],
11
+ on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentModificationError],
12
12
  sleep_method: ->(n) { Async::Task.current.sleep(n) },
13
13
  tries: 3,
14
14
  sleep: ->(n) { (n**2) + 1 + rand }
@@ -10,23 +10,6 @@ module Grumlin
10
10
  206 => :partial_content
11
11
  }.freeze
12
12
 
13
- ERRORS = {
14
- 499 => InvalidRequestArgumentsError,
15
- 500 => ServerError,
16
- 597 => ScriptEvaluationError,
17
- 599 => ServerSerializationError,
18
- 598 => ServerTimeoutError,
19
-
20
- 401 => ClientSideError,
21
- 407 => ClientSideError,
22
- 498 => ClientSideError
23
- }.freeze
24
-
25
- VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
26
- EDGE_ALREADY_EXISTS = "Edge with id already exists:"
27
- CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
28
- CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
29
-
30
13
  class DispatcherError < Grumlin::Error; end
31
14
 
32
15
  class RequestAlreadyAddedError < DispatcherError; end
@@ -47,14 +30,16 @@ module Grumlin
47
30
 
48
31
  # builds a response object, when it's ready sends it to the client via a channel
49
32
  # TODO: sometimes response does not include requestID, no idea how to handle it so far.
50
- def add_response(response) # rubocop:disable Metrics/AbcSize
33
+ def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
51
34
  request_id = response[:requestId]
52
35
  raise UnknownRequestError unless ongoing_request?(request_id)
53
36
 
54
37
  begin
55
38
  request = @requests[request_id]
56
39
 
57
- check_errors!(response[:status], request[:request])
40
+ RequestErrorFactory.build(request, response).tap do |err|
41
+ raise err unless err.nil?
42
+ end
58
43
 
59
44
  case SUCCESS[response.dig(:status, :code)]
60
45
  when :success
@@ -91,29 +76,5 @@ module Grumlin
91
76
  request = @requests.delete(request_id)
92
77
  request[:channel].close
93
78
  end
94
-
95
- def check_errors!(status, query)
96
- if (error = ERRORS[status[:code]])
97
- raise (
98
- already_exists_error(status) ||
99
- concurrent_insert_error(status) ||
100
- error
101
- ).new(status, query)
102
- end
103
-
104
- return unless SUCCESS[status[:code]].nil?
105
-
106
- raise(UnknownResponseStatus, status)
107
- end
108
-
109
- def already_exists_error(status)
110
- return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
111
- return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
112
- end
113
-
114
- def concurrent_insert_error(status)
115
- return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
116
- return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
117
- end
118
79
  end
119
80
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class RequestErrorFactory
5
+ ERRORS = {
6
+ 499 => InvalidRequestArgumentsError,
7
+ 500 => ServerError,
8
+ 597 => ScriptEvaluationError,
9
+ 599 => ServerSerializationError,
10
+ 598 => ServerTimeoutError,
11
+
12
+ 401 => ClientSideError,
13
+ 407 => ClientSideError,
14
+ 498 => ClientSideError
15
+ }.freeze
16
+
17
+ # Neptune presumably returns message as a JSON string of format
18
+ # {"detailedMessage":"",
19
+ # "requestId":"UUID",
20
+ # "code":"ConcurrentModificationException"}
21
+ # Currencly we simply search for substings to identify the exact error
22
+ # TODO: parse json and use `code` instead
23
+ VERTEX_ALREADY_EXISTS = "Vertex with id already exists:"
24
+ EDGE_ALREADY_EXISTS = "Edge with id already exists:"
25
+ CONCURRENT_VERTEX_INSERT_FAILED = "Failed to complete Insert operation for a Vertex due to conflicting concurrent"
26
+ CONCURRENT_EDGE_INSERT_FAILED = "Failed to complete Insert operation for an Edge due to conflicting concurrent"
27
+ CONCURRENCT_MODIFICATION_FAILED = "Failed to complete operation due to conflicting concurrent"
28
+
29
+ class << self
30
+ def build(request, response)
31
+ status = response[:status]
32
+ query = request[:request]
33
+
34
+ if (error = ERRORS[status[:code]])
35
+ return (
36
+ already_exists_error(status) ||
37
+ concurrent_modification_error(status) ||
38
+ error
39
+ ).new(status, query)
40
+ end
41
+
42
+ return unless RequestDispatcher::SUCCESS[status[:code]].nil?
43
+
44
+ UnknownResponseStatus.new(status)
45
+ end
46
+
47
+ def already_exists_error(status)
48
+ return VertexAlreadyExistsError if status[:message]&.include?(VERTEX_ALREADY_EXISTS)
49
+ return EdgeAlreadyExistsError if status[:message]&.include?(EDGE_ALREADY_EXISTS)
50
+ end
51
+
52
+ def concurrent_modification_error(status)
53
+ return ConcurrentVertexInsertFailedError if status[:message]&.include?(CONCURRENT_VERTEX_INSERT_FAILED)
54
+ return ConcurrentEdgeInsertFailedError if status[:message]&.include?(CONCURRENT_EDGE_INSERT_FAILED)
55
+ return ConcurrentModificationError if status[:message]&.include?(CONCURRENCT_MODIFICATION_FAILED)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -4,13 +4,18 @@ module Grumlin
4
4
  class Steppable
5
5
  extend Forwardable
6
6
 
7
+ attr_reader :session_id
8
+
7
9
  START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
8
10
  REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
9
11
  CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
10
12
 
11
13
  ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
12
14
 
13
- def initialize
15
+ def initialize(pool: Grumlin.default_pool, session_id: nil)
16
+ @pool = pool
17
+ @session_id = session_id
18
+
14
19
  return if respond_to?(:shortcuts)
15
20
 
16
21
  raise "steppable must not be initialized directly, use Grumlin::Shortcuts::Storage#g or #__ instead"
@@ -18,12 +23,12 @@ module Grumlin
18
23
 
19
24
  ALL_STEPS.each do |step|
20
25
  define_method step do |*args, **params|
21
- shortcuts.action_class.new(step, args: args, params: params, previous_step: self)
26
+ shortcuts.action_class.new(step, args: args, params: params, previous_step: self, session_id: @session_id)
22
27
  end
23
28
  end
24
29
 
25
30
  def step(name, *args, **params)
26
- shortcuts.action_class.new(name, args: args, params: params, previous_step: self)
31
+ shortcuts.action_class.new(name, args: args, params: params, previous_step: self, session_id: @session_id)
27
32
  end
28
33
 
29
34
  def_delegator :shortcuts, :__
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class Transaction
5
+ attr_reader :uuid
6
+
7
+ include Console
8
+
9
+ def initialize(traversal_start_class, pool: Grumlin.default_pool)
10
+ @traversal_start_class = traversal_start_class
11
+ @pool = pool
12
+
13
+ if supported?
14
+ @uuid = SecureRandom.uuid
15
+ return
16
+ end
17
+
18
+ logger.info(self) do
19
+ "#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
20
+ end
21
+ end
22
+
23
+ def supported?
24
+ Grumlin.features.supports_transactions?
25
+ end
26
+
27
+ def begin
28
+ @traversal_start_class.new(session_id: @uuid)
29
+ end
30
+
31
+ def commit
32
+ return unless supported?
33
+
34
+ finalize(:commit)
35
+ end
36
+
37
+ def rollback
38
+ return unless supported?
39
+
40
+ finalize(:rollback)
41
+ end
42
+
43
+ private
44
+
45
+ def finalize(action)
46
+ @pool.acquire do |client|
47
+ client.finalize_tx(action, @uuid)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,6 +4,15 @@ module Grumlin
4
4
  class TraversalStart < Steppable
5
5
  include WithExtension
6
6
 
7
+ class TraversalError < Grumlin::Error; end
8
+ class AlreadyBoundToTransationError < TraversalError; end
9
+
10
+ def tx
11
+ raise AlreadyBoundToTransationError if @session_id
12
+
13
+ Transaction.new(self.class, pool: @pool)
14
+ end
15
+
7
16
  def to_s(*)
8
17
  self.class.to_s
9
18
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.21.0"
4
+ VERSION = "0.21.1"
5
5
  end
data/lib/grumlin.rb CHANGED
@@ -98,7 +98,8 @@ module Grumlin
98
98
  class VertexAlreadyExistsError < AlreadyExistsError; end
99
99
  class EdgeAlreadyExistsError < AlreadyExistsError; end
100
100
 
101
- class ConcurrentInsertFailedError < ServerError; end
101
+ class ConcurrentModificationError < ServerError; end
102
+ class ConcurrentInsertFailedError < ConcurrentModificationError; end
102
103
 
103
104
  class ConcurrentVertexInsertFailedError < ConcurrentInsertFailedError; end
104
105
  class ConcurrentEdgeInsertFailedError < ConcurrentInsertFailedError; end
@@ -127,27 +128,26 @@ module Grumlin
127
128
 
128
129
  class WrongQueryResult < RepositoryError; end
129
130
 
130
- class Config
131
- attr_accessor :url, :pool_size, :client_concurrency, :client_factory
132
-
133
- def initialize
134
- @pool_size = 10
135
- @client_concurrency = 5
136
- @client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
137
- end
138
- end
139
-
140
131
  @pool_mutex = Mutex.new
141
132
 
142
133
  class << self
143
134
  def configure
144
135
  yield config
136
+
137
+ config.validate!
145
138
  end
146
139
 
147
140
  def config
148
141
  @config ||= Config.new
149
142
  end
150
143
 
144
+ # returns a subset of features for currently configured backend.
145
+ # The features lists are hardcoded as there is no way to get them
146
+ # from the remote server.
147
+ def features
148
+ Features.for(config.provider) # no memoization as provider may be changed
149
+ end
150
+
151
151
  def default_pool
152
152
  if Thread.current.thread_variable_get(:grumlin_default_pool)
153
153
  return Thread.current.thread_variable_get(:grumlin_default_pool)
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.21.0
4
+ version: 0.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gleb Sinyavskiy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-28 00:00:00.000000000 Z
11
+ date: 2022-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-pool
@@ -116,6 +116,7 @@ files:
116
116
  - lib/grumlin/action.rb
117
117
  - lib/grumlin/benchmark/repository.rb
118
118
  - lib/grumlin/client.rb
119
+ - lib/grumlin/config.rb
119
120
  - lib/grumlin/edge.rb
120
121
  - lib/grumlin/expressions/cardinality.rb
121
122
  - lib/grumlin/expressions/column.rb
@@ -128,12 +129,17 @@ files:
128
129
  - lib/grumlin/expressions/t.rb
129
130
  - lib/grumlin/expressions/text_p.rb
130
131
  - lib/grumlin/expressions/with_options.rb
132
+ - lib/grumlin/features.rb
133
+ - lib/grumlin/features/feature_list.rb
134
+ - lib/grumlin/features/neptune_features.rb
135
+ - lib/grumlin/features/tinkergraph_features.rb
131
136
  - lib/grumlin/path.rb
132
137
  - lib/grumlin/property.rb
133
138
  - lib/grumlin/repository.rb
134
139
  - lib/grumlin/repository/error_handling_strategy.rb
135
140
  - lib/grumlin/repository/instance_methods.rb
136
141
  - lib/grumlin/request_dispatcher.rb
142
+ - lib/grumlin/request_error_factory.rb
137
143
  - lib/grumlin/shortcut.rb
138
144
  - lib/grumlin/shortcuts.rb
139
145
  - lib/grumlin/shortcuts/properties.rb
@@ -151,6 +157,7 @@ files:
151
157
  - lib/grumlin/test/rspec.rb
152
158
  - lib/grumlin/test/rspec/db_cleaner_context.rb
153
159
  - lib/grumlin/test/rspec/gremlin_context.rb
160
+ - lib/grumlin/transaction.rb
154
161
  - lib/grumlin/transport.rb
155
162
  - lib/grumlin/traversal_start.rb
156
163
  - lib/grumlin/traversal_strategies/options_strategy.rb