grumlin 0.22.4 → 1.0.0.rc1
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/.rubocop.yml +9 -9
- data/Gemfile.lock +10 -8
- data/README.md +102 -141
- data/Rakefile +1 -1
- data/bin/console +18 -3
- data/doc/middlewares.md +97 -0
- data/grumlin.gemspec +1 -0
- data/lib/async/channel.rb +54 -56
- data/lib/grumlin/benchmark/repository.rb +10 -14
- data/lib/grumlin/client.rb +92 -112
- data/lib/grumlin/config.rb +30 -15
- 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 +8 -0
- data/lib/grumlin/middlewares/build_query.rb +20 -0
- data/lib/grumlin/middlewares/builder.rb +15 -0
- data/lib/grumlin/middlewares/cast_results.rb +7 -0
- 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 +11 -0
- data/lib/grumlin/middlewares/run_query.rb +7 -0
- data/lib/grumlin/middlewares/serialize_to_bytecode.rb +9 -0
- data/lib/grumlin/middlewares/serialize_to_steps.rb +8 -0
- 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 +18 -22
- data/lib/grumlin/shortcuts.rb +23 -25
- data/lib/grumlin/shortcuts_applyer.rb +27 -29
- data/lib/grumlin/step.rb +92 -0
- data/lib/grumlin/step_data.rb +12 -14
- data/lib/grumlin/steppable.rb +24 -22
- data/lib/grumlin/steps.rb +51 -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 +26 -27
- 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 +23 -19
- metadata +32 -6
- data/lib/grumlin/action.rb +0 -92
- 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: e5a4223c6fde72d3f4ce23f3da0081a816f395a070d8737d9915f15b166c9721
|
4
|
+
data.tar.gz: 8c95dcb15d4f55741cc370298e1e763427858908abebaec33f945e404d3a6fa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69162f44551c45c0898ffa0c3650cee448a155d1950de5ea0a2a4b570c1b2b41612f3b97bd063f88f683af5fad3c18d4b874b96392a30334c77ab0e1fb1fd226
|
7
|
+
data.tar.gz: 1fdb7d10a84da7d8c8173e2b0edb0f5b0f913fb0418fce59bb92d28de9c2e9a1743b89eca1f0ed8495b4d0519d2068c2314e9f17f23b77a4297e080512ace082
|
data/.gitignore
CHANGED
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
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grumlin (0.
|
4
|
+
grumlin (1.0.0.rc1)
|
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
@@ -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,13 @@ 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)`
|
256
151
|
- `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params)`
|
257
152
|
- `upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
258
153
|
- `upsert_vertices(edges, batch_size: 100, on_failure: :retry, start: g, **params)`
|
259
154
|
|
260
155
|
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
|
156
|
+
with [retryable](https://github.com/nfedyashev/retryable). **params will be merged to the default config for upserts
|
262
157
|
and passed to `Retryable.retryable`. In case if you want to modify retryable behaviour you are to do so.
|
263
158
|
|
264
159
|
If you want to use these methods inside a transaction simply pass your `gtx` as `start` parameter:
|
@@ -270,7 +165,7 @@ end
|
|
270
165
|
|
271
166
|
If you don't want to define you own repository, simply use
|
272
167
|
|
273
|
-
`Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`.
|
168
|
+
`Grumlin::Repository.new` returns an instance of an anonymous class extending `Grumlin::Repository`.
|
274
169
|
|
275
170
|
**Usage**
|
276
171
|
|
@@ -295,6 +190,70 @@ it may be useful for debugging. Note that one needs to call a termination step m
|
|
295
190
|
|
296
191
|
method will return profiling data of the results.
|
297
192
|
|
193
|
+
#### Shortcuts
|
194
|
+
|
195
|
+
**Shortcuts** is a way to share and organize gremlin code. They let developers define their own steps consisting of
|
196
|
+
sequences of standard gremlin steps, other shortcuts and even add new initially unsupported by Grumlin steps.
|
197
|
+
Remember ActiveRecord scopes? Shortcuts are very similar.
|
198
|
+
|
199
|
+
**Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
|
200
|
+
be ignored because methods have higher priority.
|
201
|
+
|
202
|
+
**Defining**:
|
203
|
+
```ruby
|
204
|
+
|
205
|
+
# Defining shortcuts
|
206
|
+
class ColorShortcut
|
207
|
+
extend Grumlin::Shortcuts
|
208
|
+
|
209
|
+
# Custom step
|
210
|
+
shortcut :hasColor do |color|
|
211
|
+
has(:color, color)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class ChooseShortcut
|
216
|
+
extend Grumlin::Shortcuts
|
217
|
+
|
218
|
+
# Standard Gremlin step
|
219
|
+
shortcut :choose do |*args|
|
220
|
+
step(:choose, *args)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class AllShortcuts
|
225
|
+
extend Grumlin::Shortcuts
|
226
|
+
|
227
|
+
# Adding shortcuts from other modules
|
228
|
+
shortcuts_from ColorShortcut
|
229
|
+
shortcuts_from ChooseShortcut
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
##### Overriding standard steps and shortcuts
|
234
|
+
|
235
|
+
Sometimes it may be useful to override standard steps. Grumlin does not allow it by default, but one
|
236
|
+
is still able to override standard steps if they know what they are doing:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
shortcut :addV, override: true do |label|
|
240
|
+
super(label).property(:default, :value)
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
This will create a new shortcut that overrides the standard step `addV` and adds default properties to all vertices
|
245
|
+
created by the repository that uses this shortcut.
|
246
|
+
|
247
|
+
Shortcuts also can be overridden, but super() is not available.
|
248
|
+
|
249
|
+
##### Middlewares
|
250
|
+
|
251
|
+
Middlewares can be used to perform certain actions before and after every query made by `Grumlin`. It can be useful for
|
252
|
+
measuring query execution time or performing some modification or validation to the query before it reaches the server or
|
253
|
+
modify the response before client gets it.
|
254
|
+
|
255
|
+
See [doc/middlewares.md](doc/middlewares.md) for more info and examples.
|
256
|
+
|
298
257
|
#### Transactions
|
299
258
|
|
300
259
|
Since 0.22.0 `Grumlin` supports transactions when working with providers that supports them:
|
@@ -315,41 +274,43 @@ end # commits automatically
|
|
315
274
|
|
316
275
|
#### IRB
|
317
276
|
|
318
|
-
|
319
|
-
|
320
|
-
```ruby
|
321
|
-
Async do
|
322
|
-
include Grumlin::Sugar
|
323
|
-
|
324
|
-
IRB.start
|
325
|
-
ensure
|
326
|
-
Grumlin.close
|
327
|
-
end
|
328
|
-
```
|
329
|
-
|
330
|
-
Please check out [bin/console](bin/console) for full source. A similar trick may be applied to PRY.
|
277
|
+
Please check out [bin/console](bin/console) for inspiration. A similar trick may be applied to PRY.
|
331
278
|
|
332
279
|
#### Rails console
|
333
280
|
|
334
281
|
In order to make it possible to execute gremlin queries from the rails console you need to define
|
335
|
-
a custom console class. It should look
|
282
|
+
a custom console class. It should look somewhat like
|
336
283
|
|
337
284
|
```ruby
|
338
|
-
class
|
339
|
-
|
285
|
+
class Async::RailsConsole
|
286
|
+
extend Grumlin::Repository
|
287
|
+
|
288
|
+
def start
|
289
|
+
self.class.shortcuts_from Shortcuts::Content
|
290
|
+
|
340
291
|
IRB::WorkSpace.prepend(Rails::Console::BacktraceCleaner)
|
341
292
|
IRB::ExtendCommandBundle.include(Rails::ConsoleMethods)
|
342
293
|
|
343
|
-
|
344
|
-
|
294
|
+
IRB.setup(binding.source_location[0], argv: [])
|
295
|
+
workspace = IRB::WorkSpace.new(binding)
|
296
|
+
|
297
|
+
begin
|
298
|
+
Async do
|
299
|
+
IRB::Irb.new(workspace).run(IRB.conf)
|
300
|
+
ensure
|
301
|
+
Grumlin.close
|
302
|
+
end
|
303
|
+
rescue StandardError, Interrupt, Async::Stop, IRB::Abort
|
304
|
+
retry
|
305
|
+
end
|
306
|
+
end
|
345
307
|
|
346
|
-
|
347
|
-
|
308
|
+
def inspect
|
309
|
+
'main'
|
310
|
+
end
|
348
311
|
|
349
|
-
|
350
|
-
|
351
|
-
Grumlin.close
|
352
|
-
end
|
312
|
+
def to_s
|
313
|
+
inspect
|
353
314
|
end
|
354
315
|
end
|
355
316
|
```
|
@@ -373,13 +334,13 @@ require 'async/rspec'
|
|
373
334
|
require require "grumlin/test/rspec"
|
374
335
|
...
|
375
336
|
config.include_context(Async::RSpec::Reactor) # Runs async reactor
|
376
|
-
config.include_context(Grumlin::Test::RSpec::GremlinContext) # Injects
|
337
|
+
config.include_context(Grumlin::Test::RSpec::GremlinContext) # Injects `g`, `__` and expressions, makes sure client is closed after every test
|
377
338
|
config.include_context(Grumlin::Test::RSpec::DBCleanerContext) # Cleans the database before every test
|
378
339
|
...
|
379
340
|
```
|
380
341
|
|
381
|
-
It is highly recommended to use `Grumlin::
|
382
|
-
|
342
|
+
It is highly recommended to use `Grumlin::Repository` and not trying to use lower level APIs as they are subject to
|
343
|
+
change.
|
383
344
|
|
384
345
|
## Development
|
385
346
|
|
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
ADDED
@@ -0,0 +1,97 @@
|
|
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) 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.
|
22
|
+
|
23
|
+
|
24
|
+
A minimal middleware that measures query execution time and puts it back to `env`:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class MeasureExecutionTime < Grumlin::Middlewares::Middleware # Middleware provides only an initializer with one argument for `app`
|
28
|
+
def call(env)
|
29
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
30
|
+
result = @app.call(env)
|
31
|
+
env[:execution_time] = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
When placed right before `Grumlin::Middlewares::CastResults` your middleware will have access to every intermediate result
|
38
|
+
of the query execution process:
|
39
|
+
- `env[:traversal]` - contains the original traversal
|
40
|
+
- `env[:steps]` - contains the `Steps` representing the traversal
|
41
|
+
- `env[:steps_without_shortcuts]` - contains the `Steps` representing the traversal, but with all shortcuts applied
|
42
|
+
- `env[:bytecode]` - raw bytecode of the traversal
|
43
|
+
- `env[:query]` - raw message that will be sent to the server. `requestId` can be found here: `env[:query][:requestId]`
|
44
|
+
|
45
|
+
After the query is performed (after `@app.call(env)`), these keys become available:
|
46
|
+
- `env[:results]` - raw results received from the server
|
47
|
+
- `env[:parsed_results]` - server results mapped to ruby types, basically the query results as the client gets it
|
48
|
+
|
49
|
+
Other useful parts of `env`:
|
50
|
+
- `env[:session_id]` - id of the session when executed inside a transaction, otherwise `nil`
|
51
|
+
- `env[:pool]` - connection pool that will be used to interact with the server
|
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/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"
|