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
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'callable_tree'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            HooksSample =
         | 
| 6 | 
            +
              CallableTree::Node::Internal::Builder
         | 
| 7 | 
            +
              .new
         | 
| 8 | 
            +
              .hookable
         | 
| 9 | 
            +
              .build
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            CallableTree::Node::Root.new.append(
         | 
| 12 | 
            +
              HooksSample
         | 
| 13 | 
            +
              .new
         | 
| 14 | 
            +
              .append(
         | 
| 15 | 
            +
                # anonymous external node
         | 
| 16 | 
            +
                lambda do |input, **_options|
         | 
| 17 | 
            +
                  puts "external input: #{input}"
         | 
| 18 | 
            +
                  input * 2
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              )
         | 
| 21 | 
            +
              .before_matcher do |input, **_options|
         | 
| 22 | 
            +
                puts "before_matcher input: #{input}"
         | 
| 23 | 
            +
                input + 1
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
              .around_matcher do |input, **_options, &block|
         | 
| 26 | 
            +
                puts "around_matcher input: #{input}"
         | 
| 27 | 
            +
                matched = block.call
         | 
| 28 | 
            +
                puts "around_matcher matched: #{matched}"
         | 
| 29 | 
            +
                !matched
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
              .after_matcher do |matched, **_options|
         | 
| 32 | 
            +
                puts "after_matcher matched: #{matched}"
         | 
| 33 | 
            +
                !matched
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
              .before_caller do |input, **_options|
         | 
| 36 | 
            +
                puts "before_caller input: #{input}"
         | 
| 37 | 
            +
                input + 1
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
              .around_caller do |input, **_options, &block|
         | 
| 40 | 
            +
                puts "around_caller input: #{input}"
         | 
| 41 | 
            +
                output = block.call
         | 
| 42 | 
            +
                puts "around_caller output: #{output}"
         | 
| 43 | 
            +
                output * input
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
              .after_caller do |output, **_options|
         | 
| 46 | 
            +
                puts "after_caller output: #{output}"
         | 
| 47 | 
            +
                output * 2
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
              .before_terminator do |output, *_inputs, **_options|
         | 
| 50 | 
            +
                puts "before_terminator output: #{output}"
         | 
| 51 | 
            +
                output + 1
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
              .around_terminator do |output, *_inputs, **_options, &block|
         | 
| 54 | 
            +
                puts "around_terminator output: #{output}"
         | 
| 55 | 
            +
                terminated = block.call
         | 
| 56 | 
            +
                puts "around_terminator terminated: #{terminated}"
         | 
| 57 | 
            +
                !terminated
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
              .after_terminator do |terminated, **_options|
         | 
| 60 | 
            +
                puts "after_terminator terminated: #{terminated}"
         | 
| 61 | 
            +
                !terminated
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
            ).tap do |tree|
         | 
| 64 | 
            +
              options = { foo: :bar }
         | 
| 65 | 
            +
              output = tree.call(1, **options)
         | 
| 66 | 
            +
              puts "result: #{output}"
         | 
| 67 | 
            +
            end
         | 
    
        data/examples/builder/logging.rb
    CHANGED
    
    | @@ -17,9 +17,8 @@ JSONParser = | |
| 17 17 | 
             
                  block.call(json, **options)
         | 
| 18 18 | 
             
                end
         | 
| 19 19 | 
             
              end
         | 
| 20 | 
            -
              .terminator  | 
| 21 | 
            -
             | 
| 22 | 
            -
              end
         | 
| 20 | 
            +
              .terminator { true }
         | 
| 21 | 
            +
              # .identifier { |_node_:| :as_you_like } # optional
         | 
| 23 22 | 
             
              .hookable
         | 
| 24 23 | 
             
              .build
         | 
| 25 24 |  | 
| @@ -35,9 +34,8 @@ XMLParser = | |
| 35 34 | 
             
                  block.call(REXML::Document.new(file), **options)
         | 
| 36 35 | 
             
                end
         | 
| 37 36 | 
             
              end
         | 
| 38 | 
            -
              .terminator  | 
| 39 | 
            -
             | 
| 40 | 
            -
              end
         | 
| 37 | 
            +
              .terminator { true }
         | 
| 38 | 
            +
              # .identifier { |_node_:| :as_you_like } # optional
         | 
| 41 39 | 
             
              .hookable
         | 
| 42 40 | 
             
              .build
         | 
| 43 41 |  | 
| @@ -52,6 +50,7 @@ def build_json_scraper(type) | |
| 52 50 | 
             
                    .map { |element| [element['name'], element['emoji']] }
         | 
| 53 51 | 
             
                    .to_h
         | 
| 54 52 | 
             
                end
         | 
| 53 | 
            +
                # .identifier { |_node_:| :as_you_like } # optional
         | 
| 55 54 | 
             
                .hookable
         | 
| 56 55 | 
             
                .build
         | 
| 57 56 | 
             
            end
         | 
| @@ -72,6 +71,7 @@ def build_xml_scraper(type) | |
| 72 71 | 
             
                    .map { |element| [element['name'], element['emoji']] }
         | 
| 73 72 | 
             
                    .to_h
         | 
| 74 73 | 
             
                end
         | 
| 74 | 
            +
                # .identifier { |_node_:| :as_you_like } # optional
         | 
| 75 75 | 
             
                .hookable
         | 
| 76 76 | 
             
                .build
         | 
| 77 77 | 
             
            end
         | 
| @@ -79,35 +79,38 @@ end | |
| 79 79 | 
             
            AnimalsXMLScraper = build_xml_scraper(:animals)
         | 
| 80 80 | 
             
            FruitsXMLScraper = build_xml_scraper(:fruits)
         | 
| 81 81 |  | 
| 82 | 
            -
             | 
| 83 | 
            -
               | 
| 84 | 
            -
               | 
| 85 | 
            -
               | 
| 86 | 
            -
             | 
| 87 | 
            -
               | 
| 88 | 
            -
                prefix = list_style.rjust(_node_.depth * indent_size - indent_size + list_style.length, blank)
         | 
| 89 | 
            -
                puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
         | 
| 90 | 
            -
                matched
         | 
| 91 | 
            -
              end
         | 
| 82 | 
            +
            module Logging
         | 
| 83 | 
            +
              INDENT_SIZE = 2
         | 
| 84 | 
            +
              BLANK = ' '
         | 
| 85 | 
            +
              LIST_STYLE = '*'
         | 
| 86 | 
            +
              INPUT_LABEL  = 'Input :'
         | 
| 87 | 
            +
              OUTPUT_LABEL = 'Output:'
         | 
| 92 88 |  | 
| 93 | 
            -
               | 
| 94 | 
            -
                 | 
| 95 | 
            -
             | 
| 89 | 
            +
              def self.loggable(node)
         | 
| 90 | 
            +
                node.after_matcher! do |matched, _node_:, **|
         | 
| 91 | 
            +
                  prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
         | 
| 92 | 
            +
                  puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
         | 
| 93 | 
            +
                  matched
         | 
| 94 | 
            +
                end
         | 
| 96 95 |  | 
| 97 | 
            -
                node
         | 
| 98 | 
            -
                   | 
| 99 | 
            -
                     | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                     | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 96 | 
            +
                if node.external?
         | 
| 97 | 
            +
                  node
         | 
| 98 | 
            +
                    .before_caller! do |input, *, _node_:, **|
         | 
| 99 | 
            +
                      input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
         | 
| 100 | 
            +
                      puts "#{input_prefix} #{input}"
         | 
| 101 | 
            +
                      input
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                    .after_caller! do |output, _node_:, **|
         | 
| 104 | 
            +
                      output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
         | 
| 105 | 
            +
                      puts "#{output_prefix} #{output}"
         | 
| 106 | 
            +
                      output
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                end
         | 
| 108 109 | 
             
              end
         | 
| 109 110 | 
             
            end
         | 
| 110 111 |  | 
| 112 | 
            +
            loggable = Logging.method(:loggable)
         | 
| 113 | 
            +
             | 
| 111 114 | 
             
            tree = CallableTree::Node::Root.new.seekable.append(
         | 
| 112 115 | 
             
              JSONParser.new.tap(&loggable).seekable.append(
         | 
| 113 116 | 
             
                AnimalsJSONScraper.new.tap(&loggable).verbosify,
         | 
| @@ -0,0 +1,70 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'callable_tree'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Node
         | 
| 6 | 
            +
              class HooksSample
         | 
| 7 | 
            +
                include CallableTree::Node::Internal
         | 
| 8 | 
            +
                prepend CallableTree::Node::Hooks::Matcher
         | 
| 9 | 
            +
                prepend CallableTree::Node::Hooks::Caller
         | 
| 10 | 
            +
                prepend CallableTree::Node::Hooks::Terminator
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            CallableTree::Node::Root.new.append(
         | 
| 15 | 
            +
              Node::HooksSample
         | 
| 16 | 
            +
                .new
         | 
| 17 | 
            +
                .append(
         | 
| 18 | 
            +
                  # anonymous external node
         | 
| 19 | 
            +
                  lambda do |input, **_options|
         | 
| 20 | 
            +
                    puts "external input: #{input}"
         | 
| 21 | 
            +
                    input * 2
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                )
         | 
| 24 | 
            +
                .before_matcher do |input, **_options|
         | 
| 25 | 
            +
                  puts "before_matcher input: #{input}"
         | 
| 26 | 
            +
                  input + 1
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                .around_matcher do |input, **_options, &block|
         | 
| 29 | 
            +
                  puts "around_matcher input: #{input}"
         | 
| 30 | 
            +
                  matched = block.call
         | 
| 31 | 
            +
                  puts "around_matcher matched: #{matched}"
         | 
| 32 | 
            +
                  !matched
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                .after_matcher do |matched, **_options|
         | 
| 35 | 
            +
                  puts "after_matcher matched: #{matched}"
         | 
| 36 | 
            +
                  !matched
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
                .before_caller do |input, **_options|
         | 
| 39 | 
            +
                  puts "before_caller input: #{input}"
         | 
| 40 | 
            +
                  input + 1
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
                .around_caller do |input, **_options, &block|
         | 
| 43 | 
            +
                  puts "around_caller input: #{input}"
         | 
| 44 | 
            +
                  output = block.call
         | 
| 45 | 
            +
                  puts "around_caller output: #{output}"
         | 
| 46 | 
            +
                  output * input
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
                .after_caller do |output, **_options|
         | 
| 49 | 
            +
                  puts "after_caller output: #{output}"
         | 
| 50 | 
            +
                  output * 2
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                .before_terminator do |output, *_inputs, **_options|
         | 
| 53 | 
            +
                  puts "before_terminator output: #{output}"
         | 
| 54 | 
            +
                  output + 1
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                .around_terminator do |output, *_inputs, **_options, &block|
         | 
| 57 | 
            +
                  puts "around_terminator output: #{output}"
         | 
| 58 | 
            +
                  terminated = block.call
         | 
| 59 | 
            +
                  puts "around_terminator terminated: #{terminated}"
         | 
| 60 | 
            +
                  !terminated
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                .after_terminator do |terminated, **_options|
         | 
| 63 | 
            +
                  puts "after_terminator terminated: #{terminated}"
         | 
| 64 | 
            +
                  !terminated
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
            ).tap do |tree|
         | 
| 67 | 
            +
              options = { foo: :bar }
         | 
| 68 | 
            +
              output = tree.call(1, **options)
         | 
| 69 | 
            +
              puts "result: #{output}"
         | 
| 70 | 
            +
            end
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
| @@ -5,36 +5,6 @@ require 'json' | |
| 5 5 | 
             
            require 'rexml/document'
         | 
| 6 6 |  | 
| 7 7 | 
             
            module Node
         | 
| 8 | 
            -
              module Logging
         | 
| 9 | 
            -
                INDENT_SIZE = 2
         | 
| 10 | 
            -
                BLANK = ' '
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                module Match
         | 
| 13 | 
            -
                  LIST_STYLE = '*'
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                  def match?(_input, **_options)
         | 
| 16 | 
            -
                    super.tap do |matched|
         | 
| 17 | 
            -
                      prefix = LIST_STYLE.rjust(depth * INDENT_SIZE - INDENT_SIZE + LIST_STYLE.length, BLANK)
         | 
| 18 | 
            -
                      puts "#{prefix} #{identity}: [matched: #{matched}]"
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                module Call
         | 
| 24 | 
            -
                  INPUT_LABEL  = 'Input :'
         | 
| 25 | 
            -
                  OUTPUT_LABEL = 'Output:'
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  def call(input, **_options)
         | 
| 28 | 
            -
                    super.tap do |output|
         | 
| 29 | 
            -
                      input_prefix = INPUT_LABEL.rjust(depth * INDENT_SIZE + INPUT_LABEL.length, BLANK)
         | 
| 30 | 
            -
                      puts "#{input_prefix} #{input}"
         | 
| 31 | 
            -
                      output_prefix = OUTPUT_LABEL.rjust(depth * INDENT_SIZE + OUTPUT_LABEL.length, BLANK)
         | 
| 32 | 
            -
                      puts "#{output_prefix} #{output}"
         | 
| 33 | 
            -
                    end
         | 
| 34 | 
            -
                  end
         | 
| 35 | 
            -
                end
         | 
| 36 | 
            -
              end
         | 
| 37 | 
            -
             | 
| 38 8 | 
             
              class Identity
         | 
| 39 9 | 
             
                attr_reader :klass, :type
         | 
| 40 10 |  | 
| @@ -51,7 +21,7 @@ module Node | |
| 51 21 | 
             
              module JSON
         | 
| 52 22 | 
             
                class Parser
         | 
| 53 23 | 
             
                  include CallableTree::Node::Internal
         | 
| 54 | 
            -
                  prepend  | 
| 24 | 
            +
                  prepend CallableTree::Node::Hooks::Matcher
         | 
| 55 25 |  | 
| 56 26 | 
             
                  def match?(input, **_options)
         | 
| 57 27 | 
             
                    File.extname(input) == '.json'
         | 
| @@ -71,8 +41,8 @@ module Node | |
| 71 41 |  | 
| 72 42 | 
             
                class Scraper
         | 
| 73 43 | 
             
                  include CallableTree::Node::External
         | 
| 74 | 
            -
                  prepend  | 
| 75 | 
            -
                  prepend  | 
| 44 | 
            +
                  prepend CallableTree::Node::Hooks::Matcher
         | 
| 45 | 
            +
                  prepend CallableTree::Node::Hooks::Caller
         | 
| 76 46 |  | 
| 77 47 | 
             
                  def initialize(type:)
         | 
| 78 48 | 
             
                    @type = type
         | 
| @@ -97,7 +67,7 @@ module Node | |
| 97 67 | 
             
              module XML
         | 
| 98 68 | 
             
                class Parser
         | 
| 99 69 | 
             
                  include CallableTree::Node::Internal
         | 
| 100 | 
            -
                  prepend  | 
| 70 | 
            +
                  prepend CallableTree::Node::Hooks::Matcher
         | 
| 101 71 |  | 
| 102 72 | 
             
                  def match?(input, **_options)
         | 
| 103 73 | 
             
                    File.extname(input) == '.xml'
         | 
| @@ -116,8 +86,8 @@ module Node | |
| 116 86 |  | 
| 117 87 | 
             
                class Scraper
         | 
| 118 88 | 
             
                  include CallableTree::Node::External
         | 
| 119 | 
            -
                  prepend  | 
| 120 | 
            -
                  prepend  | 
| 89 | 
            +
                  prepend CallableTree::Node::Hooks::Matcher
         | 
| 90 | 
            +
                  prepend CallableTree::Node::Hooks::Caller
         | 
| 121 91 |  | 
| 122 92 | 
             
                  def initialize(type:)
         | 
| 123 93 | 
             
                    @type = type
         | 
| @@ -142,18 +112,50 @@ module Node | |
| 142 112 | 
             
              end
         | 
| 143 113 | 
             
            end
         | 
| 144 114 |  | 
| 115 | 
            +
            module Logging
         | 
| 116 | 
            +
              INDENT_SIZE = 2
         | 
| 117 | 
            +
              BLANK = ' '
         | 
| 118 | 
            +
              LIST_STYLE = '*'
         | 
| 119 | 
            +
              INPUT_LABEL  = 'Input :'
         | 
| 120 | 
            +
              OUTPUT_LABEL = 'Output:'
         | 
| 121 | 
            +
             | 
| 122 | 
            +
              def self.loggable(node)
         | 
| 123 | 
            +
                node.after_matcher! do |matched, _node_:, **|
         | 
| 124 | 
            +
                  prefix = LIST_STYLE.rjust((_node_.depth * INDENT_SIZE) - INDENT_SIZE + LIST_STYLE.length, BLANK)
         | 
| 125 | 
            +
                  puts "#{prefix} #{_node_.identity}: [matched: #{matched}]"
         | 
| 126 | 
            +
                  matched
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                if node.external?
         | 
| 130 | 
            +
                  node
         | 
| 131 | 
            +
                    .before_caller! do |input, *, _node_:, **|
         | 
| 132 | 
            +
                      input_prefix = INPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + INPUT_LABEL.length, BLANK)
         | 
| 133 | 
            +
                      puts "#{input_prefix} #{input}"
         | 
| 134 | 
            +
                      input
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
                    .after_caller! do |output, _node_:, **|
         | 
| 137 | 
            +
                      output_prefix = OUTPUT_LABEL.rjust((_node_.depth * INDENT_SIZE) + OUTPUT_LABEL.length, BLANK)
         | 
| 138 | 
            +
                      puts "#{output_prefix} #{output}"
         | 
| 139 | 
            +
                      output
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
            end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
            loggable = Logging.method(:loggable)
         | 
| 146 | 
            +
             | 
| 145 147 | 
             
            tree = CallableTree::Node::Root.new.append(
         | 
| 146 | 
            -
              Node::JSON::Parser.new.append(
         | 
| 147 | 
            -
                Node::JSON::Scraper.new(type: :animals).verbosify,
         | 
| 148 | 
            -
                Node::JSON::Scraper.new(type: :fruits).verbosify
         | 
| 148 | 
            +
              Node::JSON::Parser.new.tap(&loggable).append(
         | 
| 149 | 
            +
                Node::JSON::Scraper.new(type: :animals).tap(&loggable).verbosify,
         | 
| 150 | 
            +
                Node::JSON::Scraper.new(type: :fruits).tap(&loggable).verbosify
         | 
| 149 151 | 
             
              ),
         | 
| 150 | 
            -
              Node::XML::Parser.new.append(
         | 
| 151 | 
            -
                Node::XML::Scraper.new(type: :animals).verbosify,
         | 
| 152 | 
            -
                Node::XML::Scraper.new(type: :fruits).verbosify
         | 
| 152 | 
            +
              Node::XML::Parser.new.tap(&loggable).append(
         | 
| 153 | 
            +
                Node::XML::Scraper.new(type: :animals).tap(&loggable).verbosify,
         | 
| 154 | 
            +
                Node::XML::Scraper.new(type: :fruits).tap(&loggable).verbosify
         | 
| 153 155 | 
             
              )
         | 
| 154 156 | 
             
            )
         | 
| 155 157 |  | 
| 156 | 
            -
            Dir.glob("#{__dir__} | 
| 158 | 
            +
            Dir.glob("#{__dir__}/../docs/*") do |file|
         | 
| 157 159 | 
             
              options = { foo: :bar }
         | 
| 158 160 | 
             
              pp tree.call(file, **options)
         | 
| 159 161 | 
             
              puts '---'
         | 
| @@ -24,6 +24,11 @@ module CallableTree | |
| 24 24 | 
             
                    self
         | 
| 25 25 | 
             
                  end
         | 
| 26 26 |  | 
| 27 | 
            +
                  def identifier(&block)
         | 
| 28 | 
            +
                    @identifier = block
         | 
| 29 | 
            +
                    self
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 27 32 | 
             
                  def hookable(hookable = true)
         | 
| 28 33 | 
             
                    @hookable = hookable
         | 
| 29 34 | 
             
                    self
         | 
| @@ -33,6 +38,7 @@ module CallableTree | |
| 33 38 | 
             
                    matcher = @matcher
         | 
| 34 39 | 
             
                    caller = @caller
         | 
| 35 40 | 
             
                    terminator = @terminator
         | 
| 41 | 
            +
                    identifier = @identifier
         | 
| 36 42 | 
             
                    hookable = @hookable
         | 
| 37 43 |  | 
| 38 44 | 
             
                    validate(
         | 
| @@ -47,6 +53,7 @@ module CallableTree | |
| 47 53 | 
             
                        if hookable
         | 
| 48 54 | 
             
                          prepend Hooks::Matcher
         | 
| 49 55 | 
             
                          prepend Hooks::Caller
         | 
| 56 | 
            +
                          prepend Hooks::Terminator
         | 
| 50 57 | 
             
                        end
         | 
| 51 58 |  | 
| 52 59 | 
             
                        if matcher
         | 
| @@ -72,6 +79,12 @@ module CallableTree | |
| 72 79 | 
             
                            end
         | 
| 73 80 | 
             
                          end
         | 
| 74 81 | 
             
                        end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                        if identifier
         | 
| 84 | 
            +
                          define_method(:identity) do
         | 
| 85 | 
            +
                            identifier.call(_node_: self) { super() }
         | 
| 86 | 
            +
                          end
         | 
| 87 | 
            +
                        end
         | 
| 75 88 | 
             
                      end
         | 
| 76 89 | 
             
                  end
         | 
| 77 90 |  | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CallableTree
         | 
| 4 | 
            +
              module Node
         | 
| 5 | 
            +
                module Hooks
         | 
| 6 | 
            +
                  module Terminator
         | 
| 7 | 
            +
                    def self.included(_subclass)
         | 
| 8 | 
            +
                      raise ::CallableTree::Error, "#{self} must be prepended"
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def before_terminator(&block)
         | 
| 12 | 
            +
                      clone.before_terminator!(&block)
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def before_terminator!(&block)
         | 
| 16 | 
            +
                      before_terminator_callbacks << block
         | 
| 17 | 
            +
                      self
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    def around_terminator(&block)
         | 
| 21 | 
            +
                      clone.around_terminator!(&block)
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    def around_terminator!(&block)
         | 
| 25 | 
            +
                      around_terminator_callbacks << block
         | 
| 26 | 
            +
                      self
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def after_terminator(&block)
         | 
| 30 | 
            +
                      clone.after_terminator!(&block)
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def after_terminator!(&block)
         | 
| 34 | 
            +
                      after_terminator_callbacks << block
         | 
| 35 | 
            +
                      self
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def terminate?(output, *inputs, **options)
         | 
| 39 | 
            +
                      output = before_terminator_callbacks.reduce(output) do |output, callable|
         | 
| 40 | 
            +
                        callable.call(output, *inputs, **options, _node_: self)
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      terminated =
         | 
| 44 | 
            +
                        if around_terminator_callbacks.empty?
         | 
| 45 | 
            +
                          super(output, *inputs, **options)
         | 
| 46 | 
            +
                        else
         | 
| 47 | 
            +
                          around_terminator_callbacks_head, *around_terminator_callbacks_tail = around_terminator_callbacks
         | 
| 48 | 
            +
                          terminator = proc { super(output, *inputs, **options) }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                          terminated =
         | 
| 51 | 
            +
                            around_terminator_callbacks_head
         | 
| 52 | 
            +
                            .call(
         | 
| 53 | 
            +
                              output,
         | 
| 54 | 
            +
                              *inputs,
         | 
| 55 | 
            +
                              **options,
         | 
| 56 | 
            +
                              _node_: self
         | 
| 57 | 
            +
                            ) { terminator.call }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                          around_terminator_callbacks_tail.reduce(terminated) do |terminated, callable|
         | 
| 60 | 
            +
                            callable.call(
         | 
| 61 | 
            +
                              output,
         | 
| 62 | 
            +
                              *inputs,
         | 
| 63 | 
            +
                              **options,
         | 
| 64 | 
            +
                              _node_: self
         | 
| 65 | 
            +
                            ) { terminated }
         | 
| 66 | 
            +
                          end
         | 
| 67 | 
            +
                        end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      after_terminator_callbacks.reduce(terminated) do |terminated, callable|
         | 
| 70 | 
            +
                        callable.call(terminated, **options, _node_: self)
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    def before_terminator_callbacks
         | 
| 75 | 
            +
                      @before_terminator_callbacks ||= []
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    def around_terminator_callbacks
         | 
| 79 | 
            +
                      @around_terminator_callbacks ||= []
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    def after_terminator_callbacks
         | 
| 83 | 
            +
                      @after_terminator_callbacks ||= []
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    private
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    attr_writer :before_terminator_callbacks, :around_terminator_callbacks, :after_terminator_callbacks
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    def initialize_copy(_node)
         | 
| 91 | 
            +
                      super
         | 
| 92 | 
            +
                      self.before_terminator_callbacks = before_terminator_callbacks.map(&:itself)
         | 
| 93 | 
            +
                      self.around_terminator_callbacks = around_terminator_callbacks.map(&:itself)
         | 
| 94 | 
            +
                      self.after_terminator_callbacks = after_terminator_callbacks.map(&:itself)
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
            end
         | 
    
        data/lib/callable_tree.rb
    CHANGED
    
    | @@ -9,6 +9,7 @@ require_relative 'callable_tree/version' | |
| 9 9 | 
             
            require_relative 'callable_tree/node'
         | 
| 10 10 | 
             
            require_relative 'callable_tree/node/hooks/matcher'
         | 
| 11 11 | 
             
            require_relative 'callable_tree/node/hooks/caller'
         | 
| 12 | 
            +
            require_relative 'callable_tree/node/hooks/terminator'
         | 
| 12 13 | 
             
            require_relative 'callable_tree/node/internal/strategy'
         | 
| 13 14 | 
             
            require_relative 'callable_tree/node/internal/strategy/broadcast'
         | 
| 14 15 | 
             
            require_relative 'callable_tree/node/internal/strategy/seek'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: callable_tree
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3. | 
| 4 | 
            +
              version: 0.3.8
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - jsmmr
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-05-05 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: Builds a tree by linking callable nodes. The nodes that match the conditions
         | 
| 14 14 | 
             
              are called in a chain from the root node to the leaf node. These are like nested
         | 
| @@ -23,6 +23,7 @@ files: | |
| 23 23 | 
             
            - ".github/workflows/codeql-analysis.yml"
         | 
| 24 24 | 
             
            - ".gitignore"
         | 
| 25 25 | 
             
            - ".rspec"
         | 
| 26 | 
            +
            - ".rubocop.yml"
         | 
| 26 27 | 
             
            - ".ruby-version"
         | 
| 27 28 | 
             
            - CHANGELOG.md
         | 
| 28 29 | 
             
            - Gemfile
         | 
| @@ -33,22 +34,22 @@ files: | |
| 33 34 | 
             
            - bin/console
         | 
| 34 35 | 
             
            - bin/setup
         | 
| 35 36 | 
             
            - callable_tree.gemspec
         | 
| 36 | 
            -
            - examples/builder/hooks | 
| 37 | 
            +
            - examples/builder/hooks.rb
         | 
| 37 38 | 
             
            - examples/builder/internal-broadcastable.rb
         | 
| 38 39 | 
             
            - examples/builder/internal-composable.rb
         | 
| 39 40 | 
             
            - examples/builder/internal-seekable.rb
         | 
| 40 41 | 
             
            - examples/builder/logging.rb
         | 
| 42 | 
            +
            - examples/class/external-verbosify.rb
         | 
| 43 | 
            +
            - examples/class/hooks.rb
         | 
| 44 | 
            +
            - examples/class/identity.rb
         | 
| 45 | 
            +
            - examples/class/internal-broadcastable.rb
         | 
| 46 | 
            +
            - examples/class/internal-composable.rb
         | 
| 47 | 
            +
            - examples/class/internal-seekable.rb
         | 
| 48 | 
            +
            - examples/class/logging.rb
         | 
| 41 49 | 
             
            - examples/docs/animals.json
         | 
| 42 50 | 
             
            - examples/docs/animals.xml
         | 
| 43 51 | 
             
            - examples/docs/fruits.json
         | 
| 44 52 | 
             
            - examples/docs/fruits.xml
         | 
| 45 | 
            -
            - examples/external-verbosify.rb
         | 
| 46 | 
            -
            - examples/hooks-caller.rb
         | 
| 47 | 
            -
            - examples/identity.rb
         | 
| 48 | 
            -
            - examples/internal-broadcastable.rb
         | 
| 49 | 
            -
            - examples/internal-composable.rb
         | 
| 50 | 
            -
            - examples/internal-seekable.rb
         | 
| 51 | 
            -
            - examples/logging.rb
         | 
| 52 53 | 
             
            - lib/callable_tree.rb
         | 
| 53 54 | 
             
            - lib/callable_tree/node.rb
         | 
| 54 55 | 
             
            - lib/callable_tree/node/builder.rb
         | 
| @@ -57,6 +58,7 @@ files: | |
| 57 58 | 
             
            - lib/callable_tree/node/external/verbose.rb
         | 
| 58 59 | 
             
            - lib/callable_tree/node/hooks/caller.rb
         | 
| 59 60 | 
             
            - lib/callable_tree/node/hooks/matcher.rb
         | 
| 61 | 
            +
            - lib/callable_tree/node/hooks/terminator.rb
         | 
| 60 62 | 
             
            - lib/callable_tree/node/internal.rb
         | 
| 61 63 | 
             
            - lib/callable_tree/node/internal/builder.rb
         | 
| 62 64 | 
             
            - lib/callable_tree/node/internal/strategy.rb
         | 
| @@ -71,7 +73,8 @@ licenses: | |
| 71 73 | 
             
            metadata:
         | 
| 72 74 | 
             
              homepage_uri: https://github.com/jsmmr/ruby_callable_tree
         | 
| 73 75 | 
             
              source_code_uri: https://github.com/jsmmr/ruby_callable_tree
         | 
| 74 | 
            -
              changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3. | 
| 76 | 
            +
              changelog_uri: https://github.com/jsmmr/ruby_callable_tree/blob/v0.3.8/CHANGELOG.md
         | 
| 77 | 
            +
              rubygems_mfa_required: 'true'
         | 
| 75 78 | 
             
            post_install_message:
         | 
| 76 79 | 
             
            rdoc_options: []
         | 
| 77 80 | 
             
            require_paths:
         | 
| @@ -1,38 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'callable_tree'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            Root =
         | 
| 6 | 
            -
              CallableTree::Node::Internal::Builder
         | 
| 7 | 
            -
              .new
         | 
| 8 | 
            -
              .hookable
         | 
| 9 | 
            -
              .build
         | 
| 10 | 
            -
             | 
| 11 | 
            -
            Root
         | 
| 12 | 
            -
              .new
         | 
| 13 | 
            -
              .before_caller do |input, **_options|
         | 
| 14 | 
            -
                puts "before_caller input: #{input}"
         | 
| 15 | 
            -
                input + 1
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
              .append(
         | 
| 18 | 
            -
                # anonymous external node
         | 
| 19 | 
            -
                lambda do |input, **_options|
         | 
| 20 | 
            -
                  puts "external input: #{input}"
         | 
| 21 | 
            -
                  input * 2
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
              )
         | 
| 24 | 
            -
              .around_caller do |input, **_options, &block|
         | 
| 25 | 
            -
                puts "around_caller input: #{input}"
         | 
| 26 | 
            -
                output = block.call
         | 
| 27 | 
            -
                puts "around_caller output: #{output}"
         | 
| 28 | 
            -
                output * input
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
              .after_caller do |output, **_options|
         | 
| 31 | 
            -
                puts "after_caller output: #{output}"
         | 
| 32 | 
            -
                output * 2
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
              .tap do |tree|
         | 
| 35 | 
            -
                options = { foo: :bar }
         | 
| 36 | 
            -
                output = tree.call(1, **options)
         | 
| 37 | 
            -
                puts "result: #{output}"
         | 
| 38 | 
            -
              end
         |