grumlin 0.23.0 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -9
- data/Gemfile.lock +1 -1
- data/README.md +100 -142
- data/Rakefile +1 -1
- data/bin/console +18 -3
- data/doc/middlewares.md +49 -10
- data/lib/async/channel.rb +54 -56
- data/lib/grumlin/benchmark/repository.rb +10 -14
- data/lib/grumlin/client.rb +93 -95
- data/lib/grumlin/config.rb +33 -33
- data/lib/grumlin/dummy_transaction.rb +13 -15
- data/lib/grumlin/edge.rb +18 -20
- data/lib/grumlin/expressions/cardinality.rb +5 -9
- data/lib/grumlin/expressions/column.rb +5 -9
- data/lib/grumlin/expressions/expression.rb +7 -11
- data/lib/grumlin/expressions/operator.rb +5 -9
- data/lib/grumlin/expressions/order.rb +5 -9
- data/lib/grumlin/expressions/p.rb +27 -31
- data/lib/grumlin/expressions/pop.rb +5 -9
- data/lib/grumlin/expressions/scope.rb +5 -9
- data/lib/grumlin/expressions/t.rb +5 -9
- data/lib/grumlin/expressions/text_p.rb +5 -9
- data/lib/grumlin/expressions/with_options.rb +17 -21
- data/lib/grumlin/features/feature_list.rb +8 -12
- data/lib/grumlin/features/neptune_features.rb +5 -9
- data/lib/grumlin/features/tinkergraph_features.rb +5 -9
- data/lib/grumlin/features.rb +8 -10
- data/lib/grumlin/middlewares/apply_shortcuts.rb +4 -8
- data/lib/grumlin/middlewares/build_query.rb +16 -20
- data/lib/grumlin/middlewares/builder.rb +15 -0
- data/lib/grumlin/middlewares/cast_results.rb +3 -7
- data/lib/grumlin/middlewares/find_blocklisted_steps.rb +14 -0
- data/lib/grumlin/middlewares/find_mutating_steps.rb +9 -0
- data/lib/grumlin/middlewares/middleware.rb +6 -10
- data/lib/grumlin/middlewares/run_query.rb +3 -7
- data/lib/grumlin/middlewares/serialize_to_bytecode.rb +5 -9
- data/lib/grumlin/middlewares/serialize_to_steps.rb +4 -8
- data/lib/grumlin/path.rb +11 -13
- data/lib/grumlin/property.rb +14 -16
- data/lib/grumlin/query_validators/blocklisted_steps_validator.rb +22 -0
- data/lib/grumlin/query_validators/validator.rb +36 -0
- data/lib/grumlin/repository/error_handling_strategy.rb +36 -40
- data/lib/grumlin/repository/instance_methods.rb +115 -118
- data/lib/grumlin/repository.rb +82 -58
- data/lib/grumlin/request_dispatcher.rb +55 -57
- data/lib/grumlin/request_error_factory.rb +53 -55
- data/lib/grumlin/shortcut.rb +19 -21
- data/lib/grumlin/shortcuts/properties.rb +12 -16
- data/lib/grumlin/shortcuts/storage.rb +67 -74
- data/lib/grumlin/shortcuts/upserts.rb +19 -22
- data/lib/grumlin/shortcuts.rb +23 -25
- data/lib/grumlin/shortcuts_applyer.rb +27 -29
- data/lib/grumlin/step.rb +88 -90
- data/lib/grumlin/step_data.rb +12 -14
- data/lib/grumlin/steppable.rb +23 -25
- data/lib/grumlin/steps.rb +52 -54
- data/lib/grumlin/steps_serializers/bytecode.rb +53 -56
- data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +17 -21
- data/lib/grumlin/steps_serializers/serializer.rb +7 -11
- data/lib/grumlin/steps_serializers/string.rb +26 -30
- data/lib/grumlin/test/rspec/db_cleaner_context.rb +8 -12
- data/lib/grumlin/test/rspec/gremlin_context.rb +18 -16
- data/lib/grumlin/test/rspec.rb +1 -5
- data/lib/grumlin/transaction.rb +34 -36
- data/lib/grumlin/transport.rb +71 -73
- data/lib/grumlin/traversal_start.rb +31 -33
- data/lib/grumlin/traversal_strategies/options_strategy.rb +3 -7
- data/lib/grumlin/traverser.rb +5 -7
- data/lib/grumlin/typed_value.rb +11 -13
- data/lib/grumlin/typing.rb +70 -72
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin/vertex.rb +14 -16
- data/lib/grumlin/vertex_property.rb +14 -16
- data/lib/grumlin/with_extension.rb +17 -19
- data/lib/grumlin.rb +13 -0
- metadata +9 -6
- data/lib/grumlin/middlewares/frozen_builder.rb +0 -18
- data/lib/grumlin/sugar.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1321ed876a7cc42db4e40d16fe7a7c7baa4cd0d955ad58901ecfd07051700e7b
|
4
|
+
data.tar.gz: 1b66499966537dff77767ddc5936222a7129b29004f05a60d4f578ede6c08a9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6158025426480ae13930848e370014e2fc77325d650d5540c11a516b4ed982ee5b3526049d7f8e1b669d9b2ddffe2370e4dc5e8ce4cda55755e69f3b72750d3
|
7
|
+
data.tar.gz: 33c02aca842d77dbe1284504b9c00195a72301bbcf23ba69cbeeffa5ca75d4d95aa71791e3d7228e863c62d1048739c70f3df90515f0339698741c1938e49484
|
data/.rubocop.yml
CHANGED
@@ -65,10 +65,6 @@ RSpec/DescribeClass:
|
|
65
65
|
RSpec/MultipleMemoizedHelpers:
|
66
66
|
Max: 7
|
67
67
|
|
68
|
-
Style/WordArray:
|
69
|
-
Exclude:
|
70
|
-
- spec/**/*_spec.rb
|
71
|
-
|
72
68
|
Style/StringLiterals:
|
73
69
|
Enabled: true
|
74
70
|
EnforcedStyle: double_quotes
|
@@ -83,9 +79,13 @@ Style/Documentation:
|
|
83
79
|
Style/MultilineBlockChain:
|
84
80
|
Enabled: false
|
85
81
|
|
82
|
+
Style/SymbolArray:
|
83
|
+
EnforcedStyle: brackets
|
84
|
+
|
85
|
+
Style/WordArray:
|
86
|
+
EnforcedStyle: brackets
|
87
|
+
Exclude:
|
88
|
+
- spec/**/*_spec.rb
|
86
89
|
|
87
|
-
|
88
|
-
|
89
|
-
# EnforcedStyle: brackets
|
90
|
-
# Style/WordArray:
|
91
|
-
# EnforcedStyle: brackets
|
90
|
+
Style/ClassAndModuleChildren:
|
91
|
+
EnforcedStyle: compact
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -82,116 +82,11 @@ Current differences between providers:
|
|
82
82
|
|
83
83
|
**Warning**: Not all steps and expressions defined in the reference documentation are supported.
|
84
84
|
|
85
|
-
#### Sugar
|
86
|
-
|
87
|
-
Grumlin provides an easy to use module called `Grumlin::Sugar`. Once included in your class it injects some useful
|
88
|
-
constants and methods turning your class into an entrypoint for traversals with pure gremlin experience.
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
class MyRepository
|
92
|
-
include Grumlin::Sugar
|
93
|
-
|
94
|
-
def nodes(property1:, property2:)
|
95
|
-
g.V()
|
96
|
-
.has(T.label, "node")
|
97
|
-
.has(:property1, property1)
|
98
|
-
.has(:property2, property2)
|
99
|
-
.order.by(:property3, Order.asc).limit(10)
|
100
|
-
.toList
|
101
|
-
end
|
102
|
-
end
|
103
|
-
```
|
104
|
-
|
105
|
-
#### Shortcuts
|
106
|
-
|
107
|
-
**Shortcuts** is a way to share and organize gremlin code. They let developers define their own steps consisting of
|
108
|
-
sequences of standard gremlin steps, other shortcuts and even add new initially unsupported by Grumlin steps.
|
109
|
-
Remember ActiveRecord scopes? Shortcuts are very similar.
|
110
|
-
|
111
|
-
**Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
|
112
|
-
be ignored because methods have higher priority.
|
113
|
-
|
114
|
-
Shortcuts are designed to be used with `Grumlin::Repository` but still can be used separately, with `Grumlin::Sugar`
|
115
|
-
for example.
|
116
|
-
|
117
|
-
**Defining**:
|
118
|
-
```ruby
|
119
|
-
|
120
|
-
# Defining shortcuts
|
121
|
-
class ColorShortcut
|
122
|
-
extend Grumlin::Shortcuts
|
123
|
-
|
124
|
-
# Custom step
|
125
|
-
shortcut :hasColor do |color|
|
126
|
-
has(:color, color)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
class ChooseShortcut
|
131
|
-
extend Grumlin::Shortcuts
|
132
|
-
|
133
|
-
# Standard Gremlin step
|
134
|
-
shortcut :choose do |*args|
|
135
|
-
step(:choose, *args)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
class AllShortcuts
|
140
|
-
extend Grumlin::Shortcuts
|
141
|
-
|
142
|
-
# Adding shortcuts from other modules
|
143
|
-
shortcuts_from ColorShortcut
|
144
|
-
shortcuts_from ChooseShortcut
|
145
|
-
end
|
146
|
-
```
|
147
|
-
|
148
|
-
**Using with Grumlin::Sugar**:
|
149
|
-
```ruby
|
150
|
-
class MyRepository
|
151
|
-
include Grumlin::Sugar
|
152
|
-
extend Grumlin::Shortcuts
|
153
|
-
|
154
|
-
shortcuts_from AllShortcuts
|
155
|
-
|
156
|
-
# Wrapping a traversal
|
157
|
-
def red_triangles
|
158
|
-
g(self.class.shortcuts).V.hasLabel(:triangle)
|
159
|
-
.hasColor("red")
|
160
|
-
.toList
|
161
|
-
end
|
162
|
-
|
163
|
-
# Wrapping _
|
164
|
-
def something_else
|
165
|
-
g(self.class.shortcuts).V.hasColor("red")
|
166
|
-
.repeat(__(self.class.shortcuts))
|
167
|
-
.out(:has)
|
168
|
-
.hasColor("blue")
|
169
|
-
.toList
|
170
|
-
end
|
171
|
-
end
|
172
|
-
```
|
173
|
-
|
174
|
-
##### Overriding standard steps and shortcuts
|
175
|
-
|
176
|
-
Sometimes it may be useful to override standard steps. Grumlin does not allow it by default, but one
|
177
|
-
is still able to override standard steps if they know what they are doing:
|
178
|
-
|
179
|
-
```ruby
|
180
|
-
shortcut :addV, override: true do |label|
|
181
|
-
super(label).property(:default, :value)
|
182
|
-
end
|
183
|
-
```
|
184
|
-
|
185
|
-
This will create a new shortcut that overrides the standard step `addV` and adds default properties to all vertices
|
186
|
-
created by the repository that uses this shortcut.
|
187
|
-
|
188
|
-
Shortcuts also can be overridden, but super() is not available.
|
189
|
-
|
190
85
|
#### Grumlin::Repository
|
191
|
-
`Grumlin::Repository`
|
192
|
-
|
193
|
-
|
194
|
-
or `Grumlin::Shortcuts` can be inherited**, successors don't need to extend them again and have access to shortcuts
|
86
|
+
`Grumlin::Repository` - is a starting point for all traversals. It provides easy access to `g`, `__` and usual gremlin
|
87
|
+
expressions for you class. It has support for defining your own shortcuts and is even shipped with a couple of useful
|
88
|
+
shortcuts to make gremlin code more rubyish. **Classes extending `Grumlin::Repository`
|
89
|
+
or `Grumlin::Shortcuts` can be inherited**, successors don't need to extend them again and have access to shortcuts
|
195
90
|
defined in the ancestor.
|
196
91
|
|
197
92
|
**Definition**
|
@@ -199,8 +94,8 @@ defined in the ancestor.
|
|
199
94
|
```ruby
|
200
95
|
class MyRepository
|
201
96
|
extend Grumlin::Repository
|
202
|
-
|
203
|
-
|
97
|
+
# read_only! - forbids mutating queries for this repository. May be useful for separation reads and writes
|
98
|
+
|
204
99
|
# It can add shortcuts from another repository or a shortcuts module
|
205
100
|
shortcuts_from ChooseShortcut
|
206
101
|
|
@@ -252,13 +147,18 @@ Each `return_mode` is mapped to a particular termination step:
|
|
252
147
|
- `drop_in_batches(traversal, batch_size: 10_000)`
|
253
148
|
|
254
149
|
and a few methods that emulate upserts:
|
255
|
-
- `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
150
|
+
- `upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
151
|
+
- `upsert_vertices(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
256
152
|
- `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
257
153
|
- `upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
258
|
-
|
154
|
+
|
155
|
+
**Note**: all upsert methods expect your provider has support for user supplied string ids for nodes and edges
|
156
|
+
respectively. For edges and if `create_properties[T.id]` if nil, grumlin will generate a uuid-like id out of `from` and
|
157
|
+
`to` vertex ids and edge's label to ensure uniqueness of the edge. If you manually provide an id, it's your
|
158
|
+
responsibility to ensure it's uniquely identifies the edge using it's `from`, `to` and `label`.
|
259
159
|
|
260
160
|
All of them support 3 different modes for error handling: `:retry`, `:ignore` and `:raise`. Retry mode is implemented
|
261
|
-
with [retryable](https://github.com/nfedyashev/retryable). **params will be merged to the default config for upserts
|
161
|
+
with [retryable](https://github.com/nfedyashev/retryable). **params will be merged to the default config for upserts
|
262
162
|
and passed to `Retryable.retryable`. In case if you want to modify retryable behaviour you are to do so.
|
263
163
|
|
264
164
|
If you want to use these methods inside a transaction simply pass your `gtx` as `start` parameter:
|
@@ -270,7 +170,7 @@ end
|
|
270
170
|
|
271
171
|
If you don't want to define you own repository, simply use
|
272
172
|
|
273
|
-
`Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`.
|
173
|
+
`Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`.
|
274
174
|
|
275
175
|
**Usage**
|
276
176
|
|
@@ -295,6 +195,62 @@ it may be useful for debugging. Note that one needs to call a termination step m
|
|
295
195
|
|
296
196
|
method will return profiling data of the results.
|
297
197
|
|
198
|
+
#### Shortcuts
|
199
|
+
|
200
|
+
**Shortcuts** is a way to share and organize gremlin code. They let developers define their own steps consisting of
|
201
|
+
sequences of standard gremlin steps, other shortcuts and even add new initially unsupported by Grumlin steps.
|
202
|
+
Remember ActiveRecord scopes? Shortcuts are very similar.
|
203
|
+
|
204
|
+
**Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
|
205
|
+
be ignored because methods have higher priority.
|
206
|
+
|
207
|
+
**Defining**:
|
208
|
+
```ruby
|
209
|
+
|
210
|
+
# Defining shortcuts
|
211
|
+
class ColorShortcut
|
212
|
+
extend Grumlin::Shortcuts
|
213
|
+
|
214
|
+
# Custom step
|
215
|
+
shortcut :hasColor do |color|
|
216
|
+
has(:color, color)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class ChooseShortcut
|
221
|
+
extend Grumlin::Shortcuts
|
222
|
+
|
223
|
+
# Standard Gremlin step
|
224
|
+
shortcut :choose do |*args|
|
225
|
+
step(:choose, *args)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class AllShortcuts
|
230
|
+
extend Grumlin::Shortcuts
|
231
|
+
|
232
|
+
# Adding shortcuts from other modules
|
233
|
+
shortcuts_from ColorShortcut
|
234
|
+
shortcuts_from ChooseShortcut
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
##### Overriding standard steps and shortcuts
|
239
|
+
|
240
|
+
Sometimes it may be useful to override standard steps. Grumlin does not allow it by default, but one
|
241
|
+
is still able to override standard steps if they know what they are doing:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
shortcut :addV, override: true do |label|
|
245
|
+
super(label).property(:default, :value)
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
This will create a new shortcut that overrides the standard step `addV` and adds default properties to all vertices
|
250
|
+
created by the repository that uses this shortcut.
|
251
|
+
|
252
|
+
Shortcuts also can be overridden, but super() is not available.
|
253
|
+
|
298
254
|
##### Middlewares
|
299
255
|
|
300
256
|
Middlewares can be used to perform certain actions before and after every query made by `Grumlin`. It can be useful for
|
@@ -323,41 +279,43 @@ end # commits automatically
|
|
323
279
|
|
324
280
|
#### IRB
|
325
281
|
|
326
|
-
|
327
|
-
|
328
|
-
```ruby
|
329
|
-
Async do
|
330
|
-
include Grumlin::Sugar
|
331
|
-
|
332
|
-
IRB.start
|
333
|
-
ensure
|
334
|
-
Grumlin.close
|
335
|
-
end
|
336
|
-
```
|
337
|
-
|
338
|
-
Please check out [bin/console](bin/console) for full source. A similar trick may be applied to PRY.
|
282
|
+
Please check out [bin/console](bin/console) for inspiration. A similar trick may be applied to PRY.
|
339
283
|
|
340
284
|
#### Rails console
|
341
285
|
|
342
286
|
In order to make it possible to execute gremlin queries from the rails console you need to define
|
343
|
-
a custom console class. It should look
|
287
|
+
a custom console class. It should look somewhat like
|
344
288
|
|
345
289
|
```ruby
|
346
|
-
class
|
347
|
-
|
290
|
+
class Async::RailsConsole
|
291
|
+
extend Grumlin::Repository
|
292
|
+
|
293
|
+
def start
|
294
|
+
self.class.shortcuts_from Shortcuts::Content
|
295
|
+
|
348
296
|
IRB::WorkSpace.prepend(Rails::Console::BacktraceCleaner)
|
349
297
|
IRB::ExtendCommandBundle.include(Rails::ConsoleMethods)
|
350
298
|
|
351
|
-
|
352
|
-
|
299
|
+
IRB.setup(binding.source_location[0], argv: [])
|
300
|
+
workspace = IRB::WorkSpace.new(binding)
|
301
|
+
|
302
|
+
begin
|
303
|
+
Async do
|
304
|
+
IRB::Irb.new(workspace).run(IRB.conf)
|
305
|
+
ensure
|
306
|
+
Grumlin.close
|
307
|
+
end
|
308
|
+
rescue StandardError, Interrupt, Async::Stop, IRB::Abort
|
309
|
+
retry
|
310
|
+
end
|
311
|
+
end
|
353
312
|
|
354
|
-
|
355
|
-
|
313
|
+
def inspect
|
314
|
+
'main'
|
315
|
+
end
|
356
316
|
|
357
|
-
|
358
|
-
|
359
|
-
Grumlin.close
|
360
|
-
end
|
317
|
+
def to_s
|
318
|
+
inspect
|
361
319
|
end
|
362
320
|
end
|
363
321
|
```
|
@@ -381,13 +339,13 @@ require 'async/rspec'
|
|
381
339
|
require require "grumlin/test/rspec"
|
382
340
|
...
|
383
341
|
config.include_context(Async::RSpec::Reactor) # Runs async reactor
|
384
|
-
config.include_context(Grumlin::Test::RSpec::GremlinContext) # Injects
|
342
|
+
config.include_context(Grumlin::Test::RSpec::GremlinContext) # Injects `g`, `__` and expressions, makes sure client is closed after every test
|
385
343
|
config.include_context(Grumlin::Test::RSpec::DBCleanerContext) # Cleans the database before every test
|
386
344
|
...
|
387
345
|
```
|
388
346
|
|
389
|
-
It is highly recommended to use `Grumlin::
|
390
|
-
|
347
|
+
It is highly recommended to use `Grumlin::Repository` and not trying to use lower level APIs as they are subject to
|
348
|
+
change.
|
391
349
|
|
392
350
|
## Development
|
393
351
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -13,10 +13,25 @@ Grumlin.configure do |config|
|
|
13
13
|
config.url = ENV.fetch("GREMLIN_URL", "ws://localhost:8182/gremlin")
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
class Repository
|
17
|
+
extend Grumlin::Repository
|
18
|
+
|
19
|
+
def start_irb
|
20
|
+
IRB.setup(nil)
|
21
|
+
IRB.conf[:PROMPT][:DEFAULT] = { PROMPT_I: "%N(main):%03n:%i> ",
|
22
|
+
PROMPT_N: "%N(main):%03n:%i> ",
|
23
|
+
PROMPT_S: "%N(main):%03n:%i%l ",
|
24
|
+
PROMPT_C: "%N(main):%03n:%i* ",
|
25
|
+
RETURN: "=> %s\n" }
|
26
|
+
workspace = IRB::WorkSpace.new(binding)
|
27
|
+
irb = IRB::Irb.new(workspace)
|
28
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
29
|
+
irb.eval_input
|
30
|
+
end
|
31
|
+
end
|
18
32
|
|
19
|
-
|
33
|
+
Async do
|
34
|
+
Repository.new.start_irb
|
20
35
|
ensure
|
21
36
|
Grumlin.close
|
22
37
|
end
|
data/doc/middlewares.md
CHANGED
@@ -14,10 +14,11 @@ execution process:
|
|
14
14
|
Normally these middlewares must never be rearranged or removed from the stack. Middlewares added after
|
15
15
|
`Middlewares::RunQuery` will not be executed.
|
16
16
|
|
17
|
-
|
17
|
+
## Writing a middleware for Grumlin
|
18
18
|
|
19
|
-
This entire feature is built on top of [ibsciss-middleware](https://github.com/Ibsciss/ruby-middleware)
|
20
|
-
|
19
|
+
This entire feature is built on top of [ibsciss-middleware](https://github.com/Ibsciss/ruby-middleware) but uses a
|
20
|
+
slightly modified `Builder` which caches the chain for better performance.
|
21
|
+
Please refer to ibsciss-middleware docs if you want to implement a middleware.
|
21
22
|
|
22
23
|
|
23
24
|
A minimal middleware that measures query execution time and puts it back to `env`:
|
@@ -31,13 +32,6 @@ class MeasureExecutionTime < Grumlin::Middlewares::Middleware # Middleware provi
|
|
31
32
|
result
|
32
33
|
end
|
33
34
|
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
35
|
```
|
42
36
|
|
43
37
|
When placed right before `Grumlin::Middlewares::CastResults` your middleware will have access to every intermediate result
|
@@ -56,3 +50,48 @@ Other useful parts of `env`:
|
|
56
50
|
- `env[:session_id]` - id of the session when executed inside a transaction, otherwise `nil`
|
57
51
|
- `env[:pool]` - connection pool that will be used to interact with the server
|
58
52
|
- `env[:need_results]` - flag stating whether the client needs the query execution results(`toList`, `next`) or not(`iterate`)
|
53
|
+
|
54
|
+
## Adding your middleware to the stack
|
55
|
+
|
56
|
+
There are two ways of adding middlewares to your queries:
|
57
|
+
- `global` - add the middleware to every single query from every single repository
|
58
|
+
- `per repository` - add the middleware to queries performed by particular repositories
|
59
|
+
|
60
|
+
Grumlin has one global middleware stack and every repository defines it's which is by default is a
|
61
|
+
copy of the global stack. This means one can easily modify repository's stack without worrying about the global one.
|
62
|
+
|
63
|
+
When inherited a class extending `Grumlin::Repository` will copy it's middlewares to the subclass, they can also be
|
64
|
+
modified independently from the parent's middlewares.
|
65
|
+
|
66
|
+
### Adding a middleware globally
|
67
|
+
```ruby
|
68
|
+
Grumlin.configure do |cfg|
|
69
|
+
cfg.url = ENV.fetch("GREMLIN_URL", "ws://localhost:8182/gremlin")
|
70
|
+
cfg.provider = :tinkergraph
|
71
|
+
|
72
|
+
cfg.middlewares do |b|
|
73
|
+
b.insert_before Grumlin::Middlewares::CastResults, MeasureExecutionTime
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
### Adding a middleware to a repository
|
79
|
+
```ruby
|
80
|
+
class MyRepository
|
81
|
+
# MyRepository inherits a copy of global middlewares
|
82
|
+
extend Grumlin::Repository
|
83
|
+
|
84
|
+
# SomeOtherMiddleware will be added to MyRepository's middlewares only
|
85
|
+
middlewares do |b|
|
86
|
+
b.insert_before Grumlin::Middlewares::CastResults, MeasureExecutionTime
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class AnotherRepository < MyRepository
|
91
|
+
# AnotherRepository inherits parent's middlewares
|
92
|
+
middlewares do |b|
|
93
|
+
# SomeOtherMiddleware will be added to AnotherRepository's middlewares only
|
94
|
+
b.insert_before Grumlin::Middlewares::CastResults, SomeOtherMiddleware
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
data/lib/async/channel.rb
CHANGED
@@ -1,74 +1,72 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class
|
8
|
-
class ChannelError < StandardError; end
|
9
|
-
|
10
|
-
class ChannelClosedError < ChannelError; end
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@queue = Async::Queue.new
|
14
|
-
@closed = false
|
15
|
-
end
|
3
|
+
# Channel is a wrapper around Async::Queue that provides
|
4
|
+
# a protocol and handy tools for passing data, exceptions and closing.
|
5
|
+
# It is designed to be used only with one publisher and one subscriber
|
6
|
+
class Async::Channel
|
7
|
+
class ChannelError < StandardError; end
|
16
8
|
|
17
|
-
|
18
|
-
@closed
|
19
|
-
end
|
9
|
+
class ChannelClosedError < ChannelError; end
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
|
11
|
+
def initialize
|
12
|
+
@queue = Async::Queue.new
|
13
|
+
@closed = false
|
14
|
+
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
def closed?
|
17
|
+
@closed
|
18
|
+
end
|
28
19
|
|
29
|
-
|
30
|
-
|
20
|
+
def open?
|
21
|
+
!@closed
|
22
|
+
end
|
31
23
|
|
32
|
-
|
33
|
-
|
24
|
+
# Methods for a publisher
|
25
|
+
def <<(payload)
|
26
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if @closed
|
34
27
|
|
35
|
-
|
36
|
-
|
28
|
+
@queue << [:payload, payload]
|
29
|
+
end
|
37
30
|
|
38
|
-
|
39
|
-
|
31
|
+
def exception(exception)
|
32
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if closed?
|
40
33
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
34
|
+
@queue << [:exception, exception]
|
35
|
+
end
|
44
36
|
|
45
|
-
|
46
|
-
|
37
|
+
def close
|
38
|
+
return if closed?
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
40
|
+
@queue << [:close]
|
41
|
+
@closed = true
|
42
|
+
end
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
44
|
+
def close!
|
45
|
+
return if closed?
|
46
|
+
|
47
|
+
exception(ChannelClosedError.new("Channel was forcefully closed"))
|
48
|
+
close
|
49
|
+
end
|
50
|
+
|
51
|
+
# Methods for a subscriber
|
52
|
+
def dequeue
|
53
|
+
each do |payload| # rubocop:disable Lint/UnreachableLoop this is intended
|
54
|
+
return payload
|
57
55
|
end
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
58
|
+
def each
|
59
|
+
raise(ChannelClosedError, "Cannot receive from a closed channel") if closed?
|
60
|
+
|
61
|
+
@queue.each do |type, payload|
|
62
|
+
case type
|
63
|
+
when :exception
|
64
|
+
payload.set_backtrace(caller + (payload.backtrace || [])) # A hack to preserve full backtrace
|
65
|
+
raise payload
|
66
|
+
when :payload
|
67
|
+
yield payload
|
68
|
+
when :close
|
69
|
+
break
|
72
70
|
end
|
73
71
|
end
|
74
72
|
end
|
@@ -1,21 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Repository
|
6
|
-
extend Grumlin::Repository
|
3
|
+
class Grumlin::Benchmark::Repository
|
4
|
+
extend Grumlin::Repository
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
shortcut :simple_test do
|
7
|
+
self.V
|
8
|
+
end
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def simple_test
|
11
|
+
g.V.bytecode.serialize
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
14
|
+
def simple_test_with_shortcut
|
15
|
+
g.simple_test.bytecode.serialize
|
20
16
|
end
|
21
17
|
end
|