callable_tree 0.3.5 → 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.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +2 -2
- data/README.md +366 -108
- data/callable_tree.gemspec +1 -0
- data/examples/builder/hooks.rb +67 -0
- data/examples/builder/internal-seekable.rb +69 -73
- data/examples/builder/logging.rb +129 -0
- data/examples/{external-verbosify.rb → class/external-verbosify.rb} +1 -1
- data/examples/class/hooks.rb +70 -0
- data/examples/{identity.rb → class/identity.rb} +1 -1
- data/examples/{internal-broadcastable.rb → class/internal-broadcastable.rb} +0 -0
- data/examples/{internal-composable.rb → class/internal-composable.rb} +0 -0
- data/examples/{internal-seekable.rb → class/internal-seekable.rb} +1 -1
- data/examples/{logging.rb → class/logging.rb} +45 -43
- data/lib/callable_tree/node/builder.rb +20 -4
- data/lib/callable_tree/node/external/builder.rb +1 -1
- data/lib/callable_tree/node/external/verbose.rb +1 -1
- data/lib/callable_tree/node/external.rb +15 -0
- data/lib/callable_tree/node/hooks/caller.rb +110 -0
- data/lib/callable_tree/node/hooks/matcher.rb +101 -0
- data/lib/callable_tree/node/hooks/terminator.rb +99 -0
- data/lib/callable_tree/node/internal.rb +27 -12
- data/lib/callable_tree/node/root.rb +3 -1
- data/lib/callable_tree/node.rb +8 -0
- data/lib/callable_tree/version.rb +1 -1
- data/lib/callable_tree.rb +3 -1
- metadata +17 -12
- data/examples/builder/hooks-call.rb +0 -38
- data/examples/hooks-call.rb +0 -39
- data/lib/callable_tree/node/hooks/call.rb +0 -81
@@ -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
|
@@ -4,89 +4,85 @@ require 'callable_tree'
|
|
4
4
|
require 'json'
|
5
5
|
require 'rexml/document'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
File.extname(input) == '.json'
|
13
|
-
end
|
14
|
-
.caller do |input, **options, &block|
|
15
|
-
File.open(input) do |file|
|
16
|
-
json = ::JSON.load(file)
|
17
|
-
# The following block call is equivalent to calling `super` in the class style.
|
18
|
-
block.call(json, **options)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
.terminator do
|
22
|
-
true
|
23
|
-
end
|
24
|
-
.build
|
7
|
+
JSONParser =
|
8
|
+
CallableTree::Node::Internal::Builder
|
9
|
+
.new
|
10
|
+
.matcher do |input, **_options|
|
11
|
+
File.extname(input) == '.json'
|
25
12
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
.matcher do |input, **_options|
|
33
|
-
File.extname(input) == '.xml'
|
34
|
-
end
|
35
|
-
.caller do |input, **options, &block|
|
36
|
-
File.open(input) do |file|
|
37
|
-
# The following block call is equivalent to calling `super` in the class style.
|
38
|
-
block.call(REXML::Document.new(file), **options)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
.terminator do
|
42
|
-
true
|
43
|
-
end
|
44
|
-
.build
|
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
|
45
19
|
end
|
46
|
-
|
20
|
+
.terminator do
|
21
|
+
true
|
22
|
+
end
|
23
|
+
.build
|
47
24
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
.build
|
25
|
+
XMLParser =
|
26
|
+
CallableTree::Node::Internal::Builder
|
27
|
+
.new
|
28
|
+
.matcher do |input, **_options|
|
29
|
+
File.extname(input) == '.xml'
|
30
|
+
end
|
31
|
+
.caller do |input, **options, &block|
|
32
|
+
File.open(input) do |file|
|
33
|
+
# The following block call is equivalent to calling `super` in the class style.
|
34
|
+
block.call(REXML::Document.new(file), **options)
|
35
|
+
end
|
61
36
|
end
|
37
|
+
.terminator do
|
38
|
+
true
|
39
|
+
end
|
40
|
+
.build
|
41
|
+
|
42
|
+
def build_json_scraper(type)
|
43
|
+
CallableTree::Node::External::Builder
|
44
|
+
.new
|
45
|
+
.matcher do |input, **_options|
|
46
|
+
!!input[type.to_s]
|
47
|
+
end
|
48
|
+
.caller do |input, **_options|
|
49
|
+
input[type.to_s]
|
50
|
+
.map { |element| [element['name'], element['emoji']] }
|
51
|
+
.to_h
|
52
|
+
end
|
53
|
+
.build
|
62
54
|
end
|
63
55
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
.
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
56
|
+
AnimalsJSONScraper = build_json_scraper(:animals)
|
57
|
+
FruitsJSONScraper = build_json_scraper(:fruits)
|
58
|
+
|
59
|
+
def build_xml_scraper(type)
|
60
|
+
CallableTree::Node::External::Builder
|
61
|
+
.new
|
62
|
+
.matcher do |input, **_options|
|
63
|
+
!input.get_elements("//#{type}").empty?
|
64
|
+
end
|
65
|
+
.caller do |input, **_options|
|
66
|
+
input
|
67
|
+
.get_elements("//#{type}")
|
68
|
+
.first
|
69
|
+
.map { |element| [element['name'], element['emoji']] }
|
70
|
+
.to_h
|
71
|
+
end
|
72
|
+
.build
|
80
73
|
end
|
81
74
|
|
75
|
+
AnimalsXMLScraper = build_xml_scraper(:animals)
|
76
|
+
FruitsXMLScraper = build_xml_scraper(:fruits)
|
77
|
+
|
82
78
|
tree = CallableTree::Node::Root.new.seekable.append(
|
83
|
-
JSONParser.
|
84
|
-
|
85
|
-
|
79
|
+
JSONParser.new.seekable.append(
|
80
|
+
AnimalsJSONScraper.new,
|
81
|
+
FruitsJSONScraper.new
|
86
82
|
),
|
87
|
-
XMLParser.
|
88
|
-
|
89
|
-
|
83
|
+
XMLParser.new.seekable.append(
|
84
|
+
AnimalsXMLScraper.new,
|
85
|
+
FruitsXMLScraper.new
|
90
86
|
)
|
91
87
|
)
|
92
88
|
|
@@ -0,0 +1,129 @@
|
|
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_:| :as_you_like } # optional
|
22
|
+
.hookable
|
23
|
+
.build
|
24
|
+
|
25
|
+
XMLParser =
|
26
|
+
CallableTree::Node::Internal::Builder
|
27
|
+
.new
|
28
|
+
.matcher do |input, **_options|
|
29
|
+
File.extname(input) == '.xml'
|
30
|
+
end
|
31
|
+
.caller do |input, **options, &block|
|
32
|
+
File.open(input) do |file|
|
33
|
+
# The following block call is equivalent to calling `super` in the class style.
|
34
|
+
block.call(REXML::Document.new(file), **options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
.terminator { true }
|
38
|
+
# .identifier { |_node_:| :as_you_like } # optional
|
39
|
+
.hookable
|
40
|
+
.build
|
41
|
+
|
42
|
+
def build_json_scraper(type)
|
43
|
+
CallableTree::Node::External::Builder
|
44
|
+
.new
|
45
|
+
.matcher do |input, **_options|
|
46
|
+
!!input[type.to_s]
|
47
|
+
end
|
48
|
+
.caller do |input, **_options|
|
49
|
+
input[type.to_s]
|
50
|
+
.map { |element| [element['name'], element['emoji']] }
|
51
|
+
.to_h
|
52
|
+
end
|
53
|
+
# .identifier { |_node_:| :as_you_like } # optional
|
54
|
+
.hookable
|
55
|
+
.build
|
56
|
+
end
|
57
|
+
|
58
|
+
AnimalsJSONScraper = build_json_scraper(:animals)
|
59
|
+
FruitsJSONScraper = build_json_scraper(:fruits)
|
60
|
+
|
61
|
+
def build_xml_scraper(type)
|
62
|
+
CallableTree::Node::External::Builder
|
63
|
+
.new
|
64
|
+
.matcher do |input, **_options|
|
65
|
+
!input.get_elements("//#{type}").empty?
|
66
|
+
end
|
67
|
+
.caller do |input, **_options|
|
68
|
+
input
|
69
|
+
.get_elements("//#{type}")
|
70
|
+
.first
|
71
|
+
.map { |element| [element['name'], element['emoji']] }
|
72
|
+
.to_h
|
73
|
+
end
|
74
|
+
# .identifier { |_node_:| :as_you_like } # optional
|
75
|
+
.hookable
|
76
|
+
.build
|
77
|
+
end
|
78
|
+
|
79
|
+
AnimalsXMLScraper = build_xml_scraper(:animals)
|
80
|
+
FruitsXMLScraper = build_xml_scraper(:fruits)
|
81
|
+
|
82
|
+
module Logging
|
83
|
+
INDENT_SIZE = 2
|
84
|
+
BLANK = ' '
|
85
|
+
LIST_STYLE = '*'
|
86
|
+
INPUT_LABEL = 'Input :'
|
87
|
+
OUTPUT_LABEL = 'Output:'
|
88
|
+
|
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
|
95
|
+
|
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
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
loggable = Logging.method(:loggable)
|
113
|
+
|
114
|
+
tree = CallableTree::Node::Root.new.seekable.append(
|
115
|
+
JSONParser.new.tap(&loggable).seekable.append(
|
116
|
+
AnimalsJSONScraper.new.tap(&loggable).verbosify,
|
117
|
+
FruitsJSONScraper.new.tap(&loggable).verbosify
|
118
|
+
),
|
119
|
+
XMLParser.new.tap(&loggable).seekable.append(
|
120
|
+
AnimalsXMLScraper.new.tap(&loggable).verbosify,
|
121
|
+
FruitsXMLScraper.new.tap(&loggable).verbosify
|
122
|
+
)
|
123
|
+
)
|
124
|
+
|
125
|
+
Dir.glob("#{__dir__}/../docs/*") do |file|
|
126
|
+
options = { foo: :bar }
|
127
|
+
pp tree.call(file, **options)
|
128
|
+
puts '---'
|
129
|
+
end
|
@@ -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
|
File without changes
|
File without changes
|
@@ -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
|
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
|
75
|
-
prepend
|
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
|
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
|
120
|
-
prepend
|
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__}
|
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(
|
@@ -44,11 +50,15 @@ module CallableTree
|
|
44
50
|
::Class
|
45
51
|
.new do
|
46
52
|
include node_type
|
47
|
-
|
53
|
+
if hookable
|
54
|
+
prepend Hooks::Matcher
|
55
|
+
prepend Hooks::Caller
|
56
|
+
prepend Hooks::Terminator
|
57
|
+
end
|
48
58
|
|
49
59
|
if matcher
|
50
60
|
define_method(:match?) do |*inputs, **options|
|
51
|
-
matcher.call(*inputs, **options) do |*inputs, **options|
|
61
|
+
matcher.call(*inputs, **options, _node_: self) do |*inputs, **options|
|
52
62
|
super(*inputs, **options)
|
53
63
|
end
|
54
64
|
end
|
@@ -56,7 +66,7 @@ module CallableTree
|
|
56
66
|
|
57
67
|
if caller
|
58
68
|
define_method(:call) do |*inputs, **options|
|
59
|
-
caller.call(*inputs, **options) do |*inputs, **options|
|
69
|
+
caller.call(*inputs, **options, _node_: self) do |*inputs, **options|
|
60
70
|
super(*inputs, **options)
|
61
71
|
end
|
62
72
|
end
|
@@ -64,11 +74,17 @@ module CallableTree
|
|
64
74
|
|
65
75
|
if terminator
|
66
76
|
define_method(:terminate?) do |output, *inputs, **options|
|
67
|
-
terminator.call(output, *inputs, **options) do |output, *inputs, **options|
|
77
|
+
terminator.call(output, *inputs, **options, _node_: self) do |output, *inputs, **options|
|
68
78
|
super(output, *inputs, **options)
|
69
79
|
end
|
70
80
|
end
|
71
81
|
end
|
82
|
+
|
83
|
+
if identifier
|
84
|
+
define_method(:identity) do
|
85
|
+
identifier.call(_node_: self) { super() }
|
86
|
+
end
|
87
|
+
end
|
72
88
|
end
|
73
89
|
end
|
74
90
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module CallableTree
|
4
4
|
module Node
|
5
5
|
module External
|
6
|
-
# TODO: Add :inputs
|
7
6
|
Output = Struct.new(:value, :options, :routes)
|
8
7
|
|
9
8
|
module Verbose
|
@@ -13,6 +12,7 @@ module CallableTree
|
|
13
12
|
|
14
13
|
def call(*inputs, **options)
|
15
14
|
output = super(*inputs, **options)
|
15
|
+
options.delete(:_node_)
|
16
16
|
routes = self.routes
|
17
17
|
|
18
18
|
Output.new(output, options, routes)
|