callable_tree 0.3.8 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
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. An instance of this node has several strategies (`seekable`, `broadcastable`, `composable`). The strategy can be changed by calling the method of the instance.
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 (experimental).
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
- .map { |element| [element['name'], element['emoji']] }
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
- .map { |element| [element['name'], element['emoji']] }
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 (experimental)
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, &block|
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
- block.call(json, **options)
180
+ original.call(json, **options)
183
181
  end
184
182
  end
185
- .terminator do
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, &block|
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
- block.call(REXML::Document.new(file), **options)
195
+ original.call(REXML::Document.new(file), **options)
200
196
  end
201
197
  end
202
- .terminator do
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
- .map { |element| [element['name'], element['emoji']] }
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
- .map { |element| [element['name'], element['emoji']] }
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 calls all child nodes of the internal node and ignores their `terminate?` methods, and then outputs their results as array.
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 (experimental)
322
+ ##### Builder style
331
323
 
332
324
  `examples/builder/internal-broadcastable.rb`:
333
325
  ```ruby
334
- less_than = proc do |num|
326
+ def less_than(num)
335
327
  # The following block call is equivalent to calling `super` in the class style.
336
- proc { |input, &block| block.call(input) && input < num }
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 = proc do |num|
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 = proc do |num|
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 = proc do |num|
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 calls all child nodes of the internal node in order to input the output of the previous node to the next node and ignores their `terminate?` methods, and then outputs a single result.
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 (experimental)
469
+ ##### Builder style
477
470
 
478
471
  `examples/builder/internal-composable.rb`:
479
472
  ```ruby
480
- less_than = proc do |num|
473
+ def less_than(num)
481
474
  # The following block call is equivalent to calling `super` in the class style.
482
- proc { |input, &block| block.call(input) && input < num }
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 = proc do |num|
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 = proc do |num|
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 = proc do |num|
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/class/external-verbosify.rb`:
565
+ `examples/builder/external-verbosify.rb`:
573
566
  ```ruby
574
567
  ...
575
568
 
576
- tree = CallableTree::Node::Root.new.append(
577
- Node::JSON::Parser.new.append(
578
- Node::JSON::Scraper.new(type: :animals).verbosify,
579
- Node::JSON::Scraper.new(type: :fruits).verbosify
569
+ tree = CallableTree::Node::Root.new.seekable.append(
570
+ JSONParser.new.seekable.append(
571
+ AnimalsJSONScraper.new.verbosify,
572
+ FruitsJSONScraper.new.verbosify
580
573
  ),
581
- Node::XML::Parser.new.append(
582
- Node::XML::Scraper.new(type: :animals).verbosify,
583
- Node::XML::Scraper.new(type: :fruits).verbosify
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/class/external-verbosify.rb`:
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
- value={"Dog"=>"🐶", "Cat"=>"🐱"},
595
- options={:foo=>:bar},
596
- routes=[Node::JSON::Scraper, Node::JSON::Parser, CallableTree::Node::Root]>
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
- value={"Dog"=>"🐶", "Cat"=>"🐱"},
600
- options={:foo=>:bar},
601
- routes=[Node::XML::Scraper, Node::XML::Parser, CallableTree::Node::Root]>
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
- value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
605
- options={:foo=>:bar},
606
- routes=[Node::JSON::Scraper, Node::JSON::Parser, CallableTree::Node::Root]>
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
- value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
610
- options={:foo=>:bar},
611
- routes=[Node::XML::Scraper, Node::XML::Parser, CallableTree::Node::Root]>
602
+ value={"Red Apple"=>"🍎", "Green Apple"=>"🍏"},
603
+ options={:foo=>:bar},
604
+ routes=[FruitsXMLScraper, XMLParser, CallableTree::Node::Root]>
612
605
  ---
613
606
  ```
614
607
 
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`
608
+ #### Logging
619
609
 
620
- If you want to customize the node identity, override this method.
610
+ This is an example of logging.
621
611
 
622
- `examples/class/identity.rb`:
612
+ `examples/builder/logging.rb`:
623
613
  ```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
614
+ JSONParser =
615
+ CallableTree::Node::Internal::Builder
616
+ .new
617
+ ...
618
+ .hookable
619
+ .build
632
620
 
633
- def to_s
634
- "#{klass}(#{type})"
635
- end
636
- end
621
+ XMLParser =
622
+ CallableTree::Node::Internal::Builder
623
+ .new
624
+ ...
625
+ .hookable
626
+ .build
637
627
 
638
- module JSON
628
+ def build_json_scraper(type)
629
+ CallableTree::Node::External::Builder
630
+ .new
639
631
  ...
632
+ .hookable
633
+ .build
634
+ end
640
635
 
641
- class Scraper
642
- include CallableTree::Node::External
636
+ ...
643
637
 
644
- def initialize(type:)
645
- @type = type
646
- end
638
+ def build_xml_scraper(type)
639
+ CallableTree::Node::External::Builder
640
+ .new
641
+ ...
642
+ .hookable
643
+ .build
644
+ end
647
645
 
648
- def identity
649
- Identity.new(klass: super, type: @type)
650
- end
646
+ ...
651
647
 
652
- ...
653
- end
654
- end
648
+ module Logging
649
+ INDENT_SIZE = 2
650
+ BLANK = ' '
651
+ LIST_STYLE = '*'
652
+ INPUT_LABEL = 'Input :'
653
+ OUTPUT_LABEL = 'Output:'
655
654
 
656
- module XML
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
- class Scraper
660
- include CallableTree::Node::External
662
+ return unless node.external?
661
663
 
662
- def initialize(type:)
663
- @type = type
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
- def identity
667
- Identity.new(klass: super, type: @type)
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
- Run `examples/class/identity.rb`:
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/class/identity.rb
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
- #### Logging
740
+ #### `CallableTree::Node#identity`
724
741
 
725
- This is an example of logging.
742
+ If you want to customize the node identity, specify identifier.
726
743
 
727
- `examples/class/logging.rb`:
744
+ `examples/builder/identity.rb`:
728
745
  ```ruby
729
- module Node
746
+ JSONParser =
747
+ CallableTree::Node::Internal::Builder
748
+ .new
730
749
  ...
750
+ .identifier { |_node_:| _node_.object_id }
751
+ .build
731
752
 
732
- module JSON
733
- class Parser
734
- include CallableTree::Node::Internal
735
- prepend CallableTree::Node::Hooks::Matcher
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
- end
764
- end
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
- module Logging
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
- if node.external?
782
- node
783
- .before_caller! do |input, *, _node_:, **|
784
- input_prefix = INPUT_LABEL.rjust(_node_.depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
785
- puts "#{input_prefix} #{input}"
786
- input
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
- Also, see `examples/class/hooks.rb` for detail about `CallableTree::Node::Hooks::*`.
814
-
815
- Run `examples/class/logging.rb`:
781
+ Run `examples/builder/identity.rb`:
816
782
  ```sh
817
- % ruby examples/class/logging.rb
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
- value={"Dog"=>"🐶", "Cat"=>"🐱"},
824
- 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]>
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
- value={"Dog"=>"🐶", "Cat"=>"🐱"},
839
- 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]>
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