grumlin 0.15.6 → 0.16.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: 195f614a2cbebbe43f6dd2aedb3aeb54891c4377f50f85f21f44ee3325c87f58
4
- data.tar.gz: 0f0a3c0b5aa8f03fb4d613c406b08497685ee9fb4cbea2e8a0b99385f749ebf3
3
+ metadata.gz: 4a18e5e39768c0142eb219751991d67cb8512dd6d8cbeaa42494be193d44b694
4
+ data.tar.gz: 3c6833ffce68c409d78a656a892a81801b7820b9ea793dc60600e69c96315f76
5
5
  SHA512:
6
- metadata.gz: 21a7d91e9ce0a9e143f4bba9bb15d4bd31f6337f72500038bf09cd471ab05535d9d3ade4cfba6d6b6d779e09600b6352ed83b3e4bd70aeadd1417e51ba663937
7
- data.tar.gz: 9cd14a8f18b856eca87d3b51b48c3f3c0d8d454b76fae92466bd7aee370019bbbb0b398236187acfcfed2322520aa4a3aa68a0e6bfb8b02a073e27f0db3cf525
6
+ metadata.gz: 2c48059b03c81f68ff26802268f150c8d8af5476659e8f730de3c64c90f45896cdfa08dfd3bcc2db85240c34ef601b26317cbde269f3f834d6e3dd9f27a99f3e
7
+ data.tar.gz: 8033907ed22e1aa349b39ce3f247977242221d28aeea2f34c721e2bc6ec807f0a08dd21e450ef183fb989dac04534dc4fedbc1358e805ce5c8c57b59ab18b968
data/.rubocop.yml CHANGED
@@ -80,3 +80,10 @@ Style/Documentation:
80
80
 
81
81
  Style/MultilineBlockChain:
82
82
  Enabled: false
83
+
84
+
85
+ # TODO:
86
+ # Style/SymbolArray:
87
+ # EnforcedStyle: brackets
88
+ # Style/WordArray:
89
+ # EnforcedStyle: brackets
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.16.0] - 2022-03-11
2
+
3
+ - Query building is rewritten from scratch. No public APIs were changed. [Details](https://github.com/babbel/grumlin/pull/64)
4
+ - Add support for [TextP](https://tinkerpop.apache.org/javadocs/current/core/org/apache/tinkerpop/gremlin/process/traversal/TextP.html)
5
+
1
6
  ## [0.15.4] - 2022-01-20
2
7
 
3
8
  - Move step and expression definitions to a yaml file for better diffs
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.15.6)
4
+ grumlin (0.16.0)
5
5
  async-pool (~> 0.3)
6
6
  async-websocket (~> 0.19)
7
7
  oj (~> 3.12)
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class Action
5
+ START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
6
+ CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
7
+ REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
8
+
9
+ ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
10
+
11
+ attr_reader :name, :args, :params, :shortcuts, :next_step, :configuration_steps, :previous_step
12
+
13
+ def initialize(name, args: [], params: {}, previous_step: nil, shortcuts: {}, pool: Grumlin.default_pool)
14
+ @name = name.to_sym
15
+ @args = args # TODO: add recursive validation: only json types or Action
16
+ @params = params # TODO: add recursive validation: only json types
17
+ @previous_step = previous_step
18
+ @shortcuts = shortcuts
19
+ @pool = pool
20
+ end
21
+
22
+ ALL_STEPS.each do |step|
23
+ define_method step do |*args, **params|
24
+ step(step, *args, **params)
25
+ end
26
+ end
27
+
28
+ def step(name, *args, **params)
29
+ Action.new(name, args: args, params: params, previous_step: self, shortcuts: @shortcuts, pool: @pool)
30
+ end
31
+
32
+ def configuration_step?
33
+ CONFIGURATION_STEPS.include?(@name)
34
+ end
35
+
36
+ def start_step?
37
+ START_STEPS.include?(@name)
38
+ end
39
+
40
+ def regular_step?
41
+ REGULAR_STEPS.include?(@name)
42
+ end
43
+
44
+ def supported_step?
45
+ ALL_STEPS.include?(@name)
46
+ end
47
+
48
+ def shortcut?
49
+ @shortcuts.key?(@name)
50
+ end
51
+
52
+ def arguments
53
+ @arguments ||= [*@args].tap do |args|
54
+ args << @params if @params.any?
55
+ end
56
+ end
57
+
58
+ def method_missing(name, *args, **params)
59
+ return step(name, *args, **params) if @shortcuts.key?(name)
60
+
61
+ super
62
+ end
63
+
64
+ def ==(other)
65
+ self.class == other.class &&
66
+ @name == other.name &&
67
+ @args == other.args &&
68
+ @params == other.params &&
69
+ @previous_step == other.previous_step &&
70
+ @shortcuts == other.shortcuts
71
+ end
72
+
73
+ def steps
74
+ @steps ||= Steps.from(self)
75
+ end
76
+
77
+ def to_s(**params)
78
+ StepsSerializers::String.new(steps, **params).serialize
79
+ end
80
+
81
+ # TODO: add human readable mode
82
+ def inspect
83
+ conf_steps, regular_steps = StepsSerializers::HumanReadableBytecode.new(steps).serialize
84
+ "#{conf_steps.any? ? conf_steps : nil}#{regular_steps}"
85
+ end
86
+
87
+ def bytecode(no_return: false)
88
+ StepsSerializers::Bytecode.new(steps, no_return: no_return)
89
+ end
90
+
91
+ def next
92
+ to_enum.next
93
+ end
94
+
95
+ def hasNext # rubocop:disable Naming/MethodName
96
+ to_enum.peek
97
+ true
98
+ rescue StopIteration
99
+ false
100
+ end
101
+
102
+ def to_enum
103
+ @to_enum ||= toList.to_enum
104
+ end
105
+
106
+ def toList
107
+ @pool.acquire do |client|
108
+ client.write(bytecode)
109
+ end
110
+ end
111
+
112
+ def iterate
113
+ @pool.acquire do |client|
114
+ client.write(bytecode(no_return: true))
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def respond_to_missing?(name, _include_private = false)
121
+ @shortcuts.key?(name)
122
+ end
123
+ end
124
+ end
@@ -97,7 +97,7 @@ module Grumlin
97
97
  def write(bytecode)
98
98
  raise NotConnectedError unless connected?
99
99
 
100
- request = to_query(bytecode.to_bytecode)
100
+ request = to_query(bytecode)
101
101
  channel = @request_dispatcher.add_request(request)
102
102
  @transport.write(request)
103
103
 
@@ -130,7 +130,7 @@ module Grumlin
130
130
  op: "bytecode",
131
131
  processor: "traversal",
132
132
  args: {
133
- gremlin: bytecode,
133
+ gremlin: { :@type => "g:Bytecode", :@value => bytecode.serialize },
134
134
  aliases: { g: :g }
135
135
  }
136
136
  }
@@ -2,28 +2,23 @@
2
2
 
3
3
  module Grumlin
4
4
  module Expressions
5
- module P
6
- class << self
7
- class Predicate < TypedValue
8
- def initialize(name, args:, arg_type: nil)
9
- super(type: "P")
10
- @name = name
11
- @args = args
12
- @arg_type = arg_type
13
- end
5
+ class P
6
+ class Predicate
7
+ attr_reader :namespace, :name, :value, :type
14
8
 
15
- def value
16
- @value ||= {
17
- predicate: @name,
18
- value: TypedValue.new(type: @arg_type, value: @args).to_bytecode
19
- }
20
- end
9
+ def initialize(namespace, name, value:, type: nil)
10
+ @namespace = namespace
11
+ @name = name
12
+ @value = value
13
+ @type = type
21
14
  end
15
+ end
22
16
 
17
+ class << self
23
18
  # TODO: support more predicates
24
19
  %i[eq neq].each do |predicate|
25
20
  define_method predicate do |*args|
26
- Predicate.new(predicate, args: args[0])
21
+ Predicate.new("P", predicate, value: args[0])
27
22
  end
28
23
  end
29
24
 
@@ -36,7 +31,7 @@ module Grumlin
36
31
  else
37
32
  args.to_a
38
33
  end
39
- Predicate.new(predicate, args: args, arg_type: "List")
34
+ Predicate.new("P", predicate, value: args, type: "List")
40
35
  end
41
36
  end
42
37
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Expressions
5
+ class TextP < P
6
+ class << self
7
+ %i[containing endingWith notContaining notEndingWith notStartingWith startingWith].each do |predicate|
8
+ define_method predicate do |*args|
9
+ P::Predicate.new("TextP", predicate, value: args[0])
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,16 +2,30 @@
2
2
 
3
3
  module Grumlin
4
4
  module Expressions
5
- module WithOptions
5
+ class WithOptions
6
6
  WITH_OPTIONS = Grumlin.definitions.dig(:expressions, :with_options).freeze
7
7
 
8
8
  class << self
9
9
  WITH_OPTIONS.each do |k, v|
10
10
  define_method k do
11
- v
11
+ name = "@#{k}"
12
+ return instance_variable_get(name) if instance_variable_defined?(name)
13
+
14
+ instance_variable_set(name, WithOptions.new(k, v))
12
15
  end
13
16
  end
14
17
  end
18
+
19
+ attr_reader :name, :value
20
+
21
+ def initialize(name, value)
22
+ @name = name
23
+ @value = value
24
+ end
25
+
26
+ def to_s
27
+ "WithOptions.#{@name}"
28
+ end
15
29
  end
16
30
  end
17
31
  end
@@ -4,11 +4,11 @@ module Grumlin
4
4
  module Repository
5
5
  module InstanceMethods
6
6
  def __
7
- with_shortcuts(Grumlin::Expressions::U)
7
+ TraversalStart.new(self.class.shortcuts)
8
8
  end
9
9
 
10
10
  def g
11
- with_shortcuts(Grumlin::Traversal.new)
11
+ TraversalStart.new(self.class.shortcuts)
12
12
  end
13
13
  end
14
14
 
@@ -5,13 +5,17 @@ module Grumlin
5
5
  module Properties
6
6
  extend Grumlin::Shortcuts
7
7
 
8
- shortcut :props do |*_args, **props|
8
+ shortcut :props do |props|
9
+ next if props.nil? # TODO: fixme, add proper support for **params
10
+
9
11
  props.reduce(self) do |tt, (prop, value)|
10
12
  tt.property(prop, value)
11
13
  end
12
14
  end
13
15
 
14
- shortcut :hasAll do |*, **props|
16
+ shortcut :hasAll do |props|
17
+ next if props.nil? # TODO: fixme, add proper support for **params
18
+
15
19
  props.reduce(self) do |tt, (prop, value)|
16
20
  tt.has(prop, value)
17
21
  end
@@ -2,14 +2,7 @@
2
2
 
3
3
  module Grumlin
4
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
5
  def self.extended(base)
12
- base.include(InstanceMethods)
13
6
  base.include(Grumlin::Expressions)
14
7
  end
15
8
 
@@ -21,7 +14,7 @@ module Grumlin
21
14
  def shortcut(name, shortcut = nil, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
22
15
  name = name.to_sym
23
16
  # TODO: blocklist of names to avoid conflicts with standard methods?
24
- if Grumlin::AnonymousStep::SUPPORTED_STEPS.include?(name)
17
+ if Grumlin::Action::REGULAR_STEPS.include?(name)
25
18
  raise ArgumentError,
26
19
  "cannot use names of standard gremlin steps"
27
20
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class ShortcutsApplyer
5
+ class << self
6
+ def call(steps)
7
+ new.call(steps)
8
+ end
9
+ end
10
+
11
+ def call(steps)
12
+ return steps unless steps.uses_shortcuts?
13
+
14
+ shortcuts = steps.shortcuts
15
+
16
+ configuration_steps = process_steps(steps.configuration_steps, shortcuts)
17
+ regular_steps = process_steps(steps.steps, shortcuts)
18
+
19
+ Steps.new(shortcuts).tap do |processed_steps|
20
+ (configuration_steps + regular_steps).each do |step|
21
+ processed_steps.add(step.name, step.arguments)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def process_steps(steps, shortcuts) # rubocop:disable Metrics/AbcSize
29
+ steps.each_with_object([]) do |step, result|
30
+ arguments = step.arguments.map do |arg|
31
+ arg.is_a?(Steps) ? ShortcutsApplyer.call(arg) : arg
32
+ end
33
+
34
+ if shortcuts.include?(step.name)
35
+ t = TraversalStart.new(shortcuts)
36
+ action = shortcuts[step.name].apply(t, *arguments)
37
+ next if action.nil? || action == t # Shortcut did not add any steps
38
+
39
+ new_steps = ShortcutsApplyer.call(Steps.from(action))
40
+ result.concat(new_steps.configuration_steps)
41
+ result.concat(new_steps.steps)
42
+ else
43
+ result << StepData.new(step.name, arguments)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class StepData
5
+ attr_reader :name, :arguments
6
+
7
+ def initialize(name, arguments)
8
+ @name = name
9
+ @arguments = arguments
10
+ end
11
+
12
+ def ==(other)
13
+ self.class == other.class &&
14
+ @name == other.name &&
15
+ @arguments == other.arguments
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class Steps
5
+ CONFIGURATION_STEPS = Action::CONFIGURATION_STEPS
6
+ ALL_STEPS = Action::ALL_STEPS
7
+
8
+ def self.from(action)
9
+ raise ArgumentError, "expected: #{Action}, given: #{action.class}" unless action.is_a?(Action)
10
+
11
+ shortcuts = action.shortcuts
12
+ actions = []
13
+
14
+ until action.nil?
15
+ actions.unshift(action)
16
+ action = action.previous_step
17
+ end
18
+
19
+ new(shortcuts).tap do |chain|
20
+ actions.each do |act|
21
+ chain.add(act.name, act.arguments)
22
+ end
23
+ end
24
+ end
25
+
26
+ attr_reader :configuration_steps, :steps, :shortcuts
27
+
28
+ def initialize(shortcuts, configuration_steps: [], steps: [])
29
+ @shortcuts = shortcuts
30
+ @configuration_steps = configuration_steps
31
+ @steps = steps
32
+ end
33
+
34
+ def add(name, arguments)
35
+ return add_configuration_step(name, arguments) if CONFIGURATION_STEPS.include?(name)
36
+
37
+ StepData.new(name, cast_arguments(arguments)).tap do |step|
38
+ @steps << step
39
+ end
40
+ end
41
+
42
+ def uses_shortcuts?
43
+ shortcuts?(@configuration_steps) || shortcuts?(@steps)
44
+ end
45
+
46
+ def ==(other)
47
+ self.class == other.class &&
48
+ @shortcuts == other.shortcuts &&
49
+ @configuration_steps == other.configuration_steps &&
50
+ @steps == other.steps
51
+ end
52
+
53
+ # TODO: add #bytecode, to_s, inspect
54
+
55
+ private
56
+
57
+ def shortcuts?(steps_ary)
58
+ steps_ary.any? do |step|
59
+ @shortcuts.include?(step.name) || step.arguments.any? do |arg|
60
+ arg.is_a?(Steps) ? arg.uses_shortcuts? : false
61
+ end
62
+ end
63
+ end
64
+
65
+ def add_configuration_step(name, arguments)
66
+ raise ArgumentError, "cannot use configuration steps after start step was used" unless @steps.empty?
67
+
68
+ StepData.new(name, cast_arguments(arguments)).tap do |step|
69
+ @configuration_steps << step
70
+ end
71
+ end
72
+
73
+ def cast_arguments(arguments)
74
+ arguments.map { |arg| arg.is_a?(Action) ? Steps.from(arg) : arg }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module StepsSerializers
5
+ class Bytecode < Serializer
6
+ # constructor params: no_return: true|false, default false
7
+ # TODO: add pretty
8
+
9
+ NONE_STEP = StepData.new("none", [])
10
+
11
+ def serialize
12
+ steps = ShortcutsApplyer.call(@steps)
13
+ no_return = @params[:no_return] || false
14
+
15
+ {
16
+ step: (steps.steps + (no_return ? [NONE_STEP] : [])).map { |s| serialize_step(s) }
17
+ }.tap do |v|
18
+ v.merge!(source: steps.configuration_steps.map { |s| serialize_step(s) }) if steps.configuration_steps.any?
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def serialize_step(step)
25
+ [step.name, *step.arguments.map { |arg| serialize_arg(arg) }]
26
+ end
27
+
28
+ def serialize_arg(arg)
29
+ return serialize_typed_value(arg) if arg.is_a?(TypedValue)
30
+ return serialize_predicate(arg) if arg.is_a?(Expressions::P::Predicate)
31
+ return arg.value if arg.is_a?(Expressions::WithOptions)
32
+
33
+ return arg unless arg.is_a?(Steps)
34
+
35
+ { :@type => "g:Bytecode", :@value => Bytecode.new(arg, **@params.merge(no_return: false)).serialize }
36
+ end
37
+
38
+ def serialize_typed_value(value)
39
+ return value.value if value.type.nil?
40
+
41
+ {
42
+ "@type": "g:#{value.type}",
43
+ "@value": value.value
44
+ }
45
+ end
46
+
47
+ def serialize_predicate(value)
48
+ {
49
+ "@type": "g:#{value.namespace}",
50
+ "@value": {
51
+ predicate: value.name,
52
+ value: if value.type.nil?
53
+ value.value
54
+ else
55
+ {
56
+ "@type": "g:#{value.type}",
57
+ "@value": value.value
58
+ }
59
+ end
60
+ }
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module StepsSerializers
5
+ class HumanReadableBytecode < Serializer
6
+ def serialize
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) }
13
+ end
14
+
15
+ private
16
+
17
+ def serialize_step(step)
18
+ [step.name, *step.arguments.map { |arg| serialize_arg(arg) }]
19
+ end
20
+
21
+ def serialize_arg(arg)
22
+ return arg.to_s if arg.is_a?(TypedValue)
23
+ return serialize_predicate(arg) if arg.is_a?(Expressions::P::Predicate)
24
+ return arg.value if arg.is_a?(Expressions::WithOptions)
25
+
26
+ return arg unless arg.is_a?(Steps)
27
+
28
+ HumanReadableBytecode.new(arg, **@params.merge(no_return: false)).serialize[1]
29
+ end
30
+
31
+ def serialize_predicate(arg)
32
+ "#{arg.name}(#{arg.value})"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module StepsSerializers
5
+ class Serializer
6
+ def initialize(steps, **params)
7
+ @steps = steps
8
+ @params = params
9
+ end
10
+
11
+ def serialize
12
+ raise NotImplementedError
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module StepsSerializers
5
+ class String < Serializer
6
+ # constructor params: apply_shortcuts: true|false, default: false
7
+ # constructor params: anonymous: true|false, default: false
8
+ # TODO: add pretty
9
+
10
+ def serialize
11
+ steps = @params[:apply_shortcuts] ? ShortcutsApplyer.call(@steps) : @steps
12
+
13
+ configuration_steps = serialize_steps(steps.configuration_steps)
14
+ regular_steps = serialize_steps(steps.steps)
15
+
16
+ "#{prefix}.#{(configuration_steps + regular_steps).join(".")}"
17
+ end
18
+
19
+ private
20
+
21
+ def prefix
22
+ @prefix ||= @params[:anonymous] ? "__" : "g"
23
+ end
24
+
25
+ def serialize_arg(arg)
26
+ return "\"#{arg}\"" if arg.is_a?(::String) || arg.is_a?(Symbol)
27
+ return "#{arg.type}.#{arg.value}" if arg.is_a?(Grumlin::TypedValue)
28
+ return arg.to_s if arg.is_a?(Grumlin::Expressions::WithOptions)
29
+
30
+ return arg unless arg.is_a?(Steps)
31
+
32
+ StepsSerializers::String.new(arg, anonymous: true, **@params).serialize
33
+ 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
+ end
41
+ end
42
+ end
data/lib/grumlin/sugar.rb CHANGED
@@ -6,12 +6,12 @@ module Grumlin
6
6
  base.include(Grumlin::Expressions)
7
7
  end
8
8
 
9
- def __
10
- Grumlin::Expressions::U
9
+ def __(shortcuts = {})
10
+ Grumlin::TraversalStart.new(shortcuts) # TODO: allow only regular and start steps
11
11
  end
12
12
 
13
- def g
14
- Grumlin::Traversal.new
13
+ def g(shortcuts = {})
14
+ Grumlin::TraversalStart.new(shortcuts)
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class TraversalStart
5
+ START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
6
+ REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
7
+ CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
8
+
9
+ ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
10
+
11
+ ALL_STEPS.each do |step|
12
+ define_method step do |*args, **params|
13
+ step(step, *args, **params)
14
+ end
15
+ end
16
+
17
+ attr_reader :shortcuts
18
+
19
+ def initialize(shortcuts)
20
+ @shortcuts = shortcuts
21
+ end
22
+
23
+ def step(name, *args, **params)
24
+ Action.new(name, args: args, params: params, shortcuts: @shortcuts)
25
+ end
26
+
27
+ def method_missing(name, *args, **params)
28
+ return step(name, *args, **params) if @shortcuts.key?(name)
29
+
30
+ super
31
+ end
32
+
33
+ def __
34
+ TraversalStart.new(@shortcuts) # TODO: allow only regular and start steps
35
+ end
36
+
37
+ def to_s(*)
38
+ self.class.to_s
39
+ end
40
+
41
+ def inspect
42
+ self.class.inspect
43
+ end
44
+
45
+ private
46
+
47
+ def respond_to_missing?(name, _include_private = false)
48
+ @shortcuts.key?(name)
49
+ end
50
+ end
51
+ end
@@ -9,17 +9,6 @@ module Grumlin
9
9
  @value = value
10
10
  end
11
11
 
12
- def to_bytecode
13
- @to_bytecode ||= if type.nil?
14
- value
15
- else
16
- {
17
- "@type": "g:#{type}",
18
- "@value": value
19
- }
20
- end
21
- end
22
-
23
12
  def inspect
24
13
  "<#{type}.#{value}>"
25
14
  end
@@ -27,9 +16,5 @@ module Grumlin
27
16
  def to_s
28
17
  inspect
29
18
  end
30
-
31
- def to_readable_bytecode
32
- inspect
33
- end
34
19
  end
35
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.15.6"
4
+ VERSION = "0.16.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.15.6
4
+ version: 0.16.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-09 00:00:00.000000000 Z
11
+ date: 2022-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-pool
@@ -98,8 +98,7 @@ files:
98
98
  - lib/async/channel.rb
99
99
  - lib/definitions.yml
100
100
  - lib/grumlin.rb
101
- - lib/grumlin/anonymous_step.rb
102
- - lib/grumlin/bytecode.rb
101
+ - lib/grumlin/action.rb
103
102
  - lib/grumlin/client.rb
104
103
  - lib/grumlin/edge.rb
105
104
  - lib/grumlin/expressions/expression.rb
@@ -109,23 +108,28 @@ files:
109
108
  - lib/grumlin/expressions/pop.rb
110
109
  - lib/grumlin/expressions/scope.rb
111
110
  - lib/grumlin/expressions/t.rb
112
- - lib/grumlin/expressions/u.rb
111
+ - lib/grumlin/expressions/text_p.rb
113
112
  - lib/grumlin/expressions/with_options.rb
114
113
  - lib/grumlin/path.rb
115
114
  - lib/grumlin/property.rb
116
115
  - lib/grumlin/repository.rb
117
116
  - lib/grumlin/request_dispatcher.rb
118
117
  - lib/grumlin/shortcut.rb
119
- - lib/grumlin/shortcut_proxy.rb
120
118
  - lib/grumlin/shortcuts.rb
121
119
  - lib/grumlin/shortcuts/properties.rb
122
- - lib/grumlin/step.rb
120
+ - lib/grumlin/shortcuts_applyer.rb
121
+ - lib/grumlin/step_data.rb
122
+ - lib/grumlin/steps.rb
123
+ - lib/grumlin/steps_serializers/bytecode.rb
124
+ - lib/grumlin/steps_serializers/human_readable_bytecode.rb
125
+ - lib/grumlin/steps_serializers/serializer.rb
126
+ - lib/grumlin/steps_serializers/string.rb
123
127
  - lib/grumlin/sugar.rb
124
128
  - lib/grumlin/test/rspec.rb
125
129
  - lib/grumlin/test/rspec/db_cleaner_context.rb
126
130
  - lib/grumlin/test/rspec/gremlin_context.rb
127
131
  - lib/grumlin/transport.rb
128
- - lib/grumlin/traversal.rb
132
+ - lib/grumlin/traversal_start.rb
129
133
  - lib/grumlin/traverser.rb
130
134
  - lib/grumlin/typed_value.rb
131
135
  - lib/grumlin/typing.rb
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- class AnonymousStep
5
- attr_reader :name, :previous_step, :configuration_steps
6
-
7
- SUPPORTED_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
8
-
9
- def initialize(name, *args, configuration_steps: [], previous_step: nil, **params)
10
- @name = name
11
- @previous_step = previous_step
12
- @args = args
13
- @params = params
14
- @configuration_steps = configuration_steps
15
- end
16
-
17
- SUPPORTED_STEPS.each do |step|
18
- define_method(step) do |*args, **params|
19
- step(step, *args, **params)
20
- end
21
- end
22
-
23
- def step(name, *args, **params)
24
- self.class.new(name, *args, previous_step: self, configuration_steps: configuration_steps, **params)
25
- end
26
-
27
- def inspect
28
- bytecode.inspect
29
- end
30
-
31
- def to_s
32
- inspect
33
- end
34
-
35
- def bytecode(no_return: false)
36
- @bytecode ||= Bytecode.new(self, no_return: no_return)
37
- end
38
-
39
- def args
40
- [*@args].tap do |args|
41
- args << @params if @params.any?
42
- end
43
- end
44
- end
45
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- # Incapsulates logic of converting step chains and step arguments to queries that can be sent to the server
5
- # and to human readable strings.
6
- class Bytecode < TypedValue
7
- class NoneStep
8
- def to_bytecode
9
- ["none"]
10
- end
11
- end
12
-
13
- NONE_STEP = NoneStep.new
14
-
15
- def initialize(step, no_return: false)
16
- super(type: "Bytecode")
17
- @step = step
18
- @no_return = no_return
19
- end
20
-
21
- def inspect
22
- configuration_steps = @step.configuration_steps.map do |s|
23
- serialize_arg(s, serialization_method: :to_readable_bytecode)
24
- end
25
- "#{configuration_steps.any? ? configuration_steps : nil}#{to_readable_bytecode}"
26
- end
27
-
28
- def to_s
29
- inspect
30
- end
31
-
32
- def to_readable_bytecode
33
- @to_readable_bytecode ||= steps.map { |s| serialize_arg(s, serialization_method: :to_readable_bytecode) }
34
- end
35
-
36
- def value
37
- @value ||= { step: (steps + (@no_return ? [NONE_STEP] : [])).map { |s| serialize_arg(s) } }.tap do |v|
38
- v.merge!(source: @step.configuration_steps.map { |s| serialize_arg(s) }) if @step.configuration_steps.any?
39
- end
40
- end
41
-
42
- private
43
-
44
- # Serializes step or a step argument to either an executable query or a human readable string representation
45
- # depending on the `serialization_method` parameter. It should be either `:to_readable_bytecode` for human readable
46
- # representation or `:to_bytecode` for query.
47
- def serialize_arg(arg, serialization_method: :to_bytecode)
48
- return arg.public_send(serialization_method) if arg.respond_to?(serialization_method)
49
- return arg unless arg.is_a?(AnonymousStep)
50
-
51
- arg.args.each.with_object([arg.name.to_s]) do |a, res|
52
- res << if a.respond_to?(:bytecode)
53
- a.bytecode.public_send(serialization_method)
54
- else
55
- serialize_arg(a, serialization_method: serialization_method)
56
- end
57
- end
58
- end
59
-
60
- def steps
61
- @steps ||= [].tap do |result|
62
- step = @step
63
- until step.nil?
64
- result.unshift(step)
65
- step = step.previous_step
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- module Expressions
5
- # The module is called U because Underscore and implements __
6
- module U
7
- class << self
8
- Grumlin::AnonymousStep::SUPPORTED_STEPS.each do |step|
9
- define_method step do |*args, **params|
10
- AnonymousStep.new(step, *args, **params)
11
- end
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,53 +0,0 @@
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, **params)
17
- return @parent.public_send(name, *args, **params) if %i[__ g].include?(name) && !@parent.nil?
18
-
19
- return wrap_result(@object.public_send(name, *args, **params)) if @object.respond_to?(name)
20
-
21
- return wrap_result(@shortcuts[name].apply(self, *args, **params)) 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
- if result.is_a?(AnonymousStep) || result.is_a?(Traversal)
47
- return self.class.new(result, @shortcuts, parent: @parent)
48
- end
49
-
50
- result
51
- end
52
- end
53
- end
data/lib/grumlin/step.rb DELETED
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- class Step < AnonymousStep
5
- attr_reader :client
6
-
7
- def initialize(pool, name, *args, configuration_steps: [], previous_step: nil, **params)
8
- super(name, *args, previous_step: previous_step, configuration_steps: configuration_steps, **params)
9
- @pool = pool
10
- end
11
-
12
- def next
13
- to_enum.next
14
- end
15
-
16
- def hasNext # rubocop:disable Naming/MethodName
17
- to_enum.peek
18
- true
19
- rescue StopIteration
20
- false
21
- end
22
-
23
- def to_enum
24
- @to_enum ||= toList.to_enum
25
- end
26
-
27
- def toList
28
- @pool.acquire do |client|
29
- client.write(bytecode)
30
- end
31
- end
32
-
33
- def iterate
34
- @pool.acquire do |client|
35
- client.write(bytecode(no_return: true))
36
- end
37
- end
38
-
39
- def step(step_name, *args, **params)
40
- self.class.new(@pool, step_name, *args, previous_step: self, configuration_steps: @configuration_steps, **params)
41
- end
42
- end
43
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grumlin
4
- class Traversal
5
- SUPPORTED_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
6
-
7
- CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
8
-
9
- attr_reader :configuration_steps
10
-
11
- def initialize(pool = Grumlin.default_pool, configuration_steps: [])
12
- @pool = pool
13
- @configuration_steps = configuration_steps
14
- end
15
-
16
- def inspect
17
- "#<#{self.class}>"
18
- end
19
-
20
- def to_s
21
- inspect
22
- end
23
-
24
- CONFIGURATION_STEPS.each do |step|
25
- define_method step do |*args, **params|
26
- self.class.new(@pool, configuration_steps: @configuration_steps + [AnonymousStep.new(step, *args, **params)])
27
- end
28
- end
29
-
30
- SUPPORTED_STEPS.each do |step|
31
- define_method step do |*args, **params|
32
- Step.new(@pool, step, *args, configuration_steps: @configuration_steps, **params)
33
- end
34
- end
35
- end
36
- end