callable_tree 0.2.1 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23417eaf997718c6d0e9b5fc52dea3fe4923ca9b426d9d41e57b5e27015d239a
4
- data.tar.gz: 34be74baf1014e41efbe0073fedfdf76dc02444c927bea2b089d9457842c04b8
3
+ metadata.gz: c16eb0b2fd83ffd98d61bca938bd449dac9c004e8240c4f1a3b61242bf841163
4
+ data.tar.gz: 9919ffcda275a5a5bcf81b0bfae40087cae1f701fc0b7b915fece81c21a79d6b
5
5
  SHA512:
6
- metadata.gz: 686e18552c5decad326d7bd0775a2be0b3726245edea1ccf2dbdf0ca3e264ec88883bf79cea5af873b074feff1b63e49b59522b5f3b7fc00e06122d494277cea
7
- data.tar.gz: 7db8a88a1bc19677f453237dd15137da05f3fdb2ddb25aeedd11df82e6afd7c769d3da3c61e9905c40285d35ea54059c6749b4f32a354fc542d7bd26fdd8992f
6
+ metadata.gz: 98c022cc2b12dccf2333f1a1fc079febe80f597bbbfbd4565384b88987c92c2426d7986ee80c11e1361139b95d495fdff9237e4f986d57dbb7eb355ce8244a0b
7
+ data.tar.gz: bcee44379ab11292a5c3bba4b0d37f2bdc2a6f587f9a2a757c11c6093d29fdbd1fe20bd9e0ee6561d98785121222ae1959b412ca8b4ecb5ddf579504f5a704c4
@@ -5,13 +5,13 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  matrix:
8
- ruby: ['2.4', '2.5', '2.6', '2.7', '3.0']
8
+ ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1']
9
9
  steps:
10
10
  - uses: actions/checkout@v2
11
11
  - uses: ruby/setup-ruby@v1
12
12
  with:
13
13
  ruby-version: ${{ matrix.ruby }}
14
- - run: gem install bundler:2.2.4
14
+ - run: gem install bundler:2.3.3
15
15
  - uses: actions/cache@v2
16
16
  with:
17
17
  path: vendor/bundle
@@ -0,0 +1,70 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ main, develop ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ main, develop ]
20
+ schedule:
21
+ - cron: '35 15 * * 0'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'ruby' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v2
42
+
43
+ # Initializes the CodeQL tools for scanning.
44
+ - name: Initialize CodeQL
45
+ uses: github/codeql-action/init@v1
46
+ with:
47
+ languages: ${{ matrix.language }}
48
+ # If you wish to specify custom queries, you can do so here or in a config file.
49
+ # By default, queries listed here will override any specified in a config file.
50
+ # Prefix the list here with "+" to use these queries and those in the config file.
51
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
52
+
53
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
+ # If this step fails, then you should remove it and run the build manually (see below)
55
+ - name: Autobuild
56
+ uses: github/codeql-action/autobuild@v1
57
+
58
+ # ℹ️ Command-line programs to run using the OS shell.
59
+ # 📚 https://git.io/JvXDl
60
+
61
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62
+ # and modify them (or add more) to build your code if your project
63
+ # uses a compiled language
64
+
65
+ #- run: |
66
+ # make bootstrap
67
+ # make release
68
+
69
+ - name: Perform CodeQL Analysis
70
+ uses: github/codeql-action/analyze@v1
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.0.0
1
+ 3.1.0
data/CHANGELOG.md CHANGED
@@ -1,12 +1,40 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.1] - 2022-01-10
4
+
5
+ - Add `CallableTree::Node::Internal#seek?` to check whether the node's strategy is `seek` or not.
6
+ - Add `CallableTree::Node::Internal#broadcast?` to check whether the node's strategy is `broadcast` or not.
7
+ - Add `CallableTree::Node::Internal#compose?` to check whether the node's strategy is `compose` or not.
8
+
9
+ ## [0.3.0] - 2021-12-27
10
+
11
+ - Change `CallableTree::Node#match?` to accept inputs to the node as variable length arguments.
12
+ - Change `CallableTree::Node#call` to accept inputs to the node as variable length arguments.
13
+ - Change `CallableTree::Node#terminate?` to accept inputs to the node as variable length arguments, after the `output` argument.
14
+ - Add `CallableTree::Node::Internal#[]` to return the child node using `index`.
15
+ - Change `CallableTree::Node::Internal#children` to return a new array including child nodes of the node.
16
+ - Add `CallableTree::Node::Internal#children!` to return destructively changeable array including child nodes of the node.
17
+
18
+ ## [0.2.3] - 2021-11-07
19
+
20
+ - Add `CallableTree::Node::Internal#shake` to recursively execute `CallableTree::Node::Internal#reject`, including child nodes. The child nodes that are empty because their children have been rejected will also be rejected.
21
+ - Add `CallableTree::Node::Internal#shake!` that make destructive change.
22
+ - Add `CallableTree::Node#outline` that may be useful for writing the specs.
23
+
24
+ ## [0.2.2] - 2021-10-24
25
+
26
+ - Add `CallableTree::Node::Internal#reject` to return a new node instance without rejected child nodes.
27
+ - Add `CallableTree::Node::Internal#reject!` to destructively reject child nodes.
28
+
3
29
  ## [0.2.1] - 2021-07-24
4
- - Add `CallableTree::Node::root?`.
30
+
31
+ - Add `CallableTree::Node#root?`.
5
32
  - Add `CallableTree::Node::Internal#seek!` that make destructive change.
6
33
  - Add `CallableTree::Node::Internal#broadcast!` that make destructive change.
7
34
  - Add `CallableTree::Node::Internal#compose!` that make destructive change.
8
35
 
9
36
  ## [0.2.0] - 2021-06-15
37
+
10
38
  - Change `CallableTree::Node::Internal#append` to return a new instance.
11
39
  To keep the same behavior as the older version, use `CallableTree::Node::External#append!` that make destructive change.
12
40
  - Remove `CallableTree::Node::Internal#<<`. Use `CallableTree::Node::External#append!` instead.
@@ -14,6 +42,7 @@
14
42
  To keep the same behavior as the older version, use `CallableTree::Node::External#verbosify!` that make destructive change.
15
43
 
16
44
  ## [0.1.3] - 2021-06-12
45
+
17
46
  - Minor improvements
18
47
 
19
48
  ## [0.1.2] - 2021-05-29
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- callable_tree (0.2.1)
4
+ callable_tree (0.3.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- diff-lcs (1.4.4)
9
+ diff-lcs (1.5.0)
10
10
  rake (13.0.6)
11
11
  rspec (3.10.0)
12
12
  rspec-core (~> 3.10.0)
@@ -20,7 +20,7 @@ GEM
20
20
  rspec-mocks (3.10.2)
21
21
  diff-lcs (>= 1.2.0, < 2.0)
22
22
  rspec-support (~> 3.10.0)
23
- rspec-support (3.10.2)
23
+ rspec-support (3.10.3)
24
24
 
25
25
  PLATFORMS
26
26
  x86_64-darwin-19
@@ -32,4 +32,4 @@ DEPENDENCIES
32
32
  rspec (~> 3.0)
33
33
 
34
34
  BUNDLED WITH
35
- 2.2.17
35
+ 2.3.3
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021  
3
+ Copyright (c) 2021 jsmmr
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # CallableTree
2
2
 
3
+ [![build](https://github.com/jsmmr/ruby_callable_tree/actions/workflows/build.yml/badge.svg)](https://github.com/jsmmr/ruby_callable_tree/actions/workflows/build.yml)
4
+ [![CodeQL](https://github.com/jsmmr/ruby_callable_tree/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/jsmmr/ruby_callable_tree/actions/workflows/codeql-analysis.yml)
5
+
3
6
  ## Installation
4
7
 
5
8
  Add this line to your application's Gemfile:
@@ -40,11 +43,11 @@ module Node
40
43
  class Parser
41
44
  include CallableTree::Node::Internal
42
45
 
43
- def match?(input, **options)
46
+ def match?(input, **_options)
44
47
  File.extname(input) == '.json'
45
48
  end
46
49
 
47
- # If there is need to convert the input value for
50
+ # If there is need to convert the input values for
48
51
  # child nodes, override the `call` method.
49
52
  def call(input, **options)
50
53
  File.open(input) do |file|
@@ -56,7 +59,7 @@ module Node
56
59
  # If a returned value of the `call` method is `nil`,
57
60
  # but there is no need to call the sibling nodes,
58
61
  # override the `terminate?` method to return `true`.
59
- def terminate?(output, **options)
62
+ def terminate?(_output, *_inputs, **_options)
60
63
  true
61
64
  end
62
65
  end
@@ -68,11 +71,11 @@ module Node
68
71
  @type = type
69
72
  end
70
73
 
71
- def match?(input, **options)
74
+ def match?(input, **_options)
72
75
  !!input[@type.to_s]
73
76
  end
74
77
 
75
- def call(input, **options)
78
+ def call(input, **_options)
76
79
  input[@type.to_s]
77
80
  .map { |element| [element['name'], element['emoji']] }
78
81
  .to_h
@@ -84,11 +87,11 @@ module Node
84
87
  class Parser
85
88
  include CallableTree::Node::Internal
86
89
 
87
- def match?(input, **options)
90
+ def match?(input, **_options)
88
91
  File.extname(input) == '.xml'
89
92
  end
90
93
 
91
- # If there is need to convert the input value for
94
+ # If there is need to convert the input values for
92
95
  # child nodes, override the `call` method.
93
96
  def call(input, **options)
94
97
  File.open(input) do |file|
@@ -99,7 +102,7 @@ module Node
99
102
  # If a returned value of the `call` method is `nil`,
100
103
  # but there is no need to call the sibling nodes,
101
104
  # override the `terminate?` method to return `true`.
102
- def terminate?(output, **options)
105
+ def terminate?(_output, *_inputs, **_options)
103
106
  true
104
107
  end
105
108
  end
@@ -111,11 +114,11 @@ module Node
111
114
  @type = type
112
115
  end
113
116
 
114
- def match?(input, **options)
117
+ def match?(input, **_options)
115
118
  !input.get_elements("//#{@type}").empty?
116
119
  end
117
120
 
118
- def call(input, **options)
121
+ def call(input, **_options)
119
122
  input
120
123
  .get_elements("//#{@type}")
121
124
  .first
@@ -138,7 +141,7 @@ tree = CallableTree::Node::Root.new.append(
138
141
  )#.seek
139
142
  )#.seek
140
143
 
141
- Dir.glob(__dir__ + '/docs/*') do |file|
144
+ Dir.glob("#{__dir__}/docs/*") do |file|
142
145
  options = { foo: :bar }
143
146
  pp tree.call(file, **options)
144
147
  puts '---'
@@ -180,12 +183,12 @@ end
180
183
 
181
184
  tree = CallableTree::Node::Root.new.append(
182
185
  Node::LessThan.new(5).append(
183
- lambda { |input, **| input * 2 }, # anonymous external node
184
- lambda { |input, **| input + 1 } # anonymous external node
186
+ ->(input) { input * 2 }, # anonymous external node
187
+ ->(input) { input + 1 } # anonymous external node
185
188
  ).broadcast,
186
189
  Node::LessThan.new(10).append(
187
- lambda { |input, **| input * 3 }, # anonymous external node
188
- lambda { |input, **| input - 1 } # anonymous external node
190
+ ->(input) { input * 3 }, # anonymous external node
191
+ ->(input) { input - 1 } # anonymous external node
189
192
  ).broadcast
190
193
  ).broadcast
191
194
 
@@ -432,28 +435,28 @@ This is an example of logging.
432
435
  module Node
433
436
  module Logging
434
437
  INDENT_SIZE = 2
435
- BLANK = ' '.freeze
438
+ BLANK = ' '
436
439
 
437
440
  module Match
438
- LIST_STYLE = '*'.freeze
441
+ LIST_STYLE = '*'
439
442
 
440
- def match?(_input, **)
443
+ def match?(_input, **_options)
441
444
  super.tap do |matched|
442
- prefix = LIST_STYLE.rjust(self.depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
443
- puts "#{prefix} #{self.identity}: [matched: #{matched}]"
445
+ prefix = LIST_STYLE.rjust(depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
446
+ puts "#{prefix} #{identity}: [matched: #{matched}]"
444
447
  end
445
448
  end
446
449
  end
447
450
 
448
451
  module Call
449
- INPUT_LABEL = 'Input :'.freeze
450
- OUTPUT_LABEL = 'Output:'.freeze
452
+ INPUT_LABEL = 'Input :'
453
+ OUTPUT_LABEL = 'Output:'
451
454
 
452
- def call(input, **)
455
+ def call(input, **_options)
453
456
  super.tap do |output|
454
- input_prefix = INPUT_LABEL.rjust(self.depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
457
+ input_prefix = INPUT_LABEL.rjust(depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
455
458
  puts "#{input_prefix} #{input}"
456
- output_prefix = OUTPUT_LABEL.rjust(self.depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
459
+ output_prefix = OUTPUT_LABEL.rjust(depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
457
460
  puts "#{output_prefix} #{output}"
458
461
  end
459
462
  end
@@ -577,24 +580,24 @@ module Node
577
580
  end
578
581
 
579
582
  Node::HooksSample.new
580
- .before_call do |input, **options|
583
+ .before_call do |input, **_options|
581
584
  puts "before_call input: #{input}";
582
585
  input + 1
583
586
  end
584
587
  .append(
585
588
  # anonymous external node
586
- lambda do |input, **options|
589
+ lambda do |input, **_options|
587
590
  puts "external input: #{input}"
588
591
  input * 2
589
592
  end
590
593
  )
591
- .around_call do |input, **options, &block|
594
+ .around_call do |input, **_options, &block|
592
595
  puts "around_call input: #{input}"
593
596
  output = block.call
594
597
  puts "around_call output: #{output}"
595
598
  output * input
596
599
  end
597
- .after_call do |output, **options|
600
+ .after_call do |output, **_options|
598
601
  puts "after_call output: #{output}"
599
602
  output * 2
600
603
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
  require 'json'
3
5
  require 'rexml/document'
@@ -7,7 +9,7 @@ module Node
7
9
  class Parser
8
10
  include CallableTree::Node::Internal
9
11
 
10
- def match?(input, **options)
12
+ def match?(input, **_options)
11
13
  File.extname(input) == '.json'
12
14
  end
13
15
 
@@ -18,7 +20,7 @@ module Node
18
20
  end
19
21
  end
20
22
 
21
- def terminate?(_output, **)
23
+ def terminate?(_output, *_inputs, **_options)
22
24
  true
23
25
  end
24
26
  end
@@ -30,11 +32,11 @@ module Node
30
32
  @type = type
31
33
  end
32
34
 
33
- def match?(input, **options)
35
+ def match?(input, **_options)
34
36
  !!input[@type.to_s]
35
37
  end
36
38
 
37
- def call(input, **options)
39
+ def call(input, **_options)
38
40
  input[@type.to_s]
39
41
  .map { |element| [element['name'], element['emoji']] }
40
42
  .to_h
@@ -46,7 +48,7 @@ module Node
46
48
  class Parser
47
49
  include CallableTree::Node::Internal
48
50
 
49
- def match?(input, **options)
51
+ def match?(input, **_options)
50
52
  File.extname(input) == '.xml'
51
53
  end
52
54
 
@@ -56,7 +58,7 @@ module Node
56
58
  end
57
59
  end
58
60
 
59
- def terminate?(_output, **)
61
+ def terminate?(_output, *_inputs, **_options)
60
62
  true
61
63
  end
62
64
  end
@@ -68,11 +70,11 @@ module Node
68
70
  @type = type
69
71
  end
70
72
 
71
- def match?(input, **options)
73
+ def match?(input, **_options)
72
74
  !input.get_elements("//#{@type}").empty?
73
75
  end
74
76
 
75
- def call(input, **options)
77
+ def call(input, **_options)
76
78
  input
77
79
  .get_elements("//#{@type}")
78
80
  .first
@@ -94,7 +96,7 @@ tree = CallableTree::Node::Root.new.append(
94
96
  )
95
97
  )
96
98
 
97
- Dir.glob(__dir__ + '/docs/*') do |file|
99
+ Dir.glob("#{__dir__}/docs/*") do |file|
98
100
  options = { foo: :bar }
99
101
  pp tree.call(file, **options)
100
102
  puts '---'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
 
3
5
  module Node
@@ -8,24 +10,24 @@ module Node
8
10
  end
9
11
 
10
12
  Node::HooksSample.new
11
- .before_call do |input, **options|
13
+ .before_call do |input, **_options|
12
14
  puts "before_call input: #{input}";
13
15
  input + 1
14
16
  end
15
17
  .append(
16
18
  # anonymous external node
17
- lambda do |input, **options|
19
+ lambda do |input, **_options|
18
20
  puts "external input: #{input}"
19
21
  input * 2
20
22
  end
21
23
  )
22
- .around_call do |input, **options, &block|
24
+ .around_call do |input, **_options, &block|
23
25
  puts "around_call input: #{input}"
24
26
  output = block.call
25
27
  puts "around_call output: #{output}"
26
28
  output * input
27
29
  end
28
- .after_call do |output, **options|
30
+ .after_call do |output, **_options|
29
31
  puts "after_call output: #{output}"
30
32
  output * 2
31
33
  end
data/examples/identity.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
  require 'json'
3
5
  require 'rexml/document'
@@ -20,7 +22,7 @@ module Node
20
22
  class Parser
21
23
  include CallableTree::Node::Internal
22
24
 
23
- def match?(input, **options)
25
+ def match?(input, **_options)
24
26
  File.extname(input) == '.json'
25
27
  end
26
28
 
@@ -31,7 +33,7 @@ module Node
31
33
  end
32
34
  end
33
35
 
34
- def terminate?(_output, **)
36
+ def terminate?(_output, *_inputs, **_options)
35
37
  true
36
38
  end
37
39
  end
@@ -47,11 +49,11 @@ module Node
47
49
  Identity.new(klass: super, type: @type)
48
50
  end
49
51
 
50
- def match?(input, **options)
52
+ def match?(input, **_options)
51
53
  !!input[@type.to_s]
52
54
  end
53
55
 
54
- def call(input, **options)
56
+ def call(input, **_options)
55
57
  input[@type.to_s]
56
58
  .map { |element| [element['name'], element['emoji']] }
57
59
  .to_h
@@ -63,7 +65,7 @@ module Node
63
65
  class Parser
64
66
  include CallableTree::Node::Internal
65
67
 
66
- def match?(input, **options)
68
+ def match?(input, **_options)
67
69
  File.extname(input) == '.xml'
68
70
  end
69
71
 
@@ -73,7 +75,7 @@ module Node
73
75
  end
74
76
  end
75
77
 
76
- def terminate?(_output, **)
78
+ def terminate?(_output, *_inputs, **_options)
77
79
  true
78
80
  end
79
81
  end
@@ -89,11 +91,11 @@ module Node
89
91
  Identity.new(klass: super, type: @type)
90
92
  end
91
93
 
92
- def match?(input, **options)
94
+ def match?(input, **_options)
93
95
  !input.get_elements("//#{@type}").empty?
94
96
  end
95
97
 
96
- def call(input, **options)
98
+ def call(input, **_options)
97
99
  input
98
100
  .get_elements("//#{@type}")
99
101
  .first
@@ -115,7 +117,7 @@ tree = CallableTree::Node::Root.new.append(
115
117
  )
116
118
  )
117
119
 
118
- Dir.glob(__dir__ + '/docs/*') do |file|
120
+ Dir.glob("#{__dir__}/docs/*") do |file|
119
121
  options = { foo: :bar }
120
122
  pp tree.call(file, **options)
121
123
  puts '---'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
 
3
5
  module Node
@@ -16,12 +18,12 @@ end
16
18
 
17
19
  tree = CallableTree::Node::Root.new.append(
18
20
  Node::LessThan.new(5).append(
19
- lambda { |input, **| input * 2 }, # anonymous external node
20
- lambda { |input, **| input + 1 } # anonymous external node
21
+ ->(input) { input * 2 }, # anonymous external node
22
+ ->(input) { input + 1 } # anonymous external node
21
23
  ).broadcast,
22
24
  Node::LessThan.new(10).append(
23
- lambda { |input, **| input * 3 }, # anonymous external node
24
- lambda { |input, **| input - 1 } # anonymous external node
25
+ ->(input) { input * 3 }, # anonymous external node
26
+ ->(input) { input - 1 } # anonymous external node
25
27
  ).broadcast
26
28
  ).broadcast
27
29
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
 
3
5
  module Node
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
  require 'json'
3
5
  require 'rexml/document'
@@ -7,7 +9,7 @@ module Node
7
9
  class Parser
8
10
  include CallableTree::Node::Internal
9
11
 
10
- def match?(input, **options)
12
+ def match?(input, **_options)
11
13
  File.extname(input) == '.json'
12
14
  end
13
15
 
@@ -18,7 +20,7 @@ module Node
18
20
  end
19
21
  end
20
22
 
21
- def terminate?(_output, **)
23
+ def terminate?(_output, *_inputs, **_options)
22
24
  true
23
25
  end
24
26
  end
@@ -30,11 +32,11 @@ module Node
30
32
  @type = type
31
33
  end
32
34
 
33
- def match?(input, **options)
35
+ def match?(input, **_options)
34
36
  !!input[@type.to_s]
35
37
  end
36
38
 
37
- def call(input, **options)
39
+ def call(input, **_options)
38
40
  input[@type.to_s]
39
41
  .map { |element| [element['name'], element['emoji']] }
40
42
  .to_h
@@ -46,7 +48,7 @@ module Node
46
48
  class Parser
47
49
  include CallableTree::Node::Internal
48
50
 
49
- def match?(input, **options)
51
+ def match?(input, **_options)
50
52
  File.extname(input) == '.xml'
51
53
  end
52
54
 
@@ -56,7 +58,7 @@ module Node
56
58
  end
57
59
  end
58
60
 
59
- def terminate?(_output, **)
61
+ def terminate?(_output, *_inputs, **_options)
60
62
  true
61
63
  end
62
64
  end
@@ -68,11 +70,11 @@ module Node
68
70
  @type = type
69
71
  end
70
72
 
71
- def match?(input, **options)
73
+ def match?(input, **_options)
72
74
  !input.get_elements("//#{@type}").empty?
73
75
  end
74
76
 
75
- def call(input, **options)
77
+ def call(input, **_options)
76
78
  input
77
79
  .get_elements("//#{@type}")
78
80
  .first
@@ -94,7 +96,7 @@ tree = CallableTree::Node::Root.new.append(
94
96
  )
95
97
  )
96
98
 
97
- Dir.glob(__dir__ + '/docs/*') do |file|
99
+ Dir.glob("#{__dir__}/docs/*") do |file|
98
100
  options = { foo: :bar }
99
101
  pp tree.call(file, **options)
100
102
  puts '---'
data/examples/logging.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'callable_tree'
2
4
  require 'json'
3
5
  require 'rexml/document'
@@ -5,28 +7,28 @@ require 'rexml/document'
5
7
  module Node
6
8
  module Logging
7
9
  INDENT_SIZE = 2
8
- BLANK = ' '.freeze
10
+ BLANK = ' '
9
11
 
10
12
  module Match
11
- LIST_STYLE = '*'.freeze
13
+ LIST_STYLE = '*'
12
14
 
13
- def match?(_input, **)
15
+ def match?(_input, **_options)
14
16
  super.tap do |matched|
15
- prefix = LIST_STYLE.rjust(self.depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
16
- puts "#{prefix} #{self.identity}: [matched: #{matched}]"
17
+ prefix = LIST_STYLE.rjust(depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
18
+ puts "#{prefix} #{identity}: [matched: #{matched}]"
17
19
  end
18
20
  end
19
21
  end
20
22
 
21
23
  module Call
22
- INPUT_LABEL = 'Input :'.freeze
23
- OUTPUT_LABEL = 'Output:'.freeze
24
+ INPUT_LABEL = 'Input :'
25
+ OUTPUT_LABEL = 'Output:'
24
26
 
25
- def call(input, **)
27
+ def call(input, **_options)
26
28
  super.tap do |output|
27
- input_prefix = INPUT_LABEL.rjust(self.depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
29
+ input_prefix = INPUT_LABEL.rjust(depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
28
30
  puts "#{input_prefix} #{input}"
29
- output_prefix = OUTPUT_LABEL.rjust(self.depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
31
+ output_prefix = OUTPUT_LABEL.rjust(depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
30
32
  puts "#{output_prefix} #{output}"
31
33
  end
32
34
  end
@@ -51,7 +53,7 @@ module Node
51
53
  include CallableTree::Node::Internal
52
54
  prepend Logging::Match
53
55
 
54
- def match?(input, **options)
56
+ def match?(input, **_options)
55
57
  File.extname(input) == '.json'
56
58
  end
57
59
 
@@ -62,7 +64,7 @@ module Node
62
64
  end
63
65
  end
64
66
 
65
- def terminate?(_output, **)
67
+ def terminate?(_output, *_inputs, **_options)
66
68
  true
67
69
  end
68
70
  end
@@ -80,11 +82,11 @@ module Node
80
82
  Identity.new(klass: super, type: @type)
81
83
  end
82
84
 
83
- def match?(input, **options)
85
+ def match?(input, **_options)
84
86
  !!input[@type.to_s]
85
87
  end
86
88
 
87
- def call(input, **options)
89
+ def call(input, **_options)
88
90
  input[@type.to_s]
89
91
  .map { |element| [element['name'], element['emoji']] }
90
92
  .to_h
@@ -97,7 +99,7 @@ module Node
97
99
  include CallableTree::Node::Internal
98
100
  prepend Logging::Match
99
101
 
100
- def match?(input, **options)
102
+ def match?(input, **_options)
101
103
  File.extname(input) == '.xml'
102
104
  end
103
105
 
@@ -107,7 +109,7 @@ module Node
107
109
  end
108
110
  end
109
111
 
110
- def terminate?(_output, **)
112
+ def terminate?(_output, *_inputs, **_options)
111
113
  true
112
114
  end
113
115
  end
@@ -125,11 +127,11 @@ module Node
125
127
  Identity.new(klass: super, type: @type)
126
128
  end
127
129
 
128
- def match?(input, **options)
130
+ def match?(input, **_options)
129
131
  !input.get_elements("//#{@type}").empty?
130
132
  end
131
133
 
132
- def call(input, **options)
134
+ def call(input, **_options)
133
135
  input
134
136
  .get_elements("//#{@type}")
135
137
  .first
@@ -151,7 +153,7 @@ tree = CallableTree::Node::Root.new.append(
151
153
  )
152
154
  )
153
155
 
154
- Dir.glob(__dir__ + '/docs/*') do |file|
156
+ Dir.glob("#{__dir__}/docs/*") do |file|
155
157
  options = { foo: :bar }
156
158
  pp tree.call(file, **options)
157
159
  puts '---'
@@ -3,6 +3,7 @@
3
3
  module CallableTree
4
4
  module Node
5
5
  module External
6
+ # TODO: Add :inputs
6
7
  Output = Struct.new(:value, :options, :routes)
7
8
 
8
9
  module Verbose
@@ -10,8 +11,8 @@ module CallableTree
10
11
  true
11
12
  end
12
13
 
13
- def call(input = nil, **options)
14
- output = super(input, **options)
14
+ def call(*inputs, **options)
15
+ output = super(*inputs, **options)
15
16
  routes = self.routes
16
17
 
17
18
  Output.new(output, options, routes)
@@ -37,6 +37,10 @@ module CallableTree
37
37
  .class
38
38
  end
39
39
 
40
+ def outline
41
+ { identity => nil }
42
+ end
43
+
40
44
  private
41
45
 
42
46
  def initialize_copy(_node)
@@ -23,15 +23,17 @@ module CallableTree
23
23
  self
24
24
  end
25
25
 
26
- def call(input = nil, **options)
27
- input = before_callbacks.reduce(input) do |input, callable|
28
- callable.call(input, self, **options)
26
+ def call(*inputs, **options)
27
+ input_head, *input_tail = inputs
28
+
29
+ input_head = before_callbacks.reduce(input_head) do |input_head, callable|
30
+ callable.call(input_head, *input_tail, self, **options)
29
31
  end
30
32
 
31
- output = super(input, **options)
33
+ output = super(input_head, *input_tail, **options)
32
34
 
33
35
  output = around_callbacks.reduce(output) do |output, callable|
34
- callable.call(input, self, **options) { output }
36
+ callable.call(input_head, *input_tail, self, **options) { output }
35
37
  end
36
38
 
37
39
  after_callbacks.reduce(output) do |output, callable|
@@ -5,9 +5,11 @@ module CallableTree
5
5
  module Internal
6
6
  module Strategy
7
7
  class Broadcast
8
- def call(nodes, input:, options:)
8
+ include Strategy
9
+
10
+ def call(nodes, *inputs, **options)
9
11
  nodes.map do |node|
10
- node.call(input, **options) if node.match?(input, **options)
12
+ node.call(*inputs, **options) if node.match?(*inputs, **options)
11
13
  end
12
14
  end
13
15
  end
@@ -5,10 +5,14 @@ module CallableTree
5
5
  module Internal
6
6
  module Strategy
7
7
  class Compose
8
- def call(nodes, input:, options:)
9
- nodes.reduce(input) do |input, node|
10
- if node.match?(input, **options)
11
- node.call(input, **options)
8
+ include Strategy
9
+
10
+ def call(nodes, *inputs, **options)
11
+ head, *tail = inputs
12
+ nodes.reduce(head) do |input, node|
13
+ inputs = [input, *tail]
14
+ if node.match?(*inputs, **options)
15
+ node.call(*inputs, **options)
12
16
  else
13
17
  input
14
18
  end
@@ -5,13 +5,15 @@ module CallableTree
5
5
  module Internal
6
6
  module Strategy
7
7
  class Seek
8
- def call(nodes, input:, options:)
8
+ include Strategy
9
+
10
+ def call(nodes, *inputs, **options)
9
11
  nodes
10
12
  .lazy
11
- .select { |node| node.match?(input, **options) }
13
+ .select { |node| node.match?(*inputs, **options) }
12
14
  .map do |node|
13
- output = node.call(input, **options)
14
- terminated = node.terminate?(output, **options)
15
+ output = node.call(*inputs, **options)
16
+ terminated = node.terminate?(output, *inputs, **options)
15
17
  [output, terminated]
16
18
  end
17
19
  .select { |_output, terminated| terminated }
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CallableTree
4
+ module Node
5
+ module Internal
6
+ module Strategy
7
+ def call(_nodes, *_inputs, **_options)
8
+ raise ::CallableTree::Error, 'Not implemented'
9
+ end
10
+
11
+ def name
12
+ @name ||= self.class.name.split('::').last.downcase.to_sym
13
+ end
14
+
15
+ def ==(other)
16
+ name == other.name
17
+ end
18
+
19
+ def eql?(other)
20
+ instance_of?(other.class) && self == other
21
+ end
22
+
23
+ def hash
24
+ self.class.name.hash
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,10 +3,17 @@
3
3
  module CallableTree
4
4
  module Node
5
5
  module Internal
6
+ extend ::Forwardable
6
7
  include Node
7
8
 
9
+ def_delegators :child_nodes, :[], :at
10
+
8
11
  def children
9
- @children ||= []
12
+ [*child_nodes]
13
+ end
14
+
15
+ def children!
16
+ child_nodes
10
17
  end
11
18
 
12
19
  def append(*callables)
@@ -18,67 +25,121 @@ module CallableTree
18
25
  def append!(*callables)
19
26
  callables
20
27
  .map { |callable| nodeify(callable) }
21
- .tap { |nodes| children.push(*nodes) } # Use Array#push for Ruby 2.4
28
+ .tap { |nodes| child_nodes.push(*nodes) }
29
+
30
+ self
31
+ end
32
+
33
+ def reject(&block)
34
+ clone.tap do |node|
35
+ node.reject!(&block)
36
+ end
37
+ end
22
38
 
39
+ def reject!(&block)
40
+ child_nodes.reject!(&block)
23
41
  self
24
42
  end
25
43
 
26
- def match?(_input = nil, **_options)
27
- !children.empty?
44
+ def shake(&block)
45
+ clone.tap do |node|
46
+ node.shake!(&block)
47
+ end
48
+ end
49
+
50
+ def shake!(&block)
51
+ reject!(&block) if block_given?
52
+
53
+ reject! do |node|
54
+ node.is_a?(Internal) && node.shake!(&block).child_nodes.empty?
55
+ end
56
+ end
57
+
58
+ def match?(*, **)
59
+ !child_nodes.empty?
28
60
  end
29
61
 
30
- def call(input = nil, **options)
31
- strategy.call(children, input: input, options: options)
62
+ def call(*inputs, **options)
63
+ strategy.call(child_nodes, *inputs, **options)
64
+ end
65
+
66
+ def seek?
67
+ strategy.is_a?(Strategy::Seek)
32
68
  end
33
69
 
34
70
  def seek
35
- if strategy.is_a?(Strategy::Seek)
71
+ if seek?
36
72
  self
37
73
  else
38
74
  clone.tap do |node|
39
- node.send(:strategy=, Strategy::Seek.new)
75
+ node.strategy = Strategy::Seek.new
40
76
  end
41
77
  end
42
78
  end
43
79
 
44
80
  def seek!
45
- self.strategy = Strategy::Seek.new unless strategy.is_a?(Strategy::Seek)
46
- self
81
+ tap do |node|
82
+ node.strategy = Strategy::Seek.new unless seek?
83
+ end
84
+ end
85
+
86
+ def broadcast?
87
+ strategy.is_a?(Strategy::Broadcast)
47
88
  end
48
89
 
49
90
  def broadcast
50
- if strategy.is_a?(Strategy::Broadcast)
91
+ if broadcast?
51
92
  self
52
93
  else
53
94
  clone.tap do |node|
54
- node.send(:strategy=, Strategy::Broadcast.new)
95
+ node.strategy = Strategy::Broadcast.new
55
96
  end
56
97
  end
57
98
  end
58
99
 
59
100
  def broadcast!
60
- self.strategy = Strategy::Broadcast.new unless strategy.is_a?(Strategy::Broadcast)
61
- self
101
+ tap do |node|
102
+ node.strategy = Strategy::Broadcast.new unless broadcast?
103
+ end
104
+ end
105
+
106
+ def compose?
107
+ strategy.is_a?(Strategy::Compose)
62
108
  end
63
109
 
64
110
  def compose
65
- if strategy.is_a?(Strategy::Compose)
111
+ if compose?
66
112
  self
67
113
  else
68
114
  clone.tap do |node|
69
- node.send(:strategy=, Strategy::Compose.new)
115
+ node.strategy = Strategy::Compose.new
70
116
  end
71
117
  end
72
118
  end
73
119
 
74
120
  def compose!
75
- self.strategy = Strategy::Compose.new unless strategy.is_a?(Strategy::Compose)
76
- self
121
+ tap do |node|
122
+ node.strategy = Strategy::Compose.new unless compose?
123
+ end
124
+ end
125
+
126
+ def outline(&block)
127
+ key = block ? block.call(self) : identity
128
+ value = child_nodes.reduce({}) { |memo, node| memo.merge!(node.outline(&block)) }
129
+ { key => value }
130
+ end
131
+
132
+ protected
133
+
134
+ attr_writer :strategy
135
+
136
+ def child_nodes
137
+ @child_nodes ||= []
77
138
  end
78
139
 
79
140
  private
80
141
 
81
- attr_writer :children, :strategy
142
+ attr_writer :child_nodes
82
143
 
83
144
  def nodeify(callable)
84
145
  if callable.is_a?(Node)
@@ -86,7 +147,7 @@ module CallableTree
86
147
  else
87
148
  External.proxify(callable)
88
149
  end
89
- .tap { |node| node.send(:parent=, self) }
150
+ .tap { |node| node.parent = self }
90
151
  end
91
152
 
92
153
  def strategy
@@ -96,8 +157,8 @@ module CallableTree
96
157
  def initialize_copy(_node)
97
158
  super
98
159
  self.parent = nil
99
- self.children = children.map do |node|
100
- node.clone.tap { |new_node| new_node.send(:parent=, self) }
160
+ self.child_nodes = child_nodes.map do |node|
161
+ node.clone.tap { |new_node| new_node.parent = self }
101
162
  end
102
163
  end
103
164
  end
@@ -30,19 +30,23 @@ module CallableTree
30
30
  root? ? 0 : parent.depth + 1
31
31
  end
32
32
 
33
- def match?(_input = nil, **_options)
33
+ def outline
34
+ raise ::CallableTree::Error, 'Not implemented'
35
+ end
36
+
37
+ def match?(*_inputs, **_options)
34
38
  true
35
39
  end
36
40
 
37
- def call(_input = nil, **_options)
41
+ def call(*_inputs, **_options)
38
42
  raise ::CallableTree::Error, 'Not implemented'
39
43
  end
40
44
 
41
- def terminate?(output = nil, **_options)
45
+ def terminate?(output, *_inputs, **_options)
42
46
  !output.nil?
43
47
  end
44
48
 
45
- private
49
+ protected
46
50
 
47
51
  attr_writer :parent
48
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CallableTree
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.1'
5
5
  end
data/lib/callable_tree.rb CHANGED
@@ -8,6 +8,7 @@ require 'forwardable'
8
8
  require_relative 'callable_tree/version'
9
9
  require_relative 'callable_tree/node'
10
10
  require_relative 'callable_tree/node/hooks/call'
11
+ require_relative 'callable_tree/node/internal/strategy'
11
12
  require_relative 'callable_tree/node/internal/strategy/broadcast'
12
13
  require_relative 'callable_tree/node/internal/strategy/seek'
13
14
  require_relative 'callable_tree/node/internal/strategy/compose'
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.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - jsmmr
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-24 00:00:00.000000000 Z
11
+ date: 2022-01-10 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
@@ -20,6 +20,7 @@ extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
22
  - ".github/workflows/build.yml"
23
+ - ".github/workflows/codeql-analysis.yml"
23
24
  - ".gitignore"
24
25
  - ".rspec"
25
26
  - ".ruby-version"
@@ -49,6 +50,7 @@ files:
49
50
  - lib/callable_tree/node/external/verbose.rb
50
51
  - lib/callable_tree/node/hooks/call.rb
51
52
  - lib/callable_tree/node/internal.rb
53
+ - lib/callable_tree/node/internal/strategy.rb
52
54
  - lib/callable_tree/node/internal/strategy/broadcast.rb
53
55
  - lib/callable_tree/node/internal/strategy/compose.rb
54
56
  - lib/callable_tree/node/internal/strategy/seek.rb
@@ -60,7 +62,7 @@ licenses:
60
62
  metadata:
61
63
  homepage_uri: https://github.com/jsmmr/ruby_callable_tree
62
64
  source_code_uri: https://github.com/jsmmr/ruby_callable_tree
63
- changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.2.1/CHANGELOG.md
65
+ changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.1/CHANGELOG.md
64
66
  post_install_message:
65
67
  rdoc_options: []
66
68
  require_paths:
@@ -76,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
78
  - !ruby/object:Gem::Version
77
79
  version: '0'
78
80
  requirements: []
79
- rubygems_version: 3.2.16
81
+ rubygems_version: 3.3.3
80
82
  signing_key:
81
83
  specification_version: 4
82
84
  summary: Builds a tree by linking callable nodes. The nodes that match the conditions