callable_tree 0.3.5 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
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