callable_tree 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+
5
+ HooksSample =
6
+ CallableTree::Node::Internal::Builder
7
+ .new
8
+ .hookable
9
+ .build
10
+
11
+ CallableTree::Node::Root.new.append(
12
+ HooksSample
13
+ .new
14
+ .append(
15
+ # anonymous external node
16
+ lambda do |input, **_options|
17
+ puts "external input: #{input}"
18
+ input * 2
19
+ end
20
+ )
21
+ .before_matcher do |input, **_options|
22
+ puts "before_matcher input: #{input}"
23
+ input + 1
24
+ end
25
+ .around_matcher do |input, **_options, &block|
26
+ puts "around_matcher input: #{input}"
27
+ matched = block.call
28
+ puts "around_matcher matched: #{matched}"
29
+ !matched
30
+ end
31
+ .after_matcher do |matched, **_options|
32
+ puts "after_matcher matched: #{matched}"
33
+ !matched
34
+ end
35
+ .before_caller do |input, **_options|
36
+ puts "before_caller input: #{input}"
37
+ input + 1
38
+ end
39
+ .around_caller do |input, **_options, &block|
40
+ puts "around_caller input: #{input}"
41
+ output = block.call
42
+ puts "around_caller output: #{output}"
43
+ output * input
44
+ end
45
+ .after_caller do |output, **_options|
46
+ puts "after_caller output: #{output}"
47
+ output * 2
48
+ end
49
+ .before_terminator do |output, *_inputs, **_options|
50
+ puts "before_terminator output: #{output}"
51
+ output + 1
52
+ end
53
+ .around_terminator do |output, *_inputs, **_options, &block|
54
+ puts "around_terminator output: #{output}"
55
+ terminated = block.call
56
+ puts "around_terminator terminated: #{terminated}"
57
+ !terminated
58
+ end
59
+ .after_terminator do |terminated, **_options|
60
+ puts "after_terminator terminated: #{terminated}"
61
+ !terminated
62
+ end
63
+ ).tap do |tree|
64
+ options = { foo: :bar }
65
+ output = tree.call(1, **options)
66
+ puts "result: #{output}"
67
+ end
@@ -17,9 +17,8 @@ JSONParser =
17
17
  block.call(json, **options)
18
18
  end
19
19
  end
20
- .terminator do
21
- true
22
- end
20
+ .terminator { true }
21
+ # .identifier { |_node_:| :as_you_like } # optional
23
22
  .hookable
24
23
  .build
25
24
 
@@ -35,9 +34,8 @@ XMLParser =
35
34
  block.call(REXML::Document.new(file), **options)
36
35
  end
37
36
  end
38
- .terminator do
39
- true
40
- end
37
+ .terminator { true }
38
+ # .identifier { |_node_:| :as_you_like } # optional
41
39
  .hookable
42
40
  .build
43
41
 
@@ -52,6 +50,7 @@ def build_json_scraper(type)
52
50
  .map { |element| [element['name'], element['emoji']] }
53
51
  .to_h
54
52
  end
53
+ # .identifier { |_node_:| :as_you_like } # optional
55
54
  .hookable
56
55
  .build
57
56
  end
@@ -72,6 +71,7 @@ def build_xml_scraper(type)
72
71
  .map { |element| [element['name'], element['emoji']] }
73
72
  .to_h
74
73
  end
74
+ # .identifier { |_node_:| :as_you_like } # optional
75
75
  .hookable
76
76
  .build
77
77
  end
@@ -79,35 +79,38 @@ end
79
79
  AnimalsXMLScraper = build_xml_scraper(:animals)
80
80
  FruitsXMLScraper = build_xml_scraper(:fruits)
81
81
 
82
- loggable = proc do |node|
83
- indent_size = 2
84
- blank = ' '
85
- list_style = '*'
86
-
87
- node.after_matcher! do |matched, _node_:, **|
88
- prefix = list_style.rjust(_node_.depth * indent_size - indent_size + list_style.length, blank)
89
- puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
90
- matched
91
- end
82
+ module Logging
83
+ INDENT_SIZE = 2
84
+ BLANK = ' '
85
+ LIST_STYLE = '*'
86
+ INPUT_LABEL = 'Input :'
87
+ OUTPUT_LABEL = 'Output:'
92
88
 
93
- if node.external?
94
- input_label = 'Input :'
95
- output_label = 'Output:'
89
+ def self.loggable(node)
90
+ node.after_matcher! do |matched, _node_:, **|
91
+ prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
92
+ puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
93
+ matched
94
+ end
96
95
 
97
- node
98
- .before_caller! do |input, *, _node_:, **|
99
- input_prefix = input_label.rjust(_node_.depth * indent_size + input_label.length, blank)
100
- puts "#{input_prefix} #{input}"
101
- input
102
- end
103
- .after_caller! do |output, _node_:, **|
104
- output_prefix = output_label.rjust(_node_.depth * indent_size + output_label.length, blank)
105
- puts "#{output_prefix} #{output}"
106
- output
107
- end
96
+ if node.external?
97
+ node
98
+ .before_caller! do |input, *, _node_:, **|
99
+ input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
100
+ puts "#{input_prefix} #{input}"
101
+ input
102
+ end
103
+ .after_caller! do |output, _node_:, **|
104
+ output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
105
+ puts "#{output_prefix} #{output}"
106
+ output
107
+ end
108
+ end
108
109
  end
109
110
  end
110
111
 
112
+ loggable = Logging.method(:loggable)
113
+
111
114
  tree = CallableTree::Node::Root.new.seekable.append(
112
115
  JSONParser.new.tap(&loggable).seekable.append(
113
116
  AnimalsJSONScraper.new.tap(&loggable).verbosify,
@@ -96,7 +96,7 @@ tree = CallableTree::Node::Root.new.append(
96
96
  )
97
97
  )
98
98
 
99
- Dir.glob("#{__dir__}/docs/*") do |file|
99
+ Dir.glob("#{__dir__}/../docs/*") do |file|
100
100
  options = { foo: :bar }
101
101
  pp tree.call(file, **options)
102
102
  puts '---'
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+
5
+ module Node
6
+ class HooksSample
7
+ include CallableTree::Node::Internal
8
+ prepend CallableTree::Node::Hooks::Matcher
9
+ prepend CallableTree::Node::Hooks::Caller
10
+ prepend CallableTree::Node::Hooks::Terminator
11
+ end
12
+ end
13
+
14
+ CallableTree::Node::Root.new.append(
15
+ Node::HooksSample
16
+ .new
17
+ .append(
18
+ # anonymous external node
19
+ lambda do |input, **_options|
20
+ puts "external input: #{input}"
21
+ input * 2
22
+ end
23
+ )
24
+ .before_matcher do |input, **_options|
25
+ puts "before_matcher input: #{input}"
26
+ input + 1
27
+ end
28
+ .around_matcher do |input, **_options, &block|
29
+ puts "around_matcher input: #{input}"
30
+ matched = block.call
31
+ puts "around_matcher matched: #{matched}"
32
+ !matched
33
+ end
34
+ .after_matcher do |matched, **_options|
35
+ puts "after_matcher matched: #{matched}"
36
+ !matched
37
+ end
38
+ .before_caller do |input, **_options|
39
+ puts "before_caller input: #{input}"
40
+ input + 1
41
+ end
42
+ .around_caller do |input, **_options, &block|
43
+ puts "around_caller input: #{input}"
44
+ output = block.call
45
+ puts "around_caller output: #{output}"
46
+ output * input
47
+ end
48
+ .after_caller do |output, **_options|
49
+ puts "after_caller output: #{output}"
50
+ output * 2
51
+ end
52
+ .before_terminator do |output, *_inputs, **_options|
53
+ puts "before_terminator output: #{output}"
54
+ output + 1
55
+ end
56
+ .around_terminator do |output, *_inputs, **_options, &block|
57
+ puts "around_terminator output: #{output}"
58
+ terminated = block.call
59
+ puts "around_terminator terminated: #{terminated}"
60
+ !terminated
61
+ end
62
+ .after_terminator do |terminated, **_options|
63
+ puts "after_terminator terminated: #{terminated}"
64
+ !terminated
65
+ end
66
+ ).tap do |tree|
67
+ options = { foo: :bar }
68
+ output = tree.call(1, **options)
69
+ puts "result: #{output}"
70
+ end
@@ -117,7 +117,7 @@ tree = CallableTree::Node::Root.new.append(
117
117
  )
118
118
  )
119
119
 
120
- Dir.glob("#{__dir__}/docs/*") do |file|
120
+ Dir.glob("#{__dir__}/../docs/*") do |file|
121
121
  options = { foo: :bar }
122
122
  pp tree.call(file, **options)
123
123
  puts '---'
@@ -96,7 +96,7 @@ tree = CallableTree::Node::Root.new.seekable.append(
96
96
  )
97
97
  )
98
98
 
99
- Dir.glob("#{__dir__}/docs/*") do |file|
99
+ Dir.glob("#{__dir__}/../docs/*") do |file|
100
100
  options = { foo: :bar }
101
101
  pp tree.call(file, **options)
102
102
  puts '---'
@@ -5,36 +5,6 @@ require 'json'
5
5
  require 'rexml/document'
6
6
 
7
7
  module Node
8
- module Logging
9
- INDENT_SIZE = 2
10
- BLANK = ' '
11
-
12
- module Match
13
- LIST_STYLE = '*'
14
-
15
- def match?(_input, **_options)
16
- super.tap do |matched|
17
- prefix = LIST_STYLE.rjust(depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
18
- puts "#{prefix} #{identity}: [matched: #{matched}]"
19
- end
20
- end
21
- end
22
-
23
- module Call
24
- INPUT_LABEL = 'Input :'
25
- OUTPUT_LABEL = 'Output:'
26
-
27
- def call(input, **_options)
28
- super.tap do |output|
29
- input_prefix = INPUT_LABEL.rjust(depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
30
- puts "#{input_prefix} #{input}"
31
- output_prefix = OUTPUT_LABEL.rjust(depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
32
- puts "#{output_prefix} #{output}"
33
- end
34
- end
35
- end
36
- end
37
-
38
8
  class Identity
39
9
  attr_reader :klass, :type
40
10
 
@@ -51,7 +21,7 @@ module Node
51
21
  module JSON
52
22
  class Parser
53
23
  include CallableTree::Node::Internal
54
- prepend Logging::Match
24
+ prepend CallableTree::Node::Hooks::Matcher
55
25
 
56
26
  def match?(input, **_options)
57
27
  File.extname(input) == '.json'
@@ -71,8 +41,8 @@ module Node
71
41
 
72
42
  class Scraper
73
43
  include CallableTree::Node::External
74
- prepend Logging::Match
75
- prepend Logging::Call
44
+ prepend CallableTree::Node::Hooks::Matcher
45
+ prepend CallableTree::Node::Hooks::Caller
76
46
 
77
47
  def initialize(type:)
78
48
  @type = type
@@ -97,7 +67,7 @@ module Node
97
67
  module XML
98
68
  class Parser
99
69
  include CallableTree::Node::Internal
100
- prepend Logging::Match
70
+ prepend CallableTree::Node::Hooks::Matcher
101
71
 
102
72
  def match?(input, **_options)
103
73
  File.extname(input) == '.xml'
@@ -116,8 +86,8 @@ module Node
116
86
 
117
87
  class Scraper
118
88
  include CallableTree::Node::External
119
- prepend Logging::Match
120
- prepend Logging::Call
89
+ prepend CallableTree::Node::Hooks::Matcher
90
+ prepend CallableTree::Node::Hooks::Caller
121
91
 
122
92
  def initialize(type:)
123
93
  @type = type
@@ -142,18 +112,50 @@ module Node
142
112
  end
143
113
  end
144
114
 
115
+ module Logging
116
+ INDENT_SIZE = 2
117
+ BLANK = ' '
118
+ LIST_STYLE = '*'
119
+ INPUT_LABEL = 'Input :'
120
+ OUTPUT_LABEL = 'Output:'
121
+
122
+ def self.loggable(node)
123
+ node.after_matcher! do |matched, _node_:, **|
124
+ prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
125
+ puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
126
+ matched
127
+ end
128
+
129
+ if node.external?
130
+ node
131
+ .before_caller! do |input, *, _node_:, **|
132
+ input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
133
+ puts "#{input_prefix} #{input}"
134
+ input
135
+ end
136
+ .after_caller! do |output, _node_:, **|
137
+ output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
138
+ puts "#{output_prefix} #{output}"
139
+ output
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ loggable = Logging.method(:loggable)
146
+
145
147
  tree = CallableTree::Node::Root.new.append(
146
- Node::JSON::Parser.new.append(
147
- Node::JSON::Scraper.new(type: :animals).verbosify,
148
- Node::JSON::Scraper.new(type: :fruits).verbosify
148
+ Node::JSON::Parser.new.tap(&loggable).append(
149
+ Node::JSON::Scraper.new(type: :animals).tap(&loggable).verbosify,
150
+ Node::JSON::Scraper.new(type: :fruits).tap(&loggable).verbosify
149
151
  ),
150
- Node::XML::Parser.new.append(
151
- Node::XML::Scraper.new(type: :animals).verbosify,
152
- Node::XML::Scraper.new(type: :fruits).verbosify
152
+ Node::XML::Parser.new.tap(&loggable).append(
153
+ Node::XML::Scraper.new(type: :animals).tap(&loggable).verbosify,
154
+ Node::XML::Scraper.new(type: :fruits).tap(&loggable).verbosify
153
155
  )
154
156
  )
155
157
 
156
- Dir.glob("#{__dir__}/docs/*") do |file|
158
+ Dir.glob("#{__dir__}/../docs/*") do |file|
157
159
  options = { foo: :bar }
158
160
  pp tree.call(file, **options)
159
161
  puts '---'
@@ -24,6 +24,11 @@ module CallableTree
24
24
  self
25
25
  end
26
26
 
27
+ def identifier(&block)
28
+ @identifier = block
29
+ self
30
+ end
31
+
27
32
  def hookable(hookable = true)
28
33
  @hookable = hookable
29
34
  self
@@ -33,6 +38,7 @@ module CallableTree
33
38
  matcher = @matcher
34
39
  caller = @caller
35
40
  terminator = @terminator
41
+ identifier = @identifier
36
42
  hookable = @hookable
37
43
 
38
44
  validate(
@@ -47,6 +53,7 @@ module CallableTree
47
53
  if hookable
48
54
  prepend Hooks::Matcher
49
55
  prepend Hooks::Caller
56
+ prepend Hooks::Terminator
50
57
  end
51
58
 
52
59
  if matcher
@@ -72,6 +79,12 @@ module CallableTree
72
79
  end
73
80
  end
74
81
  end
82
+
83
+ if identifier
84
+ define_method(:identity) do
85
+ identifier.call(_node_: self) { super() }
86
+ end
87
+ end
75
88
  end
76
89
  end
77
90
 
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Hooks
6
+ module Terminator
7
+ def self.included(_subclass)
8
+ raise ::CallableTree::Error, "#{self} must be prepended"
9
+ end
10
+
11
+ def before_terminator(&block)
12
+ clone.before_terminator!(&block)
13
+ end
14
+
15
+ def before_terminator!(&block)
16
+ before_terminator_callbacks << block
17
+ self
18
+ end
19
+
20
+ def around_terminator(&block)
21
+ clone.around_terminator!(&block)
22
+ end
23
+
24
+ def around_terminator!(&block)
25
+ around_terminator_callbacks << block
26
+ self
27
+ end
28
+
29
+ def after_terminator(&block)
30
+ clone.after_terminator!(&block)
31
+ end
32
+
33
+ def after_terminator!(&block)
34
+ after_terminator_callbacks << block
35
+ self
36
+ end
37
+
38
+ def terminate?(output, *inputs, **options)
39
+ output = before_terminator_callbacks.reduce(output) do |output, callable|
40
+ callable.call(output, *inputs, **options, _node_: self)
41
+ end
42
+
43
+ terminated =
44
+ if around_terminator_callbacks.empty?
45
+ super(output, *inputs, **options)
46
+ else
47
+ around_terminator_callbacks_head, *around_terminator_callbacks_tail = around_terminator_callbacks
48
+ terminator = proc { super(output, *inputs, **options) }
49
+
50
+ terminated =
51
+ around_terminator_callbacks_head
52
+ .call(
53
+ output,
54
+ *inputs,
55
+ **options,
56
+ _node_: self
57
+ ) { terminator.call }
58
+
59
+ around_terminator_callbacks_tail.reduce(terminated) do |terminated, callable|
60
+ callable.call(
61
+ output,
62
+ *inputs,
63
+ **options,
64
+ _node_: self
65
+ ) { terminated }
66
+ end
67
+ end
68
+
69
+ after_terminator_callbacks.reduce(terminated) do |terminated, callable|
70
+ callable.call(terminated, **options, _node_: self)
71
+ end
72
+ end
73
+
74
+ def before_terminator_callbacks
75
+ @before_terminator_callbacks ||= []
76
+ end
77
+
78
+ def around_terminator_callbacks
79
+ @around_terminator_callbacks ||= []
80
+ end
81
+
82
+ def after_terminator_callbacks
83
+ @after_terminator_callbacks ||= []
84
+ end
85
+
86
+ private
87
+
88
+ attr_writer :before_terminator_callbacks, :around_terminator_callbacks, :after_terminator_callbacks
89
+
90
+ def initialize_copy(_node)
91
+ super
92
+ self.before_terminator_callbacks = before_terminator_callbacks.map(&:itself)
93
+ self.around_terminator_callbacks = around_terminator_callbacks.map(&:itself)
94
+ self.after_terminator_callbacks = after_terminator_callbacks.map(&:itself)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -6,6 +6,7 @@ module CallableTree
6
6
  include Internal
7
7
  prepend Hooks::Matcher
8
8
  prepend Hooks::Caller
9
+ prepend Hooks::Terminator
9
10
 
10
11
  def self.inherited(subclass)
11
12
  raise ::CallableTree::Error, "#{subclass} cannot inherit #{self}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CallableTree
4
- VERSION = '0.3.7'
4
+ VERSION = '0.3.8'
5
5
  end
data/lib/callable_tree.rb CHANGED
@@ -9,6 +9,7 @@ require_relative 'callable_tree/version'
9
9
  require_relative 'callable_tree/node'
10
10
  require_relative 'callable_tree/node/hooks/matcher'
11
11
  require_relative 'callable_tree/node/hooks/caller'
12
+ require_relative 'callable_tree/node/hooks/terminator'
12
13
  require_relative 'callable_tree/node/internal/strategy'
13
14
  require_relative 'callable_tree/node/internal/strategy/broadcast'
14
15
  require_relative 'callable_tree/node/internal/strategy/seek'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: callable_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - jsmmr
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-09 00:00:00.000000000 Z
11
+ date: 2022-05-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Builds a tree by linking callable nodes. The nodes that match the conditions
14
14
  are called in a chain from the root node to the leaf node. These are like nested
@@ -23,6 +23,7 @@ files:
23
23
  - ".github/workflows/codeql-analysis.yml"
24
24
  - ".gitignore"
25
25
  - ".rspec"
26
+ - ".rubocop.yml"
26
27
  - ".ruby-version"
27
28
  - CHANGELOG.md
28
29
  - Gemfile
@@ -33,22 +34,22 @@ files:
33
34
  - bin/console
34
35
  - bin/setup
35
36
  - callable_tree.gemspec
36
- - examples/builder/hooks-caller.rb
37
+ - examples/builder/hooks.rb
37
38
  - examples/builder/internal-broadcastable.rb
38
39
  - examples/builder/internal-composable.rb
39
40
  - examples/builder/internal-seekable.rb
40
41
  - examples/builder/logging.rb
42
+ - examples/class/external-verbosify.rb
43
+ - examples/class/hooks.rb
44
+ - examples/class/identity.rb
45
+ - examples/class/internal-broadcastable.rb
46
+ - examples/class/internal-composable.rb
47
+ - examples/class/internal-seekable.rb
48
+ - examples/class/logging.rb
41
49
  - examples/docs/animals.json
42
50
  - examples/docs/animals.xml
43
51
  - examples/docs/fruits.json
44
52
  - examples/docs/fruits.xml
45
- - examples/external-verbosify.rb
46
- - examples/hooks-caller.rb
47
- - examples/identity.rb
48
- - examples/internal-broadcastable.rb
49
- - examples/internal-composable.rb
50
- - examples/internal-seekable.rb
51
- - examples/logging.rb
52
53
  - lib/callable_tree.rb
53
54
  - lib/callable_tree/node.rb
54
55
  - lib/callable_tree/node/builder.rb
@@ -57,6 +58,7 @@ files:
57
58
  - lib/callable_tree/node/external/verbose.rb
58
59
  - lib/callable_tree/node/hooks/caller.rb
59
60
  - lib/callable_tree/node/hooks/matcher.rb
61
+ - lib/callable_tree/node/hooks/terminator.rb
60
62
  - lib/callable_tree/node/internal.rb
61
63
  - lib/callable_tree/node/internal/builder.rb
62
64
  - lib/callable_tree/node/internal/strategy.rb
@@ -71,7 +73,8 @@ licenses:
71
73
  metadata:
72
74
  homepage_uri: https://github.com/jsmmr/ruby_callable_tree
73
75
  source_code_uri: https://github.com/jsmmr/ruby_callable_tree
74
- changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.7/CHANGELOG.md
76
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.8/CHANGELOG.md
77
+ rubygems_mfa_required: 'true'
75
78
  post_install_message:
76
79
  rdoc_options: []
77
80
  require_paths:
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'callable_tree'
4
-
5
- Root =
6
- CallableTree::Node::Internal::Builder
7
- .new
8
- .hookable
9
- .build
10
-
11
- Root
12
- .new
13
- .before_caller do |input, **_options|
14
- puts "before_caller input: #{input}"
15
- input + 1
16
- end
17
- .append(
18
- # anonymous external node
19
- lambda do |input, **_options|
20
- puts "external input: #{input}"
21
- input * 2
22
- end
23
- )
24
- .around_caller do |input, **_options, &block|
25
- puts "around_caller input: #{input}"
26
- output = block.call
27
- puts "around_caller output: #{output}"
28
- output * input
29
- end
30
- .after_caller do |output, **_options|
31
- puts "after_caller output: #{output}"
32
- output * 2
33
- end
34
- .tap do |tree|
35
- options = { foo: :bar }
36
- output = tree.call(1, **options)
37
- puts "result: #{output}"
38
- end