rspock 2.1.0 → 2.3.0

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/CLAUDE.md +2 -0
  3. data/.cursor/rules/base.mdc +35 -0
  4. data/.cursor/rules/best-practices.mdc +27 -0
  5. data/Gemfile.lock +11 -5
  6. data/README.md +90 -7
  7. data/lib/rspock/ast/comparison_to_assertion_transformation.rb +1 -1
  8. data/lib/rspock/ast/interaction_to_block_identity_assertion_transformation.rb +30 -0
  9. data/lib/rspock/ast/interaction_to_mocha_mock_transformation.rb +104 -0
  10. data/lib/rspock/ast/node.rb +97 -0
  11. data/lib/rspock/ast/parser/block.rb +88 -0
  12. data/lib/rspock/ast/parser/cleanup_block.rb +22 -0
  13. data/lib/rspock/ast/parser/expect_block.rb +26 -0
  14. data/lib/rspock/ast/parser/given_block.rb +22 -0
  15. data/lib/rspock/ast/parser/interaction_parser.rb +131 -0
  16. data/lib/rspock/ast/parser/test_method_parser.rb +103 -0
  17. data/lib/rspock/ast/parser/then_block.rb +29 -0
  18. data/lib/rspock/ast/parser/when_block.rb +22 -0
  19. data/lib/rspock/ast/parser/where_block.rb +94 -0
  20. data/lib/rspock/ast/test_method_transformation.rb +114 -115
  21. data/lib/rspock/ast/transformation.rb +16 -24
  22. data/lib/rspock/backtrace_filter.rb +1 -1
  23. data/lib/rspock/helpers/block_capture.rb +41 -0
  24. data/lib/rspock/version.rb +1 -1
  25. data/rspock.gemspec +1 -0
  26. metadata +32 -12
  27. data/lib/rspock/ast/block.rb +0 -95
  28. data/lib/rspock/ast/cleanup_block.rb +0 -16
  29. data/lib/rspock/ast/end_block.rb +0 -17
  30. data/lib/rspock/ast/expect_block.rb +0 -21
  31. data/lib/rspock/ast/given_block.rb +0 -16
  32. data/lib/rspock/ast/interaction_transformation.rb +0 -148
  33. data/lib/rspock/ast/start_block.rb +0 -28
  34. data/lib/rspock/ast/then_block.rb +0 -34
  35. data/lib/rspock/ast/when_block.rb +0 -16
  36. data/lib/rspock/ast/where_block.rb +0 -86
@@ -1,37 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
  require 'ast_transform/abstract_transformation'
3
- require 'rspock/ast/start_block'
4
- require 'rspock/ast/given_block'
5
- require 'rspock/ast/when_block'
6
- require 'rspock/ast/then_block'
7
- require 'rspock/ast/expect_block'
8
- require 'rspock/ast/cleanup_block'
9
- require 'rspock/ast/where_block'
10
- require 'rspock/ast/end_block'
3
+ require 'rspock/ast/parser/given_block'
4
+ require 'rspock/ast/parser/when_block'
5
+ require 'rspock/ast/parser/then_block'
6
+ require 'rspock/ast/parser/expect_block'
7
+ require 'rspock/ast/parser/cleanup_block'
8
+ require 'rspock/ast/parser/where_block'
11
9
  require 'rspock/ast/test_method_transformation'
12
10
 
13
11
  module RSpock
14
12
  module AST
15
13
  class Transformation < ASTTransform::AbstractTransformation
16
- DefaultSourceMap = {
17
- Given: RSpock::AST::GivenBlock,
18
- When: RSpock::AST::WhenBlock,
19
- Then: RSpock::AST::ThenBlock,
20
- Expect: RSpock::AST::ExpectBlock,
21
- Cleanup: RSpock::AST::CleanupBlock,
22
- Where: RSpock::AST::WhereBlock,
14
+ DEFAULT_BLOCK_REGISTRY = {
15
+ Given: Parser::GivenBlock,
16
+ When: Parser::WhenBlock,
17
+ Then: Parser::ThenBlock,
18
+ Expect: Parser::ExpectBlock,
19
+ Cleanup: Parser::CleanupBlock,
20
+ Where: Parser::WhereBlock,
23
21
  }.freeze
24
22
 
25
23
  def initialize(
26
- start_block_class: StartBlock,
27
- end_block_class: EndBlock,
28
- source_map: DefaultSourceMap,
24
+ block_registry: DEFAULT_BLOCK_REGISTRY,
29
25
  strict: true
30
26
  )
31
27
  super()
32
- @start_block_class = start_block_class
33
- @source_map = source_map
34
- @end_block_class = end_block_class
28
+ @block_registry = block_registry
35
29
  @strict = strict
36
30
  end
37
31
 
@@ -93,9 +87,7 @@ module RSpock
93
87
  end
94
88
 
95
89
  TestMethodTransformation.new(
96
- @source_map,
97
- @start_block_class,
98
- @end_block_class,
90
+ @block_registry,
99
91
  strict: @strict
100
92
  ).run(node)
101
93
  end
@@ -33,7 +33,7 @@ module RSpock
33
33
  return location unless source_map
34
34
 
35
35
  line_number = source_map.line(lineno) || '?'
36
- location.gsub(/#{ASTTransform.output_path}\/([\S]+):(\d+)/, "\\1:#{line_number}")
36
+ location.sub("#{file_path}:#{lineno}", "#{source_map.source_file_path}:#{line_number}")
37
37
  end
38
38
 
39
39
  private
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpock
4
+ module Helpers
5
+ module BlockCapture
6
+ # Installs a block-capture wrapper on +obj+ for +method_name+.
7
+ # Must be called AFTER Mocha's expects/stubs so the wrapper sits
8
+ # in front of whatever Mocha installed.
9
+ #
10
+ # Returns a lambda that, when called, returns the captured block
11
+ # (or nil if no block was passed).
12
+ def self.capture(obj, method_name)
13
+ state = { captured: nil }
14
+
15
+ if obj.respond_to?(method_name, true)
16
+ # Real objects or objects where Mocha defined the method on
17
+ # the singleton class. Prepend a module so we intercept the
18
+ # call before Mocha's stub (prepend wins over define_singleton_method).
19
+ s = state
20
+ capture_mod = Module.new do
21
+ define_method(method_name) do |*args, **kwargs, &blk|
22
+ s[:captured] = blk
23
+ super(*args, **kwargs, &blk)
24
+ end
25
+ end
26
+ obj.singleton_class.prepend(capture_mod)
27
+ else
28
+ # Mock objects where the method goes through method_missing.
29
+ original_mm = obj.method(:method_missing)
30
+ s = state
31
+ obj.define_singleton_method(:method_missing) do |name, *args, **kwargs, &blk|
32
+ s[:captured] = blk if name == method_name
33
+ original_mm.call(name, *args, **kwargs, &blk)
34
+ end
35
+ end
36
+
37
+ -> { state[:captured] }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module RSpock
2
- VERSION = "2.1.0"
2
+ VERSION = "2.3.0"
3
3
  end
data/rspock.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "minitest", "~> 5.14"
27
27
  spec.add_development_dependency "minitest-reporters", "~> 1.4"
28
28
  spec.add_development_dependency "pry", ">= 0.14"
29
+ spec.add_development_dependency "pry-byebug", "~> 3.9"
29
30
  spec.add_development_dependency "rake", "~> 13.0"
30
31
  spec.add_development_dependency "simplecov", "~> 0.22"
31
32
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean-Philippe Duchesne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-21 00:00:00.000000000 Z
11
+ date: 2026-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +185,9 @@ executables: []
171
185
  extensions: []
172
186
  extra_rdoc_files: []
173
187
  files:
188
+ - ".claude/CLAUDE.md"
189
+ - ".cursor/rules/base.mdc"
190
+ - ".cursor/rules/best-practices.mdc"
174
191
  - ".github/workflows/ci.yml"
175
192
  - ".github/workflows/release.yml"
176
193
  - ".gitignore"
@@ -190,25 +207,28 @@ files:
190
207
  - lib/generators/templates/rspock_initializer.rb
191
208
  - lib/minitest/rspock_plugin.rb
192
209
  - lib/rspock.rb
193
- - lib/rspock/ast/block.rb
194
- - lib/rspock/ast/cleanup_block.rb
195
210
  - lib/rspock/ast/comparison_to_assertion_transformation.rb
196
- - lib/rspock/ast/end_block.rb
197
- - lib/rspock/ast/expect_block.rb
198
- - lib/rspock/ast/given_block.rb
199
211
  - lib/rspock/ast/header_nodes_transformation.rb
200
- - lib/rspock/ast/interaction_transformation.rb
212
+ - lib/rspock/ast/interaction_to_block_identity_assertion_transformation.rb
213
+ - lib/rspock/ast/interaction_to_mocha_mock_transformation.rb
201
214
  - lib/rspock/ast/method_call_to_lvar_transformation.rb
202
- - lib/rspock/ast/start_block.rb
215
+ - lib/rspock/ast/node.rb
216
+ - lib/rspock/ast/parser/block.rb
217
+ - lib/rspock/ast/parser/cleanup_block.rb
218
+ - lib/rspock/ast/parser/expect_block.rb
219
+ - lib/rspock/ast/parser/given_block.rb
220
+ - lib/rspock/ast/parser/interaction_parser.rb
221
+ - lib/rspock/ast/parser/test_method_parser.rb
222
+ - lib/rspock/ast/parser/then_block.rb
223
+ - lib/rspock/ast/parser/when_block.rb
224
+ - lib/rspock/ast/parser/where_block.rb
203
225
  - lib/rspock/ast/test_method_def_transformation.rb
204
226
  - lib/rspock/ast/test_method_dstr_transformation.rb
205
227
  - lib/rspock/ast/test_method_transformation.rb
206
- - lib/rspock/ast/then_block.rb
207
228
  - lib/rspock/ast/transformation.rb
208
- - lib/rspock/ast/when_block.rb
209
- - lib/rspock/ast/where_block.rb
210
229
  - lib/rspock/backtrace_filter.rb
211
230
  - lib/rspock/declarative.rb
231
+ - lib/rspock/helpers/block_capture.rb
212
232
  - lib/rspock/minitest/backtrace_filter.rb
213
233
  - lib/rspock/railtie.rb
214
234
  - lib/rspock/tasks/rspock.rake
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
- module RSpock
3
- module AST
4
- class BlockError < StandardError; end
5
-
6
- class Block
7
- # Constructs a new Block.
8
- #
9
- # @param type [Symbol] The Block type.
10
- # @param node [Parser::AST::Node] The node associated to this Block.
11
- def initialize(type, node)
12
- @type = type
13
- @node = node
14
- @children = []
15
- @node_container = true
16
- end
17
-
18
- attr_reader :type, :node
19
-
20
- # Adds the given +child_node+ to this Block.
21
- #
22
- # @param child_node [Parser::AST::Node] The node to be added.
23
- #
24
- # @raise [BlockError] if this Block cannot contain other nodes.
25
- def <<(child_node)
26
- raise BlockError, succession_error_msg unless node_container?
27
-
28
- @children << child_node
29
- end
30
-
31
- # Adds the given +child_node+ to the beginning of this Block.
32
- #
33
- # @param child_node [Parser::AST::Node] The node to be added.
34
- #
35
- # @raise [BlockError] if this Block cannot contain other nodes.
36
- def unshift(child_node)
37
- raise BlockError, succession_error_msg unless node_container?
38
-
39
- @children.unshift(child_node)
40
- end
41
-
42
- # Checks whether this Block can contain other nodes.
43
- #
44
- # @return [Boolean] True if this Block can contain other nodes, false otherwise.
45
- def node_container?
46
- @node_container
47
- end
48
-
49
- # Sets whether this Block can contain other nodes.
50
- #
51
- # @param value [Boolean] True if this Block can contain other nodes, false otherwise.
52
- def node_container=(value)
53
- @node_container = value
54
- end
55
-
56
- # Retrieves the Parser::Source::Range for this Block.
57
- #
58
- # @return [Parser::Source::Range] The range.
59
- def range
60
- node&.loc&.expression || "?"
61
- end
62
-
63
- # Retrieves the valid successors for this Block.
64
- # Note: Defaults to [:End].
65
- #
66
- # @return [Array<Symbol>] This Block's successors.
67
- def successors
68
- @successors ||= [:End].freeze
69
- end
70
-
71
- # Retrieves the duped array of children AST nodes for this Block.
72
- #
73
- # @return [Array<Parser::AST::Node>] The children nodes.
74
- def children
75
- @children.dup
76
- end
77
-
78
- # Checks whether or not the given +block+ is a valid successor for this Block.
79
- #
80
- # @param block [Block] The candidate successor.
81
- #
82
- # @return [Boolean] True if the given block is a valid successor, false otherwise.
83
- def valid_successor?(block)
84
- successors.include?(block.type)
85
- end
86
-
87
- # Retrieves the error message for succession errors.
88
- #
89
- # @return [String] The error message.
90
- def succession_error_msg
91
- "Block #{type} @ #{range} must be followed by one of these Blocks: #{successors}"
92
- end
93
- end
94
- end
95
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
-
4
- module RSpock
5
- module AST
6
- class CleanupBlock < Block
7
- def initialize(node)
8
- super(:Cleanup, node)
9
- end
10
-
11
- def successors
12
- @successors ||= [:Where, :End].freeze
13
- end
14
- end
15
- end
16
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
-
4
- module RSpock
5
- module AST
6
- class EndBlock < Block
7
- def initialize
8
- super(:End, nil)
9
- @node_container = false
10
- end
11
-
12
- def successors
13
- @successors ||= [].freeze
14
- end
15
- end
16
- end
17
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
- require 'rspock/ast/comparison_to_assertion_transformation'
4
-
5
- module RSpock
6
- module AST
7
- class ExpectBlock < Block
8
- def initialize(node)
9
- super(:Expect, node)
10
- end
11
-
12
- def successors
13
- @successors ||= [:Cleanup, :Where, :End].freeze
14
- end
15
-
16
- def children
17
- super.map { |child| ComparisonToAssertionTransformation.new(:_test_index_, :_line_number_).run(child) }
18
- end
19
- end
20
- end
21
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
-
4
- module RSpock
5
- module AST
6
- class GivenBlock < Block
7
- def initialize(node)
8
- super(:Given, node)
9
- end
10
-
11
- def successors
12
- @successors ||= [:When, :Expect].freeze
13
- end
14
- end
15
- end
16
- end
@@ -1,148 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'ast_transform/abstract_transformation'
3
-
4
- module RSpock
5
- module AST
6
- class InteractionTransformation < ASTTransform::AbstractTransformation
7
- class InteractionError < RuntimeError; end
8
-
9
- def run(node)
10
- return node unless interaction_node?(node)
11
-
12
- parse_node(node)
13
- transform_node
14
- end
15
-
16
- def interaction_node?(node)
17
- return false if node.nil?
18
-
19
- node.type == :send && node.children[1] == :*
20
- end
21
-
22
- private
23
-
24
- ALLOWED_NODES = [:send, :lvar, :int]
25
- private_constant(:ALLOWED_NODES)
26
-
27
- def transform_node
28
- result = chain_call(@receiver_node, :expects, s(:sym, @message))
29
- result = chain_call(result, :with, *@arg_nodes) unless @arg_nodes.empty?
30
-
31
- if any_matcher_node?(@times_node)
32
- result = chain_call(result, :at_least, s(:int, 0))
33
- elsif ALLOWED_NODES.include?(@times_node.type)
34
- result = chain_call(result, :times, @times_node)
35
- elsif @times_node.type == :begin && @times_node.children[0]&.type == :irange
36
- min_node, max_node = @times_node.children[0].children
37
-
38
- result = transform_irange_node(result, min_node, max_node)
39
- elsif @times_node.type == :begin && @times_node.children[0]&.type == :erange
40
- min_node, max_node = @times_node.children[0].children
41
- max_node = chain_call(max_node, :-, s(:int, 1))
42
-
43
- result = transform_erange_node(result, min_node, max_node)
44
- else
45
- raise ArgumentError, "Unrecognized times constraint in interaction: #{@times_node&.loc&.expression || "?"}"
46
- end
47
-
48
- result
49
- end
50
-
51
- def chain_call(receiver_node, method_name, *arg_nodes)
52
- s(:send, receiver_node, method_name, *arg_nodes)
53
- end
54
-
55
- def transform_irange_node(receiver_node, min_node, max_node)
56
- result = receiver_node
57
-
58
- if any_matcher_node?(min_node) && any_matcher_node?(max_node)
59
- result = chain_call(result, :at_least, s(:int, 0))
60
- elsif !any_matcher_node?(min_node) && any_matcher_node?(max_node)
61
- result = chain_call(result, :at_least, min_node)
62
- elsif any_matcher_node?(min_node) && !any_matcher_node?(max_node)
63
- result = chain_call(result, :at_least, s(:int, 0))
64
- result = chain_call(result, :at_most, max_node)
65
- elsif !any_matcher_node?(min_node) && !any_matcher_node?(max_node)
66
- result = chain_call(result, :at_least, min_node)
67
- result = chain_call(result, :at_most, max_node)
68
- end
69
-
70
- result
71
- end
72
-
73
- def transform_erange_node(receiver_node, min_node, max_node)
74
- result = receiver_node
75
-
76
- if any_matcher_node?(min_node) && any_matcher_node?(max_node.children[0])
77
- result = chain_call(result, :at_least, s(:int, 0))
78
- elsif !any_matcher_node?(min_node) && any_matcher_node?(max_node.children[0])
79
- result = chain_call(result, :at_least, min_node)
80
- elsif any_matcher_node?(min_node) && !any_matcher_node?(max_node.children[0])
81
- result = chain_call(result, :at_least, s(:int, 0))
82
- result = chain_call(result, :at_most, max_node)
83
- elsif !any_matcher_node?(min_node) && !any_matcher_node?(max_node.children[0])
84
- result = chain_call(result, :at_least, min_node)
85
- result = chain_call(result, :at_most, max_node)
86
- end
87
-
88
- result
89
- end
90
-
91
- def any_matcher_node?(node)
92
- node.type == :send && node.children[0].nil? && node.children[1] == :_
93
- end
94
-
95
- def parse_node(node)
96
- parse_lhs(node.children[0])
97
- parse_rhs(node.children[2])
98
- end
99
-
100
- def parse_lhs(node)
101
- @times_node = node
102
-
103
- case @times_node.type
104
- when *ALLOWED_NODES
105
- # OK
106
- when :begin
107
- if node.children.count > 1
108
- raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
109
- end
110
- case node.children[0].type
111
- when :irange, :erange
112
- unless ALLOWED_NODES.include?(node.children[0].children[0].type)
113
- raise_lhs_error(node.children[0].children[0], msg_prefix: "Minimum range of ")
114
- end
115
- unless ALLOWED_NODES.include?(node.children[0].children[1].type)
116
- raise_lhs_error(node.children[0].children[1], msg_prefix: "Maximum range of ")
117
- end
118
- else
119
- raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
120
- end
121
- else
122
- raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
123
- end
124
- end
125
-
126
- def parse_rhs(node)
127
- if node.type != :send
128
- raise InteractionError, "Right-hand side of Interaction @ #{range(node)} must be a :send node."
129
- end
130
-
131
- @receiver_node, @message, *@arg_nodes = node.children
132
-
133
- if @receiver_node.nil?
134
- raise InteractionError, "Right-hand side of Interaction @ #{range(node)} must have a receiver."
135
- end
136
- end
137
-
138
- def range(node)
139
- node&.loc&.expression || "?"
140
- end
141
-
142
- def raise_lhs_error(node, msg_prefix: "", msg_suffix: "")
143
- raise InteractionError, "#{msg_prefix}Interaction @ #{range(node)} must be one of "\
144
- "#{ALLOWED_NODES}#{msg_suffix}."
145
- end
146
- end
147
- end
148
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
-
4
- module RSpock
5
- module AST
6
- class StartBlock < Block
7
- def initialize(node)
8
- super(:Start, node)
9
- @node_container = false
10
- end
11
-
12
- def successors
13
- if @children.empty?
14
- SUCCESSORS_WITHOUT_CHILDREN
15
- else
16
- SUCCESSORS_WITH_CHILDREN
17
- end
18
- end
19
-
20
- def succession_error_msg
21
- "Test method @ #{range} must start with one of these Blocks: #{successors}"
22
- end
23
-
24
- SUCCESSORS_WITHOUT_CHILDREN = [:Given, :When, :Expect].freeze
25
- SUCCESSORS_WITH_CHILDREN = [:End].freeze
26
- end
27
- end
28
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
- require 'rspock/ast/comparison_to_assertion_transformation'
4
- require 'rspock/ast/interaction_transformation'
5
-
6
- module RSpock
7
- module AST
8
- class ThenBlock < Block
9
- def initialize(node)
10
- super(:Then, node)
11
- end
12
-
13
- def successors
14
- @successors ||= [:Cleanup, :Where, :End].freeze
15
- end
16
-
17
- def children
18
- super.reject { |child| interaction_transformation.interaction_node?(child) }
19
- .map { |child| ComparisonToAssertionTransformation.new(:_test_index_, :_line_number_).run(child) }
20
- end
21
-
22
- def interactions
23
- @children.select { |child| interaction_transformation.interaction_node?(child) }
24
- .map { |child| interaction_transformation.run(child) }
25
- end
26
-
27
- private
28
-
29
- def interaction_transformation
30
- @interaction_transformation ||= RSpock::AST::InteractionTransformation.new
31
- end
32
- end
33
- end
34
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'rspock/ast/block'
3
-
4
- module RSpock
5
- module AST
6
- class WhenBlock < Block
7
- def initialize(node)
8
- super(:When, node)
9
- end
10
-
11
- def successors
12
- @successors ||= [:Then].freeze
13
- end
14
- end
15
- end
16
- end