callable_tree 0.3.7 → 0.3.8
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/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +366 -108
- data/callable_tree.gemspec +1 -0
- data/examples/builder/hooks.rb +67 -0
- data/examples/builder/logging.rb +33 -30
- data/examples/{external-verbosify.rb → class/external-verbosify.rb} +1 -1
- data/examples/class/hooks.rb +70 -0
- data/examples/{identity.rb → class/identity.rb} +1 -1
- data/examples/{internal-broadcastable.rb → class/internal-broadcastable.rb} +0 -0
- data/examples/{internal-composable.rb → class/internal-composable.rb} +0 -0
- data/examples/{internal-seekable.rb → class/internal-seekable.rb} +1 -1
- data/examples/{logging.rb → class/logging.rb} +45 -43
- data/lib/callable_tree/node/builder.rb +13 -0
- data/lib/callable_tree/node/hooks/terminator.rb +99 -0
- data/lib/callable_tree/node/root.rb +1 -0
- data/lib/callable_tree/version.rb +1 -1
- data/lib/callable_tree.rb +1 -0
- metadata +14 -11
- data/examples/builder/hooks-caller.rb +0 -38
- data/examples/hooks-caller.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 227de74d1378baca4b51a6b1e808ee59d984f20a53fc2ae77fb8b7902e6e4558
|
4
|
+
data.tar.gz: 2a138d14128114298e95642885f72da427b7ce69f1753ce855598c91e35df9fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ceb4df3df81b33ea9b8298c48b1068b9707e7b26550e1b8a35bc72501d495692c8969418ccb8a1a3e09f1661117f3bdd9aa9c0fd83a8eb87970305c961deb0b
|
7
|
+
data.tar.gz: a90186fd44e964090a656957a7ab29a423b2287164550093332f7f149f263d5d841fd8ba213d8e56e9e12fdc576f89fe5043966f5bfc74301891e6453520cea3
|
data/.rubocop.yml
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.1.
|
1
|
+
3.1.2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.8] - 2022-05-05
|
4
|
+
|
5
|
+
- (Experimental) Add `CallableTree::Node::Internal::Builder#identifier`.
|
6
|
+
- (Experimental) Add `CallableTree::Node::External::Builder#identifier`.
|
7
|
+
- (Experimental) Add `CallableTree::Node::Hooks::Terminator`.
|
8
|
+
|
3
9
|
## [0.3.7] - 2022-04-09
|
4
10
|
|
5
11
|
- Add `CallableTree::Node#internal?`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -21,10 +21,10 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
## Usage
|
23
23
|
|
24
|
-
Builds a tree by linking
|
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. 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. An instance of this node has several strategies (`seekable`, `broadcastable`, `composable`). The strategy can be changed by calling the method of the instance.
|
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,11 +32,15 @@ Builds a tree by linking instances of the nodes. The `call` method of the node w
|
|
32
32
|
|
33
33
|
### Basic
|
34
34
|
|
35
|
-
|
35
|
+
There are two ways to define the nodes: class style and builder style (experimental).
|
36
|
+
|
37
|
+
#### `CallableTree::Node::Internal#seekable` (default strategy)
|
36
38
|
|
37
39
|
This strategy does not call the next sibling node if the `call` method of the current node returns a value other than `nil`. This behavior is changeable by overriding the `terminate?` method.
|
38
40
|
|
39
|
-
|
41
|
+
##### Class style
|
42
|
+
|
43
|
+
`examples/class/internal-seekable.rb`:
|
40
44
|
```ruby
|
41
45
|
module Node
|
42
46
|
module JSON
|
@@ -148,9 +152,115 @@ Dir.glob("#{__dir__}/docs/*") do |file|
|
|
148
152
|
end
|
149
153
|
```
|
150
154
|
|
151
|
-
Run `examples/internal-seekable.rb`:
|
155
|
+
Run `examples/class/internal-seekable.rb`:
|
156
|
+
```sh
|
157
|
+
% ruby examples/class/internal-seekable.rb
|
158
|
+
{"Dog"=>"🐶", "Cat"=>"🐱"}
|
159
|
+
---
|
160
|
+
{"Dog"=>"🐶", "Cat"=>"🐱"}
|
161
|
+
---
|
162
|
+
{"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
163
|
+
---
|
164
|
+
{"Red Apple"=>"🍎", "Green Apple"=>"🍏"}
|
165
|
+
---
|
166
|
+
```
|
167
|
+
|
168
|
+
##### Builder style (experimental)
|
169
|
+
|
170
|
+
`examples/builder/internal-seekable.rb`:
|
171
|
+
```ruby
|
172
|
+
JSONParser =
|
173
|
+
CallableTree::Node::Internal::Builder
|
174
|
+
.new
|
175
|
+
.matcher do |input, **_options|
|
176
|
+
File.extname(input) == '.json'
|
177
|
+
end
|
178
|
+
.caller do |input, **options, &block|
|
179
|
+
File.open(input) do |file|
|
180
|
+
json = ::JSON.load(file)
|
181
|
+
# The following block call is equivalent to calling `super` in the class style.
|
182
|
+
block.call(json, **options)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
.terminator do
|
186
|
+
true
|
187
|
+
end
|
188
|
+
.build
|
189
|
+
|
190
|
+
XMLParser =
|
191
|
+
CallableTree::Node::Internal::Builder
|
192
|
+
.new
|
193
|
+
.matcher do |input, **_options|
|
194
|
+
File.extname(input) == '.xml'
|
195
|
+
end
|
196
|
+
.caller do |input, **options, &block|
|
197
|
+
File.open(input) do |file|
|
198
|
+
# The following block call is equivalent to calling `super` in the class style.
|
199
|
+
block.call(REXML::Document.new(file), **options)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
.terminator do
|
203
|
+
true
|
204
|
+
end
|
205
|
+
.build
|
206
|
+
|
207
|
+
def build_json_scraper(type)
|
208
|
+
CallableTree::Node::External::Builder
|
209
|
+
.new
|
210
|
+
.matcher do |input, **_options|
|
211
|
+
!!input[type.to_s]
|
212
|
+
end
|
213
|
+
.caller do |input, **_options|
|
214
|
+
input[type.to_s]
|
215
|
+
.map { |element| [element['name'], element['emoji']] }
|
216
|
+
.to_h
|
217
|
+
end
|
218
|
+
.build
|
219
|
+
end
|
220
|
+
|
221
|
+
AnimalsJSONScraper = build_json_scraper(:animals)
|
222
|
+
FruitsJSONScraper = build_json_scraper(:fruits)
|
223
|
+
|
224
|
+
def build_xml_scraper(type)
|
225
|
+
CallableTree::Node::External::Builder
|
226
|
+
.new
|
227
|
+
.matcher do |input, **_options|
|
228
|
+
!input.get_elements("//#{type}").empty?
|
229
|
+
end
|
230
|
+
.caller do |input, **_options|
|
231
|
+
input
|
232
|
+
.get_elements("//#{type}")
|
233
|
+
.first
|
234
|
+
.map { |element| [element['name'], element['emoji']] }
|
235
|
+
.to_h
|
236
|
+
end
|
237
|
+
.build
|
238
|
+
end
|
239
|
+
|
240
|
+
AnimalsXMLScraper = build_xml_scraper(:animals)
|
241
|
+
FruitsXMLScraper = build_xml_scraper(:fruits)
|
242
|
+
|
243
|
+
tree = CallableTree::Node::Root.new.seekable.append(
|
244
|
+
JSONParser.new.seekable.append(
|
245
|
+
AnimalsJSONScraper.new,
|
246
|
+
FruitsJSONScraper.new
|
247
|
+
),
|
248
|
+
XMLParser.new.seekable.append(
|
249
|
+
AnimalsXMLScraper.new,
|
250
|
+
FruitsXMLScraper.new
|
251
|
+
)
|
252
|
+
)
|
253
|
+
|
254
|
+
Dir.glob("#{__dir__}/../docs/*") do |file|
|
255
|
+
options = { foo: :bar }
|
256
|
+
pp tree.call(file, **options)
|
257
|
+
puts '---'
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
Run `examples/builder/internal-seekable.rb`:
|
152
262
|
```sh
|
153
|
-
% ruby examples/internal-seekable.rb
|
263
|
+
% ruby examples/builder/internal-seekable.rb
|
154
264
|
{"Dog"=>"🐶", "Cat"=>"🐱"}
|
155
265
|
---
|
156
266
|
{"Dog"=>"🐶", "Cat"=>"🐱"}
|
@@ -165,7 +275,9 @@ Run `examples/internal-seekable.rb`:
|
|
165
275
|
|
166
276
|
This strategy calls all child nodes of the internal node and ignores their `terminate?` methods, and then outputs their results as array.
|
167
277
|
|
168
|
-
|
278
|
+
##### Class style
|
279
|
+
|
280
|
+
`examples/class/internal-broadcastable.rb`:
|
169
281
|
```ruby
|
170
282
|
module Node
|
171
283
|
class LessThan
|
@@ -199,9 +311,99 @@ end
|
|
199
311
|
|
200
312
|
```
|
201
313
|
|
202
|
-
Run `examples/internal-broadcastable.rb`:
|
314
|
+
Run `examples/class/internal-broadcastable.rb`:
|
203
315
|
```sh
|
204
|
-
% ruby examples/internal-broadcastable.rb
|
316
|
+
% ruby examples/class/internal-broadcastable.rb
|
317
|
+
0 -> [[0, 1], [0, -1]]
|
318
|
+
1 -> [[2, 2], [3, 0]]
|
319
|
+
2 -> [[4, 3], [6, 1]]
|
320
|
+
3 -> [[6, 4], [9, 2]]
|
321
|
+
4 -> [[8, 5], [12, 3]]
|
322
|
+
5 -> [nil, [15, 4]]
|
323
|
+
6 -> [nil, [18, 5]]
|
324
|
+
7 -> [nil, [21, 6]]
|
325
|
+
8 -> [nil, [24, 7]]
|
326
|
+
9 -> [nil, [27, 8]]
|
327
|
+
10 -> [nil, nil]
|
328
|
+
```
|
329
|
+
|
330
|
+
##### Builder style (experimental)
|
331
|
+
|
332
|
+
`examples/builder/internal-broadcastable.rb`:
|
333
|
+
```ruby
|
334
|
+
less_than = proc do |num|
|
335
|
+
# The following block call is equivalent to calling `super` in the class style.
|
336
|
+
proc { |input, &block| block.call(input) && input < num }
|
337
|
+
end
|
338
|
+
|
339
|
+
LessThan5 =
|
340
|
+
CallableTree::Node::Internal::Builder
|
341
|
+
.new
|
342
|
+
.matcher(&less_than.call(5))
|
343
|
+
.build
|
344
|
+
|
345
|
+
LessThan10 =
|
346
|
+
CallableTree::Node::Internal::Builder
|
347
|
+
.new
|
348
|
+
.matcher(&less_than.call(10))
|
349
|
+
.build
|
350
|
+
|
351
|
+
add = proc do |num|
|
352
|
+
proc { |input| input + num }
|
353
|
+
end
|
354
|
+
|
355
|
+
Add1 =
|
356
|
+
CallableTree::Node::External::Builder
|
357
|
+
.new
|
358
|
+
.caller(&add.call(1))
|
359
|
+
.build
|
360
|
+
|
361
|
+
subtract = proc do |num|
|
362
|
+
proc { |input| input - num }
|
363
|
+
end
|
364
|
+
|
365
|
+
Subtract1 =
|
366
|
+
CallableTree::Node::External::Builder
|
367
|
+
.new
|
368
|
+
.caller(&subtract.call(1))
|
369
|
+
.build
|
370
|
+
|
371
|
+
multiply = proc do |num|
|
372
|
+
proc { |input| input * num }
|
373
|
+
end
|
374
|
+
|
375
|
+
Multiply2 =
|
376
|
+
CallableTree::Node::External::Builder
|
377
|
+
.new
|
378
|
+
.caller(&multiply.call(2))
|
379
|
+
.build
|
380
|
+
|
381
|
+
Multiply3 =
|
382
|
+
CallableTree::Node::External::Builder
|
383
|
+
.new
|
384
|
+
.caller(&multiply.call(3))
|
385
|
+
.build
|
386
|
+
|
387
|
+
tree = CallableTree::Node::Root.new.broadcastable.append(
|
388
|
+
LessThan5.new.broadcastable.append(
|
389
|
+
Multiply2.new,
|
390
|
+
Add1.new
|
391
|
+
),
|
392
|
+
LessThan10.new.broadcastable.append(
|
393
|
+
Multiply3.new,
|
394
|
+
Subtract1.new
|
395
|
+
)
|
396
|
+
)
|
397
|
+
|
398
|
+
(0..10).each do |input|
|
399
|
+
output = tree.call(input)
|
400
|
+
puts "#{input} -> #{output}"
|
401
|
+
end
|
402
|
+
```
|
403
|
+
|
404
|
+
Run `examples/builder/internal-broadcastable.rb`:
|
405
|
+
```sh
|
406
|
+
% ruby examples/builder/internal-broadcastable.rb
|
205
407
|
0 -> [[0, 1], [0, -1]]
|
206
408
|
1 -> [[2, 2], [3, 0]]
|
207
409
|
2 -> [[4, 3], [6, 1]]
|
@@ -219,7 +421,9 @@ Run `examples/internal-broadcastable.rb`:
|
|
219
421
|
|
220
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.
|
221
423
|
|
222
|
-
|
424
|
+
##### Class style
|
425
|
+
|
426
|
+
`examples/class/internal-composable.rb`:
|
223
427
|
```ruby
|
224
428
|
module Node
|
225
429
|
class LessThan
|
@@ -253,9 +457,99 @@ end
|
|
253
457
|
|
254
458
|
```
|
255
459
|
|
256
|
-
Run `examples/internal-composable.rb`:
|
460
|
+
Run `examples/class/internal-composable.rb`:
|
461
|
+
```sh
|
462
|
+
% ruby examples/class/internal-composable.rb
|
463
|
+
0 -> 2
|
464
|
+
1 -> 8
|
465
|
+
2 -> 14
|
466
|
+
3 -> 20
|
467
|
+
4 -> 26
|
468
|
+
5 -> 14
|
469
|
+
6 -> 17
|
470
|
+
7 -> 20
|
471
|
+
8 -> 23
|
472
|
+
9 -> 26
|
473
|
+
10 -> 10
|
474
|
+
```
|
475
|
+
|
476
|
+
##### Builder style (experimental)
|
477
|
+
|
478
|
+
`examples/builder/internal-composable.rb`:
|
479
|
+
```ruby
|
480
|
+
less_than = proc do |num|
|
481
|
+
# The following block call is equivalent to calling `super` in the class style.
|
482
|
+
proc { |input, &block| block.call(input) && input < num }
|
483
|
+
end
|
484
|
+
|
485
|
+
LessThan5 =
|
486
|
+
CallableTree::Node::Internal::Builder
|
487
|
+
.new
|
488
|
+
.matcher(&less_than.call(5))
|
489
|
+
.build
|
490
|
+
|
491
|
+
LessThan10 =
|
492
|
+
CallableTree::Node::Internal::Builder
|
493
|
+
.new
|
494
|
+
.matcher(&less_than.call(10))
|
495
|
+
.build
|
496
|
+
|
497
|
+
add = proc do |num|
|
498
|
+
proc { |input| input + num }
|
499
|
+
end
|
500
|
+
|
501
|
+
Add1 =
|
502
|
+
CallableTree::Node::External::Builder
|
503
|
+
.new
|
504
|
+
.caller(&add.call(1))
|
505
|
+
.build
|
506
|
+
|
507
|
+
subtract = proc do |num|
|
508
|
+
proc { |input| input - num }
|
509
|
+
end
|
510
|
+
|
511
|
+
Subtract1 =
|
512
|
+
CallableTree::Node::External::Builder
|
513
|
+
.new
|
514
|
+
.caller(&subtract.call(1))
|
515
|
+
.build
|
516
|
+
|
517
|
+
multiply = proc do |num|
|
518
|
+
proc { |input| input * num }
|
519
|
+
end
|
520
|
+
|
521
|
+
Multiply2 =
|
522
|
+
CallableTree::Node::External::Builder
|
523
|
+
.new
|
524
|
+
.caller(&multiply.call(2))
|
525
|
+
.build
|
526
|
+
|
527
|
+
Multiply3 =
|
528
|
+
CallableTree::Node::External::Builder
|
529
|
+
.new
|
530
|
+
.caller(&multiply.call(3))
|
531
|
+
.build
|
532
|
+
|
533
|
+
tree = CallableTree::Node::Root.new.composable.append(
|
534
|
+
LessThan5.new.composable.append(
|
535
|
+
Multiply2.new,
|
536
|
+
Add1.new
|
537
|
+
),
|
538
|
+
LessThan10.new.composable.append(
|
539
|
+
Multiply3.new,
|
540
|
+
Subtract1.new
|
541
|
+
)
|
542
|
+
)
|
543
|
+
|
544
|
+
(0..10).each do |input|
|
545
|
+
output = tree.call(input)
|
546
|
+
puts "#{input} -> #{output}"
|
547
|
+
end
|
548
|
+
```
|
549
|
+
|
550
|
+
Run `examples/builder/internal-composable.rb`:
|
257
551
|
```sh
|
258
|
-
% ruby examples/internal-composable.rb
|
552
|
+
% ruby examples/builder/internal-composable.rb
|
259
553
|
0 -> 2
|
260
554
|
1 -> 8
|
261
555
|
2 -> 14
|
@@ -275,7 +569,7 @@ Run `examples/internal-composable.rb`:
|
|
275
569
|
|
276
570
|
If you want verbose output results, call this method.
|
277
571
|
|
278
|
-
`examples/external-verbosify.rb`:
|
572
|
+
`examples/class/external-verbosify.rb`:
|
279
573
|
```ruby
|
280
574
|
...
|
281
575
|
|
@@ -293,9 +587,9 @@ tree = CallableTree::Node::Root.new.append(
|
|
293
587
|
...
|
294
588
|
```
|
295
589
|
|
296
|
-
Run `examples/external-verbosify.rb`:
|
590
|
+
Run `examples/class/external-verbosify.rb`:
|
297
591
|
```sh
|
298
|
-
% ruby examples/external-verbosify.rb
|
592
|
+
% ruby examples/class/external-verbosify.rb
|
299
593
|
#<struct CallableTree::Node::External::Output
|
300
594
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
301
595
|
options={:foo=>:bar},
|
@@ -325,7 +619,7 @@ You can work around it by overriding the `identity` method of the node.
|
|
325
619
|
|
326
620
|
If you want to customize the node identity, override this method.
|
327
621
|
|
328
|
-
`examples/identity.rb`:
|
622
|
+
`examples/class/identity.rb`:
|
329
623
|
```ruby
|
330
624
|
module Node
|
331
625
|
class Identity
|
@@ -381,9 +675,9 @@ end
|
|
381
675
|
...
|
382
676
|
```
|
383
677
|
|
384
|
-
Run `examples/identity.rb`:
|
678
|
+
Run `examples/class/identity.rb`:
|
385
679
|
```sh
|
386
|
-
% ruby examples/identity.rb
|
680
|
+
% ruby examples/class/identity.rb
|
387
681
|
#<struct CallableTree::Node::External::Output
|
388
682
|
value={"Dog"=>"🐶", "Cat"=>"🐱"},
|
389
683
|
options={:foo=>:bar},
|
@@ -430,53 +724,23 @@ Run `examples/identity.rb`:
|
|
430
724
|
|
431
725
|
This is an example of logging.
|
432
726
|
|
433
|
-
`examples/logging.rb`:
|
727
|
+
`examples/class/logging.rb`:
|
434
728
|
```ruby
|
435
729
|
module Node
|
436
|
-
module Logging
|
437
|
-
INDENT_SIZE = 2
|
438
|
-
BLANK = ' '
|
439
|
-
|
440
|
-
module Match
|
441
|
-
LIST_STYLE = '*'
|
442
|
-
|
443
|
-
def match?(_input, **_options)
|
444
|
-
super.tap do |matched|
|
445
|
-
prefix = LIST_STYLE.rjust(depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
|
446
|
-
puts "#{prefix} #{identity}: [matched: #{matched}]"
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
module Call
|
452
|
-
INPUT_LABEL = 'Input :'
|
453
|
-
OUTPUT_LABEL = 'Output:'
|
454
|
-
|
455
|
-
def call(input, **_options)
|
456
|
-
super.tap do |output|
|
457
|
-
input_prefix = INPUT_LABEL.rjust(depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
|
458
|
-
puts "#{input_prefix} #{input}"
|
459
|
-
output_prefix = OUTPUT_LABEL.rjust(depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
|
460
|
-
puts "#{output_prefix} #{output}"
|
461
|
-
end
|
462
|
-
end
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
730
|
...
|
467
731
|
|
468
732
|
module JSON
|
469
733
|
class Parser
|
470
734
|
include CallableTree::Node::Internal
|
471
|
-
prepend
|
735
|
+
prepend CallableTree::Node::Hooks::Matcher
|
472
736
|
|
473
737
|
...
|
474
738
|
end
|
475
739
|
|
476
740
|
class Scraper
|
477
741
|
include CallableTree::Node::External
|
478
|
-
prepend
|
479
|
-
prepend
|
742
|
+
prepend CallableTree::Node::Hooks::Matcher
|
743
|
+
prepend CallableTree::Node::Hooks::Caller
|
480
744
|
|
481
745
|
...
|
482
746
|
end
|
@@ -485,27 +749,72 @@ module Node
|
|
485
749
|
module XML
|
486
750
|
class Parser
|
487
751
|
include CallableTree::Node::Internal
|
488
|
-
prepend
|
752
|
+
prepend CallableTree::Node::Hooks::Matcher
|
489
753
|
|
490
754
|
...
|
491
755
|
end
|
492
756
|
|
493
757
|
class Scraper
|
494
758
|
include CallableTree::Node::External
|
495
|
-
prepend
|
496
|
-
prepend
|
759
|
+
prepend CallableTree::Node::Hooks::Matcher
|
760
|
+
prepend CallableTree::Node::Hooks::Caller
|
497
761
|
|
498
762
|
...
|
499
763
|
end
|
500
764
|
end
|
501
765
|
end
|
502
766
|
|
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
|
780
|
+
|
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
|
795
|
+
end
|
796
|
+
|
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
|
+
|
503
810
|
...
|
504
811
|
```
|
505
812
|
|
506
|
-
|
813
|
+
Also, see `examples/class/hooks.rb` for detail about `CallableTree::Node::Hooks::*`.
|
814
|
+
|
815
|
+
Run `examples/class/logging.rb`:
|
507
816
|
```sh
|
508
|
-
% ruby examples/logging.rb
|
817
|
+
% ruby examples/class/logging.rb
|
509
818
|
* Node::JSON::Parser: [matched: true]
|
510
819
|
* Node::JSON::Scraper(animals): [matched: true]
|
511
820
|
Input : {"animals"=>[{"name"=>"Dog", "emoji"=>"🐶"}, {"name"=>"Cat", "emoji"=>"🐱"}]}
|
@@ -568,57 +877,6 @@ Run `examples/logging.rb`:
|
|
568
877
|
---
|
569
878
|
```
|
570
879
|
|
571
|
-
#### `CallableTree::Node::Hooks::Caller` (experimental)
|
572
|
-
|
573
|
-
`examples/hooks-call.rb`:
|
574
|
-
```ruby
|
575
|
-
module Node
|
576
|
-
class HooksSample
|
577
|
-
include CallableTree::Node::Internal
|
578
|
-
prepend CallableTree::Node::Hooks::Caller
|
579
|
-
end
|
580
|
-
end
|
581
|
-
|
582
|
-
Node::HooksSample.new
|
583
|
-
.before_caller do |input, **_options|
|
584
|
-
puts "before_caller input: #{input}";
|
585
|
-
input + 1
|
586
|
-
end
|
587
|
-
.append(
|
588
|
-
# anonymous external node
|
589
|
-
lambda do |input, **_options|
|
590
|
-
puts "external input: #{input}"
|
591
|
-
input * 2
|
592
|
-
end
|
593
|
-
)
|
594
|
-
.around_caller do |input, **_options, &block|
|
595
|
-
puts "around_caller input: #{input}"
|
596
|
-
output = block.call
|
597
|
-
puts "around_caller output: #{output}"
|
598
|
-
output * input
|
599
|
-
end
|
600
|
-
.after_caller do |output, **_options|
|
601
|
-
puts "after_caller output: #{output}"
|
602
|
-
output * 2
|
603
|
-
end
|
604
|
-
.tap do |tree|
|
605
|
-
options = { foo: :bar }
|
606
|
-
output = tree.call(1, **options)
|
607
|
-
puts "result: #{output}"
|
608
|
-
end
|
609
|
-
```
|
610
|
-
|
611
|
-
Run `examples/hooks-caller.rb`:
|
612
|
-
```sh
|
613
|
-
% ruby examples/hooks-caller.rb
|
614
|
-
before_caller input: 1
|
615
|
-
external input: 2
|
616
|
-
around_caller input: 2
|
617
|
-
around_caller output: 4
|
618
|
-
after_caller output: 8
|
619
|
-
result: 16
|
620
|
-
```
|
621
|
-
|
622
880
|
## Contributing
|
623
881
|
|
624
882
|
Bug reports and pull requests are welcome on GitHub at https://github.com/jsmmr/ruby_callable_tree.
|