callable_tree 0.3.8 → 0.3.9

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,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+ require 'json'
5
+ require 'rexml/document'
6
+
7
+ JSONParser =
8
+ CallableTree::Node::Internal::Builder
9
+ .new
10
+ .matcher do |input, **_options|
11
+ File.extname(input) == '.json'
12
+ end
13
+ .caller do |input, **options, &block|
14
+ File.open(input) do |file|
15
+ json = ::JSON.load(file)
16
+ # The following block call is equivalent to calling `super` in the class style.
17
+ block.call(json, **options)
18
+ end
19
+ end
20
+ .terminator { true }
21
+ .build
22
+
23
+ XMLParser =
24
+ CallableTree::Node::Internal::Builder
25
+ .new
26
+ .matcher do |input, **_options|
27
+ File.extname(input) == '.xml'
28
+ end
29
+ .caller do |input, **options, &block|
30
+ File.open(input) do |file|
31
+ # The following block call is equivalent to calling `super` in the class style.
32
+ block.call(REXML::Document.new(file), **options)
33
+ end
34
+ end
35
+ .terminator { true }
36
+ .build
37
+
38
+ def build_json_scraper(type)
39
+ CallableTree::Node::External::Builder
40
+ .new
41
+ .matcher do |input, **_options|
42
+ !input[type.to_s].nil?
43
+ end
44
+ .caller do |input, **_options|
45
+ input[type.to_s]
46
+ .to_h { |element| [element['name'], element['emoji']] }
47
+ end
48
+ .build
49
+ end
50
+
51
+ AnimalsJSONScraper = build_json_scraper(:animals)
52
+ FruitsJSONScraper = build_json_scraper(:fruits)
53
+
54
+ def build_xml_scraper(type)
55
+ CallableTree::Node::External::Builder
56
+ .new
57
+ .matcher do |input, **_options|
58
+ !input.get_elements("//#{type}").empty?
59
+ end
60
+ .caller do |input, **_options|
61
+ input
62
+ .get_elements("//#{type}")
63
+ .first
64
+ .to_h { |element| [element['name'], element['emoji']] }
65
+ end
66
+ .build
67
+ end
68
+
69
+ AnimalsXMLScraper = build_xml_scraper(:animals)
70
+ FruitsXMLScraper = build_xml_scraper(:fruits)
71
+
72
+ tree = CallableTree::Node::Root.new.seekable.append(
73
+ JSONParser.new.seekable.append(
74
+ AnimalsJSONScraper.new.verbosify,
75
+ FruitsJSONScraper.new.verbosify
76
+ ),
77
+ XMLParser.new.seekable.append(
78
+ AnimalsXMLScraper.new.verbosify,
79
+ FruitsXMLScraper.new.verbosify
80
+ )
81
+ )
82
+
83
+ Dir.glob("#{__dir__}/../docs/*") do |file|
84
+ options = { foo: :bar }
85
+ pp tree.call(file, **options)
86
+ puts '---'
87
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'callable_tree'
4
+ require 'json'
5
+ require 'rexml/document'
6
+
7
+ JSONParser =
8
+ CallableTree::Node::Internal::Builder
9
+ .new
10
+ .matcher do |input, **_options|
11
+ File.extname(input) == '.json'
12
+ end
13
+ .caller do |input, **options, &block|
14
+ File.open(input) do |file|
15
+ json = ::JSON.load(file)
16
+ # The following block call is equivalent to calling `super` in the class style.
17
+ block.call(json, **options)
18
+ end
19
+ end
20
+ .terminator { true }
21
+ .identifier { |_node_:| _node_.object_id }
22
+ .build
23
+
24
+ XMLParser =
25
+ CallableTree::Node::Internal::Builder
26
+ .new
27
+ .matcher do |input, **_options|
28
+ File.extname(input) == '.xml'
29
+ end
30
+ .caller do |input, **options, &block|
31
+ File.open(input) do |file|
32
+ # The following block call is equivalent to calling `super` in the class style.
33
+ block.call(REXML::Document.new(file), **options)
34
+ end
35
+ end
36
+ .terminator { true }
37
+ .identifier { |_node_:| _node_.object_id }
38
+ .build
39
+
40
+ def build_json_scraper(type)
41
+ CallableTree::Node::External::Builder
42
+ .new
43
+ .matcher do |input, **_options|
44
+ !input[type.to_s].nil?
45
+ end
46
+ .caller do |input, **_options|
47
+ input[type.to_s]
48
+ .to_h { |element| [element['name'], element['emoji']] }
49
+ end
50
+ .identifier { |_node_:| _node_.object_id }
51
+ .build
52
+ end
53
+
54
+ AnimalsJSONScraper = build_json_scraper(:animals)
55
+ FruitsJSONScraper = build_json_scraper(:fruits)
56
+
57
+ def build_xml_scraper(type)
58
+ CallableTree::Node::External::Builder
59
+ .new
60
+ .matcher do |input, **_options|
61
+ !input.get_elements("//#{type}").empty?
62
+ end
63
+ .caller do |input, **_options|
64
+ input
65
+ .get_elements("//#{type}")
66
+ .first
67
+ .to_h { |element| [element['name'], element['emoji']] }
68
+ end
69
+ .identifier { |_node_:| _node_.object_id }
70
+ .build
71
+ end
72
+
73
+ AnimalsXMLScraper = build_xml_scraper(:animals)
74
+ FruitsXMLScraper = build_xml_scraper(:fruits)
75
+
76
+ tree = CallableTree::Node::Root.new.seekable.append(
77
+ JSONParser.new.seekable.append(
78
+ AnimalsJSONScraper.new.verbosify,
79
+ FruitsJSONScraper.new.verbosify
80
+ ),
81
+ XMLParser.new.seekable.append(
82
+ AnimalsXMLScraper.new.verbosify,
83
+ FruitsXMLScraper.new.verbosify
84
+ )
85
+ )
86
+
87
+ Dir.glob("#{__dir__}/../docs/*") do |file|
88
+ options = { foo: :bar }
89
+ pp tree.call(file, **options)
90
+ puts '---'
91
+ end
@@ -2,57 +2,57 @@
2
2
 
3
3
  require 'callable_tree'
4
4
 
5
- less_than = proc do |num|
5
+ def less_than(num)
6
6
  # The following block call is equivalent to calling `super` in the class style.
7
- proc { |input, &block| block.call(input) && input < num }
7
+ proc { |input, &original| original.call(input) && input < num }
8
8
  end
9
9
 
10
10
  LessThan5 =
11
11
  CallableTree::Node::Internal::Builder
12
12
  .new
13
- .matcher(&less_than.call(5))
13
+ .matcher(&method(:less_than).call(5))
14
14
  .build
15
15
 
16
16
  LessThan10 =
17
17
  CallableTree::Node::Internal::Builder
18
18
  .new
19
- .matcher(&less_than.call(10))
19
+ .matcher(&method(:less_than).call(10))
20
20
  .build
21
21
 
22
- add = proc do |num|
22
+ def add(num)
23
23
  proc { |input| input + num }
24
24
  end
25
25
 
26
26
  Add1 =
27
27
  CallableTree::Node::External::Builder
28
28
  .new
29
- .caller(&add.call(1))
29
+ .caller(&method(:add).call(1))
30
30
  .build
31
31
 
32
- subtract = proc do |num|
32
+ def subtract(num)
33
33
  proc { |input| input - num }
34
34
  end
35
35
 
36
36
  Subtract1 =
37
37
  CallableTree::Node::External::Builder
38
38
  .new
39
- .caller(&subtract.call(1))
39
+ .caller(&method(:subtract).call(1))
40
40
  .build
41
41
 
42
- multiply = proc do |num|
42
+ def multiply(num)
43
43
  proc { |input| input * num }
44
44
  end
45
45
 
46
46
  Multiply2 =
47
47
  CallableTree::Node::External::Builder
48
48
  .new
49
- .caller(&multiply.call(2))
49
+ .caller(&method(:multiply).call(2))
50
50
  .build
51
51
 
52
52
  Multiply3 =
53
53
  CallableTree::Node::External::Builder
54
54
  .new
55
- .caller(&multiply.call(3))
55
+ .caller(&method(:multiply).call(3))
56
56
  .build
57
57
 
58
58
  tree = CallableTree::Node::Root.new.broadcastable.append(
@@ -2,57 +2,57 @@
2
2
 
3
3
  require 'callable_tree'
4
4
 
5
- less_than = proc do |num|
5
+ def less_than(num)
6
6
  # The following block call is equivalent to calling `super` in the class style.
7
- proc { |input, &block| block.call(input) && input < num }
7
+ proc { |input, &original| original.call(input) && input < num }
8
8
  end
9
9
 
10
10
  LessThan5 =
11
11
  CallableTree::Node::Internal::Builder
12
12
  .new
13
- .matcher(&less_than.call(5))
13
+ .matcher(&method(:less_than).call(5))
14
14
  .build
15
15
 
16
16
  LessThan10 =
17
17
  CallableTree::Node::Internal::Builder
18
18
  .new
19
- .matcher(&less_than.call(10))
19
+ .matcher(&method(:less_than).call(10))
20
20
  .build
21
21
 
22
- add = proc do |num|
22
+ def add(num)
23
23
  proc { |input| input + num }
24
24
  end
25
25
 
26
26
  Add1 =
27
27
  CallableTree::Node::External::Builder
28
28
  .new
29
- .caller(&add.call(1))
29
+ .caller(&method(:add).call(1))
30
30
  .build
31
31
 
32
- subtract = proc do |num|
32
+ def subtract(num)
33
33
  proc { |input| input - num }
34
34
  end
35
35
 
36
36
  Subtract1 =
37
37
  CallableTree::Node::External::Builder
38
38
  .new
39
- .caller(&subtract.call(1))
39
+ .caller(&method(:subtract).call(1))
40
40
  .build
41
41
 
42
- multiply = proc do |num|
42
+ def multiply(num)
43
43
  proc { |input| input * num }
44
44
  end
45
45
 
46
46
  Multiply2 =
47
47
  CallableTree::Node::External::Builder
48
48
  .new
49
- .caller(&multiply.call(2))
49
+ .caller(&method(:multiply).call(2))
50
50
  .build
51
51
 
52
52
  Multiply3 =
53
53
  CallableTree::Node::External::Builder
54
54
  .new
55
- .caller(&multiply.call(3))
55
+ .caller(&method(:multiply).call(3))
56
56
  .build
57
57
 
58
58
  tree = CallableTree::Node::Root.new.composable.append(
@@ -10,16 +10,14 @@ JSONParser =
10
10
  .matcher do |input, **_options|
11
11
  File.extname(input) == '.json'
12
12
  end
13
- .caller do |input, **options, &block|
13
+ .caller do |input, **options, &original|
14
14
  File.open(input) do |file|
15
15
  json = ::JSON.load(file)
16
16
  # The following block call is equivalent to calling `super` in the class style.
17
- block.call(json, **options)
17
+ original.call(json, **options)
18
18
  end
19
19
  end
20
- .terminator do
21
- true
22
- end
20
+ .terminator { true }
23
21
  .build
24
22
 
25
23
  XMLParser =
@@ -28,27 +26,24 @@ XMLParser =
28
26
  .matcher do |input, **_options|
29
27
  File.extname(input) == '.xml'
30
28
  end
31
- .caller do |input, **options, &block|
29
+ .caller do |input, **options, &original|
32
30
  File.open(input) do |file|
33
31
  # The following block call is equivalent to calling `super` in the class style.
34
- block.call(REXML::Document.new(file), **options)
32
+ original.call(REXML::Document.new(file), **options)
35
33
  end
36
34
  end
37
- .terminator do
38
- true
39
- end
35
+ .terminator { true }
40
36
  .build
41
37
 
42
38
  def build_json_scraper(type)
43
39
  CallableTree::Node::External::Builder
44
40
  .new
45
41
  .matcher do |input, **_options|
46
- !!input[type.to_s]
42
+ !input[type.to_s].nil?
47
43
  end
48
44
  .caller do |input, **_options|
49
45
  input[type.to_s]
50
- .map { |element| [element['name'], element['emoji']] }
51
- .to_h
46
+ .to_h { |element| [element['name'], element['emoji']] }
52
47
  end
53
48
  .build
54
49
  end
@@ -66,8 +61,7 @@ def build_xml_scraper(type)
66
61
  input
67
62
  .get_elements("//#{type}")
68
63
  .first
69
- .map { |element| [element['name'], element['emoji']] }
70
- .to_h
64
+ .to_h { |element| [element['name'], element['emoji']] }
71
65
  end
72
66
  .build
73
67
  end
@@ -10,15 +10,14 @@ JSONParser =
10
10
  .matcher do |input, **_options|
11
11
  File.extname(input) == '.json'
12
12
  end
13
- .caller do |input, **options, &block|
13
+ .caller do |input, **options, &original|
14
14
  File.open(input) do |file|
15
15
  json = ::JSON.load(file)
16
16
  # The following block call is equivalent to calling `super` in the class style.
17
- block.call(json, **options)
17
+ original.call(json, **options)
18
18
  end
19
19
  end
20
20
  .terminator { true }
21
- # .identifier { |_node_:| :as_you_like } # optional
22
21
  .hookable
23
22
  .build
24
23
 
@@ -28,14 +27,13 @@ XMLParser =
28
27
  .matcher do |input, **_options|
29
28
  File.extname(input) == '.xml'
30
29
  end
31
- .caller do |input, **options, &block|
30
+ .caller do |input, **options, &original|
32
31
  File.open(input) do |file|
33
32
  # The following block call is equivalent to calling `super` in the class style.
34
- block.call(REXML::Document.new(file), **options)
33
+ original.call(REXML::Document.new(file), **options)
35
34
  end
36
35
  end
37
36
  .terminator { true }
38
- # .identifier { |_node_:| :as_you_like } # optional
39
37
  .hookable
40
38
  .build
41
39
 
@@ -43,14 +41,12 @@ def build_json_scraper(type)
43
41
  CallableTree::Node::External::Builder
44
42
  .new
45
43
  .matcher do |input, **_options|
46
- !!input[type.to_s]
44
+ !input[type.to_s].nil?
47
45
  end
48
46
  .caller do |input, **_options|
49
47
  input[type.to_s]
50
- .map { |element| [element['name'], element['emoji']] }
51
- .to_h
48
+ .to_h { |element| [element['name'], element['emoji']] }
52
49
  end
53
- # .identifier { |_node_:| :as_you_like } # optional
54
50
  .hookable
55
51
  .build
56
52
  end
@@ -68,10 +64,8 @@ def build_xml_scraper(type)
68
64
  input
69
65
  .get_elements("//#{type}")
70
66
  .first
71
- .map { |element| [element['name'], element['emoji']] }
72
- .to_h
67
+ .to_h { |element| [element['name'], element['emoji']] }
73
68
  end
74
- # .identifier { |_node_:| :as_you_like } # optional
75
69
  .hookable
76
70
  .build
77
71
  end
@@ -38,8 +38,7 @@ module Node
38
38
 
39
39
  def call(input, **_options)
40
40
  input[@type.to_s]
41
- .map { |element| [element['name'], element['emoji']] }
42
- .to_h
41
+ .to_h { |element| [element['name'], element['emoji']] }
43
42
  end
44
43
  end
45
44
  end
@@ -78,8 +77,7 @@ module Node
78
77
  input
79
78
  .get_elements("//#{@type}")
80
79
  .first
81
- .map { |element| [element['name'], element['emoji']] }
82
- .to_h
80
+ .to_h { |element| [element['name'], element['emoji']] }
83
81
  end
84
82
  end
85
83
  end
@@ -55,8 +55,7 @@ module Node
55
55
 
56
56
  def call(input, **_options)
57
57
  input[@type.to_s]
58
- .map { |element| [element['name'], element['emoji']] }
59
- .to_h
58
+ .to_h { |element| [element['name'], element['emoji']] }
60
59
  end
61
60
  end
62
61
  end
@@ -99,8 +98,7 @@ module Node
99
98
  input
100
99
  .get_elements("//#{@type}")
101
100
  .first
102
- .map { |element| [element['name'], element['emoji']] }
103
- .to_h
101
+ .to_h { |element| [element['name'], element['emoji']] }
104
102
  end
105
103
  end
106
104
  end
@@ -38,8 +38,7 @@ module Node
38
38
 
39
39
  def call(input, **_options)
40
40
  input[@type.to_s]
41
- .map { |element| [element['name'], element['emoji']] }
42
- .to_h
41
+ .to_h { |element| [element['name'], element['emoji']] }
43
42
  end
44
43
  end
45
44
  end
@@ -78,8 +77,7 @@ module Node
78
77
  input
79
78
  .get_elements("//#{@type}")
80
79
  .first
81
- .map { |element| [element['name'], element['emoji']] }
82
- .to_h
80
+ .to_h { |element| [element['name'], element['emoji']] }
83
81
  end
84
82
  end
85
83
  end
@@ -58,8 +58,7 @@ module Node
58
58
 
59
59
  def call(input, **_options)
60
60
  input[@type.to_s]
61
- .map { |element| [element['name'], element['emoji']] }
62
- .to_h
61
+ .to_h { |element| [element['name'], element['emoji']] }
63
62
  end
64
63
  end
65
64
  end
@@ -105,8 +104,7 @@ module Node
105
104
  input
106
105
  .get_elements("//#{@type}")
107
106
  .first
108
- .map { |element| [element['name'], element['emoji']] }
109
- .to_h
107
+ .to_h { |element| [element['name'], element['emoji']] }
110
108
  end
111
109
  end
112
110
  end
@@ -7,9 +7,26 @@ module CallableTree
7
7
  class Broadcast
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: false)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
- nodes.map do |node|
12
- node.call(*inputs, **options) if node.match?(*inputs, **options)
21
+ nodes.reduce([]) do |outputs, node|
22
+ output = (node.call(*inputs, **options) if node.match?(*inputs, **options))
23
+ outputs << output
24
+
25
+ if @terminator.call(node, output, *inputs, **options)
26
+ break outputs
27
+ else
28
+ outputs
29
+ end
13
30
  end
14
31
  end
15
32
  end
@@ -7,12 +7,24 @@ module CallableTree
7
7
  class Compose
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: false)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
21
  head, *tail = inputs
12
22
  nodes.reduce(head) do |input, node|
13
- inputs = [input, *tail]
14
- if node.match?(*inputs, **options)
15
- node.call(*inputs, **options)
23
+ if node.match?(input, *tail, **options)
24
+ output = node.call(input, *tail, **options)
25
+ break output if @terminator.call(node, output, input, *tail, **options)
26
+
27
+ output
16
28
  else
17
29
  input
18
30
  end
@@ -7,13 +7,23 @@ module CallableTree
7
7
  class Seek
8
8
  include Strategy
9
9
 
10
+ def initialize(terminable: true)
11
+ self.terminable = terminable
12
+ @terminator =
13
+ if terminable
14
+ proc { |node, output, *inputs, **options| node.terminate?(output, *inputs, **options) }
15
+ else
16
+ proc { false }
17
+ end
18
+ end
19
+
10
20
  def call(nodes, *inputs, **options)
11
21
  nodes
12
22
  .lazy
13
23
  .select { |node| node.match?(*inputs, **options) }
14
24
  .map do |node|
15
25
  output = node.call(*inputs, **options)
16
- terminated = node.terminate?(output, *inputs, **options)
26
+ terminated = @terminator.call(node, output, *inputs, **options)
17
27
  [output, terminated]
18
28
  end
19
29
  .select { |_output, terminated| terminated }
@@ -13,7 +13,7 @@ module CallableTree
13
13
  end
14
14
 
15
15
  def ==(other)
16
- name == other.name
16
+ name == other.name && terminable? == other.terminable?
17
17
  end
18
18
 
19
19
  def eql?(other)
@@ -21,8 +21,16 @@ module CallableTree
21
21
  end
22
22
 
23
23
  def hash
24
- self.class.name.hash
24
+ [self.class.name, terminable].join('-').hash
25
25
  end
26
+
27
+ def terminable?
28
+ terminable
29
+ end
30
+
31
+ private
32
+
33
+ attr_accessor :terminable
26
34
  end
27
35
  end
28
36
  end