grumlin 0.12.5 → 0.13.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/CHANGELOG.md +5 -1
- data/Gemfile.lock +1 -1
- data/README.md +106 -3
- data/lib/grumlin/anonymous_step.rb +5 -7
- data/lib/grumlin/bytecode.rb +3 -3
- data/lib/grumlin/{tools → expressions}/order.rb +1 -1
- data/lib/grumlin/{tools → expressions}/p.rb +1 -1
- data/lib/grumlin/{tools → expressions}/pop.rb +1 -1
- data/lib/grumlin/{tools → expressions}/scope.rb +1 -1
- data/lib/grumlin/{tools → expressions}/t.rb +1 -1
- data/lib/grumlin/{tools → expressions}/tool.rb +1 -1
- data/lib/grumlin/{tools → expressions}/u.rb +1 -1
- data/lib/grumlin/{tools → expressions}/with_options.rb +1 -1
- data/lib/grumlin/repository.rb +23 -0
- data/lib/grumlin/shortcut_proxy.rb +51 -0
- data/lib/grumlin/shortcuts/properties.rb +21 -0
- data/lib/grumlin/shortcuts.rb +40 -0
- data/lib/grumlin/step.rb +1 -3
- data/lib/grumlin/sugar.rb +2 -2
- data/lib/grumlin/test/rspec/gremlin_context.rb +2 -2
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +4 -0
- metadata +15 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45c3b3317c8b96d763dd2de25de3744646994370dce29f2bc70f1b8c2725b119
|
4
|
+
data.tar.gz: a8dce9eb4e0c9dc55383ed62d35a18e3a67fd63baf5d6f36cdf45196d36e49ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cae50acbded8f08e2d48ecda7f002c4ec1312ae1b5aac09b803580061d06e0befe4c8480213f1702421c1580105fc599e61b0b67d929635e82d24f9a9f402fcc
|
7
|
+
data.tar.gz: 8feb97c5edd5e9327fdfbf8bb6539028c09981a330917717e0eeeb3b40a74d1c00eb214cc2ce0b9669d341f4b32d8971a3f715130e77c1395bf2f27301aa04bc
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -14,7 +14,7 @@ and gremlin-server.
|
|
14
14
|
[async-websocket](https://github.com/socketry/async-websocket). Code using grumlin must be executed in an async
|
15
15
|
event loop.
|
16
16
|
|
17
|
-
**Warning
|
17
|
+
**Warning**: Grumlin is in development, but ready for simple use cases
|
18
18
|
|
19
19
|
## Table of contents
|
20
20
|
- [Install](#install)
|
@@ -51,12 +51,12 @@ end
|
|
51
51
|
|
52
52
|
### Traversing graphs
|
53
53
|
|
54
|
-
**Warning
|
54
|
+
**Warning**: Not all steps and tools described in the standard are supported
|
55
55
|
|
56
56
|
#### Sugar
|
57
57
|
|
58
58
|
Grumlin provides an easy to use module called `Grumlin::Sugar`. Once included in your class it injects some useful
|
59
|
-
constants and methods turning your class into an entrypoint for traversals.
|
59
|
+
constants and methods turning your class into an entrypoint for traversals with pure gremlin experience.
|
60
60
|
|
61
61
|
```ruby
|
62
62
|
class MyRepository
|
@@ -73,6 +73,109 @@ class MyRepository
|
|
73
73
|
end
|
74
74
|
```
|
75
75
|
|
76
|
+
#### Shortcuts
|
77
|
+
|
78
|
+
**Shortcuts** is a way to share and organize gremlin code. They let developers define their own steps consisting of
|
79
|
+
sequences of standard gremlin steps, other shortcuts and even add new initially unsupported by Grumlin steps.
|
80
|
+
Remember ActiveRecord scopes? Shortcuts are very similar. `Grumlin::Shortcuts#with_shortcuts` wraps a given object into
|
81
|
+
a proxy object that simply proxies all methods existing in the wrapped object to it and handles shortcuts.
|
82
|
+
|
83
|
+
**Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
|
84
|
+
be ignored because methods have higher priority. You cannot override supported by Grumlin steps with shortcuts,
|
85
|
+
`Grumlin::Shortcuts.shortcut` will raise an `ArgumentError`. Please carefully choose names for your shortcuts.
|
86
|
+
|
87
|
+
Shortcuts are designed to be used with `Grumlin::Repository` but still can be used separately, with `Grumlin::Sugar`
|
88
|
+
for example.
|
89
|
+
|
90
|
+
**Defining**:
|
91
|
+
```ruby
|
92
|
+
|
93
|
+
# Defining shortcuts
|
94
|
+
class ColorShortcut
|
95
|
+
extend Grumlin::Shortcuts
|
96
|
+
|
97
|
+
# Custom step
|
98
|
+
shortcut :hasColor do |color|
|
99
|
+
has(:color, color)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class ChooseShortcut
|
104
|
+
extend Grumlin::Shortcuts
|
105
|
+
|
106
|
+
# Standard Gremlin step
|
107
|
+
shortcut :choose do |*args|
|
108
|
+
step(:choose, *args)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class AllShortcuts
|
113
|
+
extend Grumlin::Shortcuts
|
114
|
+
|
115
|
+
# Adding shortcuts from other modules
|
116
|
+
shortcuts_from ColorShortcut
|
117
|
+
shortcuts_from ChooseShortcut
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
**Using with Grumlin::Sugar**:
|
122
|
+
```ruby
|
123
|
+
class MyRepository
|
124
|
+
include Grumlin::Sugar
|
125
|
+
extend Grumlin::Shortcuts
|
126
|
+
|
127
|
+
shortcuts_from AllShortcuts
|
128
|
+
|
129
|
+
# Wrapping a traversal
|
130
|
+
def red_triangles
|
131
|
+
with_shortcuts(g).V.hasLabel(:triangle)
|
132
|
+
.hasColor("red")
|
133
|
+
.toList
|
134
|
+
end
|
135
|
+
|
136
|
+
# Wrapping _
|
137
|
+
def something_else
|
138
|
+
with_shortcuts(g).V.hasColor("red")
|
139
|
+
.repeat(with_shortcuts(__)
|
140
|
+
.out(:has)
|
141
|
+
.hasColor("blue")).toList
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
#### Grumlin::Repository
|
147
|
+
`Grumlin::Repository` combines functionality of `Grumlin::Sugar` and `Grumlin::Shortcuts` as well as adds a few useful
|
148
|
+
shortcuts to make gremlin code more rubyish. Can be used as a drop in replacement for `Grumlin::Sugar`. Remember that
|
149
|
+
`Grumlin::Sugar` needs to be included, but `Grumlin::Repository` - extended. **Classes extending `Grumlin::Repository`
|
150
|
+
or `Grumlin::Shortcuts` can be inherited**, successors don't need to extend them again and have access to shortcuts
|
151
|
+
defined in the ancestor.
|
152
|
+
|
153
|
+
**Using**:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class MyRepository
|
157
|
+
extend Grumlin::Repository
|
158
|
+
|
159
|
+
# Repository supports all Grumlin::Shortcut and Grumlin::Sugar features.
|
160
|
+
# It can add shortcuts from another repository or a shortcuts module
|
161
|
+
shortcuts_from ChooseShortcut
|
162
|
+
|
163
|
+
shortcut :red_triangles do |color|
|
164
|
+
# hasAll unwraps a hash of properties into a chain of `has` steps:
|
165
|
+
# hasAll(name1: :value, name2: :value) == has(:name1, :value).has(:name2, :value)
|
166
|
+
# the `props` shortcut does exactly the same but with `property` steps.
|
167
|
+
hasAll(T.label => :triangle, color: color)
|
168
|
+
end
|
169
|
+
|
170
|
+
# g and __ are already aware of shortcuts
|
171
|
+
def red_triangles
|
172
|
+
g.V.hasLabel(:triangle)
|
173
|
+
.hasColor("red")
|
174
|
+
.toList
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
76
179
|
#### IRB
|
77
180
|
|
78
181
|
An example of how to start an IRB session with support for executing gremlin queries:
|
@@ -18,10 +18,14 @@ module Grumlin
|
|
18
18
|
|
19
19
|
SUPPORTED_STEPS.each do |step|
|
20
20
|
define_method(step) do |*args|
|
21
|
-
|
21
|
+
step(step, args)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
def step(name, args)
|
26
|
+
self.class.new(name, *args, previous_step: self)
|
27
|
+
end
|
28
|
+
|
25
29
|
def inspect
|
26
30
|
bytecode.inspect
|
27
31
|
end
|
@@ -31,11 +35,5 @@ module Grumlin
|
|
31
35
|
def bytecode(no_return: false)
|
32
36
|
@bytecode ||= Bytecode.new(self, no_return: no_return)
|
33
37
|
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def add_step(step_name, args)
|
38
|
-
self.class.new(step_name, *args, previous_step: self)
|
39
|
-
end
|
40
38
|
end
|
41
39
|
end
|
data/lib/grumlin/bytecode.rb
CHANGED
@@ -38,12 +38,12 @@ module Grumlin
|
|
38
38
|
# depending on the `serialization_method` parameter. I should be either `:to_readable_bytecode` for human readable
|
39
39
|
# representation or `:to_bytecode` for query.
|
40
40
|
def serialize_arg(arg, serialization_method: :to_bytecode)
|
41
|
-
return arg.
|
41
|
+
return arg.public_send(serialization_method) if arg.respond_to?(serialization_method)
|
42
42
|
return arg unless arg.is_a?(AnonymousStep)
|
43
43
|
|
44
44
|
arg.args.flatten.each.with_object([arg.name.to_s]) do |a, res|
|
45
|
-
res << if a.
|
46
|
-
a.bytecode.
|
45
|
+
res << if a.respond_to?(:bytecode)
|
46
|
+
a.bytecode.public_send(serialization_method)
|
47
47
|
else
|
48
48
|
serialize_arg(a, serialization_method: serialization_method)
|
49
49
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Repository
|
5
|
+
module InstanceMethods
|
6
|
+
def __
|
7
|
+
with_shortcuts(Grumlin::Expressions::U)
|
8
|
+
end
|
9
|
+
|
10
|
+
def g
|
11
|
+
with_shortcuts(Grumlin::Traversal.new)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.extended(base)
|
16
|
+
base.extend(Grumlin::Shortcuts)
|
17
|
+
base.include(Grumlin::Expressions)
|
18
|
+
base.include(InstanceMethods)
|
19
|
+
|
20
|
+
base.shortcuts_from(Grumlin::Shortcuts::Properties)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
class ShortcutProxy
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :object, :shortcuts
|
8
|
+
|
9
|
+
# shortcuts: {"name": ->(arg) {}}
|
10
|
+
def initialize(object, shortcuts, parent: nil)
|
11
|
+
@object = object
|
12
|
+
@shortcuts = shortcuts
|
13
|
+
@parent = parent
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(name, *args)
|
17
|
+
return @parent.public_send(name, *args) if %i[__ g].include?(name) && !@parent.nil?
|
18
|
+
|
19
|
+
return wrap_result(@object.public_send(name, *args)) if @object.respond_to?(name)
|
20
|
+
|
21
|
+
return wrap_result(instance_exec(*args, &@shortcuts[name])) if @shortcuts.key?(name)
|
22
|
+
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
# For some reason the interpreter thinks it's private
|
27
|
+
public def respond_to_missing?(name, include_private = false) # rubocop:disable Style/AccessModifierDeclarations
|
28
|
+
name = name.to_sym
|
29
|
+
|
30
|
+
(%i[__ g].include?(name) &&
|
31
|
+
@parent.respond_to?(name)) ||
|
32
|
+
@object.respond_to?(name) ||
|
33
|
+
@shortcuts.key?(name) ||
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def_delegator :@object, :to_s
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
@object.inspect
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def wrap_result(result)
|
46
|
+
return self.class.new(result, @shortcuts, parent: @parent) if result.is_a?(AnonymousStep)
|
47
|
+
|
48
|
+
result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Shortcuts
|
5
|
+
module Properties
|
6
|
+
extend Grumlin::Shortcuts
|
7
|
+
|
8
|
+
shortcut :props do |props = {}|
|
9
|
+
props.reduce(self) do |tt, (prop, value)|
|
10
|
+
tt.property(prop, value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
shortcut :hasAll do |props = {}|
|
15
|
+
props.reduce(self) do |tt, (prop, value)|
|
16
|
+
tt.has(prop, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grumlin
|
4
|
+
module Shortcuts
|
5
|
+
module InstanceMethods
|
6
|
+
def with_shortcuts(obj)
|
7
|
+
ShortcutProxy.new(obj, self.class.shortcuts, parent: self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.extended(base)
|
12
|
+
base.include(InstanceMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
def inherited(subclass)
|
16
|
+
super
|
17
|
+
subclass.shortcuts_from(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def shortcut(name, &block)
|
21
|
+
name = name.to_sym
|
22
|
+
# TODO: blocklist of names to avoid conflicts with standard methods?
|
23
|
+
raise ArgumentError, "cannot use names of standard gremlin steps" if Grumlin.supported_steps.include?(name)
|
24
|
+
|
25
|
+
raise ArgumentError, "shortcut '#{name}' already exists" if shortcuts.key?(name)
|
26
|
+
|
27
|
+
shortcuts[name] = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def shortcuts_from(other_shortcuts)
|
31
|
+
other_shortcuts.shortcuts.each do |name, block|
|
32
|
+
shortcut(name, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def shortcuts
|
37
|
+
@shortcuts ||= {}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/grumlin/step.rb
CHANGED
data/lib/grumlin/sugar.rb
CHANGED
@@ -11,8 +11,8 @@ module Grumlin
|
|
11
11
|
include Grumlin::Sugar
|
12
12
|
|
13
13
|
before do
|
14
|
-
Grumlin::
|
15
|
-
stub_const(tool.to_s, Grumlin::
|
14
|
+
Grumlin::Expressions.constants.each do |tool|
|
15
|
+
stub_const(tool.to_s, Grumlin::Expressions.const_get(tool))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
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.13.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: 2021-
|
11
|
+
date: 2021-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|
@@ -101,21 +101,25 @@ files:
|
|
101
101
|
- lib/grumlin/bytecode.rb
|
102
102
|
- lib/grumlin/client.rb
|
103
103
|
- lib/grumlin/edge.rb
|
104
|
+
- lib/grumlin/expressions/order.rb
|
105
|
+
- lib/grumlin/expressions/p.rb
|
106
|
+
- lib/grumlin/expressions/pop.rb
|
107
|
+
- lib/grumlin/expressions/scope.rb
|
108
|
+
- lib/grumlin/expressions/t.rb
|
109
|
+
- lib/grumlin/expressions/tool.rb
|
110
|
+
- lib/grumlin/expressions/u.rb
|
111
|
+
- lib/grumlin/expressions/with_options.rb
|
104
112
|
- lib/grumlin/path.rb
|
113
|
+
- lib/grumlin/repository.rb
|
105
114
|
- lib/grumlin/request_dispatcher.rb
|
115
|
+
- lib/grumlin/shortcut_proxy.rb
|
116
|
+
- lib/grumlin/shortcuts.rb
|
117
|
+
- lib/grumlin/shortcuts/properties.rb
|
106
118
|
- lib/grumlin/step.rb
|
107
119
|
- lib/grumlin/sugar.rb
|
108
120
|
- lib/grumlin/test/rspec.rb
|
109
121
|
- lib/grumlin/test/rspec/db_cleaner_context.rb
|
110
122
|
- lib/grumlin/test/rspec/gremlin_context.rb
|
111
|
-
- lib/grumlin/tools/order.rb
|
112
|
-
- lib/grumlin/tools/p.rb
|
113
|
-
- lib/grumlin/tools/pop.rb
|
114
|
-
- lib/grumlin/tools/scope.rb
|
115
|
-
- lib/grumlin/tools/t.rb
|
116
|
-
- lib/grumlin/tools/tool.rb
|
117
|
-
- lib/grumlin/tools/u.rb
|
118
|
-
- lib/grumlin/tools/with_options.rb
|
119
123
|
- lib/grumlin/transport.rb
|
120
124
|
- lib/grumlin/traversal.rb
|
121
125
|
- lib/grumlin/typed_value.rb
|
@@ -144,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
148
|
- !ruby/object:Gem::Version
|
145
149
|
version: '0'
|
146
150
|
requirements: []
|
147
|
-
rubygems_version: 3.2.
|
151
|
+
rubygems_version: 3.2.32
|
148
152
|
signing_key:
|
149
153
|
specification_version: 4
|
150
154
|
summary: Gremlin graph traversal language DSL and client for Ruby.
|