grumlin 0.17.0 → 0.18.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/.rubocop.yml +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +17 -9
- data/lib/grumlin/action.rb +0 -6
- data/lib/grumlin/repository.rb +71 -1
- data/lib/grumlin/shortcuts/properties.rb +3 -7
- data/lib/grumlin/shortcuts_applyer.rb +4 -4
- data/lib/grumlin/step_data.rb +6 -4
- data/lib/grumlin/steps.rb +7 -7
- data/lib/grumlin/steps_serializers/bytecode.rb +2 -2
- data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +4 -10
- data/lib/grumlin/steps_serializers/string.rb +10 -9
- data/lib/grumlin/typing.rb +1 -1
- data/lib/grumlin/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0517b4d5ffee800cfaf19b3f91a72bd0b86f82668e4258fd4af6c684e24de0ac
|
4
|
+
data.tar.gz: 938a188adee5be8f8938eb62e8585b2fbebf1946f653ce22c53b461f8a105ba4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bde74b8a4df9db236ac4a73a820214c6518da94df03417c3f0ef92a56d92fdbc49ff177a185ed4c1d5ad9b1d263e8b1f791792ebd844beb303805c489b2a9fd
|
7
|
+
data.tar.gz: 3b670eee69c2d7877312c2365a1208c848997086e172c2584bbd979e60a0dffc5911d1d000e883e84b9dda79024673e76a940d024033210df26352736fffa8a8
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -77,8 +77,7 @@ end
|
|
77
77
|
|
78
78
|
**Shortcuts** is a way to share and organize gremlin code. They let developers define their own steps consisting of
|
79
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.
|
81
|
-
a proxy object that simply proxies all methods existing in the wrapped object to it and handles shortcuts.
|
80
|
+
Remember ActiveRecord scopes? Shortcuts are very similar.
|
82
81
|
|
83
82
|
**Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
|
84
83
|
be ignored because methods have higher priority. You cannot override supported by Grumlin steps with shortcuts,
|
@@ -128,17 +127,18 @@ class MyRepository
|
|
128
127
|
|
129
128
|
# Wrapping a traversal
|
130
129
|
def red_triangles
|
131
|
-
|
132
|
-
|
133
|
-
|
130
|
+
g(self.class.shortcuts).V.hasLabel(:triangle)
|
131
|
+
.hasColor("red")
|
132
|
+
.toList
|
134
133
|
end
|
135
134
|
|
136
135
|
# Wrapping _
|
137
136
|
def something_else
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
g(self.class.shortcuts).V.hasColor("red")
|
138
|
+
.repeat(__(self.class.shortcuts))
|
139
|
+
.out(:has)
|
140
|
+
.hasColor("blue")
|
141
|
+
.toList
|
142
142
|
end
|
143
143
|
end
|
144
144
|
```
|
@@ -183,6 +183,14 @@ Each `return_mode` is mapped to a particular termination step:
|
|
183
183
|
- `:none` - `iterate`
|
184
184
|
- `:traversal` - do not execute the query and return the traversal as is
|
185
185
|
|
186
|
+
`Grumlin::Repository` also provides a set of generic CRUD operations:
|
187
|
+
- `add_vertex(label, id = nil, **properties)`
|
188
|
+
- `add_edge(label, id = nil, from:, to:, **properties)`
|
189
|
+
- `drop_vertex(id)`
|
190
|
+
- `drop_edge(id = nil, from: nil, to: nil, label: nil)`
|
191
|
+
- `upsert_vertex(label, id, create_properties: {}, update_properties: {})`
|
192
|
+
- `upsert_edge(label, from:, to:, create_properties: {}, update_properties: {})`
|
193
|
+
|
186
194
|
**Usage**
|
187
195
|
|
188
196
|
To execute the query defined in a query block one simply needs to call a method with the same name:
|
data/lib/grumlin/action.rb
CHANGED
@@ -49,12 +49,6 @@ module Grumlin
|
|
49
49
|
@shortcuts.key?(@name)
|
50
50
|
end
|
51
51
|
|
52
|
-
def arguments
|
53
|
-
@arguments ||= [*@args].tap do |args|
|
54
|
-
args << @params if @params.any?
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
52
|
def method_missing(name, *args, **params)
|
59
53
|
return step(name, *args, **params) if @shortcuts.key?(name)
|
60
54
|
|
data/lib/grumlin/repository.rb
CHANGED
@@ -10,6 +10,8 @@ module Grumlin
|
|
10
10
|
}.freeze
|
11
11
|
|
12
12
|
module InstanceMethods
|
13
|
+
include Grumlin::Expressions
|
14
|
+
|
13
15
|
def __
|
14
16
|
TraversalStart.new(self.class.shortcuts)
|
15
17
|
end
|
@@ -17,11 +19,79 @@ module Grumlin
|
|
17
19
|
def g
|
18
20
|
TraversalStart.new(self.class.shortcuts)
|
19
21
|
end
|
22
|
+
|
23
|
+
def drop_vertex(id)
|
24
|
+
g.V(id).drop.iterate
|
25
|
+
end
|
26
|
+
|
27
|
+
def drop_edge(id = nil, from: nil, to: nil, label: nil) # rubocop:disable Metrics/AbcSize
|
28
|
+
raise ArgumentError, "either id or from:, to: and label: must be passed" if [id, from, to, label].all?(&:nil?)
|
29
|
+
return g.E(id).drop.iterate unless id.nil?
|
30
|
+
|
31
|
+
raise ArgumentError, "from:, to: and label: must be passed" if [from, to, label].any?(&:nil?)
|
32
|
+
|
33
|
+
g.V(from).outE(label).where(__.inV.hasId(to)).limit(1).drop.iterate
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_vertex(label, id = nil, **properties)
|
37
|
+
id ||= properties[T.id]
|
38
|
+
properties = except(properties, T.id)
|
39
|
+
|
40
|
+
t = g.addV(label)
|
41
|
+
t = t.props(T.id => id) unless id.nil?
|
42
|
+
t.props(**properties).next
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_edge(label, id = nil, from:, to:, **properties)
|
46
|
+
id ||= properties[T.id]
|
47
|
+
properties = except(properties, T.label)
|
48
|
+
properties[T.id] = id
|
49
|
+
|
50
|
+
g.addE(label).from(__.V(from)).to(__.V(to)).props(**properties).next
|
51
|
+
end
|
52
|
+
|
53
|
+
def upsert_vertex(label, id, create_properties: {}, update_properties: {}) # rubocop:disable Metrics/AbcSize
|
54
|
+
create_properties = except(create_properties, T.id, T.label)
|
55
|
+
update_properties = except(update_properties, T.id, T.label)
|
56
|
+
g.V(id)
|
57
|
+
.fold
|
58
|
+
.coalesce(
|
59
|
+
__.unfold,
|
60
|
+
__.addV(label).props(**create_properties.merge(T.id => id))
|
61
|
+
).props(**update_properties)
|
62
|
+
.next
|
63
|
+
end
|
64
|
+
|
65
|
+
# Only from and to are used to find the existing edge, if one wants to assign an id to a created edge,
|
66
|
+
# it must be passed as T.id in via create_properties.
|
67
|
+
def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}) # rubocop:disable Metrics/AbcSize
|
68
|
+
create_properties = except(create_properties, T.label)
|
69
|
+
update_properties = except(update_properties, T.id, T.label)
|
70
|
+
|
71
|
+
g.V(from)
|
72
|
+
.outE(label).where(__.inV.hasId(to))
|
73
|
+
.fold
|
74
|
+
.coalesce(
|
75
|
+
__.unfold,
|
76
|
+
__.addE(label).from(__.V(from)).to(__.V(to)).props(**create_properties)
|
77
|
+
).props(**update_properties).next
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# A polyfill for Hash#except for ruby 2.x environments without ActiveSupport
|
83
|
+
# TODO: delete and use native Hash#except when after ruby 2.7 is deprecated.
|
84
|
+
def except(hash, *keys)
|
85
|
+
return hash.except(*keys) if hash.respond_to?(:except)
|
86
|
+
|
87
|
+
hash.each_with_object({}) do |(k, v), res|
|
88
|
+
res[k] = v unless keys.include?(k)
|
89
|
+
end
|
90
|
+
end
|
20
91
|
end
|
21
92
|
|
22
93
|
def self.extended(base)
|
23
94
|
base.extend(Grumlin::Shortcuts)
|
24
|
-
base.include(Grumlin::Expressions)
|
25
95
|
base.include(InstanceMethods)
|
26
96
|
|
27
97
|
base.shortcuts_from(Grumlin::Shortcuts::Properties)
|
@@ -5,17 +5,13 @@ module Grumlin
|
|
5
5
|
module Properties
|
6
6
|
extend Grumlin::Shortcuts
|
7
7
|
|
8
|
-
shortcut :props do
|
9
|
-
|
10
|
-
|
11
|
-
props.reduce(self) do |tt, (prop, value)|
|
8
|
+
shortcut :props do |**props|
|
9
|
+
props.compact.reduce(self) do |tt, (prop, value)|
|
12
10
|
tt.property(prop, value)
|
13
11
|
end
|
14
12
|
end
|
15
13
|
|
16
|
-
shortcut :hasAll do
|
17
|
-
next if props.nil? # TODO: fixme, add proper support for **params
|
18
|
-
|
14
|
+
shortcut :hasAll do |**props|
|
19
15
|
props.reduce(self) do |tt, (prop, value)|
|
20
16
|
tt.has(prop, value)
|
21
17
|
end
|
@@ -18,7 +18,7 @@ module Grumlin
|
|
18
18
|
|
19
19
|
Steps.new(shortcuts).tap do |processed_steps|
|
20
20
|
(configuration_steps + regular_steps).each do |step|
|
21
|
-
processed_steps.add(step.name, step.
|
21
|
+
processed_steps.add(step.name, args: step.args, params: step.params)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -27,20 +27,20 @@ module Grumlin
|
|
27
27
|
|
28
28
|
def process_steps(steps, shortcuts) # rubocop:disable Metrics/AbcSize
|
29
29
|
steps.each_with_object([]) do |step, result|
|
30
|
-
|
30
|
+
args = step.args.map do |arg|
|
31
31
|
arg.is_a?(Steps) ? ShortcutsApplyer.call(arg) : arg
|
32
32
|
end
|
33
33
|
|
34
34
|
if shortcuts.include?(step.name)
|
35
35
|
t = TraversalStart.new(shortcuts)
|
36
|
-
action = shortcuts[step.name].apply(t, *
|
36
|
+
action = shortcuts[step.name].apply(t, *args, **step.params)
|
37
37
|
next if action.nil? || action == t # Shortcut did not add any steps
|
38
38
|
|
39
39
|
new_steps = ShortcutsApplyer.call(Steps.from(action))
|
40
40
|
result.concat(new_steps.configuration_steps)
|
41
41
|
result.concat(new_steps.steps)
|
42
42
|
else
|
43
|
-
result << StepData.new(step.name,
|
43
|
+
result << StepData.new(step.name, args: args, params: step.params)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
data/lib/grumlin/step_data.rb
CHANGED
@@ -2,17 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
class StepData
|
5
|
-
attr_reader :name, :
|
5
|
+
attr_reader :name, :args, :params
|
6
6
|
|
7
|
-
def initialize(name,
|
7
|
+
def initialize(name, args: [], params: {})
|
8
8
|
@name = name
|
9
|
-
@
|
9
|
+
@args = args
|
10
|
+
@params = params
|
10
11
|
end
|
11
12
|
|
12
13
|
def ==(other)
|
13
14
|
self.class == other.class &&
|
14
15
|
@name == other.name &&
|
15
|
-
@
|
16
|
+
@args == other.args &&
|
17
|
+
@params == other.params
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
data/lib/grumlin/steps.rb
CHANGED
@@ -18,7 +18,7 @@ module Grumlin
|
|
18
18
|
|
19
19
|
new(shortcuts).tap do |chain|
|
20
20
|
actions.each do |act|
|
21
|
-
chain.add(act.name, act.
|
21
|
+
chain.add(act.name, args: act.args, params: act.params)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -31,10 +31,10 @@ module Grumlin
|
|
31
31
|
@steps = steps
|
32
32
|
end
|
33
33
|
|
34
|
-
def add(name,
|
35
|
-
return add_configuration_step(name,
|
34
|
+
def add(name, args: [], params: {})
|
35
|
+
return add_configuration_step(name, args: args, params: params) if CONFIGURATION_STEPS.include?(name)
|
36
36
|
|
37
|
-
StepData.new(name, cast_arguments(
|
37
|
+
StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
|
38
38
|
@steps << step
|
39
39
|
end
|
40
40
|
end
|
@@ -56,16 +56,16 @@ module Grumlin
|
|
56
56
|
|
57
57
|
def shortcuts?(steps_ary)
|
58
58
|
steps_ary.any? do |step|
|
59
|
-
@shortcuts.include?(step.name) || step.
|
59
|
+
@shortcuts.include?(step.name) || step.args.any? do |arg|
|
60
60
|
arg.is_a?(Steps) ? arg.uses_shortcuts? : false
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
def add_configuration_step(name,
|
65
|
+
def add_configuration_step(name, args: [], params: {})
|
66
66
|
raise ArgumentError, "cannot use configuration steps after start step was used" unless @steps.empty?
|
67
67
|
|
68
|
-
StepData.new(name, cast_arguments(
|
68
|
+
StepData.new(name, args: cast_arguments(args), params: params).tap do |step|
|
69
69
|
@configuration_steps << step
|
70
70
|
end
|
71
71
|
end
|
@@ -6,7 +6,7 @@ module Grumlin
|
|
6
6
|
# constructor params: no_return: true|false, default false
|
7
7
|
# TODO: add pretty
|
8
8
|
|
9
|
-
NONE_STEP = StepData.new("none"
|
9
|
+
NONE_STEP = StepData.new("none")
|
10
10
|
|
11
11
|
def serialize
|
12
12
|
steps = ShortcutsApplyer.call(@steps)
|
@@ -22,7 +22,7 @@ module Grumlin
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def serialize_step(step)
|
25
|
-
[step.name, *step.
|
25
|
+
[step.name, *step.args.map { |arg| serialize_arg(arg) }, step.params.any? ? step.params : nil].compact
|
26
26
|
end
|
27
27
|
|
28
28
|
def serialize_arg(arg)
|
@@ -2,22 +2,16 @@
|
|
2
2
|
|
3
3
|
module Grumlin
|
4
4
|
module StepsSerializers
|
5
|
-
class HumanReadableBytecode <
|
5
|
+
class HumanReadableBytecode < Bytecode
|
6
6
|
def serialize
|
7
7
|
steps = ShortcutsApplyer.call(@steps)
|
8
|
-
[
|
9
|
-
|
10
|
-
|
11
|
-
def serialize_steps(steps)
|
12
|
-
steps.map { |s| serialize_step(s) }
|
8
|
+
[steps.configuration_steps, steps.steps].map do |stps|
|
9
|
+
stps.map { |s| serialize_step(s) }
|
10
|
+
end
|
13
11
|
end
|
14
12
|
|
15
13
|
private
|
16
14
|
|
17
|
-
def serialize_step(step)
|
18
|
-
[step.name, *step.arguments.map { |arg| serialize_arg(arg) }]
|
19
|
-
end
|
20
|
-
|
21
15
|
def serialize_arg(arg)
|
22
16
|
return arg.to_s if arg.is_a?(TypedValue)
|
23
17
|
return serialize_predicate(arg) if arg.is_a?(Expressions::P::Predicate)
|
@@ -10,14 +10,21 @@ module Grumlin
|
|
10
10
|
def serialize
|
11
11
|
steps = @params[:apply_shortcuts] ? ShortcutsApplyer.call(@steps) : @steps
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
steps = [steps.configuration_steps, steps.steps].map do |stps|
|
14
|
+
stps.map { |step| serialize_step(step) }
|
15
|
+
end
|
15
16
|
|
16
|
-
"#{prefix}.#{(
|
17
|
+
"#{prefix}.#{(steps[0] + steps[1]).join(".")}"
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
20
21
|
|
22
|
+
def serialize_step(step)
|
23
|
+
"#{step.name}(#{(step.args + [step.params.any? ? step.params : nil].compact).map do |a|
|
24
|
+
serialize_arg(a)
|
25
|
+
end.join(", ")})"
|
26
|
+
end
|
27
|
+
|
21
28
|
def prefix
|
22
29
|
@prefix ||= @params[:anonymous] ? "__" : "g"
|
23
30
|
end
|
@@ -31,12 +38,6 @@ module Grumlin
|
|
31
38
|
|
32
39
|
StepsSerializers::String.new(arg, anonymous: true, **@params).serialize
|
33
40
|
end
|
34
|
-
|
35
|
-
def serialize_steps(steps)
|
36
|
-
steps.map do |step|
|
37
|
-
"#{step.name}(#{step.arguments.map { |a| serialize_arg(a) }.join(", ")})"
|
38
|
-
end
|
39
|
-
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
data/lib/grumlin/typing.rb
CHANGED
@@ -18,7 +18,7 @@ module Grumlin
|
|
18
18
|
# "g:VertexProperty"=> ->(value) { value }, # TODO: implement me
|
19
19
|
"g:TraversalMetrics" => ->(value) { cast_map(value[:@value]) },
|
20
20
|
"g:Metrics" => ->(value) { cast_map(value[:@value]) },
|
21
|
-
"g:T" => ->(value) { value
|
21
|
+
"g:T" => ->(value) { T.public_send(value) }
|
22
22
|
}.freeze
|
23
23
|
|
24
24
|
CASTABLE_TYPES = [Hash, String, Integer, TrueClass, FalseClass].freeze
|
data/lib/grumlin/version.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.18.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-
|
11
|
+
date: 2022-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|