callable_tree 0.1.0

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.
@@ -0,0 +1,35 @@
1
+ require 'callable_tree'
2
+
3
+ module Node
4
+ class HooksSample
5
+ include CallableTree::Node::Internal
6
+ prepend CallableTree::Node::Hooks::Call
7
+ end
8
+ end
9
+
10
+ Node::HooksSample.new
11
+ .before_call do |input, **options|
12
+ puts "before_call input: #{input}";
13
+ input + 1
14
+ end
15
+ .append(
16
+ lambda do |input, **options|
17
+ puts "external input: #{input}"
18
+ input * 2
19
+ end
20
+ )
21
+ .around_call do |input, **options, &block|
22
+ puts "around_call input: #{input}"
23
+ output = block.call
24
+ puts "around_call output: #{output}"
25
+ output * input
26
+ end
27
+ .after_call do |output, **options|
28
+ puts "after_call output: #{output}"
29
+ output * 2
30
+ end
31
+ .tap do |tree|
32
+ options = { foo: :bar }
33
+ output = tree.call(1, **options)
34
+ puts "result: #{output}"
35
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ class Error < StandardError; end
5
+ end
6
+
7
+ require 'forwardable'
8
+ require_relative 'callable_tree/version'
9
+ require_relative 'callable_tree/node'
10
+ require_relative 'callable_tree/node/external/verbose'
11
+ require_relative 'callable_tree/node/external'
12
+ require_relative 'callable_tree/node/hooks/call'
13
+ require_relative 'callable_tree/node/internal'
14
+ require_relative 'callable_tree/node/root'
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ attr_reader :parent
6
+
7
+ def ancestors
8
+ ::Enumerator.new do |y|
9
+ node = self
10
+ loop do
11
+ y << node
12
+ break unless node = node&.parent
13
+ end
14
+ end
15
+ end
16
+
17
+ def routes
18
+ ancestors.map(&:identity)
19
+ end
20
+
21
+ def identity
22
+ self.class
23
+ end
24
+
25
+ def depth
26
+ parent.nil? ? 0 : parent.depth + 1
27
+ end
28
+
29
+ def match?(_input = nil, **_options)
30
+ true
31
+ end
32
+
33
+ def call(_input = nil, **_options)
34
+ raise ::CallableTree::Error, 'Not implemented'
35
+ end
36
+
37
+ def terminate?(output = nil, **_options)
38
+ !output.nil?
39
+ end
40
+
41
+ private
42
+
43
+ attr_writer :parent
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Branch
6
+ attr_reader :parent
7
+
8
+ def ancestors
9
+ ::Enumerator.new do |y|
10
+ node = self
11
+ while node = node&.parent
12
+ y << node
13
+ end
14
+ end
15
+ end
16
+
17
+ def routes
18
+ ::Enumerator.new do |y|
19
+ y << self.class
20
+ ancestors.each { |node| y << node.class }
21
+ end
22
+ end
23
+
24
+ def depth
25
+ parent.nil? ? 0 : parent.depth + 1
26
+ end
27
+
28
+ private
29
+
30
+ attr_writer :parent
31
+ end
32
+
33
+ private_constant :Branch
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module External
6
+ include Node
7
+
8
+ def self.proxify(callable)
9
+ Proxy.new(callable)
10
+ end
11
+
12
+ def self.proxified?(node)
13
+ node.is_a?(Proxy)
14
+ end
15
+
16
+ def self.unproxify(node)
17
+ node.callable
18
+ end
19
+
20
+ def verbosify
21
+ extend Verbose
22
+ self
23
+ end
24
+
25
+ def identity
26
+ if External.proxified?(self)
27
+ External.unproxify(self)
28
+ else
29
+ self
30
+ end
31
+ .class
32
+ end
33
+
34
+ class Proxy
35
+ extend ::Forwardable
36
+ include External
37
+
38
+ def_delegators :@callable, :call
39
+ attr_reader :callable
40
+
41
+ def initialize(callable)
42
+ @callable = callable
43
+ end
44
+ end
45
+
46
+ private_constant :Proxy
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module External
6
+ Output = Struct.new(:value, :options, :routes)
7
+
8
+ module Verbose
9
+ def call(input = nil, **options)
10
+ output = super(input, **options)
11
+ routes = self.routes
12
+
13
+ Output.new(output, options, routes)
14
+ end
15
+ end
16
+
17
+ private_constant :Verbose
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Hooks
6
+ module Call
7
+ def self.included(_subclass)
8
+ raise ::CallableTree::Error, "#{self} must be prepended"
9
+ end
10
+
11
+ def before_call(&block)
12
+ before_callbacks << block
13
+ self
14
+ end
15
+
16
+ def around_call(&block)
17
+ around_callbacks << block
18
+ self
19
+ end
20
+
21
+ def after_call(&block)
22
+ after_callbacks << block
23
+ self
24
+ end
25
+
26
+ def call(input = nil, **options)
27
+ input = before_callbacks.reduce(input) do |input, callable|
28
+ callable.call(input, self, **options)
29
+ end
30
+
31
+ output = super(input, **options)
32
+
33
+ output = around_callbacks.reduce(output) do |output, callable|
34
+ callable.call(input, self, **options) { output }
35
+ end
36
+
37
+ after_callbacks.reduce(output) do |output, callable|
38
+ callable.call(output, self, **options)
39
+ end
40
+ end
41
+
42
+ def before_callbacks
43
+ @before_callbacks ||= []
44
+ end
45
+
46
+ def around_callbacks
47
+ @around_callbacks ||= []
48
+ end
49
+
50
+ def after_callbacks
51
+ @after_callbacks ||= []
52
+ end
53
+
54
+ private
55
+
56
+ attr_writer :before_callbacks, :around_callbacks, :after_callbacks
57
+
58
+ def initialize_copy(_node)
59
+ super
60
+ self.before_callbacks = before_callbacks.map(&:itself)
61
+ self.around_callbacks = around_callbacks.map(&:itself)
62
+ self.after_callbacks = after_callbacks.map(&:itself)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Internal
6
+ include Node
7
+
8
+ def children
9
+ @children ||= []
10
+ end
11
+
12
+ def <<(callable)
13
+ children <<
14
+ if callable.is_a?(Node)
15
+ callable.clone
16
+ else
17
+ External.proxify(callable)
18
+ end
19
+ .tap { |node| node.send(:parent=, self) }
20
+
21
+ self
22
+ end
23
+
24
+ def append(*callables)
25
+ callables.each { |callable| self.<<(callable) }
26
+ self
27
+ end
28
+
29
+ def match?(_input = nil, **_options)
30
+ !children.empty?
31
+ end
32
+
33
+ def call(input = nil, **options)
34
+ children
35
+ .lazy
36
+ .map { |node| Input.new(input, options, node) }
37
+ .select { |input| input.valid? }
38
+ .map { |input| input.call }
39
+ .select { |output| output.valid? }
40
+ .map { |output| output.call }
41
+ .first
42
+ end
43
+
44
+ class Input < BasicObject
45
+ def initialize(value, options, node)
46
+ @value = value
47
+ @options = options
48
+ @node = node
49
+ end
50
+
51
+ def valid?
52
+ @node.match?(@value, **@options)
53
+ end
54
+
55
+ def call
56
+ value = @node.call(@value, **@options)
57
+ Output.new(value, @options, @node)
58
+ end
59
+ end
60
+
61
+ class Output < BasicObject
62
+ def initialize(value, options, node)
63
+ @value = value
64
+ @options = options
65
+ @node = node
66
+ end
67
+
68
+ def valid?
69
+ @node.terminate?(@value, **@options)
70
+ end
71
+
72
+ def call
73
+ @value
74
+ end
75
+ end
76
+
77
+ private_constant :Input, :Output
78
+
79
+ private
80
+
81
+ attr_writer :children
82
+
83
+ def initialize_copy(_node)
84
+ super
85
+ self.children = children.map do |node|
86
+ node.clone.tap { |new_node| new_node.send(:parent=, self) }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ class Root
6
+ include Internal
7
+ prepend Hooks::Call
8
+
9
+ def self.inherited(subclass)
10
+ raise ::CallableTree::Error, "#{subclass} cannot inherit #{self}"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: callable_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - jsmmr
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-05-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Builds a tree by linking callable nodes. The nodes that match the calling
14
+ condition are called in a chain from the root node to the leaf node. This is like
15
+ nested case statements.
16
+ email:
17
+ - jsmmr@icloud.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ".github/workflows/build.yml"
23
+ - ".gitignore"
24
+ - ".rspec"
25
+ - ".ruby-version"
26
+ - CHANGELOG.md
27
+ - Gemfile
28
+ - Gemfile.lock
29
+ - LICENSE.txt
30
+ - README.md
31
+ - Rakefile
32
+ - bin/console
33
+ - bin/setup
34
+ - callable_tree.gemspec
35
+ - examples/docs/animals.json
36
+ - examples/docs/animals.xml
37
+ - examples/docs/fruits.json
38
+ - examples/docs/fruits.xml
39
+ - examples/example1.rb
40
+ - examples/example2.rb
41
+ - examples/example3.rb
42
+ - examples/example4.rb
43
+ - examples/example5.rb
44
+ - lib/callable_tree.rb
45
+ - lib/callable_tree/node.rb
46
+ - lib/callable_tree/node/branch.rb
47
+ - lib/callable_tree/node/external.rb
48
+ - lib/callable_tree/node/external/verbose.rb
49
+ - lib/callable_tree/node/hooks/call.rb
50
+ - lib/callable_tree/node/internal.rb
51
+ - lib/callable_tree/node/root.rb
52
+ - lib/callable_tree/version.rb
53
+ homepage: https://github.com/jsmmr/ruby_callable_tree
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ homepage_uri: https://github.com/jsmmr/ruby_callable_tree
58
+ source_code_uri: https://github.com/jsmmr/ruby_callable_tree
59
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/CHANGELOG.md
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.4.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.2.16
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Builds a tree by linking callable nodes. The nodes that match the calling
79
+ condition are called in a chain from the root node to the leaf node. This is like
80
+ nested case statements.
81
+ test_files: []