grumlin 0.22.5 → 0.23.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/.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
|