grumlin 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d63d3923f895d20e89fd1647495d99bb3a97888689c8f25c34973355d7c80e1
4
- data.tar.gz: 878546a65736522f3c1f00f42741b6b207e992a1983321205d05406e0a74ed6b
3
+ metadata.gz: 0517b4d5ffee800cfaf19b3f91a72bd0b86f82668e4258fd4af6c684e24de0ac
4
+ data.tar.gz: 938a188adee5be8f8938eb62e8585b2fbebf1946f653ce22c53b461f8a105ba4
5
5
  SHA512:
6
- metadata.gz: 60bf726b74019b36bb0437c78b97ea1ba78703058ba2a619805e0081d49158f30bb67cc7918b959cf0375c37a5600632a1df805563edccb0266b71c9ab70f1de
7
- data.tar.gz: cc7dc9ecfd45c67b28301593d3ba60e471e7a9993460ce39dcb912bbed5fb0aa01ed25bcf52f49634f704d6732ba563d849fb8d492276f19fe83e5e8531a8181
6
+ metadata.gz: 1bde74b8a4df9db236ac4a73a820214c6518da94df03417c3f0ef92a56d92fdbc49ff177a185ed4c1d5ad9b1d263e8b1f791792ebd844beb303805c489b2a9fd
7
+ data.tar.gz: 3b670eee69c2d7877312c2365a1208c848997086e172c2584bbd979e60a0dffc5911d1d000e883e84b9dda79024673e76a940d024033210df26352736fffa8a8
data/.rubocop.yml CHANGED
@@ -44,6 +44,7 @@ Naming/MethodParameterName:
44
44
  - outV
45
45
  - inVLabel
46
46
  - outVLabel
47
+ - to
47
48
 
48
49
  RSpec/NamedSubject:
49
50
  Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.17.0)
4
+ grumlin (0.18.0)
5
5
  async-pool (~> 0.3)
6
6
  async-websocket (~> 0.19)
7
7
  oj (~> 3.12)
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. `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.
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
- with_shortcuts(g).V.hasLabel(:triangle)
132
- .hasColor("red")
133
- .toList
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
- with_shortcuts(g).V.hasColor("red")
139
- .repeat(with_shortcuts(__)
140
- .out(:has)
141
- .hasColor("blue")).toList
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:
@@ -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
 
@@ -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 |props|
9
- next if props.nil? # TODO: fixme, add proper support for **params
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 |props|
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.arguments)
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
- arguments = step.arguments.map do |arg|
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, *arguments)
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, arguments)
43
+ result << StepData.new(step.name, args: args, params: step.params)
44
44
  end
45
45
  end
46
46
  end
@@ -2,17 +2,19 @@
2
2
 
3
3
  module Grumlin
4
4
  class StepData
5
- attr_reader :name, :arguments
5
+ attr_reader :name, :args, :params
6
6
 
7
- def initialize(name, arguments)
7
+ def initialize(name, args: [], params: {})
8
8
  @name = name
9
- @arguments = arguments
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
- @arguments == other.arguments
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.arguments)
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, arguments)
35
- return add_configuration_step(name, arguments) if CONFIGURATION_STEPS.include?(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(arguments)).tap do |step|
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.arguments.any? do |arg|
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, arguments)
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(arguments)).tap do |step|
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.arguments.map { |arg| serialize_arg(arg) }]
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 < Serializer
5
+ class HumanReadableBytecode < Bytecode
6
6
  def serialize
7
7
  steps = ShortcutsApplyer.call(@steps)
8
- [serialize_steps(steps.configuration_steps), serialize_steps(steps.steps)]
9
- end
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
- configuration_steps = serialize_steps(steps.configuration_steps)
14
- regular_steps = serialize_steps(steps.steps)
13
+ steps = [steps.configuration_steps, steps.steps].map do |stps|
14
+ stps.map { |step| serialize_step(step) }
15
+ end
15
16
 
16
- "#{prefix}.#{(configuration_steps + regular_steps).join(".")}"
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
@@ -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.to_sym }
21
+ "g:T" => ->(value) { T.public_send(value) }
22
22
  }.freeze
23
23
 
24
24
  CASTABLE_TYPES = [Hash, String, Integer, TrueClass, FalseClass].freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.17.0"
4
+ VERSION = "0.18.0"
5
5
  end
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.17.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-03-30 00:00:00.000000000 Z
11
+ date: 2022-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-pool