grumlin 0.19.7 → 0.20.2

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: 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: