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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +17 -0
  5. data/Gemfile.lock +2 -2
  6. data/README.md +366 -108
  7. data/callable_tree.gemspec +1 -0
  8. data/examples/builder/hooks.rb +67 -0
  9. data/examples/builder/internal-seekable.rb +69 -73
  10. data/examples/builder/logging.rb +129 -0
  11. data/examples/{external-verbosify.rb → class/external-verbosify.rb} +1 -1
  12. data/examples/class/hooks.rb +70 -0
  13. data/examples/{identity.rb → class/identity.rb} +1 -1
  14. data/examples/{internal-broadcastable.rb → class/internal-broadcastable.rb} +0 -0
  15. data/examples/{internal-composable.rb → class/internal-composable.rb} +0 -0
  16. data/examples/{internal-seekable.rb → class/internal-seekable.rb} +1 -1
  17. data/examples/{logging.rb → class/logging.rb} +45 -43
  18. data/lib/callable_tree/node/builder.rb +20 -4
  19. data/lib/callable_tree/node/external/builder.rb +1 -1
  20. data/lib/callable_tree/node/external/verbose.rb +1 -1
  21. data/lib/callable_tree/node/external.rb +15 -0
  22. data/lib/callable_tree/node/hooks/caller.rb +110 -0
  23. data/lib/callable_tree/node/hooks/matcher.rb +101 -0
  24. data/lib/callable_tree/node/hooks/terminator.rb +99 -0
  25. data/lib/callable_tree/node/internal.rb +27 -12
  26. data/lib/callable_tree/node/root.rb +3 -1
  27. data/lib/callable_tree/node.rb +8 -0
  28. data/lib/callable_tree/version.rb +1 -1
  29. data/lib/callable_tree.rb +3 -1
  30. metadata +17 -12
  31. data/examples/builder/hooks-call.rb +0 -38
  32. data/examples/hooks-call.rb +0 -39
  33. data/lib/callable_tree/node/hooks/call.rb +0 -81
@@ -5,6 +5,13 @@ module CallableTree
5
5
  module External
6
6
  include Node
7
7
 
8
+ def self.included(mod)
9
+ if mod.include?(Internal)
10
+ raise ::CallableTree::Error,
11
+ "#{mod} cannot include #{self} together with #{Internal}"
12
+ end
13
+ end
14
+
8
15
  def self.proxify(callable)
9
16
  Proxy.new(callable)
10
17
  end
@@ -41,6 +48,14 @@ module CallableTree
41
48
  { identity => nil }
42
49
  end
43
50
 
51
+ def internal?
52
+ false
53
+ end
54
+
55
+ def external?
56
+ true
57
+ end
58
+
44
59
  private
45
60
 
46
61
  def initialize_copy(_node)
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Hooks
6
+ module Caller
7
+ def self.included(_subclass)
8
+ raise ::CallableTree::Error, "#{self} must be prepended"
9
+ end
10
+
11
+ def before_call(&block)
12
+ clone.before_call!(&block)
13
+ end
14
+
15
+ def before_call!(&block)
16
+ before_caller_callbacks << block
17
+ self
18
+ end
19
+
20
+ def around_call(&block)
21
+ clone.around_call!(&block)
22
+ end
23
+
24
+ def around_call!(&block)
25
+ around_caller_callbacks << block
26
+ self
27
+ end
28
+
29
+ def after_call(&block)
30
+ clone.after_call!(&block)
31
+ end
32
+
33
+ def after_call!(&block)
34
+ after_caller_callbacks << block
35
+ self
36
+ end
37
+
38
+ alias before_caller before_call
39
+ alias before_caller! before_call!
40
+ alias around_caller around_call
41
+ alias around_caller! around_call!
42
+ alias after_caller after_call
43
+ alias after_caller! after_call!
44
+
45
+ def call(*inputs, **options)
46
+ input_head, *input_tail = inputs
47
+
48
+ input_head = before_caller_callbacks.reduce(input_head) do |input_head, callable|
49
+ callable.call(input_head, *input_tail, **options, _node_: self)
50
+ end
51
+
52
+ output =
53
+ if around_caller_callbacks.empty?
54
+ super(input_head, *input_tail, **options)
55
+ else
56
+ around_caller_callbacks_head, *around_caller_callbacks_tail = around_caller_callbacks
57
+ caller = proc { super(input_head, *input_tail, **options) }
58
+
59
+ output =
60
+ around_caller_callbacks_head
61
+ .call(
62
+ input_head,
63
+ *input_tail,
64
+ **options,
65
+ _node_: self
66
+ ) { caller.call }
67
+
68
+ around_caller_callbacks_tail.reduce(output) do |output, callable|
69
+ callable.call(
70
+ input_head,
71
+ *input_tail,
72
+ **options,
73
+ _node_: self
74
+ ) { output }
75
+ end
76
+ end
77
+
78
+ after_caller_callbacks.reduce(output) do |output, callable|
79
+ callable.call(output, **options, _node_: self)
80
+ end
81
+ end
82
+
83
+ def before_caller_callbacks
84
+ @before_caller_callbacks ||= []
85
+ end
86
+
87
+ def around_caller_callbacks
88
+ @around_caller_callbacks ||= []
89
+ end
90
+
91
+ def after_caller_callbacks
92
+ @after_caller_callbacks ||= []
93
+ end
94
+
95
+ private
96
+
97
+ attr_writer :before_caller_callbacks, :around_caller_callbacks, :after_caller_callbacks
98
+
99
+ def initialize_copy(_node)
100
+ super
101
+ self.before_caller_callbacks = before_caller_callbacks.map(&:itself)
102
+ self.around_caller_callbacks = around_caller_callbacks.map(&:itself)
103
+ self.after_caller_callbacks = after_caller_callbacks.map(&:itself)
104
+ end
105
+ end
106
+
107
+ Call = Caller # backward compatibility
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Hooks
6
+ module Matcher
7
+ def self.included(_subclass)
8
+ raise ::CallableTree::Error, "#{self} must be prepended"
9
+ end
10
+
11
+ def before_matcher(&block)
12
+ clone.before_matcher!(&block)
13
+ end
14
+
15
+ def before_matcher!(&block)
16
+ before_matcher_callbacks << block
17
+ self
18
+ end
19
+
20
+ def around_matcher(&block)
21
+ clone.around_matcher!(&block)
22
+ end
23
+
24
+ def around_matcher!(&block)
25
+ around_matcher_callbacks << block
26
+ self
27
+ end
28
+
29
+ def after_matcher(&block)
30
+ clone.after_matcher!(&block)
31
+ end
32
+
33
+ def after_matcher!(&block)
34
+ after_matcher_callbacks << block
35
+ self
36
+ end
37
+
38
+ def match?(*inputs, **options)
39
+ input_head, *input_tail = inputs
40
+
41
+ input_head = before_matcher_callbacks.reduce(input_head) do |input_head, callable|
42
+ callable.call(input_head, *input_tail, **options, _node_: self)
43
+ end
44
+
45
+ matched =
46
+ if around_matcher_callbacks.empty?
47
+ super(input_head, *input_tail, **options)
48
+ else
49
+ around_matcher_callbacks_head, *around_matcher_callbacks_tail = around_matcher_callbacks
50
+ matcher = proc { super(input_head, *input_tail, **options) }
51
+
52
+ matched =
53
+ around_matcher_callbacks_head
54
+ .call(
55
+ input_head,
56
+ *input_tail,
57
+ **options,
58
+ _node_: self
59
+ ) { matcher.call }
60
+
61
+ around_matcher_callbacks_tail.reduce(matched) do |matched, callable|
62
+ callable.call(
63
+ input_head,
64
+ *input_tail,
65
+ **options,
66
+ _node_: self
67
+ ) { matched }
68
+ end
69
+ end
70
+
71
+ after_matcher_callbacks.reduce(matched) do |matched, callable|
72
+ callable.call(matched, **options, _node_: self)
73
+ end
74
+ end
75
+
76
+ def before_matcher_callbacks
77
+ @before_matcher_callbacks ||= []
78
+ end
79
+
80
+ def around_matcher_callbacks
81
+ @around_matcher_callbacks ||= []
82
+ end
83
+
84
+ def after_matcher_callbacks
85
+ @after_matcher_callbacks ||= []
86
+ end
87
+
88
+ private
89
+
90
+ attr_writer :before_matcher_callbacks, :around_matcher_callbacks, :after_matcher_callbacks
91
+
92
+ def initialize_copy(_node)
93
+ super
94
+ self.before_matcher_callbacks = before_matcher_callbacks.map(&:itself)
95
+ self.around_matcher_callbacks = around_matcher_callbacks.map(&:itself)
96
+ self.after_matcher_callbacks = after_matcher_callbacks.map(&:itself)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -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,13 @@ module CallableTree
6
6
  extend ::Forwardable
7
7
  include Node
8
8
 
9
+ def self.included(mod)
10
+ if mod.include?(External)
11
+ raise ::CallableTree::Error,
12
+ "#{mod} cannot include #{self} together with #{External}"
13
+ end
14
+ end
15
+
9
16
  def_delegators :child_nodes, :[], :at
10
17
 
11
18
  def children
@@ -35,7 +42,7 @@ module CallableTree
35
42
  if recursive
36
43
  child_nodes
37
44
  .lazy
38
- .select { |node| node.is_a?(Internal) }
45
+ .select { |node| node.internal? }
39
46
  .map { |node| node.find(recursive: true, &block) }
40
47
  .reject(&:nil?)
41
48
  .first
@@ -51,7 +58,7 @@ module CallableTree
51
58
 
52
59
  if recursive
53
60
  child_nodes.each do |node|
54
- node.reject!(recursive: true, &block) if node.is_a?(Internal)
61
+ node.reject!(recursive: true, &block) if node.internal?
55
62
  end
56
63
  end
57
64
 
@@ -66,7 +73,7 @@ module CallableTree
66
73
  reject!(&block) if block_given?
67
74
 
68
75
  reject! do |node|
69
- node.is_a?(Internal) && node.shake!(&block).child_nodes.empty?
76
+ node.internal? && node.shake!(&block).child_nodes.empty?
70
77
  end
71
78
  end
72
79
 
@@ -98,9 +105,9 @@ module CallableTree
98
105
  end
99
106
  end
100
107
 
101
- alias_method :seekable?, :seek?
102
- alias_method :seekable, :seek
103
- alias_method :seekable!, :seek!
108
+ alias seekable? seek?
109
+ alias seekable seek
110
+ alias seekable! seek!
104
111
 
105
112
  def broadcast?
106
113
  strategy.is_a?(Strategy::Broadcast)
@@ -122,9 +129,9 @@ module CallableTree
122
129
  end
123
130
  end
124
131
 
125
- alias_method :broadcastable?, :broadcast?
126
- alias_method :broadcastable, :broadcast
127
- alias_method :broadcastable!, :broadcast!
132
+ alias broadcastable? broadcast?
133
+ alias broadcastable broadcast
134
+ alias broadcastable! broadcast!
128
135
 
129
136
  def compose?
130
137
  strategy.is_a?(Strategy::Compose)
@@ -146,9 +153,9 @@ module CallableTree
146
153
  end
147
154
  end
148
155
 
149
- alias_method :composable?, :compose?
150
- alias_method :composable, :compose
151
- alias_method :composable!, :compose!
156
+ alias composable? compose?
157
+ alias composable compose
158
+ alias composable! compose!
152
159
 
153
160
  def outline(&block)
154
161
  key = block ? block.call(self) : identity
@@ -156,6 +163,14 @@ module CallableTree
156
163
  { key => value }
157
164
  end
158
165
 
166
+ def internal?
167
+ true
168
+ end
169
+
170
+ def external?
171
+ false
172
+ end
173
+
159
174
  protected
160
175
 
161
176
  attr_writer :strategy
@@ -4,7 +4,9 @@ module CallableTree
4
4
  module Node
5
5
  class Root
6
6
  include Internal
7
- prepend Hooks::Call
7
+ prepend Hooks::Matcher
8
+ prepend Hooks::Caller
9
+ prepend Hooks::Terminator
8
10
 
9
11
  def self.inherited(subclass)
10
12
  raise ::CallableTree::Error, "#{subclass} cannot inherit #{self}"
@@ -46,6 +46,14 @@ module CallableTree
46
46
  !output.nil?
47
47
  end
48
48
 
49
+ def internal?
50
+ raise ::CallableTree::Error, 'Not implemented'
51
+ end
52
+
53
+ def external?
54
+ raise ::CallableTree::Error, 'Not implemented'
55
+ end
56
+
49
57
  protected
50
58
 
51
59
  attr_writer :parent
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CallableTree
4
- VERSION = '0.3.5'
4
+ VERSION = '0.3.8'
5
5
  end
data/lib/callable_tree.rb CHANGED
@@ -7,7 +7,9 @@ end
7
7
  require 'forwardable'
8
8
  require_relative 'callable_tree/version'
9
9
  require_relative 'callable_tree/node'
10
- require_relative 'callable_tree/node/hooks/call'
10
+ require_relative 'callable_tree/node/hooks/matcher'
11
+ require_relative 'callable_tree/node/hooks/caller'
12
+ require_relative 'callable_tree/node/hooks/terminator'
11
13
  require_relative 'callable_tree/node/internal/strategy'
12
14
  require_relative 'callable_tree/node/internal/strategy/broadcast'
13
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.5
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-03-20 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,28 +34,31 @@ files:
33
34
  - bin/console
34
35
  - bin/setup
35
36
  - callable_tree.gemspec
36
- - examples/builder/hooks-call.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
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
40
49
  - examples/docs/animals.json
41
50
  - examples/docs/animals.xml
42
51
  - examples/docs/fruits.json
43
52
  - examples/docs/fruits.xml
44
- - examples/external-verbosify.rb
45
- - examples/hooks-call.rb
46
- - examples/identity.rb
47
- - examples/internal-broadcastable.rb
48
- - examples/internal-composable.rb
49
- - examples/internal-seekable.rb
50
- - examples/logging.rb
51
53
  - lib/callable_tree.rb
52
54
  - lib/callable_tree/node.rb
53
55
  - lib/callable_tree/node/builder.rb
54
56
  - lib/callable_tree/node/external.rb
55
57
  - lib/callable_tree/node/external/builder.rb
56
58
  - lib/callable_tree/node/external/verbose.rb
57
- - lib/callable_tree/node/hooks/call.rb
59
+ - lib/callable_tree/node/hooks/caller.rb
60
+ - lib/callable_tree/node/hooks/matcher.rb
61
+ - lib/callable_tree/node/hooks/terminator.rb
58
62
  - lib/callable_tree/node/internal.rb
59
63
  - lib/callable_tree/node/internal/builder.rb
60
64
  - lib/callable_tree/node/internal/strategy.rb
@@ -69,7 +73,8 @@ licenses:
69
73
  metadata:
70
74
  homepage_uri: https://github.com/jsmmr/ruby_callable_tree
71
75
  source_code_uri: https://github.com/jsmmr/ruby_callable_tree
72
- changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.5/CHANGELOG.md
76
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.8/CHANGELOG.md
77
+ rubygems_mfa_required: 'true'
73
78
  post_install_message:
74
79
  rdoc_options: []
75
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_call do |input, **_options|
14
- puts "before_call 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_call do |input, **_options, &block|
25
- puts "around_call input: #{input}"
26
- output = block.call
27
- puts "around_call output: #{output}"
28
- output * input
29
- end
30
- .after_call do |output, **_options|
31
- puts "after_call 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
@@ -1,39 +0,0 @@
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::Call
9
- end
10
- end
11
-
12
- Node::HooksSample
13
- .new
14
- .before_call do |input, **_options|
15
- puts "before_call input: #{input}"
16
- input + 1
17
- end
18
- .append(
19
- # anonymous external node
20
- lambda do |input, **_options|
21
- puts "external input: #{input}"
22
- input * 2
23
- end
24
- )
25
- .around_call do |input, **_options, &block|
26
- puts "around_call input: #{input}"
27
- output = block.call
28
- puts "around_call output: #{output}"
29
- output * input
30
- end
31
- .after_call do |output, **_options|
32
- puts "after_call output: #{output}"
33
- output * 2
34
- end
35
- .tap do |tree|
36
- options = { foo: :bar }
37
- output = tree.call(1, **options)
38
- puts "result: #{output}"
39
- end