callable_tree 0.3.4 → 0.3.7

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