grumlin 0.23.0 → 1.0.0.rc2
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/.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
|