callable_tree 0.3.7 → 0.3.8

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.
@@ -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