callable_tree 0.3.4 → 0.3.7

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.
@@ -14,7 +14,13 @@ module CallableTree
14
14
  end
15
15
 
16
16
  def terminater(&block)
17
- @terminater = block
17
+ warn 'Use CallableTree::Node::Internal::Builder#terminator instead.'
18
+ @terminator = block
19
+ self
20
+ end
21
+
22
+ def terminator(&block)
23
+ @terminator = block
18
24
  self
19
25
  end
20
26
 
@@ -26,23 +32,26 @@ module CallableTree
26
32
  def build(node_type:)
27
33
  matcher = @matcher
28
34
  caller = @caller
29
- terminater = @terminater
35
+ terminator = @terminator
30
36
  hookable = @hookable
31
37
 
32
38
  validate(
33
39
  matcher: matcher,
34
40
  caller: caller,
35
- terminater: terminater
41
+ terminator: terminator
36
42
  )
37
43
 
38
44
  ::Class
39
45
  .new do
40
46
  include node_type
41
- prepend Hooks::Call if hookable
47
+ if hookable
48
+ prepend Hooks::Matcher
49
+ prepend Hooks::Caller
50
+ end
42
51
 
43
52
  if matcher
44
53
  define_method(:match?) do |*inputs, **options|
45
- matcher.call(*inputs, **options) do |*inputs, **options|
54
+ matcher.call(*inputs, **options, _node_: self) do |*inputs, **options|
46
55
  super(*inputs, **options)
47
56
  end
48
57
  end
@@ -50,15 +59,15 @@ module CallableTree
50
59
 
51
60
  if caller
52
61
  define_method(:call) do |*inputs, **options|
53
- caller.call(*inputs, **options) do |*inputs, **options|
62
+ caller.call(*inputs, **options, _node_: self) do |*inputs, **options|
54
63
  super(*inputs, **options)
55
64
  end
56
65
  end
57
66
  end
58
67
 
59
- if terminater
68
+ if terminator
60
69
  define_method(:terminate?) do |output, *inputs, **options|
61
- terminater.call(output, *inputs, **options) do |output, *inputs, **options|
70
+ terminator.call(output, *inputs, **options, _node_: self) do |output, *inputs, **options|
62
71
  super(output, *inputs, **options)
63
72
  end
64
73
  end
@@ -68,7 +77,7 @@ module CallableTree
68
77
 
69
78
  private
70
79
 
71
- def validate(matcher:, caller:, terminater:)
80
+ def validate(matcher:, caller:, terminator:)
72
81
  raise ::CallableTree::Error, 'Not implemented'
73
82
  end
74
83
  end
@@ -14,8 +14,8 @@ module CallableTree
14
14
 
15
15
  private
16
16
 
17
- def validate(matcher:, caller:, terminater:)
18
- raise Error 'caller is required' unless caller
17
+ def validate(matcher:, caller:, terminator:)
18
+ raise Error, 'caller is required' unless caller
19
19
  end
20
20
  end
21
21
  end
@@ -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)
@@ -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
@@ -12,7 +12,7 @@ module CallableTree
12
12
 
13
13
  private
14
14
 
15
- def validate(matcher:, caller:, terminater:)
15
+ def validate(matcher:, caller:, terminator:)
16
16
  # noop
17
17
  end
18
18
  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,6 +105,10 @@ module CallableTree
98
105
  end
99
106
  end
100
107
 
108
+ alias seekable? seek?
109
+ alias seekable seek
110
+ alias seekable! seek!
111
+
101
112
  def broadcast?
102
113
  strategy.is_a?(Strategy::Broadcast)
103
114
  end
@@ -118,6 +129,10 @@ module CallableTree
118
129
  end
119
130
  end
120
131
 
132
+ alias broadcastable? broadcast?
133
+ alias broadcastable broadcast
134
+ alias broadcastable! broadcast!
135
+
121
136
  def compose?
122
137
  strategy.is_a?(Strategy::Compose)
123
138
  end
@@ -138,12 +153,24 @@ module CallableTree
138
153
  end
139
154
  end
140
155
 
156
+ alias composable? compose?
157
+ alias composable compose
158
+ alias composable! compose!
159
+
141
160
  def outline(&block)
142
161
  key = block ? block.call(self) : identity
143
162
  value = child_nodes.reduce({}) { |memo, node| memo.merge!(node.outline(&block)) }
144
163
  { key => value }
145
164
  end
146
165
 
166
+ def internal?
167
+ true
168
+ end
169
+
170
+ def external?
171
+ false
172
+ end
173
+
147
174
  protected
148
175
 
149
176
  attr_writer :strategy
@@ -4,7 +4,8 @@ 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
8
9
 
9
10
  def self.inherited(subclass)
10
11
  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.4'
4
+ VERSION = '0.3.7'
5
5
  end
data/lib/callable_tree.rb CHANGED
@@ -7,7 +7,8 @@ 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'
11
12
  require_relative 'callable_tree/node/internal/strategy'
12
13
  require_relative 'callable_tree/node/internal/strategy/broadcast'
13
14
  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.4
4
+ version: 0.3.7
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-13 00:00:00.000000000 Z
11
+ date: 2022-04-09 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
@@ -33,20 +33,21 @@ files:
33
33
  - bin/console
34
34
  - bin/setup
35
35
  - callable_tree.gemspec
36
- - examples/builder/hooks-call.rb
37
- - examples/builder/internal-broadcast.rb
38
- - examples/builder/internal-compose.rb
39
- - examples/builder/internal-seek.rb
36
+ - examples/builder/hooks-caller.rb
37
+ - examples/builder/internal-broadcastable.rb
38
+ - examples/builder/internal-composable.rb
39
+ - examples/builder/internal-seekable.rb
40
+ - examples/builder/logging.rb
40
41
  - examples/docs/animals.json
41
42
  - examples/docs/animals.xml
42
43
  - examples/docs/fruits.json
43
44
  - examples/docs/fruits.xml
44
45
  - examples/external-verbosify.rb
45
- - examples/hooks-call.rb
46
+ - examples/hooks-caller.rb
46
47
  - examples/identity.rb
47
- - examples/internal-broadcast.rb
48
- - examples/internal-compose.rb
49
- - examples/internal-seek.rb
48
+ - examples/internal-broadcastable.rb
49
+ - examples/internal-composable.rb
50
+ - examples/internal-seekable.rb
50
51
  - examples/logging.rb
51
52
  - lib/callable_tree.rb
52
53
  - lib/callable_tree/node.rb
@@ -54,7 +55,8 @@ files:
54
55
  - lib/callable_tree/node/external.rb
55
56
  - lib/callable_tree/node/external/builder.rb
56
57
  - lib/callable_tree/node/external/verbose.rb
57
- - lib/callable_tree/node/hooks/call.rb
58
+ - lib/callable_tree/node/hooks/caller.rb
59
+ - lib/callable_tree/node/hooks/matcher.rb
58
60
  - lib/callable_tree/node/internal.rb
59
61
  - lib/callable_tree/node/internal/builder.rb
60
62
  - lib/callable_tree/node/internal/strategy.rb
@@ -69,7 +71,7 @@ licenses:
69
71
  metadata:
70
72
  homepage_uri: https://github.com/jsmmr/ruby_callable_tree
71
73
  source_code_uri: https://github.com/jsmmr/ruby_callable_tree
72
- changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.4/CHANGELOG.md
74
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.7/CHANGELOG.md
73
75
  post_install_message:
74
76
  rdoc_options: []
75
77
  require_paths:
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'callable_tree'
4
- require 'json'
5
- require 'rexml/document'
6
-
7
- module JSONParser
8
- def self.build
9
- CallableTree::Node::Internal::Builder
10
- .new
11
- .matcher do |input, **_options|
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
- .terminater do
22
- true
23
- end
24
- .build
25
- end
26
- end
27
-
28
- module XMLParser
29
- def self.build
30
- CallableTree::Node::Internal::Builder
31
- .new
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
- .terminater do
42
- true
43
- end
44
- .build
45
- end
46
- end
47
-
48
- module JSONScraper
49
- def self.build(type)
50
- CallableTree::Node::External::Builder
51
- .new
52
- .matcher do |input, **_options|
53
- !!input[type.to_s]
54
- end
55
- .caller do |input, **_options|
56
- input[type.to_s]
57
- .map { |element| [element['name'], element['emoji']] }
58
- .to_h
59
- end
60
- .build
61
- end
62
- end
63
-
64
- module XMLScraper
65
- def self.build(type)
66
- CallableTree::Node::External::Builder
67
- .new
68
- .matcher do |input, **_options|
69
- !input.get_elements("//#{type}").empty?
70
- end
71
- .caller do |input, **_options|
72
- input
73
- .get_elements("//#{type}")
74
- .first
75
- .map { |element| [element['name'], element['emoji']] }
76
- .to_h
77
- end
78
- .build
79
- end
80
- end
81
-
82
- tree = CallableTree::Node::Root.new.append(
83
- JSONParser.build.new.append(
84
- JSONScraper.build(:animals).new,
85
- JSONScraper.build(:fruits).new
86
- ),
87
- XMLParser.build.new.append(
88
- XMLScraper.build(:animals).new,
89
- XMLScraper.build(:fruits).new
90
- )
91
- )
92
-
93
- Dir.glob("#{__dir__}/../docs/*") do |file|
94
- options = { foo: :bar }
95
- pp tree.call(file, **options)
96
- puts '---'
97
- end