grumlin 0.22.5 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +10 -8
- data/README.md +8 -0
- data/doc/middlewares.md +58 -0
- data/grumlin.gemspec +1 -0
- data/lib/grumlin/client.rb +6 -24
- data/lib/grumlin/config.rb +15 -0
- data/lib/grumlin/dummy_transaction.rb +1 -1
- data/lib/grumlin/middlewares/apply_shortcuts.rb +12 -0
- data/lib/grumlin/middlewares/build_query.rb +24 -0
- data/lib/grumlin/middlewares/cast_results.rb +11 -0
- data/lib/grumlin/middlewares/frozen_builder.rb +18 -0
- data/lib/grumlin/middlewares/middleware.rb +15 -0
- data/lib/grumlin/middlewares/run_query.rb +11 -0
- data/lib/grumlin/middlewares/serialize_to_bytecode.rb +13 -0
- data/lib/grumlin/middlewares/serialize_to_steps.rb +12 -0
- data/lib/grumlin/repository.rb +2 -2
- data/lib/grumlin/shortcuts/storage.rb +5 -5
- data/lib/grumlin/shortcuts.rb +1 -1
- data/lib/grumlin/shortcuts_applyer.rb +3 -3
- data/lib/grumlin/{action.rb → step.rb} +13 -11
- data/lib/grumlin/steppable.rb +7 -5
- data/lib/grumlin/steps.rb +21 -22
- data/lib/grumlin/transaction.rb +9 -8
- data/lib/grumlin/traversal_start.rb +1 -1
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin/with_extension.rb +5 -5
- data/lib/grumlin.rb +23 -19
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0560f7bf90bba9601628d6042ac1286076f9a8e9039b6f8e755b6f4beedb3e1a
|
4
|
+
data.tar.gz: 83b7b4409198a6cbb5520b003e2dcdacfe8623b9aa8c057b0e9df1e29692a4f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f87b6a69c71ebd867f227b35e13a50c499d054c6688747465e05a41426167ee3ffea5afbfb1875ca5c49e6a0c2a97f42ea20b93f9cf32cd5966942ba844f839c
|
7
|
+
data.tar.gz: 4b5262eacf5e57f4ad95e6cc5f450d924858b1e238e2bf3943b9b127da53db36c76078b22d8ba5d244aa35e5d0ddfc4db45516481bd41c58bbe2123ac262dd5f
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (0.23.0)
|
5
5
|
async-pool (~> 0.3)
|
6
6
|
async-websocket (>= 0.19, < 0.20)
|
7
|
+
ibsciss-middleware (~> 0.4.0)
|
7
8
|
oj (~> 3.13)
|
8
9
|
retryable (~> 3.0)
|
9
10
|
zeitwerk (~> 2.6)
|
@@ -21,7 +22,7 @@ GEM
|
|
21
22
|
console (~> 1.10)
|
22
23
|
nio4r (~> 2.3)
|
23
24
|
timers (~> 4.1)
|
24
|
-
async-http (0.59.
|
25
|
+
async-http (0.59.2)
|
25
26
|
async (>= 1.25)
|
26
27
|
async-io (>= 1.28)
|
27
28
|
async-pool (>= 0.2)
|
@@ -29,9 +30,9 @@ GEM
|
|
29
30
|
protocol-http1 (~> 0.14.0)
|
30
31
|
protocol-http2 (~> 0.14.0)
|
31
32
|
traces (>= 0.4.0)
|
32
|
-
async-io (1.
|
33
|
+
async-io (1.34.0)
|
33
34
|
async
|
34
|
-
async-pool (0.3.
|
35
|
+
async-pool (0.3.12)
|
35
36
|
async (>= 1.25)
|
36
37
|
async-rspec (1.16.1)
|
37
38
|
rspec (~> 3.0)
|
@@ -57,6 +58,7 @@ GEM
|
|
57
58
|
fiber-local (1.0.0)
|
58
59
|
i18n (1.12.0)
|
59
60
|
concurrent-ruby (~> 1.0)
|
61
|
+
ibsciss-middleware (0.4.2)
|
60
62
|
iniparse (1.5.0)
|
61
63
|
jaro_winkler (1.5.4)
|
62
64
|
json (2.6.2)
|
@@ -78,8 +80,8 @@ GEM
|
|
78
80
|
parser (3.1.2.1)
|
79
81
|
ast (~> 2.4.1)
|
80
82
|
protocol-hpack (1.4.2)
|
81
|
-
protocol-http (0.23.
|
82
|
-
protocol-http1 (0.14.
|
83
|
+
protocol-http (0.23.12)
|
84
|
+
protocol-http1 (0.14.6)
|
83
85
|
protocol-http (~> 0.22)
|
84
86
|
protocol-http2 (0.14.2)
|
85
87
|
protocol-hpack (~> 1.4)
|
@@ -154,8 +156,8 @@ GEM
|
|
154
156
|
yard (~> 0.9, >= 0.9.24)
|
155
157
|
thor (1.2.1)
|
156
158
|
tilt (2.0.11)
|
157
|
-
timers (4.3.
|
158
|
-
traces (0.
|
159
|
+
timers (4.3.4)
|
160
|
+
traces (0.7.0)
|
159
161
|
tzinfo (2.0.5)
|
160
162
|
concurrent-ruby (~> 1.0)
|
161
163
|
unicode-display_width (2.2.0)
|
data/README.md
CHANGED
@@ -295,6 +295,14 @@ it may be useful for debugging. Note that one needs to call a termination step m
|
|
295
295
|
|
296
296
|
method will return profiling data of the results.
|
297
297
|
|
298
|
+
##### Middlewares
|
299
|
+
|
300
|
+
Middlewares can be used to perform certain actions before and after every query made by `Grumlin`. It can be useful for
|
301
|
+
measuring query execution time or performing some modification or validation to the query before it reaches the server or
|
302
|
+
modify the response before client gets it.
|
303
|
+
|
304
|
+
See [doc/middlewares.md](doc/middlewares.md) for more info and examples.
|
305
|
+
|
298
306
|
#### Transactions
|
299
307
|
|
300
308
|
Since 0.22.0 `Grumlin` supports transactions when working with providers that supports them:
|
data/doc/middlewares.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Middlewares
|
2
|
+
|
3
|
+
Every single query performed by `Grumlin` goes through a stack of middlewares just like every single request in Rails
|
4
|
+
or many other web frameworks. `Grumlin` ships with a set of middlewares, each one performs some part of the query
|
5
|
+
execution process:
|
6
|
+
|
7
|
+
- `Middlewares::SerializeToSteps` - converts a `Step` into `Steps`
|
8
|
+
- `Middlewares::ApplyShortcuts` - applies shortcuts to `Steps`
|
9
|
+
- `Middlewares::SerializeToBytecode` - converts `Steps` into bytecode
|
10
|
+
- `Middlewares::BuildQuery` - builds an actual message that will be send to server
|
11
|
+
- `Middlewares::CastResults` - casts server response into ruby objects
|
12
|
+
- `Middlewares::RunQuery` - actually sends the message to the server
|
13
|
+
|
14
|
+
Normally these middlewares must never be rearranged or removed from the stack. Middlewares added after
|
15
|
+
`Middlewares::RunQuery` will not be executed.
|
16
|
+
|
17
|
+
# Writing a middleware for Grumlin
|
18
|
+
|
19
|
+
This entire feature is built on top of [ibsciss-middleware](https://github.com/Ibsciss/ruby-middleware).
|
20
|
+
Please refer to it's docs if you want to implement a middleware.
|
21
|
+
|
22
|
+
|
23
|
+
A minimal middleware that measures query execution time and puts it back to `env`:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class MeasureExecutionTime < Grumlin::Middlewares::Middleware # Middleware provides only an initializer with one argument for `app`
|
27
|
+
def call(env)
|
28
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
29
|
+
result = @app.call(env)
|
30
|
+
env[:execution_time] = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
31
|
+
result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Grumlin.configure do |cfg|
|
36
|
+
cfg.url = ENV.fetch("GREMLIN_URL", "ws://localhost:8182/gremlin")
|
37
|
+
cfg.provider = :tinkergraph
|
38
|
+
|
39
|
+
cfg.middlewares.insert_before Grumlin::Middlewares::CastResults, MeasureExecutionTime
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
When placed right before `Grumlin::Middlewares::CastResults` your middleware will have access to every intermediate result
|
44
|
+
of the query execution process:
|
45
|
+
- `env[:traversal]` - contains the original traversal
|
46
|
+
- `env[:steps]` - contains the `Steps` representing the traversal
|
47
|
+
- `env[:steps_without_shortcuts]` - contains the `Steps` representing the traversal, but with all shortcuts applied
|
48
|
+
- `env[:bytecode]` - raw bytecode of the traversal
|
49
|
+
- `env[:query]` - raw message that will be sent to the server. `requestId` can be found here: `env[:query][:requestId]`
|
50
|
+
|
51
|
+
After the query is performed (after `@app.call(env)`), these keys become available:
|
52
|
+
- `env[:results]` - raw results received from the server
|
53
|
+
- `env[:parsed_results]` - server results mapped to ruby types, basically the query results as the client gets it
|
54
|
+
|
55
|
+
Other useful parts of `env`:
|
56
|
+
- `env[:session_id]` - id of the session when executed inside a transaction, otherwise `nil`
|
57
|
+
- `env[:pool]` - connection pool that will be used to interact with the server
|
58
|
+
- `env[:need_results]` - flag stating whether the client needs the query execution results(`toList`, `next`) or not(`iterate`)
|
data/grumlin.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
|
31
31
|
spec.add_dependency "async-pool", "~> 0.3"
|
32
32
|
spec.add_dependency "async-websocket", ">= 0.19", "< 0.20"
|
33
|
+
spec.add_dependency "ibsciss-middleware", "~> 0.4.0"
|
33
34
|
spec.add_dependency "oj", "~> 3.13"
|
34
35
|
spec.add_dependency "retryable", "~> 3.0"
|
35
36
|
spec.add_dependency "zeitwerk", "~> 2.6"
|
data/lib/grumlin/client.rb
CHANGED
@@ -24,8 +24,8 @@ module Grumlin
|
|
24
24
|
@client.close
|
25
25
|
end
|
26
26
|
|
27
|
-
def write(
|
28
|
-
@client.write(
|
27
|
+
def write(query)
|
28
|
+
@client.write(query)
|
29
29
|
ensure
|
30
30
|
@count += 1
|
31
31
|
end
|
@@ -94,15 +94,13 @@ module Grumlin
|
|
94
94
|
end
|
95
95
|
|
96
96
|
# TODO: support yielding
|
97
|
-
def write(
|
97
|
+
def write(query)
|
98
98
|
raise NotConnectedError unless connected?
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
channel = @request_dispatcher.add_request(request)
|
100
|
+
channel = @request_dispatcher.add_request(query)
|
103
101
|
begin
|
104
|
-
@transport.write(
|
105
|
-
channel.dequeue
|
102
|
+
@transport.write(query)
|
103
|
+
channel.dequeue
|
106
104
|
rescue Async::Stop, Async::TimeoutError
|
107
105
|
close(check_requests: false)
|
108
106
|
raise
|
@@ -123,21 +121,5 @@ module Grumlin
|
|
123
121
|
def build_transport
|
124
122
|
Transport.new(@url, parent: @parent, **@client_options)
|
125
123
|
end
|
126
|
-
|
127
|
-
def to_query(bytecode, session_id:)
|
128
|
-
{
|
129
|
-
requestId: SecureRandom.uuid,
|
130
|
-
op: :bytecode,
|
131
|
-
processor: session_id ? :session : :traversal,
|
132
|
-
args: {
|
133
|
-
gremlin: {
|
134
|
-
:@type => "g:Bytecode",
|
135
|
-
:@value => bytecode.serialize
|
136
|
-
},
|
137
|
-
aliases: { g: :g },
|
138
|
-
session: session_id
|
139
|
-
}.compact
|
140
|
-
}
|
141
|
-
end
|
142
124
|
end
|
143
125
|
end
|
data/lib/grumlin/config.rb
CHANGED
@@ -6,6 +6,15 @@ module Grumlin
|
|
6
6
|
|
7
7
|
SUPPORTED_PROVIDERS = %i[neptune tinkergraph].freeze
|
8
8
|
|
9
|
+
DEFAULT_MIDDLEWARES = Middleware::Builder.new do |b|
|
10
|
+
b.use Middlewares::SerializeToSteps
|
11
|
+
b.use Middlewares::ApplyShortcuts
|
12
|
+
b.use Middlewares::SerializeToBytecode
|
13
|
+
b.use Middlewares::BuildQuery
|
14
|
+
b.use Middlewares::CastResults
|
15
|
+
b.use Middlewares::RunQuery
|
16
|
+
end.freeze
|
17
|
+
|
9
18
|
class ConfigurationError < Grumlin::Error; end
|
10
19
|
|
11
20
|
class UnknownProviderError < ConfigurationError; end
|
@@ -17,6 +26,12 @@ module Grumlin
|
|
17
26
|
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
18
27
|
end
|
19
28
|
|
29
|
+
def middlewares
|
30
|
+
@middlewares ||= Middleware::Builder.new do |b|
|
31
|
+
b.use DEFAULT_MIDDLEWARES
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
20
35
|
def validate!
|
21
36
|
return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
|
22
37
|
|
@@ -6,7 +6,7 @@ module Grumlin
|
|
6
6
|
|
7
7
|
include Console
|
8
8
|
|
9
|
-
def initialize(traversal_start_class, pool: nil) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
|
9
|
+
def initialize(traversal_start_class, middlewares:, pool: nil) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
|
10
10
|
@traversal_start_class = traversal_start_class
|
11
11
|
|
12
12
|
logger.info(self) do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Middlewares
|
5
|
+
class BuildQuery < Middleware
|
6
|
+
def call(env)
|
7
|
+
env[:query] = {
|
8
|
+
requestId: SecureRandom.uuid,
|
9
|
+
op: :bytecode,
|
10
|
+
processor: env[:session_id] ? :session : :traversal,
|
11
|
+
args: {
|
12
|
+
gremlin: {
|
13
|
+
:@type => "g:Bytecode",
|
14
|
+
:@value => env[:bytecode]
|
15
|
+
},
|
16
|
+
aliases: { g: :g },
|
17
|
+
session: env[:session_id]
|
18
|
+
}.compact
|
19
|
+
}
|
20
|
+
@app.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Middlewares
|
5
|
+
class FrozenBuilder < ::Middleware::Builder
|
6
|
+
def initialize(opts = nil, &block)
|
7
|
+
super(opts, &block)
|
8
|
+
freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def freeze
|
12
|
+
super
|
13
|
+
|
14
|
+
stack.freeze
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Middlewares
|
5
|
+
class SerializeToBytecode < Middleware
|
6
|
+
def call(env)
|
7
|
+
env[:bytecode] = StepsSerializers::Bytecode.new(env[:steps_without_shortcuts],
|
8
|
+
no_return: !env[:need_results]).serialize
|
9
|
+
@app.call(env)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/grumlin/repository.rb
CHANGED
@@ -32,9 +32,9 @@ module Grumlin
|
|
32
32
|
t = instance_exec(*args, **params, &query_block)
|
33
33
|
return t if t.nil? || (t.respond_to?(:empty?) && t.empty?)
|
34
34
|
|
35
|
-
unless t.is_a?(Grumlin::
|
35
|
+
unless t.is_a?(Grumlin::Step)
|
36
36
|
raise WrongQueryResult,
|
37
|
-
"queries must return #{Grumlin::
|
37
|
+
"queries must return #{Grumlin::Step}, nil or an empty collection. Given: #{t.class}"
|
38
38
|
end
|
39
39
|
|
40
40
|
return block.call(t) unless block.nil?
|
@@ -33,10 +33,10 @@ module Grumlin
|
|
33
33
|
def add(name, shortcut)
|
34
34
|
@storage[name] = shortcut
|
35
35
|
|
36
|
-
|
36
|
+
sc = step_class
|
37
37
|
|
38
38
|
shortcut_methods_module.define_method(name) do |*args, **params|
|
39
|
-
next
|
39
|
+
next sc.new(name, args: args, params: params, previous_step: self, pool: Grumlin.default_pool)
|
40
40
|
end
|
41
41
|
extend_traversal_classes(shortcut) unless shortcut.lazy?
|
42
42
|
end
|
@@ -59,8 +59,8 @@ module Grumlin
|
|
59
59
|
@traversal_start_class ||= shortcut_aware_class(TraversalStart)
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
63
|
-
@
|
62
|
+
def step_class
|
63
|
+
@step_class ||= shortcut_aware_class(Step)
|
64
64
|
end
|
65
65
|
|
66
66
|
protected
|
@@ -91,7 +91,7 @@ module Grumlin
|
|
91
91
|
m = Module.new do
|
92
92
|
define_method(shortcut.name, &shortcut.block)
|
93
93
|
end
|
94
|
-
|
94
|
+
step_class.include(m)
|
95
95
|
traversal_start_class.include(m)
|
96
96
|
end
|
97
97
|
end
|
data/lib/grumlin/shortcuts.rb
CHANGED
@@ -15,7 +15,7 @@ module Grumlin
|
|
15
15
|
name = name.to_sym
|
16
16
|
lazy = false if override
|
17
17
|
|
18
|
-
if Grumlin::
|
18
|
+
if Grumlin::Step::REGULAR_STEPS.include?(name) && !override
|
19
19
|
raise ArgumentError,
|
20
20
|
"overriding standard gremlin steps is not allowed, if you know what you're doing, pass `override: true`"
|
21
21
|
end
|
@@ -30,10 +30,10 @@ module Grumlin
|
|
30
30
|
next result << StepData.new(step.name, args: args, params: step.params) unless shortcut&.lazy?
|
31
31
|
|
32
32
|
t = shortcuts.__
|
33
|
-
|
34
|
-
next if
|
33
|
+
step = shortcut.apply(t, *args, **step.params)
|
34
|
+
next if step.nil? || step == t # Shortcut did not add any steps
|
35
35
|
|
36
|
-
new_steps = call(Steps.from(
|
36
|
+
new_steps = call(Steps.from(step))
|
37
37
|
result.concat(new_steps.configuration_steps, new_steps.steps)
|
38
38
|
end
|
39
39
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Grumlin
|
4
|
-
class
|
4
|
+
class Step < Steppable
|
5
5
|
attr_reader :name, :args, :params, :next_step, :configuration_steps, :previous_step, :shortcut
|
6
6
|
|
7
|
-
#
|
8
|
-
def initialize(name, args: [], params: {}, previous_step: nil, pool: nil, session_id: nil
|
9
|
-
|
7
|
+
# TODO: replace pool, session_id and middlewares with a context?
|
8
|
+
def initialize(name, args: [], params: {}, previous_step: nil, pool: nil, session_id: nil, # rubocop:disable Metrics/ParameterLists
|
9
|
+
middlewares: Grumlin.default_middlewares)
|
10
|
+
super(pool: pool, session_id: session_id, middlewares: middlewares)
|
10
11
|
|
11
12
|
@name = name.to_sym
|
12
|
-
@args = args # TODO: add recursive validation: only json types or
|
13
|
+
@args = args # TODO: add recursive validation: only json types or Step
|
13
14
|
@params = params # TODO: add recursive validation: only json types
|
14
15
|
@previous_step = previous_step
|
15
16
|
@shortcut = shortcuts[@name]
|
@@ -74,19 +75,20 @@ module Grumlin
|
|
74
75
|
end
|
75
76
|
|
76
77
|
def toList
|
77
|
-
|
78
|
+
send_query(need_results: true)
|
78
79
|
end
|
79
80
|
|
80
81
|
def iterate
|
81
|
-
|
82
|
+
send_query(need_results: false)
|
82
83
|
end
|
83
84
|
|
84
85
|
private
|
85
86
|
|
86
|
-
def
|
87
|
-
@
|
88
|
-
|
89
|
-
|
87
|
+
def send_query(need_results:)
|
88
|
+
@middlewares.call(traversal: self,
|
89
|
+
need_results: need_results,
|
90
|
+
session_id: @session_id,
|
91
|
+
pool: @pool)
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
data/lib/grumlin/steppable.rb
CHANGED
@@ -12,10 +12,12 @@ module Grumlin
|
|
12
12
|
|
13
13
|
ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
|
14
14
|
|
15
|
-
def initialize(pool: nil, session_id: nil)
|
15
|
+
def initialize(pool: nil, session_id: nil, middlewares: Grumlin.default_middlewares)
|
16
16
|
@pool = pool
|
17
17
|
@session_id = session_id
|
18
18
|
|
19
|
+
@middlewares = middlewares
|
20
|
+
|
19
21
|
return if respond_to?(:shortcuts)
|
20
22
|
|
21
23
|
raise "steppable must not be initialized directly, use Grumlin::Shortcuts::Storage#g or #__ instead"
|
@@ -23,14 +25,14 @@ module Grumlin
|
|
23
25
|
|
24
26
|
ALL_STEPS.each do |step|
|
25
27
|
define_method step do |*args, **params|
|
26
|
-
shortcuts.
|
27
|
-
|
28
|
+
shortcuts.step_class.new(step, args: args, params: params, previous_step: self,
|
29
|
+
session_id: @session_id, pool: @pool, middlewares: @middlewares)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
33
|
def step(name, *args, **params)
|
32
|
-
shortcuts.
|
33
|
-
|
34
|
+
shortcuts.step_class.new(name, args: args, params: params, previous_step: self,
|
35
|
+
session_id: @session_id, pool: @pool, middlewares: @middlewares)
|
34
36
|
end
|
35
37
|
|
36
38
|
def_delegator :shortcuts, :__
|
data/lib/grumlin/steps.rb
CHANGED
@@ -2,23 +2,16 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
class Steps
|
5
|
-
CONFIGURATION_STEPS =
|
6
|
-
ALL_STEPS =
|
5
|
+
CONFIGURATION_STEPS = Step::CONFIGURATION_STEPS
|
6
|
+
ALL_STEPS = Step::ALL_STEPS
|
7
7
|
|
8
|
-
def self.from(
|
9
|
-
raise ArgumentError, "expected: #{
|
8
|
+
def self.from(step)
|
9
|
+
raise ArgumentError, "expected: #{Step}, given: #{step.class}" unless step.is_a?(Step)
|
10
10
|
|
11
|
-
shortcuts
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
actions.unshift(action)
|
16
|
-
action = action.previous_step
|
17
|
-
end
|
18
|
-
|
19
|
-
new(shortcuts).tap do |chain|
|
20
|
-
actions.each do |act|
|
21
|
-
chain.add(act.name, args: act.args, params: act.params)
|
11
|
+
new(step.shortcuts).tap do |chain|
|
12
|
+
until step.nil? || step.is_a?(TraversalStart)
|
13
|
+
chain.add(step.name, args: step.args, params: step.params, to: :begin)
|
14
|
+
step = step.previous_step
|
22
15
|
end
|
23
16
|
end
|
24
17
|
end
|
@@ -31,13 +24,16 @@ module Grumlin
|
|
31
24
|
@steps = steps
|
32
25
|
end
|
33
26
|
|
34
|
-
def add(name, args: [], params: {})
|
27
|
+
def add(name, args: [], params: {}, to: :end)
|
35
28
|
if CONFIGURATION_STEPS.include?(name) || name.to_sym == :tx
|
36
|
-
return add_configuration_step(name, args: args, params: params)
|
29
|
+
return add_configuration_step(name, args: args, params: params, to: to)
|
37
30
|
end
|
38
31
|
|
39
32
|
StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
|
40
|
-
@steps << step
|
33
|
+
next @steps << step if to == :end
|
34
|
+
next @steps.unshift(step) if to == :begin
|
35
|
+
|
36
|
+
raise ArgumentError, "'to:' must be either :begin or :end, given: '#{to}'"
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
@@ -64,16 +60,19 @@ module Grumlin
|
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
67
|
-
def add_configuration_step(name, args: [], params: {})
|
68
|
-
raise ArgumentError, "cannot use configuration steps after start step was used"
|
63
|
+
def add_configuration_step(name, args: [], params: {}, to: :end)
|
64
|
+
raise ArgumentError, "cannot use configuration steps after start step was used" if @steps.any? && to == :end
|
69
65
|
|
70
66
|
StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
|
71
|
-
@configuration_steps << step
|
67
|
+
next @configuration_steps << step if to == :end
|
68
|
+
next @configuration_steps.unshift(step) if to == :begin
|
69
|
+
|
70
|
+
raise ArgumentError, "to must be either :begin or :end, given: '#{to}'"
|
72
71
|
end
|
73
72
|
end
|
74
73
|
|
75
74
|
def cast_arguments(arguments)
|
76
|
-
arguments.map { |arg| arg.is_a?(
|
75
|
+
arguments.map { |arg| arg.is_a?(Step) ? Steps.from(arg) : arg }
|
77
76
|
end
|
78
77
|
end
|
79
78
|
end
|
data/lib/grumlin/transaction.rb
CHANGED
@@ -6,14 +6,14 @@ module Grumlin
|
|
6
6
|
|
7
7
|
include Console
|
8
8
|
|
9
|
-
COMMIT = Grumlin::Repository.new.g.step(:tx, :commit)
|
10
|
-
ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback)
|
9
|
+
COMMIT = Grumlin::Repository.new.g.step(:tx, :commit)
|
10
|
+
ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback)
|
11
11
|
|
12
|
-
def initialize(traversal_start_class, pool:)
|
12
|
+
def initialize(traversal_start_class, pool:, middlewares:)
|
13
13
|
@traversal_start_class = traversal_start_class
|
14
14
|
@pool = pool
|
15
|
-
|
16
15
|
@session_id = SecureRandom.uuid
|
16
|
+
@middlewares = middlewares
|
17
17
|
end
|
18
18
|
|
19
19
|
def begin
|
@@ -30,10 +30,11 @@ module Grumlin
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def finalize(
|
34
|
-
@
|
35
|
-
|
36
|
-
|
33
|
+
def finalize(step)
|
34
|
+
@middlewares.call(traversal: step,
|
35
|
+
need_results: false,
|
36
|
+
session_id: @session_id,
|
37
|
+
pool: @pool)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
@@ -10,7 +10,7 @@ module Grumlin
|
|
10
10
|
def tx
|
11
11
|
raise AlreadyBoundToTransactionError if @session_id
|
12
12
|
|
13
|
-
transaction = tx_class.new(self.class, pool: @pool)
|
13
|
+
transaction = tx_class.new(self.class, pool: @pool, middlewares: @middlewares)
|
14
14
|
return transaction unless block_given?
|
15
15
|
|
16
16
|
begin
|
data/lib/grumlin/version.rb
CHANGED
@@ -4,22 +4,22 @@ module Grumlin
|
|
4
4
|
module WithExtension
|
5
5
|
def with(name, value)
|
6
6
|
prev = self
|
7
|
-
strategy = if is_a?(
|
7
|
+
strategy = if is_a?(with_step_class)
|
8
8
|
prev = previous_step
|
9
9
|
TraversalStrategies::OptionsStrategy.new(args.first.value.merge(name => value))
|
10
10
|
else
|
11
11
|
TraversalStrategies::OptionsStrategy.new({ name => value })
|
12
12
|
end
|
13
|
-
|
13
|
+
with_step_class.new(:withStrategies, args: [strategy], previous_step: prev)
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
-
def
|
19
|
-
@
|
18
|
+
def with_step_class
|
19
|
+
@with_step_class ||= Class.new(shortcuts.step_class) do
|
20
20
|
include WithExtension
|
21
21
|
|
22
|
-
def
|
22
|
+
def with_step_class
|
23
23
|
self.class
|
24
24
|
end
|
25
25
|
end
|
data/lib/grumlin.rb
CHANGED
@@ -17,6 +17,7 @@ require "async/barrier"
|
|
17
17
|
require "async/http/endpoint"
|
18
18
|
require "async/websocket/client"
|
19
19
|
|
20
|
+
require "middleware"
|
20
21
|
require "retryable"
|
21
22
|
|
22
23
|
require "zeitwerk"
|
@@ -34,6 +35,7 @@ module Grumlin
|
|
34
35
|
class Error < StandardError; end
|
35
36
|
|
36
37
|
class TransactionError < Error; end
|
38
|
+
|
37
39
|
class Rollback < TransactionError; end
|
38
40
|
|
39
41
|
class UnknownError < Error; end
|
@@ -99,15 +101,19 @@ module Grumlin
|
|
99
101
|
end
|
100
102
|
|
101
103
|
class VertexAlreadyExistsError < AlreadyExistsError; end
|
104
|
+
|
102
105
|
class EdgeAlreadyExistsError < AlreadyExistsError; end
|
103
106
|
|
104
107
|
class ConcurrentModificationError < ServerError; end
|
108
|
+
|
105
109
|
class ConcurrentInsertFailedError < ConcurrentModificationError; end
|
106
110
|
|
107
111
|
class ConcurrentVertexInsertFailedError < ConcurrentInsertFailedError; end
|
112
|
+
|
108
113
|
class ConcurrentEdgeInsertFailedError < ConcurrentInsertFailedError; end
|
109
114
|
|
110
115
|
class ConcurrentVertexPropertyInsertFailedError < ConcurrentInsertFailedError; end
|
116
|
+
|
111
117
|
class ConcurrentEdgePropertyInsertFailedError < ConcurrentInsertFailedError; end
|
112
118
|
|
113
119
|
class ServerSerializationError < ServerSideError; end
|
@@ -134,8 +140,6 @@ module Grumlin
|
|
134
140
|
|
135
141
|
class WrongQueryResult < RepositoryError; end
|
136
142
|
|
137
|
-
@pool_mutex = Mutex.new
|
138
|
-
|
139
143
|
class << self
|
140
144
|
def configure
|
141
145
|
yield config
|
@@ -147,6 +151,10 @@ module Grumlin
|
|
147
151
|
@config ||= Config.new
|
148
152
|
end
|
149
153
|
|
154
|
+
def default_middlewares
|
155
|
+
config.middlewares
|
156
|
+
end
|
157
|
+
|
150
158
|
# returns a subset of features for currently configured backend.
|
151
159
|
# The features lists are hardcoded as there is no way to get them
|
152
160
|
# from the remote server.
|
@@ -155,26 +163,22 @@ module Grumlin
|
|
155
163
|
end
|
156
164
|
|
157
165
|
def default_pool
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
Async::Pool::Controller.new(Grumlin::Client::PoolResource,
|
165
|
-
limit: config.pool_size))
|
166
|
-
end
|
166
|
+
t = Thread.current
|
167
|
+
return t.thread_variable_get(:grumlin_default_pool) if t.thread_variable_get(:grumlin_default_pool)
|
168
|
+
|
169
|
+
t.thread_variable_set(:grumlin_default_pool,
|
170
|
+
Async::Pool::Controller.new(Grumlin::Client::PoolResource,
|
171
|
+
limit: config.pool_size))
|
167
172
|
end
|
168
173
|
|
169
174
|
def close
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
end
|
175
|
+
t = Thread.current
|
176
|
+
return if t.thread_variable_get(:grumlin_default_pool).nil?
|
177
|
+
|
178
|
+
pool = t.thread_variable_get(:grumlin_default_pool)
|
179
|
+
pool.wait while pool.busy?
|
180
|
+
pool.close
|
181
|
+
t.thread_variable_set(:grumlin_default_pool, nil)
|
178
182
|
end
|
179
183
|
|
180
184
|
def definitions
|
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.
|
4
|
+
version: 0.23.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: 2022-08
|
11
|
+
date: 2022-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '0.20'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: ibsciss-middleware
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.4.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.4.0
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: oj
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -110,6 +124,7 @@ files:
|
|
110
124
|
- Rakefile
|
111
125
|
- bin/console
|
112
126
|
- bin/setup
|
127
|
+
- doc/middlewares.md
|
113
128
|
- docker-compose.yml
|
114
129
|
- gremlin_server/Dockerfile
|
115
130
|
- gremlin_server/tinkergraph-empty.properties
|
@@ -119,7 +134,6 @@ files:
|
|
119
134
|
- lib/async/channel.rb
|
120
135
|
- lib/definitions.yml
|
121
136
|
- lib/grumlin.rb
|
122
|
-
- lib/grumlin/action.rb
|
123
137
|
- lib/grumlin/benchmark/repository.rb
|
124
138
|
- lib/grumlin/client.rb
|
125
139
|
- lib/grumlin/config.rb
|
@@ -140,6 +154,14 @@ files:
|
|
140
154
|
- lib/grumlin/features/feature_list.rb
|
141
155
|
- lib/grumlin/features/neptune_features.rb
|
142
156
|
- lib/grumlin/features/tinkergraph_features.rb
|
157
|
+
- lib/grumlin/middlewares/apply_shortcuts.rb
|
158
|
+
- lib/grumlin/middlewares/build_query.rb
|
159
|
+
- lib/grumlin/middlewares/cast_results.rb
|
160
|
+
- lib/grumlin/middlewares/frozen_builder.rb
|
161
|
+
- lib/grumlin/middlewares/middleware.rb
|
162
|
+
- lib/grumlin/middlewares/run_query.rb
|
163
|
+
- lib/grumlin/middlewares/serialize_to_bytecode.rb
|
164
|
+
- lib/grumlin/middlewares/serialize_to_steps.rb
|
143
165
|
- lib/grumlin/path.rb
|
144
166
|
- lib/grumlin/property.rb
|
145
167
|
- lib/grumlin/repository.rb
|
@@ -153,6 +175,7 @@ files:
|
|
153
175
|
- lib/grumlin/shortcuts/storage.rb
|
154
176
|
- lib/grumlin/shortcuts/upserts.rb
|
155
177
|
- lib/grumlin/shortcuts_applyer.rb
|
178
|
+
- lib/grumlin/step.rb
|
156
179
|
- lib/grumlin/step_data.rb
|
157
180
|
- lib/grumlin/steppable.rb
|
158
181
|
- lib/grumlin/steps.rb
|