callable_tree 0.3.8 → 0.3.10
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 +4 -4
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/build.yml +4 -6
- data/.github/workflows/codeql-analysis.yml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +14 -14
- data/README.md +191 -265
- data/examples/builder/external-verbosify.rb +87 -0
- data/examples/builder/identity.rb +91 -0
- data/examples/builder/internal-broadcastable.rb +11 -11
- data/examples/builder/internal-composable.rb +11 -11
- data/examples/builder/internal-seekable.rb +10 -16
- data/examples/builder/logging.rb +21 -27
- data/examples/class/external-verbosify.rb +2 -4
- data/examples/class/identity.rb +2 -4
- data/examples/class/internal-seekable.rb +2 -4
- data/examples/class/logging.rb +15 -17
- data/lib/callable_tree/node/external.rb +4 -4
- data/lib/callable_tree/node/internal/strategy/broadcast.rb +12 -2
- data/lib/callable_tree/node/internal/strategy/compose.rb +10 -3
- data/lib/callable_tree/node/internal/strategy/seek.rb +7 -2
- data/lib/callable_tree/node/internal/strategy.rb +32 -2
- data/lib/callable_tree/node/internal/strategyable.rb +117 -0
- data/lib/callable_tree/node/internal.rb +13 -92
- data/lib/callable_tree/node.rb +1 -1
- data/lib/callable_tree/version.rb +1 -1
- data/lib/callable_tree.rb +1 -0
- metadata +8 -4
data/README.md
CHANGED
@@ -24,7 +24,7 @@ Or install it yourself as:
|
|
24
24
|
Builds a tree by linking `CallableTree` node instances. The `call` methods of the nodes where the `match?` method returns a truthy value are called in a chain from the root node to the leaf node.
|
25
25
|
|
26
26
|
- `CallableTree::Node::Internal`
|
27
|
-
- This `module` is used to define a node that can have child nodes.
|
27
|
+
- This `module` is used to define a node that can have child nodes. This node has several strategies (`seekable`, `broadcastable`, `composable`).
|
28
28
|
- `CallableTree::Node::External`
|
29
29
|
- This `module` is used to define a leaf node that cannot have child nodes.
|
30
30
|
- `CallableTree::Node::Root`
|
@@ -32,7 +32,7 @@ Builds a tree by linking `CallableTree` node instances. The `call` methods of th
|
|
32
32
|
|
33
33
|
### Basic
|
34
34
|
|
35
|
-
There are two ways to define the nodes: class style and builder style
|
35
|
+
There are two ways to define the nodes: class style and builder style.
|
36
36
|
|
37
37
|
#### `CallableTree::Node::Internal#seekable` (default strategy)
|
38
38
|
|
@@ -81,8 +81,7 @@ module Node
|
|
81
81
|
|
82
82
|
def call(input, **_options)
|
83
83
|
input[@type.to_s]
|
84
|
-
.
|
85
|
-
.to_h
|
84
|
+
.to_h { |element| [element['name'], element['emoji']] }
|
86
85
|
end
|
87
86
|
end
|
88
87
|
end
|
@@ -126,8 +125,7 @@ module Node
|
|
126
125
|
input
|
127
126
|
.get_elements("//#{@type}")
|
128
127
|
.first
|
129
|
-
.
|
130
|
-
.to_h
|
128
|
+
.to_h { |element| [element['name'], element['emoji']] }
|
131
129
|
end
|
132
130
|
end
|
133
131
|
end
|
@@ -165,7 +163,7 @@ Run `examples/class/internal-seekable.rb`:
|
|
165
163
|
---
|
166
164
|
```
|
167
165
|
|
168
|
-
##### Builder style
|
166
|
+
##### Builder style
|
169
167
|
|
170
168
|
`examples/builder/internal-seekable.rb`:
|
171
169
|
```ruby
|
@@ -175,16 +173,14 @@ JSONParser =
|
|
175
173
|
.matcher do |input, **_options|
|
176
174
|
File.extname(input) == '.json'
|
177
175
|
end
|
178
|
-
.caller do |input, **options, &
|
176
|
+
.caller do |input, **options, &original|
|
179
177
|
File.open(input) do |file|
|
180
178
|
json = ::JSON.load(file)
|
181
179
|
# The following block call is equivalent to calling `super` in the class style.
|
182
|
-
|
180
|
+
original.call(json, **options)
|
183
181
|
end
|
184
182
|
end
|
185
|
-
.terminator
|
186
|
-
true
|
187
|
-
end
|
183
|
+
.terminator { true }
|
188
184
|
.build
|
189
185
|
|
190
186
|
XMLParser =
|
@@ -193,15 +189,13 @@ XMLParser =
|
|
193
189
|
.matcher do |input, **_options|
|
194
190
|
File.extname(input) == '.xml'
|
195
191
|
end
|
196
|
-
.caller do |input, **options, &
|
192
|
+
.caller do |input, **options, &original|
|
197
193
|
File.open(input) do |file|
|
198
194
|
# The following block call is equivalent to calling `super` in the class style.
|
199
|
-
|
195
|
+
original.call(REXML::Document.new(file), **options)
|
200
196
|
end
|
201
197
|
end
|
202
|
-
.terminator
|
203
|
-
true
|
204
|
-
end
|
198
|
+
.terminator { true }
|
205
199
|
.build
|
206
200
|
|
207
201
|
def build_json_scraper(type)
|
@@ -212,8 +206,7 @@ def build_json_scraper(type)
|
|
212
206
|
end
|
213
207
|
.caller do |input, **_options|
|
214
208
|
input[type.to_s]
|
215
|
-
.
|
216
|
-
.to_h
|
209
|
+
.to_h { |element| [element['name'], element['emoji']] }
|
217
210
|
end
|
218
211
|
.build
|
219
212
|
end
|
@@ -231,8 +224,7 @@ def build_xml_scraper(type)
|
|
231
224
|
input
|
232
225
|
.get_elements("//#{type}")
|
233
226
|
.first
|
234
|
-
.
|
235
|
-
.to_h
|
227
|
+
.to_h { |element| [element['name'], element['emoji']] }
|
236
228
|
end
|
237
229
|
.build
|
238
230
|
end
|
@@ -273,7 +265,7 @@ Run `examples/builder/internal-seekable.rb`:
|
|
273
265
|
|
274
266
|
#### `CallableTree::Node::Internal#broadcastable`
|
275
267
|
|
276
|
-
This strategy
|
268
|
+
This strategy broadcasts to output a result of the child nodes as array. It also ignores their `terminate?` methods by default.
|
277
269
|
|
278
270
|
##### Class style
|
279
271
|
|
@@ -327,61 +319,61 @@ Run `examples/class/internal-broadcastable.rb`:
|
|
327
319
|
10 -> [nil, nil]
|
328
320
|
```
|
329
321
|
|
330
|
-
##### Builder style
|
322
|
+
##### Builder style
|
331
323
|
|
332
324
|
`examples/builder/internal-broadcastable.rb`:
|
333
325
|
```ruby
|
334
|
-
less_than
|
326
|
+
def less_than(num)
|
335
327
|
# The following block call is equivalent to calling `super` in the class style.
|
336
|
-
proc { |input, &
|
328
|
+
proc { |input, &original| original.call(input) && input < num }
|
337
329
|
end
|
338
330
|
|
339
331
|
LessThan5 =
|
340
332
|
CallableTree::Node::Internal::Builder
|
341
333
|
.new
|
342
|
-
.matcher(&less_than.call(5))
|
334
|
+
.matcher(&method(:less_than).call(5))
|
343
335
|
.build
|
344
336
|
|
345
337
|
LessThan10 =
|
346
338
|
CallableTree::Node::Internal::Builder
|
347
339
|
.new
|
348
|
-
.matcher(&less_than.call(10))
|
340
|
+
.matcher(&method(:less_than).call(10))
|
349
341
|
.build
|
350
342
|
|
351
|
-
add
|
343
|
+
def add(num)
|
352
344
|
proc { |input| input + num }
|
353
345
|
end
|
354
346
|
|
355
347
|
Add1 =
|
356
348
|
CallableTree::Node::External::Builder
|
357
349
|
.new
|
358
|
-
.caller(&add.call(1))
|
350
|
+
.caller(&method(:add).call(1))
|
359
351
|
.build
|
360
352
|
|
361
|
-
subtract
|
353
|
+
def subtract(num)
|
362
354
|
proc { |input| input - num }
|
363
355
|
end
|
364
356
|
|
365
357
|
Subtract1 =
|
366
358
|
CallableTree::Node::External::Builder
|
367
359
|
.new
|
368
|
-
.caller(&subtract.call(1))
|
360
|
+
.caller(&method(:subtract).call(1))
|
369
361
|
.build
|
370
362
|
|
371
|
-
multiply
|
363
|
+
def multiply(num)
|
372
364
|
proc { |input| input * num }
|
373
365
|
end
|
374
366
|
|
375
367
|
Multiply2 =
|
376
368
|
CallableTree::Node::External::Builder
|
377
369
|
.new
|
378
|
-
.caller(&multiply.call(2))
|
370
|
+
.caller(&method(:multiply).call(2))
|
379
371
|
.build
|
380
372
|
|
381
373
|
Multiply3 =
|
382
374
|
CallableTree::Node::External::Builder
|
383
375
|
.new
|
384
|
-
.caller(&multiply.call(3))
|
376
|
+
.caller(&method(:multiply).call(3))
|
385
377
|
.build
|
386
378
|
|
387
379
|
tree = CallableTree::Node::Root.new.broadcastable.append(
|
@@ -419,7 +411,8 @@ Run `examples/builder/internal-broadcastable.rb`:
|
|
419
411
|
|
420
412
|
#### `CallableTree::Node::Internal#composable`
|
421
413
|
|
422
|
-
This strategy
|
414
|
+
This strategy composes the child nodes to input the output of the previous node into the next node and to output a result.
|
415
|
+
It also ignores their `terminate?` methods by default.
|
423
416
|
|
424
417
|
##### Class style
|
425
418
|
|
@@ -473,61 +466,61 @@ Run `examples/class/internal-composable.rb`:
|
|
473
466
|
10 -> 10
|
474
467
|
```
|
475
468
|
|
476
|
-
##### Builder style
|
469
|
+
##### Builder style
|
477
470
|
|
478
471
|
`examples/builder/internal-composable.rb`:
|
479
472
|
```ruby
|
480
|
-
less_than
|
473
|
+
def less_than(num)
|
481
474
|
# The following block call is equivalent to calling `super` in the class style.
|
482
|
-
proc { |input, &
|
475
|
+
proc { |input, &original| original.call(input) && input < num }
|
483
476
|
end
|
484
477
|
|
485
478
|
LessThan5 =
|
486
479
|
CallableTree::Node::Internal::Builder
|
487
480
|
.new
|
488
|
-
.matcher(&less_than.call(5))
|
481
|
+
.matcher(&method(:less_than).call(5))
|
489
482
|
.build
|
490
483
|
|
491
484
|
LessThan10 =
|
492
485
|
CallableTree::Node::Internal::Builder
|
493
486
|
.new
|
494
|
-
.matcher(&less_than.call(10))
|
487
|
+
.matcher(&method(:less_than).call(10))
|
495
488
|
.build
|
496
489
|
|
497
|
-
add
|
490
|
+
def add(num)
|
498
491
|
proc { |input| input + num }
|
499
492
|
end
|
500
493
|
|
501
494
|
Add1 =
|
502
495
|
CallableTree::Node::External::Builder
|
503
496
|
.new
|
504
|
-
.caller(&add.call(1))
|
497
|
+
.caller(&method(:add).call(1))
|
505
498
|
.build
|
506
499
|
|
507
|
-
subtract
|
500
|
+
def subtract(num)
|
508
501
|
proc { |input| input - num }
|
509
502
|
end
|
510
503
|
|
511
504
|
Subtract1 =
|
512
505
|
CallableTree::Node::External::Builder
|
513
506
|
.new
|
514
|
-
.caller(&subtract.call(1))
|
507
|
+
.caller(&method(:subtract).call(1))
|
515
508
|
.build
|
516
509
|
|
517
|
-
multiply
|
510
|
+
def multiply(num)
|
518
511
|
proc { |input| input * num }
|
519
512
|
end
|
520
513
|
|
521
514
|
Multiply2 =
|
522
515
|
CallableTree::Node::External::Builder
|
523
516
|
.new
|
524
|
-
.caller(&multiply.call(2))
|
517
|
+
.caller(&method(:multiply).call(2))
|
525
518
|
.build
|
526
519
|
|
527
520
|
Multiply3 =
|
528
521
|
CallableTree::Node::External::Builder
|
529
522
|
.new
|
530
|
-
.caller(&multiply.call(3))
|
523
|
+
.caller(&method(:multiply).call(3))
|
531
524
|
.build
|
532
525
|
|
533
526
|
tree = CallableTree::Node::Root.new.composable.append(
|
@@ -569,311 +562,244 @@ Run `examples/builder/internal-composable.rb`:
|
|
569
562
|
|
570
563
|
If you want verbose output results, call this method.
|
571
564
|
|
572
|
-
`examples/
|
565
|
+
`examples/builder/external-verbosify.rb`:
|
573
566
|
```ruby
|
574
567
|
...
|
575
568
|
|
576
|
-
tree = CallableTree::Node::Root.new.append(
|
577
|
-
|
578
|
-
|
579
|
-
|
569
|
+
tree = CallableTree::Node::Root.new.seekable.append(
|
570
|
+
JSONParser.new.seekable.append(
|
571
|
+
AnimalsJSONScraper.new.verbosify,
|
572
|
+
FruitsJSONScraper.new.verbosify
|
580
573
|
),
|
581
|
-
|
582
|
-
|
583
|
-
|
574
|
+
XMLParser.new.seekable.append(
|
575
|
+
AnimalsXMLScraper.new.verbosify,
|
576
|
+
FruitsXMLScraper.new.verbosify
|
584
577
|
)
|
585
578
|
)
|
586
579
|
|
587
580
|
...
|
588
581
|
```
|
589
582
|
|
590
|
-
Run `examples/
|
583
|
+
Run `examples/builder/external-verbosify.rb`:
|
591
584
|
```sh
|
592
585
|
% ruby examples/class/external-verbosify.rb
|
593
586
|
#<struct CallableTree::Node::External::Output
|
594
|
-
|
595
|
-
|
596
|
-
|
587
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
588
|
+
options={:foo=>:bar},
|
589
|
+
routes=[AnimalsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
597
590
|
---
|
598
591
|
#<struct CallableTree::Node::External::Output
|
599
|
-
|
600
|
-
|
601
|
-
|
592
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
593
|
+
options={:foo=>:bar},
|
594
|
+
routes=[AnimalsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
602
595
|
---
|
603
596
|
#<struct CallableTree::Node::External::Output
|
604
|
-
|
605
|
-
|
606
|
-
|
597
|
+
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
598
|
+
options={:foo=>:bar},
|
599
|
+
routes=[FruitsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
607
600
|
---
|
608
601
|
#<struct CallableTree::Node::External::Output
|
609
|
-
|
610
|
-
|
611
|
-
|
602
|
+
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
603
|
+
options={:foo=>:bar},
|
604
|
+
routes=[FruitsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
612
605
|
---
|
613
606
|
```
|
614
607
|
|
615
|
-
|
616
|
-
You can work around it by overriding the `identity` method of the node.
|
617
|
-
|
618
|
-
#### `CallableTree::Node#identity`
|
608
|
+
#### Logging
|
619
609
|
|
620
|
-
|
610
|
+
This is an example of logging.
|
621
611
|
|
622
|
-
`examples/
|
612
|
+
`examples/builder/logging.rb`:
|
623
613
|
```ruby
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
@type = type
|
631
|
-
end
|
614
|
+
JSONParser =
|
615
|
+
CallableTree::Node::Internal::Builder
|
616
|
+
.new
|
617
|
+
...
|
618
|
+
.hookable
|
619
|
+
.build
|
632
620
|
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
621
|
+
XMLParser =
|
622
|
+
CallableTree::Node::Internal::Builder
|
623
|
+
.new
|
624
|
+
...
|
625
|
+
.hookable
|
626
|
+
.build
|
637
627
|
|
638
|
-
|
628
|
+
def build_json_scraper(type)
|
629
|
+
CallableTree::Node::External::Builder
|
630
|
+
.new
|
639
631
|
...
|
632
|
+
.hookable
|
633
|
+
.build
|
634
|
+
end
|
640
635
|
|
641
|
-
|
642
|
-
include CallableTree::Node::External
|
636
|
+
...
|
643
637
|
|
644
|
-
|
645
|
-
|
646
|
-
|
638
|
+
def build_xml_scraper(type)
|
639
|
+
CallableTree::Node::External::Builder
|
640
|
+
.new
|
641
|
+
...
|
642
|
+
.hookable
|
643
|
+
.build
|
644
|
+
end
|
647
645
|
|
648
|
-
|
649
|
-
Identity.new(klass: super, type: @type)
|
650
|
-
end
|
646
|
+
...
|
651
647
|
|
652
|
-
|
653
|
-
|
654
|
-
|
648
|
+
module Logging
|
649
|
+
INDENT_SIZE = 2
|
650
|
+
BLANK = ' '
|
651
|
+
LIST_STYLE = '*'
|
652
|
+
INPUT_LABEL = 'Input :'
|
653
|
+
OUTPUT_LABEL = 'Output:'
|
655
654
|
|
656
|
-
|
657
|
-
|
655
|
+
def self.loggable(node)
|
656
|
+
node.after_matcher! do |matched, _node_:, **|
|
657
|
+
prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
|
658
|
+
puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
|
659
|
+
matched
|
660
|
+
end
|
658
661
|
|
659
|
-
|
660
|
-
include CallableTree::Node::External
|
662
|
+
return unless node.external?
|
661
663
|
|
662
|
-
|
663
|
-
|
664
|
+
node
|
665
|
+
.before_caller! do |input, *, _node_:, **|
|
666
|
+
input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
|
667
|
+
puts "#{input_prefix} #{input}"
|
668
|
+
input
|
664
669
|
end
|
665
|
-
|
666
|
-
|
667
|
-
|
670
|
+
.after_caller! do |output, _node_:, **|
|
671
|
+
output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
|
672
|
+
puts "#{output_prefix} #{output}"
|
673
|
+
output
|
668
674
|
end
|
669
|
-
|
670
|
-
...
|
671
|
-
end
|
672
675
|
end
|
673
676
|
end
|
674
677
|
|
678
|
+
loggable = Logging.method(:loggable)
|
679
|
+
|
680
|
+
tree = CallableTree::Node::Root.new.seekable.append(
|
681
|
+
JSONParser.new.tap(&loggable).seekable.append(
|
682
|
+
AnimalsJSONScraper.new.tap(&loggable).verbosify,
|
683
|
+
FruitsJSONScraper.new.tap(&loggable).verbosify
|
684
|
+
),
|
685
|
+
XMLParser.new.tap(&loggable).seekable.append(
|
686
|
+
AnimalsXMLScraper.new.tap(&loggable).verbosify,
|
687
|
+
FruitsXMLScraper.new.tap(&loggable).verbosify
|
688
|
+
)
|
689
|
+
)
|
690
|
+
|
675
691
|
...
|
676
692
|
```
|
677
693
|
|
678
|
-
|
694
|
+
Also, see `examples/builder/hooks.rb` for detail about `CallableTree::Node::Hooks::*`.
|
695
|
+
|
696
|
+
Run `examples/builder/logging.rb`:
|
679
697
|
```sh
|
680
|
-
% ruby examples/
|
698
|
+
% ruby examples/builder/logging.rb
|
699
|
+
* JSONParser: [matched: true]
|
700
|
+
* AnimalsJSONScraper: [matched: true]
|
701
|
+
Input : {"animals"=>[{"name"=>"Dog", "emoji"=>"🐶"}, {"name"=>"Cat", "emoji"=>"🐱"}]}
|
702
|
+
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
681
703
|
#<struct CallableTree::Node::External::Output
|
682
704
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
683
705
|
options={:foo=>:bar},
|
684
|
-
routes=
|
685
|
-
[#<Node::Identity:0x00007fb4378a9718
|
686
|
-
@klass=Node::JSON::Scraper,
|
687
|
-
@type=:animals>,
|
688
|
-
Node::JSON::Parser,
|
689
|
-
CallableTree::Node::Root]>
|
706
|
+
routes=[AnimalsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
690
707
|
---
|
708
|
+
* JSONParser: [matched: false]
|
709
|
+
* XMLParser: [matched: true]
|
710
|
+
* AnimalsXMLScraper: [matched: true]
|
711
|
+
Input : <root><animals><animal emoji='🐶' name='Dog'/><animal emoji='🐱' name='Cat'/></animals></root>
|
712
|
+
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
691
713
|
#<struct CallableTree::Node::External::Output
|
692
714
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
693
715
|
options={:foo=>:bar},
|
694
|
-
routes=
|
695
|
-
[#<Node::Identity:0x00007fb41002b6d0
|
696
|
-
@klass=Node::XML::Scraper,
|
697
|
-
@type=:animals>,
|
698
|
-
Node::XML::Parser,
|
699
|
-
CallableTree::Node::Root]>
|
716
|
+
routes=[AnimalsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
700
717
|
---
|
718
|
+
* JSONParser: [matched: true]
|
719
|
+
* AnimalsJSONScraper: [matched: false]
|
720
|
+
* FruitsJSONScraper: [matched: true]
|
721
|
+
Input : {"fruits"=>[{"name"=>"Red Apple", "emoji"=>"🍎"}, {"name"=>"Green Apple", "emoji"=>"🍏"}]}
|
722
|
+
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
701
723
|
#<struct CallableTree::Node::External::Output
|
702
724
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
703
725
|
options={:foo=>:bar},
|
704
|
-
routes=
|
705
|
-
[#<Node::Identity:0x00007fb41001b3e8
|
706
|
-
@klass=Node::JSON::Scraper,
|
707
|
-
@type=:fruits>,
|
708
|
-
Node::JSON::Parser,
|
709
|
-
CallableTree::Node::Root]>
|
726
|
+
routes=[FruitsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
710
727
|
---
|
728
|
+
* JSONParser: [matched: false]
|
729
|
+
* XMLParser: [matched: true]
|
730
|
+
* AnimalsXMLScraper: [matched: false]
|
731
|
+
* FruitsXMLScraper: [matched: true]
|
732
|
+
Input : <root><fruits><fruit emoji='🍎' name='Red Apple'/><fruit emoji='🍏' name='Green Apple'/></fruits></root>
|
733
|
+
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
711
734
|
#<struct CallableTree::Node::External::Output
|
712
735
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
713
736
|
options={:foo=>:bar},
|
714
|
-
routes=
|
715
|
-
[#<Node::Identity:0x00007fb410049d38
|
716
|
-
@klass=Node::XML::Scraper,
|
717
|
-
@type=:fruits>,
|
718
|
-
Node::XML::Parser,
|
719
|
-
CallableTree::Node::Root]>
|
720
|
-
---
|
737
|
+
routes=[FruitsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
721
738
|
```
|
722
739
|
|
723
|
-
####
|
740
|
+
#### `CallableTree::Node#identity`
|
724
741
|
|
725
|
-
|
742
|
+
If you want to customize the node identity, specify identifier.
|
726
743
|
|
727
|
-
`examples/
|
744
|
+
`examples/builder/identity.rb`:
|
728
745
|
```ruby
|
729
|
-
|
746
|
+
JSONParser =
|
747
|
+
CallableTree::Node::Internal::Builder
|
748
|
+
.new
|
730
749
|
...
|
750
|
+
.identifier { |_node_:| _node_.object_id }
|
751
|
+
.build
|
731
752
|
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
end
|
739
|
-
|
740
|
-
class Scraper
|
741
|
-
include CallableTree::Node::External
|
742
|
-
prepend CallableTree::Node::Hooks::Matcher
|
743
|
-
prepend CallableTree::Node::Hooks::Caller
|
744
|
-
|
745
|
-
...
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
module XML
|
750
|
-
class Parser
|
751
|
-
include CallableTree::Node::Internal
|
752
|
-
prepend CallableTree::Node::Hooks::Matcher
|
753
|
-
|
754
|
-
...
|
755
|
-
end
|
756
|
-
|
757
|
-
class Scraper
|
758
|
-
include CallableTree::Node::External
|
759
|
-
prepend CallableTree::Node::Hooks::Matcher
|
760
|
-
prepend CallableTree::Node::Hooks::Caller
|
753
|
+
XMLParser =
|
754
|
+
CallableTree::Node::Internal::Builder
|
755
|
+
.new
|
756
|
+
...
|
757
|
+
.identifier { |_node_:| _node_.object_id }
|
758
|
+
.build
|
761
759
|
|
762
|
-
|
763
|
-
|
764
|
-
|
760
|
+
def build_json_scraper(type)
|
761
|
+
CallableTree::Node::External::Builder
|
762
|
+
.new
|
763
|
+
...
|
764
|
+
.identifier { |_node_:| _node_.object_id }
|
765
|
+
.build
|
765
766
|
end
|
766
767
|
|
767
|
-
|
768
|
-
INDENT_SIZE = 2
|
769
|
-
BLANK = ' '
|
770
|
-
LIST_STYLE = '*'
|
771
|
-
INPUT_LABEL = 'Input :'
|
772
|
-
OUTPUT_LABEL = 'Output:'
|
773
|
-
|
774
|
-
def self.loggable(node)
|
775
|
-
node.after_matcher! do |matched, _node_:, **|
|
776
|
-
prefix = LIST_STYLE.rjust(_node_.depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
|
777
|
-
puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
|
778
|
-
matched
|
779
|
-
end
|
768
|
+
...
|
780
769
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
end
|
788
|
-
.after_caller! do |output, _node_:, **|
|
789
|
-
output_prefix = OUTPUT_LABEL.rjust(_node_.depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
|
790
|
-
puts "#{output_prefix} #{output}"
|
791
|
-
output
|
792
|
-
end
|
793
|
-
end
|
794
|
-
end
|
770
|
+
def build_xml_scraper(type)
|
771
|
+
CallableTree::Node::External::Builder
|
772
|
+
.new
|
773
|
+
...
|
774
|
+
.identifier { |_node_:| _node_.object_id }
|
775
|
+
.build
|
795
776
|
end
|
796
777
|
|
797
|
-
loggable = Logging.method(:loggable)
|
798
|
-
|
799
|
-
tree = CallableTree::Node::Root.new.append(
|
800
|
-
Node::JSON::Parser.new.tap(&loggable).append(
|
801
|
-
Node::JSON::Scraper.new(type: :animals).tap(&loggable).verbosify,
|
802
|
-
Node::JSON::Scraper.new(type: :fruits).tap(&loggable).verbosify
|
803
|
-
),
|
804
|
-
Node::XML::Parser.new.tap(&loggable).append(
|
805
|
-
Node::XML::Scraper.new(type: :animals).tap(&loggable).verbosify,
|
806
|
-
Node::XML::Scraper.new(type: :fruits).tap(&loggable).verbosify
|
807
|
-
)
|
808
|
-
)
|
809
|
-
|
810
778
|
...
|
811
779
|
```
|
812
780
|
|
813
|
-
|
814
|
-
|
815
|
-
Run `examples/class/logging.rb`:
|
781
|
+
Run `examples/builder/identity.rb`:
|
816
782
|
```sh
|
817
|
-
% ruby examples/
|
818
|
-
* Node::JSON::Parser: [matched: true]
|
819
|
-
* Node::JSON::Scraper(animals): [matched: true]
|
820
|
-
Input : {"animals"=>[{"name"=>"Dog", "emoji"=>"🐶"}, {"name"=>"Cat", "emoji"=>"🐱"}]}
|
821
|
-
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
783
|
+
% ruby examples/builder/identity.rb
|
822
784
|
#<struct CallableTree::Node::External::Output
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
[#<Node::Identity:0x00007ffd840347b8
|
827
|
-
@klass=Node::JSON::Scraper,
|
828
|
-
@type=:animals>,
|
829
|
-
Node::JSON::Parser,
|
830
|
-
CallableTree::Node::Root]>
|
785
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
786
|
+
options={:foo=>:bar},
|
787
|
+
routes=[60, 80, CallableTree::Node::Root]>
|
831
788
|
---
|
832
|
-
* Node::JSON::Parser: [matched: false]
|
833
|
-
* Node::XML::Parser: [matched: true]
|
834
|
-
* Node::XML::Scraper(animals): [matched: true]
|
835
|
-
Input : <root><animals><animal emoji='🐶' name='Dog'/><animal emoji='🐱' name='Cat'/></animals></root>
|
836
|
-
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
837
789
|
#<struct CallableTree::Node::External::Output
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
[#<Node::Identity:0x00007ffd7403f1f0
|
842
|
-
@klass=Node::XML::Scraper,
|
843
|
-
@type=:animals>,
|
844
|
-
Node::XML::Parser,
|
845
|
-
CallableTree::Node::Root]>
|
790
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
791
|
+
options={:foo=>:bar},
|
792
|
+
routes=[220, 240, CallableTree::Node::Root]>
|
846
793
|
---
|
847
|
-
* Node::JSON::Parser: [matched: true]
|
848
|
-
* Node::JSON::Scraper(animals): [matched: false]
|
849
|
-
* Node::JSON::Scraper(fruits): [matched: true]
|
850
|
-
Input : {"fruits"=>[{"name"=>"Red Apple", "emoji"=>"🍎"}, {"name"=>"Green Apple", "emoji"=>"🍏"}]}
|
851
|
-
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
852
794
|
#<struct CallableTree::Node::External::Output
|
853
795
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
854
796
|
options={:foo=>:bar},
|
855
|
-
routes=
|
856
|
-
[#<Node::Identity:0x00007ffd8512bdf0
|
857
|
-
@klass=Node::JSON::Scraper,
|
858
|
-
@type=:fruits>,
|
859
|
-
Node::JSON::Parser,
|
860
|
-
CallableTree::Node::Root]>
|
797
|
+
routes=[260, 80, CallableTree::Node::Root]>
|
861
798
|
---
|
862
|
-
* Node::JSON::Parser: [matched: false]
|
863
|
-
* Node::XML::Parser: [matched: true]
|
864
|
-
* Node::XML::Scraper(animals): [matched: false]
|
865
|
-
* Node::XML::Scraper(fruits): [matched: true]
|
866
|
-
Input : <root><fruits><fruit emoji='🍎' name='Red Apple'/><fruit emoji='🍏' name='Green Apple'/></fruits></root>
|
867
|
-
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
868
799
|
#<struct CallableTree::Node::External::Output
|
869
800
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
870
801
|
options={:foo=>:bar},
|
871
|
-
routes=
|
872
|
-
[#<Node::Identity:0x00007ffd8407a740
|
873
|
-
@klass=Node::XML::Scraper,
|
874
|
-
@type=:fruits>,
|
875
|
-
Node::XML::Parser,
|
876
|
-
CallableTree::Node::Root]>
|
802
|
+
routes=[400, 240, CallableTree::Node::Root]>
|
877
803
|
---
|
878
804
|
```
|
879
805
|
|