callable_tree 0.3.7 → 0.3.9

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +7 -0
  3. data/.github/workflows/build.yml +3 -5
  4. data/.github/workflows/codeql-analysis.yml +4 -4
  5. data/.rubocop.yml +6 -0
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +12 -0
  8. data/Gemfile.lock +3 -3
  9. data/README.md +460 -276
  10. data/callable_tree.gemspec +1 -0
  11. data/examples/builder/external-verbosify.rb +87 -0
  12. data/examples/builder/hooks.rb +67 -0
  13. data/examples/builder/identity.rb +91 -0
  14. data/examples/builder/internal-broadcastable.rb +11 -11
  15. data/examples/builder/internal-composable.rb +11 -11
  16. data/examples/builder/internal-seekable.rb +9 -15
  17. data/examples/builder/logging.rb +36 -39
  18. data/examples/{external-verbosify.rb → class/external-verbosify.rb} +3 -5
  19. data/examples/class/hooks.rb +70 -0
  20. data/examples/{identity.rb → class/identity.rb} +3 -5
  21. data/examples/{internal-broadcastable.rb → class/internal-broadcastable.rb} +0 -0
  22. data/examples/{internal-composable.rb → class/internal-composable.rb} +0 -0
  23. data/examples/{internal-seekable.rb → class/internal-seekable.rb} +3 -5
  24. data/examples/{logging.rb → class/logging.rb} +47 -47
  25. data/lib/callable_tree/node/builder.rb +13 -0
  26. data/lib/callable_tree/node/hooks/terminator.rb +99 -0
  27. data/lib/callable_tree/node/internal/strategy/broadcast.rb +19 -2
  28. data/lib/callable_tree/node/internal/strategy/compose.rb +15 -3
  29. data/lib/callable_tree/node/internal/strategy/seek.rb +11 -1
  30. data/lib/callable_tree/node/internal/strategy.rb +10 -2
  31. data/lib/callable_tree/node/internal.rb +22 -28
  32. data/lib/callable_tree/node/root.rb +1 -0
  33. data/lib/callable_tree/version.rb +1 -1
  34. data/lib/callable_tree.rb +1 -0
  35. metadata +17 -11
  36. data/examples/builder/hooks-caller.rb +0 -38
  37. data/examples/hooks-caller.rb +0 -39
@@ -32,4 +32,5 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  # For more information and examples about making a new gem, checkout our
34
34
  # guide at: https://bundler.io/guides/creating_gem.html
35
+ spec.metadata['rubygems_mfa_required'] = 'true'
35
36
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+ require 'json'
5
+ require 'rexml/document'
6
+
7
+ JSONParser =
8
+ CallableTree::Node::Internal::Builder
9
+ .new
10
+ .matcher do |input, **_options|
11
+ File.extname(input) == '.json'
12
+ end
13
+ .caller do |input, **options, &block|
14
+ File.open(input) do |file|
15
+ json = ::JSON.load(file)
16
+ # The following block call is equivalent to calling `super` in the class style.
17
+ block.call(json, **options)
18
+ end
19
+ end
20
+ .terminator { true }
21
+ .build
22
+
23
+ XMLParser =
24
+ CallableTree::Node::Internal::Builder
25
+ .new
26
+ .matcher do |input, **_options|
27
+ File.extname(input) == '.xml'
28
+ end
29
+ .caller do |input, **options, &block|
30
+ File.open(input) do |file|
31
+ # The following block call is equivalent to calling `super` in the class style.
32
+ block.call(REXML::Document.new(file), **options)
33
+ end
34
+ end
35
+ .terminator { true }
36
+ .build
37
+
38
+ def build_json_scraper(type)
39
+ CallableTree::Node::External::Builder
40
+ .new
41
+ .matcher do |input, **_options|
42
+ !input[type.to_s].nil?
43
+ end
44
+ .caller do |input, **_options|
45
+ input[type.to_s]
46
+ .to_h { |element| [element['name'], element['emoji']] }
47
+ end
48
+ .build
49
+ end
50
+
51
+ AnimalsJSONScraper = build_json_scraper(:animals)
52
+ FruitsJSONScraper = build_json_scraper(:fruits)
53
+
54
+ def build_xml_scraper(type)
55
+ CallableTree::Node::External::Builder
56
+ .new
57
+ .matcher do |input, **_options|
58
+ !input.get_elements("//#{type}").empty?
59
+ end
60
+ .caller do |input, **_options|
61
+ input
62
+ .get_elements("//#{type}")
63
+ .first
64
+ .to_h { |element| [element['name'], element['emoji']] }
65
+ end
66
+ .build
67
+ end
68
+
69
+ AnimalsXMLScraper = build_xml_scraper(:animals)
70
+ FruitsXMLScraper = build_xml_scraper(:fruits)
71
+
72
+ tree = CallableTree::Node::Root.new.seekable.append(
73
+ JSONParser.new.seekable.append(
74
+ AnimalsJSONScraper.new.verbosify,
75
+ FruitsJSONScraper.new.verbosify
76
+ ),
77
+ XMLParser.new.seekable.append(
78
+ AnimalsXMLScraper.new.verbosify,
79
+ FruitsXMLScraper.new.verbosify
80
+ )
81
+ )
82
+
83
+ Dir.glob("#{__dir__}/../docs/*") do |file|
84
+ options = { foo: :bar }
85
+ pp tree.call(file, **options)
86
+ puts '---'
87
+ end
@@ -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
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+ require 'json'
5
+ require 'rexml/document'
6
+
7
+ JSONParser =
8
+ CallableTree::Node::Internal::Builder
9
+ .new
10
+ .matcher do |input, **_options|
11
+ File.extname(input) == '.json'
12
+ end
13
+ .caller do |input, **options, &block|
14
+ File.open(input) do |file|
15
+ json = ::JSON.load(file)
16
+ # The following block call is equivalent to calling `super` in the class style.
17
+ block.call(json, **options)
18
+ end
19
+ end
20
+ .terminator { true }
21
+ .identifier { |_node_:| _node_.object_id }
22
+ .build
23
+
24
+ XMLParser =
25
+ CallableTree::Node::Internal::Builder
26
+ .new
27
+ .matcher do |input, **_options|
28
+ File.extname(input) == '.xml'
29
+ end
30
+ .caller do |input, **options, &block|
31
+ File.open(input) do |file|
32
+ # The following block call is equivalent to calling `super` in the class style.
33
+ block.call(REXML::Document.new(file), **options)
34
+ end
35
+ end
36
+ .terminator { true }
37
+ .identifier { |_node_:| _node_.object_id }
38
+ .build
39
+
40
+ def build_json_scraper(type)
41
+ CallableTree::Node::External::Builder
42
+ .new
43
+ .matcher do |input, **_options|
44
+ !input[type.to_s].nil?
45
+ end
46
+ .caller do |input, **_options|
47
+ input[type.to_s]
48
+ .to_h { |element| [element['name'], element['emoji']] }
49
+ end
50
+ .identifier { |_node_:| _node_.object_id }
51
+ .build
52
+ end
53
+
54
+ AnimalsJSONScraper = build_json_scraper(:animals)
55
+ FruitsJSONScraper = build_json_scraper(:fruits)
56
+
57
+ def build_xml_scraper(type)
58
+ CallableTree::Node::External::Builder
59
+ .new
60
+ .matcher do |input, **_options|
61
+ !input.get_elements("//#{type}").empty?
62
+ end
63
+ .caller do |input, **_options|
64
+ input
65
+ .get_elements("//#{type}")
66
+ .first
67
+ .to_h { |element| [element['name'], element['emoji']] }
68
+ end
69
+ .identifier { |_node_:| _node_.object_id }
70
+ .build
71
+ end
72
+
73
+ AnimalsXMLScraper = build_xml_scraper(:animals)
74
+ FruitsXMLScraper = build_xml_scraper(:fruits)
75
+
76
+ tree = CallableTree::Node::Root.new.seekable.append(
77
+ JSONParser.new.seekable.append(
78
+ AnimalsJSONScraper.new.verbosify,
79
+ FruitsJSONScraper.new.verbosify
80
+ ),
81
+ XMLParser.new.seekable.append(
82
+ AnimalsXMLScraper.new.verbosify,
83
+ FruitsXMLScraper.new.verbosify
84
+ )
85
+ )
86
+
87
+ Dir.glob("#{__dir__}/../docs/*") do |file|
88
+ options = { foo: :bar }
89
+ pp tree.call(file, **options)
90
+ puts '---'
91
+ end
@@ -2,57 +2,57 @@
2
2
 
3
3
  require 'callable_tree'
4
4
 
5
- less_than = proc do |num|
5
+ def less_than(num)
6
6
  # The following block call is equivalent to calling `super` in the class style.
7
- proc { |input, &block| block.call(input) && input < num }
7
+ proc { |input, &original| original.call(input) && input < num }
8
8
  end
9
9
 
10
10
  LessThan5 =
11
11
  CallableTree::Node::Internal::Builder
12
12
  .new
13
- .matcher(&less_than.call(5))
13
+ .matcher(&method(:less_than).call(5))
14
14
  .build
15
15
 
16
16
  LessThan10 =
17
17
  CallableTree::Node::Internal::Builder
18
18
  .new
19
- .matcher(&less_than.call(10))
19
+ .matcher(&method(:less_than).call(10))
20
20
  .build
21
21
 
22
- add = proc do |num|
22
+ def add(num)
23
23
  proc { |input| input + num }
24
24
  end
25
25
 
26
26
  Add1 =
27
27
  CallableTree::Node::External::Builder
28
28
  .new
29
- .caller(&add.call(1))
29
+ .caller(&method(:add).call(1))
30
30
  .build
31
31
 
32
- subtract = proc do |num|
32
+ def subtract(num)
33
33
  proc { |input| input - num }
34
34
  end
35
35
 
36
36
  Subtract1 =
37
37
  CallableTree::Node::External::Builder
38
38
  .new
39
- .caller(&subtract.call(1))
39
+ .caller(&method(:subtract).call(1))
40
40
  .build
41
41
 
42
- multiply = proc do |num|
42
+ def multiply(num)
43
43
  proc { |input| input * num }
44
44
  end
45
45
 
46
46
  Multiply2 =
47
47
  CallableTree::Node::External::Builder
48
48
  .new
49
- .caller(&multiply.call(2))
49
+ .caller(&method(:multiply).call(2))
50
50
  .build
51
51
 
52
52
  Multiply3 =
53
53
  CallableTree::Node::External::Builder
54
54
  .new
55
- .caller(&multiply.call(3))
55
+ .caller(&method(:multiply).call(3))
56
56
  .build
57
57
 
58
58
  tree = CallableTree::Node::Root.new.broadcastable.append(
@@ -2,57 +2,57 @@
2
2
 
3
3
  require 'callable_tree'
4
4
 
5
- less_than = proc do |num|
5
+ def less_than(num)
6
6
  # The following block call is equivalent to calling `super` in the class style.
7
- proc { |input, &block| block.call(input) && input < num }
7
+ proc { |input, &original| original.call(input) && input < num }
8
8
  end
9
9
 
10
10
  LessThan5 =
11
11
  CallableTree::Node::Internal::Builder
12
12
  .new
13
- .matcher(&less_than.call(5))
13
+ .matcher(&method(:less_than).call(5))
14
14
  .build
15
15
 
16
16
  LessThan10 =
17
17
  CallableTree::Node::Internal::Builder
18
18
  .new
19
- .matcher(&less_than.call(10))
19
+ .matcher(&method(:less_than).call(10))
20
20
  .build
21
21
 
22
- add = proc do |num|
22
+ def add(num)
23
23
  proc { |input| input + num }
24
24
  end
25
25
 
26
26
  Add1 =
27
27
  CallableTree::Node::External::Builder
28
28
  .new
29
- .caller(&add.call(1))
29
+ .caller(&method(:add).call(1))
30
30
  .build
31
31
 
32
- subtract = proc do |num|
32
+ def subtract(num)
33
33
  proc { |input| input - num }
34
34
  end
35
35
 
36
36
  Subtract1 =
37
37
  CallableTree::Node::External::Builder
38
38
  .new
39
- .caller(&subtract.call(1))
39
+ .caller(&method(:subtract).call(1))
40
40
  .build
41
41
 
42
- multiply = proc do |num|
42
+ def multiply(num)
43
43
  proc { |input| input * num }
44
44
  end
45
45
 
46
46
  Multiply2 =
47
47
  CallableTree::Node::External::Builder
48
48
  .new
49
- .caller(&multiply.call(2))
49
+ .caller(&method(:multiply).call(2))
50
50
  .build
51
51
 
52
52
  Multiply3 =
53
53
  CallableTree::Node::External::Builder
54
54
  .new
55
- .caller(&multiply.call(3))
55
+ .caller(&method(:multiply).call(3))
56
56
  .build
57
57
 
58
58
  tree = CallableTree::Node::Root.new.composable.append(
@@ -10,16 +10,14 @@ JSONParser =
10
10
  .matcher do |input, **_options|
11
11
  File.extname(input) == '.json'
12
12
  end
13
- .caller do |input, **options, &block|
13
+ .caller do |input, **options, &original|
14
14
  File.open(input) do |file|
15
15
  json = ::JSON.load(file)
16
16
  # The following block call is equivalent to calling `super` in the class style.
17
- block.call(json, **options)
17
+ original.call(json, **options)
18
18
  end
19
19
  end
20
- .terminator do
21
- true
22
- end
20
+ .terminator { true }
23
21
  .build
24
22
 
25
23
  XMLParser =
@@ -28,27 +26,24 @@ XMLParser =
28
26
  .matcher do |input, **_options|
29
27
  File.extname(input) == '.xml'
30
28
  end
31
- .caller do |input, **options, &block|
29
+ .caller do |input, **options, &original|
32
30
  File.open(input) do |file|
33
31
  # The following block call is equivalent to calling `super` in the class style.
34
- block.call(REXML::Document.new(file), **options)
32
+ original.call(REXML::Document.new(file), **options)
35
33
  end
36
34
  end
37
- .terminator do
38
- true
39
- end
35
+ .terminator { true }
40
36
  .build
41
37
 
42
38
  def build_json_scraper(type)
43
39
  CallableTree::Node::External::Builder
44
40
  .new
45
41
  .matcher do |input, **_options|
46
- !!input[type.to_s]
42
+ !input[type.to_s].nil?
47
43
  end
48
44
  .caller do |input, **_options|
49
45
  input[type.to_s]
50
- .map { |element| [element['name'], element['emoji']] }
51
- .to_h
46
+ .to_h { |element| [element['name'], element['emoji']] }
52
47
  end
53
48
  .build
54
49
  end
@@ -66,8 +61,7 @@ def build_xml_scraper(type)
66
61
  input
67
62
  .get_elements("//#{type}")
68
63
  .first
69
- .map { |element| [element['name'], element['emoji']] }
70
- .to_h
64
+ .to_h { |element| [element['name'], element['emoji']] }
71
65
  end
72
66
  .build
73
67
  end
@@ -10,16 +10,14 @@ JSONParser =
10
10
  .matcher do |input, **_options|
11
11
  File.extname(input) == '.json'
12
12
  end
13
- .caller do |input, **options, &block|
13
+ .caller do |input, **options, &original|
14
14
  File.open(input) do |file|
15
15
  json = ::JSON.load(file)
16
16
  # The following block call is equivalent to calling `super` in the class style.
17
- block.call(json, **options)
17
+ original.call(json, **options)
18
18
  end
19
19
  end
20
- .terminator do
21
- true
22
- end
20
+ .terminator { true }
23
21
  .hookable
24
22
  .build
25
23
 
@@ -29,15 +27,13 @@ XMLParser =
29
27
  .matcher do |input, **_options|
30
28
  File.extname(input) == '.xml'
31
29
  end
32
- .caller do |input, **options, &block|
30
+ .caller do |input, **options, &original|
33
31
  File.open(input) do |file|
34
32
  # The following block call is equivalent to calling `super` in the class style.
35
- block.call(REXML::Document.new(file), **options)
33
+ original.call(REXML::Document.new(file), **options)
36
34
  end
37
35
  end
38
- .terminator do
39
- true
40
- end
36
+ .terminator { true }
41
37
  .hookable
42
38
  .build
43
39
 
@@ -45,12 +41,11 @@ def build_json_scraper(type)
45
41
  CallableTree::Node::External::Builder
46
42
  .new
47
43
  .matcher do |input, **_options|
48
- !!input[type.to_s]
44
+ !input[type.to_s].nil?
49
45
  end
50
46
  .caller do |input, **_options|
51
47
  input[type.to_s]
52
- .map { |element| [element['name'], element['emoji']] }
53
- .to_h
48
+ .to_h { |element| [element['name'], element['emoji']] }
54
49
  end
55
50
  .hookable
56
51
  .build
@@ -69,8 +64,7 @@ def build_xml_scraper(type)
69
64
  input
70
65
  .get_elements("//#{type}")
71
66
  .first
72
- .map { |element| [element['name'], element['emoji']] }
73
- .to_h
67
+ .to_h { |element| [element['name'], element['emoji']] }
74
68
  end
75
69
  .hookable
76
70
  .build
@@ -79,35 +73,38 @@ end
79
73
  AnimalsXMLScraper = build_xml_scraper(:animals)
80
74
  FruitsXMLScraper = build_xml_scraper(:fruits)
81
75
 
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
76
+ module Logging
77
+ INDENT_SIZE = 2
78
+ BLANK = ' '
79
+ LIST_STYLE = '*'
80
+ INPUT_LABEL = 'Input :'
81
+ OUTPUT_LABEL = 'Output:'
92
82
 
93
- if node.external?
94
- input_label = 'Input :'
95
- output_label = 'Output:'
83
+ def self.loggable(node)
84
+ node.after_matcher! do |matched, _node_:, **|
85
+ prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
86
+ puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
87
+ matched
88
+ end
96
89
 
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
90
+ if node.external?
91
+ node
92
+ .before_caller! do |input, *, _node_:, **|
93
+ input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
94
+ puts "#{input_prefix} #{input}"
95
+ input
96
+ end
97
+ .after_caller! do |output, _node_:, **|
98
+ output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
99
+ puts "#{output_prefix} #{output}"
100
+ output
101
+ end
102
+ end
108
103
  end
109
104
  end
110
105
 
106
+ loggable = Logging.method(:loggable)
107
+
111
108
  tree = CallableTree::Node::Root.new.seekable.append(
112
109
  JSONParser.new.tap(&loggable).seekable.append(
113
110
  AnimalsJSONScraper.new.tap(&loggable).verbosify,
@@ -38,8 +38,7 @@ module Node
38
38
 
39
39
  def call(input, **_options)
40
40
  input[@type.to_s]
41
- .map { |element| [element['name'], element['emoji']] }
42
- .to_h
41
+ .to_h { |element| [element['name'], element['emoji']] }
43
42
  end
44
43
  end
45
44
  end
@@ -78,8 +77,7 @@ module Node
78
77
  input
79
78
  .get_elements("//#{@type}")
80
79
  .first
81
- .map { |element| [element['name'], element['emoji']] }
82
- .to_h
80
+ .to_h { |element| [element['name'], element['emoji']] }
83
81
  end
84
82
  end
85
83
  end
@@ -96,7 +94,7 @@ tree = CallableTree::Node::Root.new.append(
96
94
  )
97
95
  )
98
96
 
99
- Dir.glob("#{__dir__}/docs/*") do |file|
97
+ Dir.glob("#{__dir__}/../docs/*") do |file|
100
98
  options = { foo: :bar }
101
99
  pp tree.call(file, **options)
102
100
  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