grumlin 0.22.4 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +9 -9
  4. data/Gemfile.lock +10 -8
  5. data/README.md +102 -141
  6. data/Rakefile +1 -1
  7. data/bin/console +18 -3
  8. data/doc/middlewares.md +97 -0
  9. data/grumlin.gemspec +1 -0
  10. data/lib/async/channel.rb +54 -56
  11. data/lib/grumlin/benchmark/repository.rb +10 -14
  12. data/lib/grumlin/client.rb +92 -112
  13. data/lib/grumlin/config.rb +30 -15
  14. data/lib/grumlin/dummy_transaction.rb +13 -15
  15. data/lib/grumlin/edge.rb +18 -20
  16. data/lib/grumlin/expressions/cardinality.rb +5 -9
  17. data/lib/grumlin/expressions/column.rb +5 -9
  18. data/lib/grumlin/expressions/expression.rb +7 -11
  19. data/lib/grumlin/expressions/operator.rb +5 -9
  20. data/lib/grumlin/expressions/order.rb +5 -9
  21. data/lib/grumlin/expressions/p.rb +27 -31
  22. data/lib/grumlin/expressions/pop.rb +5 -9
  23. data/lib/grumlin/expressions/scope.rb +5 -9
  24. data/lib/grumlin/expressions/t.rb +5 -9
  25. data/lib/grumlin/expressions/text_p.rb +5 -9
  26. data/lib/grumlin/expressions/with_options.rb +17 -21
  27. data/lib/grumlin/features/feature_list.rb +8 -12
  28. data/lib/grumlin/features/neptune_features.rb +5 -9
  29. data/lib/grumlin/features/tinkergraph_features.rb +5 -9
  30. data/lib/grumlin/features.rb +8 -10
  31. data/lib/grumlin/middlewares/apply_shortcuts.rb +8 -0
  32. data/lib/grumlin/middlewares/build_query.rb +20 -0
  33. data/lib/grumlin/middlewares/builder.rb +15 -0
  34. data/lib/grumlin/middlewares/cast_results.rb +7 -0
  35. data/lib/grumlin/middlewares/find_blocklisted_steps.rb +14 -0
  36. data/lib/grumlin/middlewares/find_mutating_steps.rb +9 -0
  37. data/lib/grumlin/middlewares/middleware.rb +11 -0
  38. data/lib/grumlin/middlewares/run_query.rb +7 -0
  39. data/lib/grumlin/middlewares/serialize_to_bytecode.rb +9 -0
  40. data/lib/grumlin/middlewares/serialize_to_steps.rb +8 -0
  41. data/lib/grumlin/path.rb +11 -13
  42. data/lib/grumlin/property.rb +14 -16
  43. data/lib/grumlin/query_validators/blocklisted_steps_validator.rb +22 -0
  44. data/lib/grumlin/query_validators/validator.rb +36 -0
  45. data/lib/grumlin/repository/error_handling_strategy.rb +36 -40
  46. data/lib/grumlin/repository/instance_methods.rb +115 -118
  47. data/lib/grumlin/repository.rb +82 -58
  48. data/lib/grumlin/request_dispatcher.rb +55 -57
  49. data/lib/grumlin/request_error_factory.rb +53 -55
  50. data/lib/grumlin/shortcut.rb +19 -21
  51. data/lib/grumlin/shortcuts/properties.rb +12 -16
  52. data/lib/grumlin/shortcuts/storage.rb +67 -74
  53. data/lib/grumlin/shortcuts/upserts.rb +18 -22
  54. data/lib/grumlin/shortcuts.rb +23 -25
  55. data/lib/grumlin/shortcuts_applyer.rb +27 -29
  56. data/lib/grumlin/step.rb +92 -0
  57. data/lib/grumlin/step_data.rb +12 -14
  58. data/lib/grumlin/steppable.rb +24 -22
  59. data/lib/grumlin/steps.rb +51 -54
  60. data/lib/grumlin/steps_serializers/bytecode.rb +53 -56
  61. data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +17 -21
  62. data/lib/grumlin/steps_serializers/serializer.rb +7 -11
  63. data/lib/grumlin/steps_serializers/string.rb +26 -30
  64. data/lib/grumlin/test/rspec/db_cleaner_context.rb +8 -12
  65. data/lib/grumlin/test/rspec/gremlin_context.rb +18 -16
  66. data/lib/grumlin/test/rspec.rb +1 -5
  67. data/lib/grumlin/transaction.rb +26 -27
  68. data/lib/grumlin/transport.rb +71 -73
  69. data/lib/grumlin/traversal_start.rb +31 -33
  70. data/lib/grumlin/traversal_strategies/options_strategy.rb +3 -7
  71. data/lib/grumlin/traverser.rb +5 -7
  72. data/lib/grumlin/typed_value.rb +11 -13
  73. data/lib/grumlin/typing.rb +70 -72
  74. data/lib/grumlin/version.rb +1 -1
  75. data/lib/grumlin/vertex.rb +14 -16
  76. data/lib/grumlin/vertex_property.rb +14 -16
  77. data/lib/grumlin/with_extension.rb +17 -19
  78. data/lib/grumlin.rb +23 -19
  79. metadata +32 -6
  80. data/lib/grumlin/action.rb +0 -92
  81. data/lib/grumlin/sugar.rb +0 -15
@@ -1,101 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Transport
5
- # A transport based on https://github.com/socketry/async
6
- # and https://github.com/socketry/async-websocket
7
-
8
- include Console
9
-
10
- attr_reader :url
11
-
12
- # Transport is not reusable. Once closed should be recreated.
13
- def initialize(url, parent: Async::Task.current, **client_options)
14
- @url = url
15
- @parent = parent
16
- @client_options = client_options
17
- @request_channel = Async::Channel.new
18
- @response_channel = Async::Channel.new
19
- end
3
+ class Grumlin::Transport
4
+ # A transport based on https://github.com/socketry/async
5
+ # and https://github.com/socketry/async-websocket
20
6
 
21
- def connected?
22
- !@connection.nil?
23
- end
7
+ include Console
24
8
 
25
- def connect
26
- raise ClientClosedError if @closed
27
- raise AlreadyConnectedError if connected?
9
+ attr_reader :url
28
10
 
29
- @connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
30
- logger.debug(self) { "Connected to #{@url}." }
11
+ # Transport is not reusable. Once closed should be recreated.
12
+ def initialize(url, parent: Async::Task.current, **client_options)
13
+ @url = url
14
+ @parent = parent
15
+ @client_options = client_options
16
+ @request_channel = Async::Channel.new
17
+ @response_channel = Async::Channel.new
18
+ end
31
19
 
32
- @response_task = @parent.async { run_response_task }
20
+ def connected?
21
+ !@connection.nil?
22
+ end
33
23
 
34
- @request_task = @parent.async { run_request_task }
24
+ def connect
25
+ raise ClientClosedError if @closed
26
+ raise AlreadyConnectedError if connected?
35
27
 
36
- @response_channel
37
- end
28
+ @connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
29
+ logger.debug(self) { "Connected to #{@url}." }
38
30
 
39
- def write(message)
40
- raise NotConnectedError unless connected?
31
+ @response_task = @parent.async { run_response_task }
41
32
 
42
- @request_channel << message
43
- end
33
+ @request_task = @parent.async { run_request_task }
44
34
 
45
- def close
46
- return if @closed
35
+ @response_channel
36
+ end
47
37
 
48
- @closed = true
38
+ def write(message)
39
+ raise NotConnectedError unless connected?
49
40
 
50
- @request_channel.close
51
- @response_channel.close
41
+ @request_channel << message
42
+ end
52
43
 
53
- begin
54
- @connection.close
55
- rescue StandardError
56
- nil
57
- end
58
- @connection = nil
44
+ def close
45
+ return if @closed
59
46
 
60
- @request_task&.stop(true)
61
- @response_task&.stop(true)
62
- end
47
+ @closed = true
48
+
49
+ @request_channel.close
50
+ @response_channel.close
63
51
 
64
- def wait
65
- @request_task.wait
66
- @response_task.wait
52
+ begin
53
+ @connection.close
54
+ rescue StandardError
55
+ nil
67
56
  end
57
+ @connection = nil
68
58
 
69
- private
59
+ @request_task&.stop(true)
60
+ @response_task&.stop(true)
61
+ end
62
+
63
+ def wait
64
+ @request_task.wait
65
+ @response_task.wait
66
+ end
70
67
 
71
- def run_response_task
72
- with_guard do
73
- loop do
74
- data = @connection.read
75
- @response_channel << data
76
- end
68
+ private
69
+
70
+ def run_response_task
71
+ with_guard do
72
+ loop do
73
+ data = @connection.read
74
+ @response_channel << data
77
75
  end
78
76
  end
77
+ end
79
78
 
80
- def run_request_task
81
- with_guard do
82
- @request_channel.each do |message|
83
- @connection.write(message)
84
- @connection.flush
85
- end
79
+ def run_request_task
80
+ with_guard do
81
+ @request_channel.each do |message|
82
+ @connection.write(message)
83
+ @connection.flush
86
84
  end
87
85
  end
86
+ end
88
87
 
89
- def with_guard
90
- yield
91
- rescue Async::Stop, Async::TimeoutError, StandardError => e
92
- logger.debug(self) { "Guard error, closing." }
93
- begin
94
- @response_channel.exception(e)
95
- rescue Async::Channel::ChannelClosedError
96
- nil
97
- end
98
- close
88
+ def with_guard
89
+ yield
90
+ rescue Async::Stop, Async::TimeoutError, StandardError => e
91
+ logger.debug(self) { "Guard error, closing." }
92
+ begin
93
+ @response_channel.exception(e)
94
+ rescue Async::Channel::ChannelClosedError
95
+ nil
99
96
  end
97
+ close
100
98
  end
101
99
  end
@@ -1,42 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class TraversalStart < Steppable
5
- include WithExtension
6
-
7
- class TraversalError < Grumlin::Error; end
8
- class AlreadyBoundToTransactionError < TraversalError; end
9
-
10
- def tx
11
- raise AlreadyBoundToTransactionError if @session_id
12
-
13
- transaction = tx_class.new(self.class, pool: @pool)
14
- return transaction unless block_given?
15
-
16
- begin
17
- yield transaction.begin
18
- rescue Grumlin::Rollback
19
- transaction.rollback
20
- rescue StandardError
21
- transaction.rollback
22
- raise
23
- else
24
- transaction.commit
25
- end
3
+ class Grumlin::TraversalStart < Grumlin::Steppable
4
+ include Grumlin::WithExtension
5
+
6
+ class TraversalError < Grumlin::Error; end
7
+ class AlreadyBoundToTransactionError < TraversalError; end
8
+
9
+ def tx
10
+ raise AlreadyBoundToTransactionError if @session_id
11
+
12
+ transaction = tx_class.new(self.class, pool: @pool, middlewares: @middlewares)
13
+ return transaction unless block_given?
14
+
15
+ begin
16
+ yield transaction.begin
17
+ rescue Grumlin::Rollback
18
+ transaction.rollback
19
+ rescue StandardError
20
+ transaction.rollback
21
+ raise
22
+ else
23
+ transaction.commit
26
24
  end
25
+ end
27
26
 
28
- def to_s(*)
29
- self.class.to_s
30
- end
27
+ def to_s(*)
28
+ self.class.to_s
29
+ end
31
30
 
32
- def inspect
33
- self.class.inspect
34
- end
31
+ def inspect
32
+ self.class.inspect
33
+ end
35
34
 
36
- private
35
+ private
37
36
 
38
- def tx_class
39
- Grumlin.features.supports_transactions? ? Transaction : DummyTransaction
40
- end
37
+ def tx_class
38
+ Grumlin.features.supports_transactions? ? Grumlin::Transaction : Grumlin::DummyTransaction
41
39
  end
42
40
  end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
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
3
+ class Grumlin::TraversalStrategies::OptionsStrategy < Grumlin::TypedValue
4
+ def initialize(value)
5
+ super(type: "OptionsStrategy", value: value)
10
6
  end
11
7
  end
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Traverser
5
- attr_reader :bulk, :value
3
+ class Grumlin::Traverser
4
+ attr_reader :bulk, :value
6
5
 
7
- def initialize(value)
8
- @bulk = value.dig(:bulk, :@value) || 1
9
- @value = Typing.cast(value[:value])
10
- end
6
+ def initialize(value)
7
+ @bulk = value.dig(:bulk, :@value) || 1
8
+ @value = Grumlin::Typing.cast(value[:value])
11
9
  end
12
10
  end
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class TypedValue
5
- attr_reader :type, :value
3
+ class Grumlin::TypedValue
4
+ attr_reader :type, :value
6
5
 
7
- def initialize(type: nil, value: nil)
8
- @type = type
9
- @value = value
10
- end
6
+ def initialize(type: nil, value: nil)
7
+ @type = type
8
+ @value = value
9
+ end
11
10
 
12
- def inspect
13
- "<#{type}.#{value}>"
14
- end
11
+ def inspect
12
+ "<#{type}.#{value}>"
13
+ end
15
14
 
16
- def to_s
17
- inspect
18
- end
15
+ def to_s
16
+ inspect
19
17
  end
20
18
  end
@@ -1,89 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Typing
5
- TYPES = {
6
- "g:List" => ->(value) { cast_list(value) },
7
- "g:Set" => ->(value) { cast_list(value).to_set },
8
- "g:Map" => ->(value) { cast_map(value) },
9
- "g:Vertex" => ->(value) { cast_entity(Grumlin::Vertex, value) },
10
- "g:Edge" => ->(value) { cast_entity(Grumlin::Edge, value) },
11
- "g:Path" => ->(value) { cast_entity(Grumlin::Path, value) },
12
- "g:Traverser" => ->(value) { cast_entity(Grumlin::Traverser, value) },
13
- "g:Property" => ->(value) { cast_entity(Grumlin::Property, value) },
14
- "g:Int64" => ->(value) { cast_int(value) },
15
- "g:Int32" => ->(value) { cast_int(value) },
16
- "g:Double" => ->(value) { cast_double(value) },
17
- "g:Direction" => ->(value) { value },
18
- "g:VertexProperty" => ->(value) { cast_entity(Grumlin::VertexProperty, value) },
19
- "g:TraversalMetrics" => ->(value) { cast_map(value[:@value]) },
20
- "g:Metrics" => ->(value) { cast_map(value[:@value]) },
21
- "g:T" => ->(value) { Grumlin::Expressions::T.public_send(value) }
22
- }.freeze
23
-
24
- CASTABLE_TYPES = [Hash, String, Integer, TrueClass, FalseClass, NilClass].freeze
25
-
26
- class << self
27
- def cast(value)
28
- verify_type!(value)
29
-
30
- return value unless value.is_a?(Hash)
31
-
32
- type = TYPES[value[:@type]]
33
-
34
- verify_castable_hash!(value, type)
35
-
36
- type.call(value[:@value])
37
- end
3
+ module Grumlin::Typing
4
+ TYPES = {
5
+ "g:List" => ->(value) { cast_list(value) },
6
+ "g:Set" => ->(value) { cast_list(value).to_set },
7
+ "g:Map" => ->(value) { cast_map(value) },
8
+ "g:Vertex" => ->(value) { cast_entity(Grumlin::Vertex, value) },
9
+ "g:Edge" => ->(value) { cast_entity(Grumlin::Edge, value) },
10
+ "g:Path" => ->(value) { cast_entity(Grumlin::Path, value) },
11
+ "g:Traverser" => ->(value) { cast_entity(Grumlin::Traverser, value) },
12
+ "g:Property" => ->(value) { cast_entity(Grumlin::Property, value) },
13
+ "g:Int64" => ->(value) { cast_int(value) },
14
+ "g:Int32" => ->(value) { cast_int(value) },
15
+ "g:Double" => ->(value) { cast_double(value) },
16
+ "g:Direction" => ->(value) { value },
17
+ "g:VertexProperty" => ->(value) { cast_entity(Grumlin::VertexProperty, value) },
18
+ "g:TraversalMetrics" => ->(value) { cast_map(value[:@value]) },
19
+ "g:Metrics" => ->(value) { cast_map(value[:@value]) },
20
+ "g:T" => ->(value) { Grumlin::Expressions::T.public_send(value) }
21
+ }.freeze
22
+
23
+ CASTABLE_TYPES = [Hash, String, Integer, TrueClass, FalseClass, NilClass].freeze
24
+
25
+ class << self
26
+ def cast(value)
27
+ verify_type!(value)
28
+
29
+ return value unless value.is_a?(Hash)
30
+
31
+ type = TYPES[value[:@type]]
32
+
33
+ verify_castable_hash!(value, type)
34
+
35
+ type.call(value[:@value])
36
+ end
38
37
 
39
- private
38
+ private
40
39
 
41
- def verify_type!(value)
42
- raise TypeError, "#{value.inspect} cannot be casted" unless CASTABLE_TYPES.include?(value.class)
43
- end
40
+ def verify_type!(value)
41
+ raise TypeError, "#{value.inspect} cannot be casted" unless CASTABLE_TYPES.include?(value.class)
42
+ end
44
43
 
45
- def verify_castable_hash!(value, type)
46
- raise TypeError, "#{value} cannot be casted, @type is missing" if value[:@type].nil?
47
- raise(UnknownTypeError, value[:@type]) if type.nil?
48
- raise TypeError, "#{value} cannot be casted, @value is missing" if value[:@value].nil?
49
- end
44
+ def verify_castable_hash!(value, type)
45
+ raise TypeError, "#{value} cannot be casted, @type is missing" if value[:@type].nil?
46
+ raise(UnknownTypeError, value[:@type]) if type.nil?
47
+ raise TypeError, "#{value} cannot be casted, @value is missing" if value[:@value].nil?
48
+ end
50
49
 
51
- def cast_int(value)
52
- raise TypeError, "#{value} is not an Integer" unless value.is_a?(Integer)
50
+ def cast_int(value)
51
+ raise TypeError, "#{value} is not an Integer" unless value.is_a?(Integer)
53
52
 
54
- value
55
- end
53
+ value
54
+ end
56
55
 
57
- def cast_double(value)
58
- raise TypeError, "#{value} is not a Double" unless value.is_a?(Float)
56
+ def cast_double(value)
57
+ raise TypeError, "#{value} is not a Double" unless value.is_a?(Float)
59
58
 
60
- value
61
- end
59
+ value
60
+ end
62
61
 
63
- def cast_entity(entity, value)
64
- entity.new(**value)
65
- rescue ArgumentError, TypeError
66
- raise TypeError, "#{value} cannot be casted to #{entity.name}"
67
- end
62
+ def cast_entity(entity, value)
63
+ entity.new(**value)
64
+ rescue ArgumentError, TypeError
65
+ raise TypeError, "#{value} cannot be casted to #{entity.name}"
66
+ end
68
67
 
69
- def cast_map(value)
70
- Hash[*value].transform_keys do |key|
71
- next key.to_sym if key.respond_to?(:to_sym)
72
- next cast(key) if key[:@type] # TODO: g.V.group.by(:none_existing_property).next
68
+ def cast_map(value)
69
+ Hash[*value].transform_keys do |key|
70
+ next key.to_sym if key.respond_to?(:to_sym)
71
+ next cast(key) if key[:@type] # TODO: g.V.group.by(:none_existing_property).next
73
72
 
74
- raise UnknownMapKey, key, value
75
- end.transform_values { |v| cast(v) }
76
- rescue ArgumentError
77
- raise TypeError, "#{value} cannot be casted to Hash"
78
- end
73
+ raise UnknownMapKey, key, value
74
+ end.transform_values { |v| cast(v) }
75
+ rescue ArgumentError
76
+ raise TypeError, "#{value} cannot be casted to Hash"
77
+ end
79
78
 
80
- def cast_list(value)
81
- value.each_with_object([]) do |item, result|
82
- casted_value = cast(item)
83
- next (result << casted_value) unless casted_value.instance_of?(Traverser)
79
+ def cast_list(value)
80
+ value.each_with_object([]) do |item, result|
81
+ casted_value = cast(item)
82
+ next (result << casted_value) unless casted_value.instance_of?(Grumlin::Traverser)
84
83
 
85
- casted_value.bulk.times { result << casted_value.value }
86
- end
84
+ casted_value.bulk.times { result << casted_value.value }
87
85
  end
88
86
  end
89
87
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grumlin
4
- VERSION = "0.22.4"
4
+ VERSION = "1.0.0.rc1"
5
5
  end
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Vertex
5
- attr_reader :label, :id
3
+ class Grumlin::Vertex
4
+ attr_reader :label, :id
6
5
 
7
- def initialize(label:, id:)
8
- @label = label
9
- @id = Typing.cast(id)
10
- end
6
+ def initialize(label:, id:)
7
+ @label = label
8
+ @id = Grumlin::Typing.cast(id)
9
+ end
11
10
 
12
- def ==(other)
13
- self.class == other.class && @label == other.label && @id == other.id
14
- end
11
+ def ==(other)
12
+ self.class == other.class && @label == other.label && @id == other.id
13
+ end
15
14
 
16
- def inspect
17
- "v[#{@id}]"
18
- end
15
+ def inspect
16
+ "v[#{@id}]"
17
+ end
19
18
 
20
- def to_s
21
- inspect
22
- end
19
+ def to_s
20
+ inspect
23
21
  end
24
22
  end
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class VertexProperty
5
- attr_reader :label, :value
3
+ class Grumlin::VertexProperty
4
+ attr_reader :label, :value
6
5
 
7
- def initialize(value)
8
- @label = value[:label]
9
- @value = Typing.cast(value[:value])
10
- end
6
+ def initialize(value)
7
+ @label = value[:label]
8
+ @value = Grumlin::Typing.cast(value[:value])
9
+ end
11
10
 
12
- def inspect
13
- "vp[#{label}->#{value}]"
14
- end
11
+ def inspect
12
+ "vp[#{label}->#{value}]"
13
+ end
15
14
 
16
- def to_s
17
- inspect
18
- end
15
+ def to_s
16
+ inspect
17
+ end
19
18
 
20
- def ==(other)
21
- self.class == other.class && @label == other.label && @value == other.value
22
- end
19
+ def ==(other)
20
+ self.class == other.class && @label == other.label && @value == other.value
23
21
  end
24
22
  end
@@ -1,27 +1,25 @@
1
1
  # frozen_string_literal: true
2
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
3
+ module Grumlin::WithExtension
4
+ def with(name, value)
5
+ prev = self
6
+ strategy = if is_a?(with_step_class)
7
+ prev = previous_step
8
+ Grumlin::TraversalStrategies::OptionsStrategy.new(args.first.value.merge(name => value))
9
+ else
10
+ Grumlin::TraversalStrategies::OptionsStrategy.new({ name => value })
11
+ end
12
+ with_step_class.new(:withStrategies, args: [strategy], previous_step: prev)
13
+ end
15
14
 
16
- private
15
+ private
17
16
 
18
- def with_action_class
19
- @with_action_class ||= Class.new(shortcuts.action_class) do
20
- include WithExtension
17
+ def with_step_class
18
+ @with_step_class ||= Class.new(shortcuts.step_class) do
19
+ include Grumlin::WithExtension
21
20
 
22
- def with_action_class
23
- self.class
24
- end
21
+ def with_step_class
22
+ self.class
25
23
  end
26
24
  end
27
25
  end