@@ -36,7 +36,7 @@ Builds a tree of `CallableTree` nodes. Invokes the `call` method on nodes where
3636
3737### Basic
3838
39- There are two ways to define the nodes: class style and builder style.
39+ There are three ways to define nodes: class style, builder style, and factory style.
4040
4141#### ` CallableTree::Node::Internal#seekable ` (default strategy)
4242
@@ -261,6 +261,84 @@ Run `examples/builder/internal-seekable.rb`:
261261---
262262```
263263
264+ ##### Factory style
265+
266+ Factory style defines behaviors as procs first, then assembles the tree structure separately. This makes the tree structure clearly visible.
267+
268+ ` examples/factory/internal-seekable.rb ` :
269+ ``` ruby
270+ # === Behavior Definitions ===
271+
272+ json_matcher = -> (input, ** ) { File .extname(input) == ' .json' }
273+ json_caller = lambda do |input , ** options , & original |
274+ File .open (input) do |file |
275+ json = JSON .parse(file.read)
276+ original.call(json, ** options)
277+ end
278+ end
279+
280+ xml_matcher = -> (input, ** ) { File .extname(input) == ' .xml' }
281+ xml_caller = lambda do |input , ** options , & original |
282+ File .open (input) do |file |
283+ original.call(REXML ::Document .new (file), ** options)
284+ end
285+ end
286+
287+ animals_json_matcher = -> (input, ** ) { ! input[' animals' ].nil? }
288+ animals_json_caller = -> (input, ** ) { input[' animals' ].to_h { |e | [e[' name' ], e[' emoji' ]] } }
289+
290+ fruits_json_matcher = -> (input, ** ) { ! input[' fruits' ].nil? }
291+ fruits_json_caller = -> (input, ** ) { input[' fruits' ].to_h { |e | [e[' name' ], e[' emoji' ]] } }
292+
293+ animals_xml_matcher = -> (input, ** ) { ! input.get_elements(' //animals' ).empty? }
294+ animals_xml_caller = -> (input, ** ) { input.get_elements(' //animals' ).first.to_h { |e | [e[' name' ], e[' emoji' ]] } }
295+
296+ fruits_xml_matcher = -> (input, ** ) { ! input.get_elements(' //fruits' ).empty? }
297+ fruits_xml_caller = -> (input, ** ) { input.get_elements(' //fruits' ).first.to_h { |e | [e[' name' ], e[' emoji' ]] } }
298+
299+ terminator_true = -> (* ) { true }
300+
301+ # === Tree Structure (clearly visible!) ===
302+
303+ tree = CallableTree ::Node ::Root .new .seekable.append(
304+ CallableTree ::Node ::Internal .create(
305+ matcher: json_matcher,
306+ caller: json_caller,
307+ terminator: terminator_true
308+ ).seekable.append(
309+ CallableTree ::Node ::External .create(matcher: animals_json_matcher, caller: animals_json_caller),
310+ CallableTree ::Node ::External .create(matcher: fruits_json_matcher, caller: fruits_json_caller)
311+ ),
312+ CallableTree ::Node ::Internal .create(
313+ matcher: xml_matcher,
314+ caller: xml_caller,
315+ terminator: terminator_true
316+ ).seekable.append(
317+ CallableTree ::Node ::External .create(matcher: animals_xml_matcher, caller: animals_xml_caller),
318+ CallableTree ::Node ::External .create(matcher: fruits_xml_matcher, caller: fruits_xml_caller)
319+ )
320+ )
321+
322+ Dir .glob(" #{ __dir__ } /../docs/*" ) do |file |
323+ options = { foo: :bar }
324+ pp tree.call(file, ** options)
325+ puts ' ---'
326+ end
327+ ```
328+
329+ Run ` examples/factory/internal-seekable.rb ` :
330+ ``` sh
331+ % ruby examples/factory/internal-seekable.rb
332+ {" Dog" => " 🐶" , " Cat" => " 🐱" }
333+ ---
334+ {" Dog" => " 🐶" , " Cat" => " 🐱" }
335+ ---
336+ {" Red Apple" => " 🍎" , " Green Apple" => " 🍏" }
337+ ---
338+ {" Red Apple" => " 🍎" , " Green Apple" => " 🍏" }
339+ ---
340+ ```
341+
264342#### ` CallableTree::Node::Internal#broadcastable `
265343
266344This strategy broadcasts input to all child nodes and returns their results as an array. It ignores child ` terminate? ` methods by default.
@@ -407,6 +485,55 @@ Run `examples/builder/internal-broadcastable.rb`:
40748510 -> [nil, nil]
408486` ` `
409487
488+ # #### Factory style
489+
490+ ` examples/factory/internal-broadcastable.rb` :
491+ ` ` ` ruby
492+ # === Behavior Definitions ===
493+
494+ less_than_5_matcher = -> (input, ** , & original) { original.call(input) && input < 5 }
495+ less_than_10_matcher = ->( input, ** , & original) { original.call(input) && input < 10 }
496+
497+ multiply_2_caller = ->( input, ** ) { input * 2 }
498+ add_1_caller = ->( input, ** ) { input + 1 }
499+ multiply_3_caller = ->( input, ** ) { input * 3 }
500+ subtract_1_caller = ->( input, ** ) { input - 1 }
501+
502+ # === Tree Structure ===
503+
504+ tree = CallableTree::Node::Root.new.broadcastable.append(
505+ CallableTree::Node::Internal.create(matcher: less_than_5_matcher).broadcastable.append(
506+ CallableTree::Node::External.create(caller: multiply_2_caller),
507+ CallableTree::Node::External.create(caller: add_1_caller)
508+ ),
509+ CallableTree::Node::Internal.create(matcher: less_than_10_matcher).broadcastable.append(
510+ CallableTree::Node::External.create(caller: multiply_3_caller),
511+ CallableTree::Node::External.create(caller: subtract_1_caller)
512+ )
513+ )
514+
515+ (0..10).each do | input|
516+ output = tree.call(input)
517+ puts " #{input} -> #{output}"
518+ end
519+ ` ` `
520+
521+ Run ` examples/factory/internal-broadcastable.rb` :
522+ ` ` ` sh
523+ % ruby examples/factory/internal-broadcastable.rb
524+ 0 -> [[0, 1], [0, -1]]
525+ 1 -> [[2, 2], [3, 0]]
526+ 2 -> [[4, 3], [6, 1]]
527+ 3 -> [[6, 4], [9, 2]]
528+ 4 -> [[8, 5], [12, 3]]
529+ 5 -> [nil, [15, 4]]
530+ 6 -> [nil, [18, 5]]
531+ 7 -> [nil, [21, 6]]
532+ 8 -> [nil, [24, 7]]
533+ 9 -> [nil, [27, 8]]
534+ 10 -> [nil, nil]
535+ ` ` `
536+
410537# ### `CallableTree::Node::Internal#composable`
411538
412539This strategy chains child nodes, passing the output of the previous node as input to the next.
@@ -554,6 +681,55 @@ Run `examples/builder/internal-composable.rb`:
55468110 -> 10
555682` ` `
556683
684+ # #### Factory style
685+
686+ ` examples/factory/internal-composable.rb` :
687+ ` ` ` ruby
688+ # === Behavior Definitions ===
689+
690+ less_than_5_matcher = ->( input, ** , & original) { original.call(input) && input < 5 }
691+ less_than_10_matcher = ->( input, ** , & original) { original.call(input) && input < 10 }
692+
693+ multiply_2_caller = ->( input, ** ) { input * 2 }
694+ add_1_caller = ->( input, ** ) { input + 1 }
695+ multiply_3_caller = ->( input, ** ) { input * 3 }
696+ subtract_1_caller = ->( input, ** ) { input - 1 }
697+
698+ # === Tree Structure ===
699+
700+ tree = CallableTree::Node::Root.new.composable.append(
701+ CallableTree::Node::Internal.create(matcher: less_than_5_matcher).composable.append(
702+ CallableTree::Node::External.create(caller: multiply_2_caller),
703+ CallableTree::Node::External.create(caller: add_1_caller)
704+ ),
705+ CallableTree::Node::Internal.create(matcher: less_than_10_matcher).composable.append(
706+ CallableTree::Node::External.create(caller: multiply_3_caller),
707+ CallableTree::Node::External.create(caller: subtract_1_caller)
708+ )
709+ )
710+
711+ (0..10).each do | input|
712+ output = tree.call(input)
713+ puts " #{input} -> #{output}"
714+ end
715+ ` ` `
716+
717+ Run ` examples/factory/internal-composable.rb` :
718+ ` ` ` sh
719+ % ruby examples/factory/internal-composable.rb
720+ 0 -> 2
721+ 1 -> 8
722+ 2 -> 14
723+ 3 -> 20
724+ 4 -> 26
725+ 5 -> 14
726+ 6 -> 17
727+ 7 -> 20
728+ 8 -> 23
729+ 9 -> 26
730+ 10 -> 10
731+ ` ` `
732+
557733# ## Advanced
558734
559735# ### `CallableTree::Node::External#verbosify`
0 commit comments