callable_tree 0.3.7 → 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -55,8 +55,7 @@ module Node
55
55
 
56
56
  def call(input, **_options)
57
57
  input[@type.to_s]
58
- .map { |element| [element['name'], element['emoji']] }
59
- .to_h
58
+ .to_h { |element| [element['name'], element['emoji']] }
60
59
  end
61
60
  end
62
61
  end
@@ -99,8 +98,7 @@ module Node
99
98
  input
100
99
  .get_elements("//#{@type}")
101
100
  .first
102
- .map { |element| [element['name'], element['emoji']] }
103
- .to_h
101
+ .to_h { |element| [element['name'], element['emoji']] }
104
102
  end
105
103
  end
106
104
  end
@@ -117,7 +115,7 @@ tree = CallableTree::Node::Root.new.append(
117
115
  )
118
116
  )
119
117
 
120
- Dir.glob("#{__dir__}/docs/*") do |file|
118
+ Dir.glob("#{__dir__}/../docs/*") do |file|
121
119
  options = { foo: :bar }
122
120
  pp tree.call(file, **options)
123
121
  puts '---'
@@ -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.seekable.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 '---'
@@ -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
@@ -88,8 +58,7 @@ module Node
88
58
 
89
59
  def call(input, **_options)
90
60
  input[@type.to_s]
91
- .map { |element| [element['name'], element['emoji']] }
92
- .to_h
61
+ .to_h { |element| [element['name'], element['emoji']] }
93
62
  end
94
63
  end
95
64
  end
@@ -97,7 +66,7 @@ module Node
97
66
  module XML
98
67
  class Parser
99
68
  include CallableTree::Node::Internal
100
- prepend Logging::Match
69
+ prepend CallableTree::Node::Hooks::Matcher
101
70
 
102
71
  def match?(input, **_options)
103
72
  File.extname(input) == '.xml'
@@ -116,8 +85,8 @@ module Node
116
85
 
117
86
  class Scraper
118
87
  include CallableTree::Node::External
119
- prepend Logging::Match
120
- prepend Logging::Call
88
+ prepend CallableTree::Node::Hooks::Matcher
89
+ prepend CallableTree::Node::Hooks::Caller
121
90
 
122
91
  def initialize(type:)
123
92
  @type = type
@@ -135,25 +104,56 @@ module Node
135
104
  input
136
105
  .get_elements("//#{@type}")
137
106
  .first
138
- .map { |element| [element['name'], element['emoji']] }
139
- .to_h
107
+ .to_h { |element| [element['name'], element['emoji']] }
140
108
  end
141
109
  end
142
110
  end
143
111
  end
144
112
 
113
+ module Logging
114
+ INDENT_SIZE = 2
115
+ BLANK = ' '
116
+ LIST_STYLE = '*'
117
+ INPUT_LABEL = 'Input :'
118
+ OUTPUT_LABEL = 'Output:'
119
+
120
+ def self.loggable(node)
121
+ node.after_matcher! do |matched, _node_:, **|
122
+ prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
123
+ puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
124
+ matched
125
+ end
126
+
127
+ if node.external?
128
+ node
129
+ .before_caller! do |input, *, _node_:, **|
130
+ input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
131
+ puts "#{input_prefix} #{input}"
132
+ input
133
+ end
134
+ .after_caller! do |output, _node_:, **|
135
+ output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
136
+ puts "#{output_prefix} #{output}"
137
+ output
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ loggable = Logging.method(:loggable)
144
+
145
145
  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
146
+ Node::JSON::Parser.new.tap(&loggable).append(
147
+ Node::JSON::Scraper.new(type: :animals).tap(&loggable).verbosify,
148
+ Node::JSON::Scraper.new(type: :fruits).tap(&loggable).verbosify
149
149
  ),
150
- Node::XML::Parser.new.append(
151
- Node::XML::Scraper.new(type: :animals).verbosify,
152
- Node::XML::Scraper.new(type: :fruits).verbosify
150
+ Node::XML::Parser.new.tap(&loggable).append(
151
+ Node::XML::Scraper.new(type: :animals).tap(&loggable).verbosify,
152
+ Node::XML::Scraper.new(type: :fruits).tap(&loggable).verbosify
153
153
  )
154
154
  )
155
155
 
156
- Dir.glob("#{__dir__}/docs/*") do |file|
156
+ Dir.glob("#{__dir__}/../docs/*") do |file|
157
157
  options = { foo: :bar }
158
158
  pp tree.call(file, **options)
159
159
  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
@@ -7,9 +7,26 @@ module CallableTree
7
7
  class Broadcast
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: false)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
- nodes.map do |node|
12
- node.call(*inputs, **options) if node.match?(*inputs, **options)
21
+ nodes.reduce([]) do |outputs, node|
22
+ output = (node.call(*inputs, **options) if node.match?(*inputs, **options))
23
+ outputs << output
24
+
25
+ if @terminator.call(node, output, *inputs, **options)
26
+ break outputs
27
+ else
28
+ outputs
29
+ end
13
30
  end
14
31
  end
15
32
  end
@@ -7,12 +7,24 @@ module CallableTree
7
7
  class Compose
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: false)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
21
  head, *tail = inputs
12
22
  nodes.reduce(head) do |input, node|
13
- inputs = [input, *tail]
14
- if node.match?(*inputs, **options)
15
- node.call(*inputs, **options)
23
+ if node.match?(input, *tail, **options)
24
+ output = node.call(input, *tail, **options)
25
+ break output if @terminator.call(node, output, input, *tail, **options)
26
+
27
+ output
16
28
  else
17
29
  input
18
30
  end
@@ -7,13 +7,23 @@ module CallableTree
7
7
  class Seek
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: true)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
21
  nodes
12
22
  .lazy
13
23
  .select { |node| node.match?(*inputs, **options) }
14
24
  .map do |node|
15
25
  output = node.call(*inputs, **options)
16
- terminated = node.terminate?(output, *inputs, **options)
26
+ terminated = @terminator.call(node, output, *inputs, **options)
17
27
  [output, terminated]
18
28
  end
19
29
  .select { |_output, terminated| terminated }
@@ -13,7 +13,7 @@ module CallableTree
13
13
  end
14
14
 
15
15
  def ==(other)
16
- name == other.name
16
+ name == other.name && terminable? == other.terminable?
17
17
  end
18
18
 
19
19
  def eql?(other)
@@ -21,8 +21,16 @@ module CallableTree
21
21
  end
22
22
 
23
23
  def hash
24
- self.class.name.hash
24
+ [self.class.name, terminable].join('-').hash
25
25
  end
26
+
27
+ def terminable?
28
+ terminable
29
+ end
30
+
31
+ private
32
+
33
+ attr_accessor :terminable
26
34
  end
27
35
  end
28
36
  end
@@ -42,7 +42,7 @@ module CallableTree
42
42
  if recursive
43
43
  child_nodes
44
44
  .lazy
45
- .select { |node| node.internal? }
45
+ .select(&:internal?)
46
46
  .map { |node| node.find(recursive: true, &block) }
47
47
  .reject(&:nil?)
48
48
  .first
@@ -89,20 +89,18 @@ module CallableTree
89
89
  strategy.is_a?(Strategy::Seek)
90
90
  end
91
91
 
92
- def seek
93
- if seek?
92
+ def seek(terminable: true)
93
+ if strategy == Strategy::Seek.new(terminable: terminable)
94
94
  self
95
95
  else
96
- clone.tap do |node|
97
- node.strategy = Strategy::Seek.new
98
- end
96
+ clone.seek!(terminable: terminable)
99
97
  end
100
98
  end
101
99
 
102
- def seek!
103
- tap do |node|
104
- node.strategy = Strategy::Seek.new unless seek?
105
- end
100
+ def seek!(terminable: true)
101
+ self.strategy = Strategy::Seek.new(terminable: terminable)
102
+
103
+ self
106
104
  end
107
105
 
108
106
  alias seekable? seek?
@@ -113,20 +111,18 @@ module CallableTree
113
111
  strategy.is_a?(Strategy::Broadcast)
114
112
  end
115
113
 
116
- def broadcast
117
- if broadcast?
114
+ def broadcast(terminable: false)
115
+ if strategy == Strategy::Broadcast.new(terminable: terminable)
118
116
  self
119
117
  else
120
- clone.tap do |node|
121
- node.strategy = Strategy::Broadcast.new
122
- end
118
+ clone.broadcast!(terminable: terminable)
123
119
  end
124
120
  end
125
121
 
126
- def broadcast!
127
- tap do |node|
128
- node.strategy = Strategy::Broadcast.new unless broadcast?
129
- end
122
+ def broadcast!(terminable: false)
123
+ self.strategy = Strategy::Broadcast.new(terminable: terminable)
124
+
125
+ self
130
126
  end
131
127
 
132
128
  alias broadcastable? broadcast?
@@ -137,20 +133,18 @@ module CallableTree
137
133
  strategy.is_a?(Strategy::Compose)
138
134
  end
139
135
 
140
- def compose
141
- if compose?
136
+ def compose(terminable: false)
137
+ if strategy == Strategy::Compose.new(terminable: terminable)
142
138
  self
143
139
  else
144
- clone.tap do |node|
145
- node.strategy = Strategy::Compose.new
146
- end
140
+ clone.compose!(terminable: terminable)
147
141
  end
148
142
  end
149
143
 
150
- def compose!
151
- tap do |node|
152
- node.strategy = Strategy::Compose.new unless compose?
153
- end
144
+ def compose!(terminable: false)
145
+ self.strategy = Strategy::Compose.new(terminable: terminable)
146
+
147
+ self
154
148
  end
155
149
 
156
150
  alias composable? compose?
@@ -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.9'
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.9
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-11-06 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
@@ -19,10 +19,12 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - ".github/dependabot.yml"
22
23
  - ".github/workflows/build.yml"
23
24
  - ".github/workflows/codeql-analysis.yml"
24
25
  - ".gitignore"
25
26
  - ".rspec"
27
+ - ".rubocop.yml"
26
28
  - ".ruby-version"
27
29
  - CHANGELOG.md
28
30
  - Gemfile
@@ -33,22 +35,24 @@ files:
33
35
  - bin/console
34
36
  - bin/setup
35
37
  - callable_tree.gemspec
36
- - examples/builder/hooks-caller.rb
38
+ - examples/builder/external-verbosify.rb
39
+ - examples/builder/hooks.rb
40
+ - examples/builder/identity.rb
37
41
  - examples/builder/internal-broadcastable.rb
38
42
  - examples/builder/internal-composable.rb
39
43
  - examples/builder/internal-seekable.rb
40
44
  - examples/builder/logging.rb
45
+ - examples/class/external-verbosify.rb
46
+ - examples/class/hooks.rb
47
+ - examples/class/identity.rb
48
+ - examples/class/internal-broadcastable.rb
49
+ - examples/class/internal-composable.rb
50
+ - examples/class/internal-seekable.rb
51
+ - examples/class/logging.rb
41
52
  - examples/docs/animals.json
42
53
  - examples/docs/animals.xml
43
54
  - examples/docs/fruits.json
44
55
  - 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
56
  - lib/callable_tree.rb
53
57
  - lib/callable_tree/node.rb
54
58
  - lib/callable_tree/node/builder.rb
@@ -57,6 +61,7 @@ files:
57
61
  - lib/callable_tree/node/external/verbose.rb
58
62
  - lib/callable_tree/node/hooks/caller.rb
59
63
  - lib/callable_tree/node/hooks/matcher.rb
64
+ - lib/callable_tree/node/hooks/terminator.rb
60
65
  - lib/callable_tree/node/internal.rb
61
66
  - lib/callable_tree/node/internal/builder.rb
62
67
  - lib/callable_tree/node/internal/strategy.rb
@@ -71,7 +76,8 @@ licenses:
71
76
  metadata:
72
77
  homepage_uri: https://github.com/jsmmr/ruby_callable_tree
73
78
  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
79
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.9/CHANGELOG.md
80
+ rubygems_mfa_required: 'true'
75
81
  post_install_message:
76
82
  rdoc_options: []
77
83
  require_paths: