grumlin 0.19.7 → 0.20.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b92be5cc47881f78213096e0d47f53f2b590bca2526a50a5d1aff18dcbf10d14
4
- data.tar.gz: 324242ec87f8ac18e099f22d346b1ac56c8c16755c46d16841aa663bcc905bab
3
+ metadata.gz: e2a547fb7cbcfe53c2fab5486236b4d1021b8c4fe487307003571b24ea9e04d8
4
+ data.tar.gz: b1f73d016d9779f63a0d19c2c02afc5b975544a479aa9e9a1f5424edfde2911f
5
5
  SHA512:
6
- metadata.gz: a626ef9e710036168cd951c1da269130001121ce1b8731e5cf3ac48fee73343e07fbb1a314896151bbf24b074f75a75f0de930ccf5f4624449a5748797d046f6
7
- data.tar.gz: d4bcff4062607bd1a5c3e4d46afe51f2e088b776ee739697a3c4aee44abbc4e4c7215114e7ba9fd6b7830dc5fffd6d5605dcc1b5b975f2ae6cc6c088dd2039ca
6
+ metadata.gz: 5e8c6fa1bea702a974a9c0b22ec7c3f914ccceaf7c12f4bc8b5f8ce94eace7edd6dce4e47f318189e3e1b7af8c5e8ace6b00479283823274e3badfe25b6b0b07
7
+ data.tar.gz: cbb5bbfd192542785f041157232097ed01fe0d1444c20fc7c5606c1873229a7d7577183b8919f475085b5e7bf6700fae6cc987e0024fe6263cfab61f44ce898c
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
+ --require dead_end
2
3
  --require spec_helper
data/Gemfile CHANGED
@@ -13,6 +13,7 @@ gem "overcommit"
13
13
  gem "rake"
14
14
 
15
15
  gem "async-rspec"
16
+ gem "dead_end"
16
17
  gem "factory_bot"
17
18
  gem "rspec"
18
19
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grumlin (0.19.7)
4
+ grumlin (0.20.2)
5
5
  async-pool (~> 0.3)
6
6
  async-websocket (~> 0.19)
7
7
  oj (~> 3.13)
@@ -48,6 +48,7 @@ GEM
48
48
  concurrent-ruby (1.1.10)
49
49
  console (1.15.3)
50
50
  fiber-local
51
+ dead_end (4.0.0)
51
52
  diff-lcs (1.5.0)
52
53
  docile (1.4.0)
53
54
  e2mmap (0.1.0)
@@ -71,7 +72,7 @@ GEM
71
72
  racc (~> 1.4)
72
73
  nokogiri (1.13.6-x86_64-linux)
73
74
  racc (~> 1.4)
74
- oj (3.13.16)
75
+ oj (3.13.17)
75
76
  overcommit (0.59.1)
76
77
  childprocess (>= 0.6.3, < 5)
77
78
  iniparse (~> 1.4)
@@ -172,6 +173,7 @@ PLATFORMS
172
173
  DEPENDENCIES
173
174
  async-rspec
174
175
  benchmark-ips
176
+ dead_end
175
177
  factory_bot
176
178
  grumlin!
177
179
  memory_profiler
data/README.md CHANGED
@@ -80,8 +80,7 @@ sequences of standard gremlin steps, other shortcuts and even add new initially
80
80
  Remember ActiveRecord scopes? Shortcuts are very similar.
81
81
 
82
82
  **Important**: if a shortcut's name matches a name of a method defined on the wrapped object, this shortcut will be
83
- be ignored because methods have higher priority. You cannot override supported by Grumlin steps with shortcuts,
84
- `Grumlin::Shortcuts.shortcut` will raise an `ArgumentError`. Please carefully choose names for your shortcuts.
83
+ be ignored because methods have higher priority.
85
84
 
86
85
  Shortcuts are designed to be used with `Grumlin::Repository` but still can be used separately, with `Grumlin::Sugar`
87
86
  for example.
@@ -143,6 +142,22 @@ class MyRepository
143
142
  end
144
143
  ```
145
144
 
145
+ ##### Overriding standard steps and shortcuts
146
+
147
+ Sometimes it may be useful to override standard steps. Grumlin does not allow it by default, but one
148
+ is still able to override standard steps if they know what they are doing:
149
+
150
+ ```ruby
151
+ shortcut :addV, override: true do |label|
152
+ super(label).property(:default, :value)
153
+ end
154
+ ```
155
+
156
+ This will create a new shortcut that overrides the standard step `addV` and adds default properties to all vertices
157
+ created by the repository that uses this shortcut.
158
+
159
+ Shortcuts also can be overridden, but super() is not available.
160
+
146
161
  #### Grumlin::Repository
147
162
  `Grumlin::Repository` combines functionality of `Grumlin::Sugar` and `Grumlin::Shortcuts` as well as adds a few useful
148
163
  shortcuts to make gremlin code more rubyish. Can be used as a drop in replacement for `Grumlin::Sugar`. Remember that
@@ -167,6 +182,23 @@ class MyRepository
167
182
  hasAll(T.label => :triangle, color: color)
168
183
  end
169
184
 
185
+ # `default_vertex_properties` and `default_edge_properties`
186
+ # override `addV` and `addE` according and inject hashes returned from passed
187
+ # as properties for newly created vertices and edges.
188
+ # In case if a repository is inherited, newly defined default properties will be merged to
189
+ # default properties defined in the parent repository.
190
+ default_vertex_properties do |_label|
191
+ {
192
+ created_at: Time.now.to_i
193
+ }
194
+ end
195
+
196
+ default_edge_properties do |_label|
197
+ {
198
+ created_at: Time.now.to_i
199
+ }
200
+ end
201
+
170
202
  # g and __ are already aware of shortcuts
171
203
  query(:triangles_with_color, return_mode: :list) do |color| # :list is the default return mode, also possible: :none, :single, :traversal
172
204
  g.V.hasLabel(:triangle)
data/bin/console CHANGED
@@ -7,6 +7,8 @@ require "grumlin"
7
7
  require "irb"
8
8
  require "irb/completion"
9
9
 
10
+ require "dead_end"
11
+
10
12
  Grumlin.configure do |config|
11
13
  config.url = ENV.fetch("GREMLIN_URL", "ws://localhost:8182/gremlin")
12
14
  end
@@ -1,5 +1,7 @@
1
1
  FROM tinkerpop/gremlin-server
2
2
 
3
+ RUN rm -rf /opt/gremlin-server/ext/tinkergraph-gremlin
4
+
3
5
  ADD tinkergraph-gremlin /opt/gremlin-server/ext/tinkergraph-gremlin
4
6
 
5
7
  ADD tinkergraph-empty.properties /opt/gremlin-server/conf/
data/lib/definitions.yml CHANGED
@@ -82,6 +82,7 @@ steps:
82
82
  configuration:
83
83
  - withSack
84
84
  - withSideEffect
85
+ - withStrategies
85
86
  expressions:
86
87
  cardinality:
87
88
  - list
@@ -1,32 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
+ class Action < Steppable
5
+ attr_reader :name, :args, :params, :next_step, :configuration_steps, :previous_step, :shortcut
8
6
 
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)
7
+ def initialize(name, args: [], params: {}, previous_step: nil, pool: nil)
8
+ super()
14
9
  @name = name.to_sym
15
10
  @args = args # TODO: add recursive validation: only json types or Action
16
11
  @params = params # TODO: add recursive validation: only json types
17
12
  @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)
13
+ @shortcut = shortcuts[@name]
14
+ @pool = pool || Grumlin.default_pool
30
15
  end
31
16
 
32
17
  def configuration_step?
@@ -45,23 +30,13 @@ module Grumlin
45
30
  ALL_STEPS.include?(@name)
46
31
  end
47
32
 
48
- def shortcut?
49
- @shortcuts.key?(@name)
50
- end
51
-
52
- def method_missing(name, *args, **params)
53
- return step(name, *args, **params) if @shortcuts.key?(name)
54
-
55
- super
56
- end
57
-
58
33
  def ==(other)
59
34
  self.class == other.class &&
60
35
  @name == other.name &&
61
36
  @args == other.args &&
62
37
  @params == other.params &&
63
38
  @previous_step == other.previous_step &&
64
- @shortcuts == other.shortcuts
39
+ shortcuts == other.shortcuts
65
40
  end
66
41
 
67
42
  def steps
@@ -108,11 +83,5 @@ module Grumlin
108
83
  client.write(bytecode(no_return: true))
109
84
  end
110
85
  end
111
-
112
- private
113
-
114
- def respond_to_missing?(name, _include_private = false)
115
- @shortcuts.key?(name)
116
- end
117
86
  end
118
87
  end
@@ -5,6 +5,8 @@ module Grumlin
5
5
  module InstanceMethods
6
6
  include Grumlin::Expressions
7
7
 
8
+ extend Forwardable
9
+
8
10
  UPSERT_RETRY_PARAMS = {
9
11
  on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentInsertFailedError],
10
12
  sleep_method: ->(n) { Async::Task.current.sleep(n) },
@@ -14,12 +16,10 @@ module Grumlin
14
16
 
15
17
  DEFAULT_ERROR_HANDLING_STRATEGY = ErrorHandlingStrategy.new(mode: :retry, **UPSERT_RETRY_PARAMS)
16
18
 
17
- def __
18
- @__ ||= TraversalStart.new(self.class.shortcuts)
19
- end
19
+ def_delegators :shortcuts, :g, :__
20
20
 
21
- def g
22
- @g ||= TraversalStart.new(self.class.shortcuts)
21
+ def shortcuts
22
+ self.class.shortcuts
23
23
  end
24
24
 
25
25
  def drop_vertex(id)
@@ -56,7 +56,7 @@ module Grumlin
56
56
  with_upsert_error_handling(on_failure, params) do
57
57
  create_properties, update_properties = cleanup_properties(create_properties, update_properties)
58
58
 
59
- g.upsertV(label, id, create_properties, update_properties).next
59
+ g.upsertV(label, id, create_properties, update_properties).id.next
60
60
  end
61
61
  end
62
62
 
@@ -70,7 +70,7 @@ module Grumlin
70
70
  create_properties, update_properties = cleanup_properties(create_properties, update_properties)
71
71
 
72
72
  t.upsertV(label, id, create_properties, update_properties)
73
- end.iterate
73
+ end.id.iterate
74
74
  end
75
75
  end
76
76
  end
@@ -80,12 +80,12 @@ module Grumlin
80
80
  def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, on_failure: :retry, **params) # rubocop:disable Metrics/ParameterLists
81
81
  with_upsert_error_handling(on_failure, params) do
82
82
  create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
83
- g.upsertE(label, from, to, create_properties, update_properties).next
83
+ g.upsertE(label, from, to, create_properties, update_properties).id.next
84
84
  end
85
85
  end
86
86
 
87
87
  # edges:
88
- # [["label", "id", {create: :properties}, {update: properties}]]
88
+ # [["label", "from", "to", {create: :properties}, {update: properties}]]
89
89
  # params can override Retryable config from UPSERT_RETRY_PARAMS
90
90
  def upsert_edges(edges, batch_size: 100, on_failure: :retry, **params)
91
91
  edges.each_slice(batch_size) do |slice|
@@ -94,7 +94,7 @@ module Grumlin
94
94
  create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
95
95
 
96
96
  t.upsertE(label, from, to, create_properties, update_properties)
97
- end.iterate
97
+ end.id.iterate
98
98
  end
99
99
  end
100
100
  end
@@ -23,7 +23,7 @@ module Grumlin
23
23
 
24
24
  define_method name do |*args, query_params: {}, **params, &block|
25
25
  t = instance_exec(*args, **params, &query_block)
26
- return t if self.class.empty_result?(t)
26
+ return t if t.nil? || (t.respond_to?(:empty?) && t.empty?)
27
27
 
28
28
  unless t.is_a?(Grumlin::Action)
29
29
  raise WrongQueryResult,
@@ -45,8 +45,20 @@ module Grumlin
45
45
  end
46
46
  end
47
47
 
48
+ def default_vertex_properties(&block)
49
+ shortcut :addV, override: true do |*args|
50
+ super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
51
+ end
52
+ end
53
+
54
+ def default_edge_properties(&block)
55
+ shortcut :addE, override: true do |*args|
56
+ super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
57
+ end
58
+ end
59
+
48
60
  def validate_return_mode!(return_mode)
49
- return return_mode if RETURN_MODES.key?(return_mode)
61
+ return return_mode if RETURN_MODES.include?(return_mode)
50
62
 
51
63
  raise ArgumentError, "unsupported return mode #{return_mode}. Supported modes: #{RETURN_MODES.keys}"
52
64
  end
@@ -60,9 +72,5 @@ module Grumlin
60
72
  raise ArgumentError,
61
73
  "postprocess_with must be a String, Symbol or a callable object, given: #{postprocess_with.class}"
62
74
  end
63
-
64
- def empty_result?(result)
65
- result.nil? || (result.respond_to?(:empty?) && result.empty?)
66
- end
67
75
  end
68
76
  end
@@ -38,7 +38,7 @@ module Grumlin
38
38
  end
39
39
 
40
40
  def add_request(request)
41
- raise RequestAlreadyAddedError if @requests.key?(request[:requestId])
41
+ raise RequestAlreadyAddedError if @requests.include?(request[:requestId])
42
42
 
43
43
  Async::Channel.new.tap do |channel|
44
44
  @requests[request[:requestId]] = { request: request, result: [], channel: channel }
@@ -73,7 +73,7 @@ module Grumlin
73
73
  end
74
74
 
75
75
  def ongoing_request?(request_id)
76
- @requests.key?(request_id)
76
+ @requests.include?(request_id)
77
77
  end
78
78
 
79
79
  def clear
@@ -9,8 +9,9 @@ module Grumlin
9
9
  def_delegator :@block, :arity
10
10
  def_delegator :@block, :source_location
11
11
 
12
- def initialize(name, &block)
12
+ def initialize(name, lazy: true, &block)
13
13
  @name = name
14
+ @lazy = lazy
14
15
  @block = block
15
16
  end
16
17
 
@@ -18,6 +19,10 @@ module Grumlin
18
19
  @name == other.name && @block == other.block
19
20
  end
20
21
 
22
+ def lazy?
23
+ @lazy
24
+ end
25
+
21
26
  # TODO: to_s, inspect, preview
22
27
 
23
28
  def apply(object, *args, **params)
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module Shortcuts
5
+ class Storage
6
+ extend Forwardable
7
+
8
+ class << self
9
+ def [](other)
10
+ new(other)
11
+ end
12
+
13
+ def empty
14
+ @empty ||= new
15
+ end
16
+ end
17
+
18
+ def initialize(storage = {})
19
+ @storage = storage
20
+ storage.each do |n, s|
21
+ add(n, s)
22
+ end
23
+ end
24
+
25
+ def_delegator :@storage, :[]
26
+ def_delegator :@storage, :include?, :known?
27
+ def_delegator :@storage, :keys, :names
28
+
29
+ def ==(other)
30
+ @storage == other.storage
31
+ end
32
+
33
+ def add(name, shortcut)
34
+ @storage[name] = shortcut
35
+
36
+ ac = action_class
37
+
38
+ shortcut_methods_module.define_method(name) do |*args, **params|
39
+ next ac.new(name, args: args, params: params, previous_step: self)
40
+ end
41
+ extend_traversal_classes(shortcut) unless shortcut.lazy?
42
+ end
43
+
44
+ def add_from(other)
45
+ other.storage.each do |name, shortcut|
46
+ add(name, shortcut)
47
+ end
48
+ end
49
+
50
+ def g
51
+ __
52
+ end
53
+
54
+ def __
55
+ @__ ||= traversal_start_class.new
56
+ end
57
+
58
+ def traversal_start_class
59
+ @traversal_start_class ||= shortcut_aware_class(TraversalStart)
60
+ end
61
+
62
+ def action_class
63
+ @action_class ||= shortcut_aware_class(Action)
64
+ end
65
+
66
+ protected
67
+
68
+ attr_reader :storage
69
+
70
+ private
71
+
72
+ def shortcut_methods_module
73
+ @shortcut_methods_module ||= begin
74
+ shorts = self
75
+ Module.new do
76
+ define_method :shortcuts do
77
+ shorts
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def shortcut_aware_class(base)
84
+ methods = shortcut_methods_module
85
+ Class.new(base) do
86
+ include methods
87
+ end
88
+ end
89
+
90
+ def extend_traversal_classes(shortcut)
91
+ m = Module.new do
92
+ define_method(shortcut.name, &shortcut.block)
93
+ end
94
+ action_class.include(m)
95
+ traversal_start_class.include(m)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -5,16 +5,16 @@ module Grumlin
5
5
  module Upserts
6
6
  extend Grumlin::Shortcuts
7
7
 
8
- shortcut :upsertV do |label, id, create_properties, update_properties|
8
+ shortcut :upsertV do |label, id, create_properties = {}, update_properties = {}|
9
9
  self.V(id)
10
10
  .fold
11
- .coalesce( # TODO: extract upsert pattern to a shortcut
11
+ .coalesce(
12
12
  __.unfold,
13
13
  __.addV(label).props(**create_properties.merge(T.id => id))
14
14
  ).props(**update_properties)
15
15
  end
16
16
 
17
- shortcut :upsertE do |label, from, to, create_properties, update_properties|
17
+ shortcut :upsertE do |label, from, to, create_properties = {}, update_properties = {}|
18
18
  self.V(from)
19
19
  .outE(label).where(__.inV.hasId(to))
20
20
  .fold
@@ -11,33 +11,26 @@ module Grumlin
11
11
  subclass.shortcuts_from(self)
12
12
  end
13
13
 
14
- def shortcut(name, shortcut = nil, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
14
+ def shortcut(name, shortcut = nil, override: false, lazy: true, &block)
15
15
  name = name.to_sym
16
- # TODO: blocklist of names to avoid conflicts with standard methods?
17
- if Grumlin::Action::REGULAR_STEPS.include?(name)
18
- raise ArgumentError,
19
- "cannot use names of standard gremlin steps"
20
- end
16
+ lazy = false if override
21
17
 
22
- if (shortcut.nil? && block.nil?) || (shortcut && block)
23
- raise ArgumentError, "either shortcut or block must be passed"
18
+ if Grumlin::Action::REGULAR_STEPS.include?(name) && !override
19
+ raise ArgumentError,
20
+ "overriding standard gremlin steps is not allowed, if you know what you're doing, pass `override: true`"
24
21
  end
25
22
 
26
- shortcut ||= Shortcut.new(name, &block)
23
+ raise ArgumentError, "either shortcut or block must be passed" if [shortcut, block].count(&:nil?) != 1
27
24
 
28
- raise ArgumentError, "shortcut '#{name}' already exists" if shortcuts.key?(name) && shortcuts[name] != shortcut
29
-
30
- shortcuts[name] = shortcut
25
+ shortcuts.add(name, shortcut || Shortcut.new(name, lazy: lazy, &block))
31
26
  end
32
27
 
33
28
  def shortcuts_from(other_shortcuts)
34
- other_shortcuts.shortcuts.each do |name, shortcut|
35
- shortcut(name, shortcut)
36
- end
29
+ shortcuts.add_from(other_shortcuts.shortcuts)
37
30
  end
38
31
 
39
32
  def shortcuts
40
- @shortcuts ||= {}
33
+ @shortcuts ||= Storage.new
41
34
  end
42
35
  end
43
36
  end
@@ -4,15 +4,17 @@ module Grumlin
4
4
  class ShortcutsApplyer
5
5
  class << self
6
6
  def call(steps)
7
- return steps unless steps.uses_shortcuts?
7
+ return steps if !steps.is_a?(Steps) || !steps.uses_shortcuts?
8
8
 
9
9
  shortcuts = steps.shortcuts
10
10
 
11
- configuration_steps = process_steps(steps.configuration_steps, shortcuts)
12
- regular_steps = process_steps(steps.steps, shortcuts)
11
+ steps = [
12
+ *process_steps(steps.configuration_steps, shortcuts),
13
+ *process_steps(steps.steps, shortcuts)
14
+ ]
13
15
 
14
16
  Steps.new(shortcuts).tap do |processed_steps|
15
- (configuration_steps + regular_steps).each do |step|
17
+ steps.each do |step|
16
18
  processed_steps.add(step.name, args: step.args, params: step.params)
17
19
  end
18
20
  end
@@ -22,21 +24,17 @@ module Grumlin
22
24
 
23
25
  def process_steps(steps, shortcuts) # rubocop:disable Metrics/AbcSize
24
26
  steps.each_with_object([]) do |step, result|
25
- args = step.args.map do |arg|
26
- arg.is_a?(Steps) ? ShortcutsApplyer.call(arg) : arg
27
- end
27
+ args = step.args.map { |arg| call(arg) }
28
28
 
29
- if shortcuts.include?(step.name)
30
- t = TraversalStart.new(shortcuts)
31
- action = shortcuts[step.name].apply(t, *args, **step.params)
32
- next if action.nil? || action == t # Shortcut did not add any steps
29
+ shortcut = shortcuts[step.name]
30
+ next result << StepData.new(step.name, args: args, params: step.params) unless shortcut&.lazy?
33
31
 
34
- new_steps = ShortcutsApplyer.call(Steps.from(action))
35
- result.concat(new_steps.configuration_steps)
36
- result.concat(new_steps.steps)
37
- else
38
- result << StepData.new(step.name, args: args, params: step.params)
39
- end
32
+ t = shortcuts.__
33
+ action = shortcut.apply(t, *args, **step.params)
34
+ next if action.nil? || action == t # Shortcut did not add any steps
35
+
36
+ new_steps = call(Steps.from(action))
37
+ result.concat(new_steps.configuration_steps, new_steps.steps)
40
38
  end
41
39
  end
42
40
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ class Steppable
5
+ extend Forwardable
6
+
7
+ START_STEPS = Grumlin.definitions.dig(:steps, :start).map(&:to_sym).freeze
8
+ REGULAR_STEPS = Grumlin.definitions.dig(:steps, :regular).map(&:to_sym).freeze
9
+ CONFIGURATION_STEPS = Grumlin.definitions.dig(:steps, :configuration).map(&:to_sym).freeze
10
+
11
+ ALL_STEPS = START_STEPS + CONFIGURATION_STEPS + REGULAR_STEPS
12
+
13
+ def initialize
14
+ return if respond_to?(:shortcuts)
15
+
16
+ raise "steppable must not be initialized directly, use Grumlin::Shortcuts::Storage#g or #__ instead"
17
+ end
18
+
19
+ ALL_STEPS.each do |step|
20
+ define_method step do |*args, **params|
21
+ shortcuts.action_class.new(step, args: args, params: params, previous_step: self)
22
+ end
23
+ end
24
+
25
+ def step(name, *args, **params)
26
+ shortcuts.action_class.new(name, args: args, params: params, previous_step: self)
27
+ end
28
+
29
+ def_delegator :shortcuts, :__
30
+ end
31
+ end
data/lib/grumlin/steps.rb CHANGED
@@ -11,7 +11,7 @@ module Grumlin
11
11
  shortcuts = action.shortcuts
12
12
  actions = []
13
13
 
14
- until action.nil?
14
+ until action.nil? || action.is_a?(TraversalStart)
15
15
  actions.unshift(action)
16
16
  action = action.previous_step
17
17
  end
@@ -56,7 +56,7 @@ module Grumlin
56
56
 
57
57
  def shortcuts?(steps_ary)
58
58
  steps_ary.any? do |step|
59
- @shortcuts.include?(step.name) || step.args.any? do |arg|
59
+ @shortcuts.known?(step.name) || step.args.any? do |arg|
60
60
  arg.is_a?(Steps) ? arg.uses_shortcuts? : false
61
61
  end
62
62
  end
data/lib/grumlin/sugar.rb CHANGED
@@ -6,12 +6,10 @@ module Grumlin
6
6
  base.include(Grumlin::Expressions)
7
7
  end
8
8
 
9
- def __(shortcuts = {})
10
- Grumlin::TraversalStart.new(shortcuts) # TODO: allow only regular and start steps
11
- end
12
-
13
- def g(shortcuts = {})
14
- Grumlin::TraversalStart.new(shortcuts)
9
+ %i[__ g].each do |name|
10
+ define_method name do |cuts = Shortcuts::Storage.empty|
11
+ cuts.send(name)
12
+ end
15
13
  end
16
14
  end
17
15
  end
@@ -1,38 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
+ class TraversalStart < Steppable
5
+ include WithExtension
36
6
 
37
7
  def to_s(*)
38
8
  self.class.to_s
@@ -41,11 +11,5 @@ module Grumlin
41
11
  def inspect
42
12
  self.class.inspect
43
13
  end
44
-
45
- private
46
-
47
- def respond_to_missing?(name, _include_private = false)
48
- @shortcuts.key?(name)
49
- end
50
14
  end
51
15
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module TraversalStrategies
5
+ class OptionsStrategy < TypedValue
6
+ def initialize(value)
7
+ super(type: "OptionsStrategy", value: value)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.19.7"
4
+ VERSION = "0.20.2"
5
5
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grumlin
4
+ module WithExtension
5
+ def with(name, value)
6
+ prev = self
7
+ strategy = if is_a?(with_action_class)
8
+ prev = previous_step
9
+ TraversalStrategies::OptionsStrategy.new(args.first.value.merge(name => value))
10
+ else
11
+ TraversalStrategies::OptionsStrategy.new({ name => value })
12
+ end
13
+ with_action_class.new(:withStrategies, args: [strategy], previous_step: prev)
14
+ end
15
+
16
+ private
17
+
18
+ def with_action_class
19
+ @with_action_class ||= Class.new(shortcuts.action_class) do
20
+ include WithExtension
21
+
22
+ def with_action_class
23
+ self.class
24
+ end
25
+ end
26
+ end
27
+ end
28
+ 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.19.7
4
+ version: 0.20.2
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-07-06 00:00:00.000000000 Z
11
+ date: 2022-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-pool
@@ -138,9 +138,11 @@ files:
138
138
  - lib/grumlin/shortcut.rb
139
139
  - lib/grumlin/shortcuts.rb
140
140
  - lib/grumlin/shortcuts/properties.rb
141
+ - lib/grumlin/shortcuts/storage.rb
141
142
  - lib/grumlin/shortcuts/upserts.rb
142
143
  - lib/grumlin/shortcuts_applyer.rb
143
144
  - lib/grumlin/step_data.rb
145
+ - lib/grumlin/steppable.rb
144
146
  - lib/grumlin/steps.rb
145
147
  - lib/grumlin/steps_serializers/bytecode.rb
146
148
  - lib/grumlin/steps_serializers/human_readable_bytecode.rb
@@ -152,11 +154,13 @@ files:
152
154
  - lib/grumlin/test/rspec/gremlin_context.rb
153
155
  - lib/grumlin/transport.rb
154
156
  - lib/grumlin/traversal_start.rb
157
+ - lib/grumlin/traversal_strategies/options_strategy.rb
155
158
  - lib/grumlin/traverser.rb
156
159
  - lib/grumlin/typed_value.rb
157
160
  - lib/grumlin/typing.rb
158
161
  - lib/grumlin/version.rb
159
162
  - lib/grumlin/vertex.rb
163
+ - lib/grumlin/with_extension.rb
160
164
  - lib/tasks/benchmark.rake
161
165
  homepage: https://github.com/zhulik/grumlin
162
166
  licenses: