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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/build.yml +3 -5
- data/.github/workflows/codeql-analysis.yml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +3 -3
- data/README.md +183 -257
- 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 +9 -15
- data/examples/builder/logging.rb +7 -13
- 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 +2 -4
- data/lib/callable_tree/node/internal/strategy/broadcast.rb +19 -2
- data/lib/callable_tree/node/internal/strategy/compose.rb +15 -3
- data/lib/callable_tree/node/internal/strategy/seek.rb +11 -1
- data/lib/callable_tree/node/internal/strategy.rb +10 -2
- data/lib/callable_tree/node/internal.rb +22 -28
- data/lib/callable_tree/version.rb +1 -1
- metadata +6 -3
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,154 +562,46 @@ 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
|
-
|
612
|
-
---
|
613
|
-
```
|
614
|
-
|
615
|
-
At first glance, this looks good, but the `routes` are ambiguous when there are multiple nodes of the same class.
|
616
|
-
You can work around it by overriding the `identity` method of the node.
|
617
|
-
|
618
|
-
#### `CallableTree::Node#identity`
|
619
|
-
|
620
|
-
If you want to customize the node identity, override this method.
|
621
|
-
|
622
|
-
`examples/class/identity.rb`:
|
623
|
-
```ruby
|
624
|
-
module Node
|
625
|
-
class Identity
|
626
|
-
attr_reader :klass, :type
|
627
|
-
|
628
|
-
def initialize(klass:, type:)
|
629
|
-
@klass = klass
|
630
|
-
@type = type
|
631
|
-
end
|
632
|
-
|
633
|
-
def to_s
|
634
|
-
"#{klass}(#{type})"
|
635
|
-
end
|
636
|
-
end
|
637
|
-
|
638
|
-
module JSON
|
639
|
-
...
|
640
|
-
|
641
|
-
class Scraper
|
642
|
-
include CallableTree::Node::External
|
643
|
-
|
644
|
-
def initialize(type:)
|
645
|
-
@type = type
|
646
|
-
end
|
647
|
-
|
648
|
-
def identity
|
649
|
-
Identity.new(klass: super, type: @type)
|
650
|
-
end
|
651
|
-
|
652
|
-
...
|
653
|
-
end
|
654
|
-
end
|
655
|
-
|
656
|
-
module XML
|
657
|
-
...
|
658
|
-
|
659
|
-
class Scraper
|
660
|
-
include CallableTree::Node::External
|
661
|
-
|
662
|
-
def initialize(type:)
|
663
|
-
@type = type
|
664
|
-
end
|
665
|
-
|
666
|
-
def identity
|
667
|
-
Identity.new(klass: super, type: @type)
|
668
|
-
end
|
669
|
-
|
670
|
-
...
|
671
|
-
end
|
672
|
-
end
|
673
|
-
end
|
674
|
-
|
675
|
-
...
|
676
|
-
```
|
677
|
-
|
678
|
-
Run `examples/class/identity.rb`:
|
679
|
-
```sh
|
680
|
-
% ruby examples/class/identity.rb
|
681
|
-
#<struct CallableTree::Node::External::Output
|
682
|
-
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
683
|
-
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]>
|
690
|
-
---
|
691
|
-
#<struct CallableTree::Node::External::Output
|
692
|
-
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
693
|
-
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]>
|
700
|
-
---
|
701
|
-
#<struct CallableTree::Node::External::Output
|
702
|
-
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
703
|
-
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]>
|
710
|
-
---
|
711
|
-
#<struct CallableTree::Node::External::Output
|
712
|
-
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
713
|
-
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]>
|
602
|
+
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
603
|
+
options={:foo=>:bar},
|
604
|
+
routes=[FruitsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
720
605
|
---
|
721
606
|
```
|
722
607
|
|
@@ -724,46 +609,42 @@ Run `examples/class/identity.rb`:
|
|
724
609
|
|
725
610
|
This is an example of logging.
|
726
611
|
|
727
|
-
`examples/
|
612
|
+
`examples/builder/logging.rb`:
|
728
613
|
```ruby
|
729
|
-
|
614
|
+
JSONParser =
|
615
|
+
CallableTree::Node::Internal::Builder
|
616
|
+
.new
|
730
617
|
...
|
618
|
+
.hookable
|
619
|
+
.build
|
731
620
|
|
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
|
621
|
+
XMLParser =
|
622
|
+
CallableTree::Node::Internal::Builder
|
623
|
+
.new
|
624
|
+
...
|
625
|
+
.hookable
|
626
|
+
.build
|
753
627
|
|
754
|
-
|
755
|
-
|
628
|
+
def build_json_scraper(type)
|
629
|
+
CallableTree::Node::External::Builder
|
630
|
+
.new
|
631
|
+
...
|
632
|
+
.hookable
|
633
|
+
.build
|
634
|
+
end
|
756
635
|
|
757
|
-
|
758
|
-
include CallableTree::Node::External
|
759
|
-
prepend CallableTree::Node::Hooks::Matcher
|
760
|
-
prepend CallableTree::Node::Hooks::Caller
|
636
|
+
...
|
761
637
|
|
762
|
-
|
763
|
-
|
764
|
-
|
638
|
+
def build_xml_scraper(type)
|
639
|
+
CallableTree::Node::External::Builder
|
640
|
+
.new
|
641
|
+
...
|
642
|
+
.hookable
|
643
|
+
.build
|
765
644
|
end
|
766
645
|
|
646
|
+
...
|
647
|
+
|
767
648
|
module Logging
|
768
649
|
INDENT_SIZE = 2
|
769
650
|
BLANK = ' '
|
@@ -773,7 +654,7 @@ module Logging
|
|
773
654
|
|
774
655
|
def self.loggable(node)
|
775
656
|
node.after_matcher! do |matched, _node_:, **|
|
776
|
-
prefix = LIST_STYLE.rjust(_node_.depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
|
657
|
+
prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
|
777
658
|
puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
|
778
659
|
matched
|
779
660
|
end
|
@@ -781,12 +662,12 @@ module Logging
|
|
781
662
|
if node.external?
|
782
663
|
node
|
783
664
|
.before_caller! do |input, *, _node_:, **|
|
784
|
-
input_prefix = INPUT_LABEL.rjust(_node_.depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
|
665
|
+
input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
|
785
666
|
puts "#{input_prefix} #{input}"
|
786
667
|
input
|
787
668
|
end
|
788
669
|
.after_caller! do |output, _node_:, **|
|
789
|
-
output_prefix = OUTPUT_LABEL.rjust(_node_.depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
|
670
|
+
output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
|
790
671
|
puts "#{output_prefix} #{output}"
|
791
672
|
output
|
792
673
|
end
|
@@ -796,84 +677,129 @@ end
|
|
796
677
|
|
797
678
|
loggable = Logging.method(:loggable)
|
798
679
|
|
799
|
-
tree = CallableTree::Node::Root.new.append(
|
800
|
-
|
801
|
-
|
802
|
-
|
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
|
803
684
|
),
|
804
|
-
|
805
|
-
|
806
|
-
|
685
|
+
XMLParser.new.tap(&loggable).seekable.append(
|
686
|
+
AnimalsXMLScraper.new.tap(&loggable).verbosify,
|
687
|
+
FruitsXMLScraper.new.tap(&loggable).verbosify
|
807
688
|
)
|
808
689
|
)
|
809
690
|
|
810
691
|
...
|
811
692
|
```
|
812
693
|
|
813
|
-
Also, see `examples/
|
694
|
+
Also, see `examples/builder/hooks.rb` for detail about `CallableTree::Node::Hooks::*`.
|
814
695
|
|
815
|
-
Run `examples/
|
696
|
+
Run `examples/builder/logging.rb`:
|
816
697
|
```sh
|
817
|
-
% ruby examples/
|
818
|
-
*
|
819
|
-
*
|
698
|
+
% ruby examples/builder/logging.rb
|
699
|
+
* JSONParser: [matched: true]
|
700
|
+
* AnimalsJSONScraper: [matched: true]
|
820
701
|
Input : {"animals"=>[{"name"=>"Dog", "emoji"=>"🐶"}, {"name"=>"Cat", "emoji"=>"🐱"}]}
|
821
702
|
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
822
703
|
#<struct CallableTree::Node::External::Output
|
823
704
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
824
705
|
options={:foo=>:bar},
|
825
|
-
routes=
|
826
|
-
[#<Node::Identity:0x00007ffd840347b8
|
827
|
-
@klass=Node::JSON::Scraper,
|
828
|
-
@type=:animals>,
|
829
|
-
Node::JSON::Parser,
|
830
|
-
CallableTree::Node::Root]>
|
706
|
+
routes=[AnimalsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
831
707
|
---
|
832
|
-
*
|
833
|
-
*
|
834
|
-
*
|
708
|
+
* JSONParser: [matched: false]
|
709
|
+
* XMLParser: [matched: true]
|
710
|
+
* AnimalsXMLScraper: [matched: true]
|
835
711
|
Input : <root><animals><animal emoji='🐶' name='Dog'/><animal emoji='🐱' name='Cat'/></animals></root>
|
836
712
|
Output: {"Dog"=>"🐶", "Cat"=>"🐱"}
|
837
713
|
#<struct CallableTree::Node::External::Output
|
838
714
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
839
715
|
options={:foo=>:bar},
|
840
|
-
routes=
|
841
|
-
[#<Node::Identity:0x00007ffd7403f1f0
|
842
|
-
@klass=Node::XML::Scraper,
|
843
|
-
@type=:animals>,
|
844
|
-
Node::XML::Parser,
|
845
|
-
CallableTree::Node::Root]>
|
716
|
+
routes=[AnimalsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
846
717
|
---
|
847
|
-
*
|
848
|
-
*
|
849
|
-
*
|
718
|
+
* JSONParser: [matched: true]
|
719
|
+
* AnimalsJSONScraper: [matched: false]
|
720
|
+
* FruitsJSONScraper: [matched: true]
|
850
721
|
Input : {"fruits"=>[{"name"=>"Red Apple", "emoji"=>"🍎"}, {"name"=>"Green Apple", "emoji"=>"🍏"}]}
|
851
722
|
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
852
723
|
#<struct CallableTree::Node::External::Output
|
853
724
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
854
725
|
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]>
|
726
|
+
routes=[FruitsJSONScraper, JSONParser, CallableTree::Node::Root]>
|
861
727
|
---
|
862
|
-
*
|
863
|
-
*
|
864
|
-
*
|
865
|
-
*
|
728
|
+
* JSONParser: [matched: false]
|
729
|
+
* XMLParser: [matched: true]
|
730
|
+
* AnimalsXMLScraper: [matched: false]
|
731
|
+
* FruitsXMLScraper: [matched: true]
|
866
732
|
Input : <root><fruits><fruit emoji='🍎' name='Red Apple'/><fruit emoji='🍏' name='Green Apple'/></fruits></root>
|
867
733
|
Output: {"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
868
734
|
#<struct CallableTree::Node::External::Output
|
869
735
|
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
870
736
|
options={:foo=>:bar},
|
871
|
-
routes=
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
737
|
+
routes=[FruitsXMLScraper, XMLParser, CallableTree::Node::Root]>
|
738
|
+
```
|
739
|
+
|
740
|
+
#### `CallableTree::Node#identity`
|
741
|
+
|
742
|
+
If you want to customize the node identity, specify identifier.
|
743
|
+
|
744
|
+
`examples/builder/identity.rb`:
|
745
|
+
```ruby
|
746
|
+
JSONParser =
|
747
|
+
CallableTree::Node::Internal::Builder
|
748
|
+
.new
|
749
|
+
...
|
750
|
+
.identifier { |_node_:| _node_.object_id }
|
751
|
+
.build
|
752
|
+
|
753
|
+
XMLParser =
|
754
|
+
CallableTree::Node::Internal::Builder
|
755
|
+
.new
|
756
|
+
...
|
757
|
+
.identifier { |_node_:| _node_.object_id }
|
758
|
+
.build
|
759
|
+
|
760
|
+
def build_json_scraper(type)
|
761
|
+
CallableTree::Node::External::Builder
|
762
|
+
.new
|
763
|
+
...
|
764
|
+
.identifier { |_node_:| _node_.object_id }
|
765
|
+
.build
|
766
|
+
end
|
767
|
+
|
768
|
+
...
|
769
|
+
|
770
|
+
def build_xml_scraper(type)
|
771
|
+
CallableTree::Node::External::Builder
|
772
|
+
.new
|
773
|
+
...
|
774
|
+
.identifier { |_node_:| _node_.object_id }
|
775
|
+
.build
|
776
|
+
end
|
777
|
+
|
778
|
+
...
|
779
|
+
```
|
780
|
+
|
781
|
+
Run `examples/class/identity.rb`:
|
782
|
+
```sh
|
783
|
+
% ruby examples/builder/identity.rb
|
784
|
+
#<struct CallableTree::Node::External::Output
|
785
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
786
|
+
options={:foo=>:bar},
|
787
|
+
routes=[60, 80, CallableTree::Node::Root]>
|
788
|
+
---
|
789
|
+
#<struct CallableTree::Node::External::Output
|
790
|
+
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
791
|
+
options={:foo=>:bar},
|
792
|
+
routes=[220, 240, CallableTree::Node::Root]>
|
793
|
+
---
|
794
|
+
#<struct CallableTree::Node::External::Output
|
795
|
+
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
796
|
+
options={:foo=>:bar},
|
797
|
+
routes=[260, 80, CallableTree::Node::Root]>
|
798
|
+
---
|
799
|
+
#<struct CallableTree::Node::External::Output
|
800
|
+
value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
|
801
|
+
options={:foo=>:bar},
|
802
|
+
routes=[400, 240, CallableTree::Node::Root]>
|
877
803
|
---
|
878
804
|
```
|
879
805
|
|