callable_tree 0.3.10 → 0.3.12

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +19 -5
  3. data/.github/workflows/codeql-analysis.yml +4 -4
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +3 -0
  6. data/.rubocop_todo.yml +128 -0
  7. data/.ruby-version +1 -1
  8. data/AGENTS.md +56 -0
  9. data/CHANGELOG.md +11 -0
  10. data/CONTRIBUTING.md +68 -0
  11. data/Gemfile +4 -2
  12. data/README.md +195 -21
  13. data/callable_tree.gemspec +8 -2
  14. data/examples/builder/external-verbosify.rb +3 -2
  15. data/examples/builder/hooks.rb +2 -1
  16. data/examples/builder/identity.rb +3 -2
  17. data/examples/builder/internal-broadcastable.rb +2 -1
  18. data/examples/builder/internal-composable.rb +2 -1
  19. data/examples/builder/internal-seekable.rb +3 -2
  20. data/examples/builder/logging.rb +3 -2
  21. data/examples/class/external-verbosify.rb +3 -2
  22. data/examples/class/hooks.rb +2 -1
  23. data/examples/class/identity.rb +3 -2
  24. data/examples/class/internal-broadcastable.rb +2 -1
  25. data/examples/class/internal-composable.rb +2 -1
  26. data/examples/class/internal-seekable.rb +3 -2
  27. data/examples/class/logging.rb +3 -2
  28. data/examples/factory/external-verbosify.rb +69 -0
  29. data/examples/factory/hooks.rb +67 -0
  30. data/examples/factory/identity.rb +93 -0
  31. data/examples/factory/internal-broadcastable.rb +37 -0
  32. data/examples/factory/internal-composable.rb +37 -0
  33. data/examples/factory/internal-seekable.rb +71 -0
  34. data/examples/factory/logging.rb +121 -0
  35. data/lib/callable_tree/node/builder.rb +1 -0
  36. data/lib/callable_tree/node/external/pod.rb +91 -0
  37. data/lib/callable_tree/node/external/verbose.rb +1 -1
  38. data/lib/callable_tree/node/external.rb +1 -0
  39. data/lib/callable_tree/node/hooks/terminator.rb +1 -1
  40. data/lib/callable_tree/node/internal/pod.rb +91 -0
  41. data/lib/callable_tree/node/internal/{strategyable.rb → strategizable.rb} +4 -4
  42. data/lib/callable_tree/node/internal.rb +2 -2
  43. data/lib/callable_tree/version.rb +1 -1
  44. data/lib/callable_tree.rb +3 -1
  45. data/mise.toml +2 -0
  46. metadata +26 -15
  47. data/Gemfile.lock +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dccb97d338f0f2d01a1986b2b0418660467fde837a79bd06b67f0df235bee5f2
4
- data.tar.gz: 5002a705a96346ea92704eea7c8282beead6d4ef62c7ff2a9865f4b24e629faa
3
+ metadata.gz: 2eaeaea579c042e73c734e49279adb3321ab6008e4f6165ff4a701228217af00
4
+ data.tar.gz: e0c848254ac6802960723abb285423c21a477c228396cc384cd84a22fa31d565
5
5
  SHA512:
6
- metadata.gz: 1d16dd16df13a3c5f5b6a4c5292d50acbf571482dbde0496e29d69d745ad887b4274e84edd5ace4707fe2bdf54d324389a2d1b662ffb94d1e430e5819762f40d
7
- data.tar.gz: f531ae0eaaf51065f69e61a8eb4e70b83ba32bc280d699c2fce00b4b4a9c1ac3d8fb5fe61bcb3a7c40805edae1a26c4943bd9d09a4d4760a4e0f67a3dd4f62e9
6
+ metadata.gz: a144f0e3bcd69957274c4eb80008a812aa0f980163339f9aa0f501cc4ee0b63c1c1097df6dcfb2633aa449be79e620214b81f962b1c0a3b9cc81acdbc037beeb
7
+ data.tar.gz: 1c3c8fb5f5e4043864506b91ee7dd8b87e616ddecefdf593d9a302de92d056f8c505a483e6b6173a6ce01fe016366ccd56d58aa295f46902a7cf70c3bce039f3
@@ -1,18 +1,32 @@
1
1
  name: build
2
2
  on: [push]
3
3
  jobs:
4
- build:
4
+ lint:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v6
8
+ - uses: ruby/setup-ruby@v1
9
+ with:
10
+ ruby-version: 4.0
11
+ bundler-cache: true
12
+ - name: Run bundle install
13
+ run: |
14
+ bundle config path vendor/bundle
15
+ bundle install --jobs 4 --retry 3
16
+ - run: bundle exec rubocop
17
+
18
+ test:
5
19
  runs-on: ubuntu-latest
6
20
  strategy:
7
21
  matrix:
8
- ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2']
22
+ ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', '3.4', '4.0']
9
23
  steps:
10
- - uses: actions/checkout@v3
24
+ - uses: actions/checkout@v6
11
25
  - uses: ruby/setup-ruby@v1
12
26
  with:
13
27
  ruby-version: ${{ matrix.ruby }}
14
- - run: gem install bundler:2.3.26
15
- - uses: actions/cache@v3
28
+ bundler-cache: true
29
+ - uses: actions/cache@v5
16
30
  with:
17
31
  path: vendor/bundle
18
32
  key: ${{ runner.os }}-ruby-${{ matrix.ruby }}-gems-${{ hashFiles('**/Gemfile.lock') }}
@@ -38,11 +38,11 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout repository
41
- uses: actions/checkout@v3
41
+ uses: actions/checkout@v6
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v2
45
+ uses: github/codeql-action/init@v4
46
46
  with:
47
47
  languages: ${{ matrix.language }}
48
48
  # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
53
53
  # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
54
  # If this step fails, then you should remove it and run the build manually (see below)
55
55
  - name: Autobuild
56
- uses: github/codeql-action/autobuild@v2
56
+ uses: github/codeql-action/autobuild@v4
57
57
 
58
58
  # ℹ️ Command-line programs to run using the OS shell.
59
59
  # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
67
67
  # make release
68
68
 
69
69
  - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v2
70
+ uses: github/codeql-action/analyze@v4
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ /Gemfile.lock
data/.rubocop.yml CHANGED
@@ -1,5 +1,8 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  AllCops:
2
4
  NewCops: enable
5
+ TargetRubyVersion: 2.4
3
6
  Style/HashSyntax:
4
7
  EnforcedShorthandSyntax: never
5
8
  Naming/BlockForwarding:
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,128 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2026-01-02 20:06:51 UTC using RuboCop version 1.82.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: AllowedMethods.
11
+ # AllowedMethods: enums
12
+ Lint/ConstantDefinitionInBlock:
13
+ Exclude:
14
+ - 'spec/callable_tree/node/external_spec.rb'
15
+ - 'spec/callable_tree/node/internal/strategizable_spec.rb'
16
+
17
+ # Offense count: 1
18
+ # Configuration parameters: AllowedParentClasses.
19
+ Lint/MissingSuper:
20
+ Exclude:
21
+ - 'lib/callable_tree/node/root.rb'
22
+
23
+ # Offense count: 10
24
+ # Configuration parameters: AllowKeywordBlockArguments.
25
+ Lint/UnderscorePrefixedVariableName:
26
+ Exclude:
27
+ - 'examples/builder/identity.rb'
28
+ - 'examples/builder/logging.rb'
29
+ - 'examples/class/logging.rb'
30
+ - 'examples/factory/identity.rb'
31
+ - 'examples/factory/logging.rb'
32
+
33
+ # Offense count: 8
34
+ # This cop supports safe autocorrection (--autocorrect).
35
+ # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
36
+ # NotImplementedExceptions: NotImplementedError
37
+ Lint/UnusedMethodArgument:
38
+ Exclude:
39
+ - 'lib/callable_tree/node/builder.rb'
40
+ - 'lib/callable_tree/node/external/builder.rb'
41
+ - 'spec/callable_tree/node/builder_spec.rb'
42
+
43
+ # Offense count: 7
44
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
45
+ Metrics/AbcSize:
46
+ Max: 25
47
+
48
+ # Offense count: 38
49
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
50
+ # AllowedMethods: refine
51
+ Metrics/BlockLength:
52
+ Max: 798
53
+
54
+ # Offense count: 1
55
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
56
+ Metrics/CyclomaticComplexity:
57
+ Max: 8
58
+
59
+ # Offense count: 14
60
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
61
+ Metrics/MethodLength:
62
+ Max: 45
63
+
64
+ # Offense count: 8
65
+ # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
66
+ # CheckDefinitionPathHierarchyRoots: lib, spec, test, src
67
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
68
+ Naming/FileName:
69
+ Exclude:
70
+ - 'Rakefile.rb'
71
+ - 'examples/builder/external-verbosify.rb'
72
+ - 'examples/builder/internal-broadcastable.rb'
73
+ - 'examples/builder/internal-composable.rb'
74
+ - 'examples/builder/internal-seekable.rb'
75
+ - 'examples/class/external-verbosify.rb'
76
+ - 'examples/class/internal-broadcastable.rb'
77
+ - 'examples/class/internal-composable.rb'
78
+ - 'examples/class/internal-seekable.rb'
79
+ - 'examples/factory/external-verbosify.rb'
80
+ - 'examples/factory/internal-broadcastable.rb'
81
+ - 'examples/factory/internal-composable.rb'
82
+ - 'examples/factory/internal-seekable.rb'
83
+
84
+ # Offense count: 1
85
+ # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
86
+ # AllowedMethods: call
87
+ # WaywardPredicates: nonzero?
88
+ Naming/PredicateMethod:
89
+ Exclude:
90
+ - 'spec/callable_tree/node/builder_spec.rb'
91
+
92
+ # Offense count: 1
93
+ Style/ClassVars:
94
+ Exclude:
95
+ - 'lib/callable_tree/node/internal/strategizable.rb'
96
+
97
+ # Offense count: 40
98
+ # Configuration parameters: AllowedConstants.
99
+ Style/Documentation:
100
+ Enabled: false
101
+
102
+ # Offense count: 45
103
+ Style/MultilineBlockChain:
104
+ Exclude:
105
+ - 'examples/builder/external-verbosify.rb'
106
+ - 'examples/builder/hooks.rb'
107
+ - 'examples/builder/identity.rb'
108
+ - 'examples/builder/internal-seekable.rb'
109
+ - 'examples/builder/logging.rb'
110
+ - 'examples/class/hooks.rb'
111
+ - 'examples/class/logging.rb'
112
+ - 'examples/factory/hooks.rb'
113
+ - 'examples/factory/logging.rb'
114
+ - 'lib/callable_tree/node/internal/strategy/seek.rb'
115
+
116
+ # Offense count: 1
117
+ # Configuration parameters: AllowedMethods.
118
+ # AllowedMethods: respond_to_missing?
119
+ Style/OptionalBooleanParameter:
120
+ Exclude:
121
+ - 'lib/callable_tree/node/builder.rb'
122
+
123
+ # Offense count: 5
124
+ # This cop supports safe autocorrection (--autocorrect).
125
+ # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
126
+ # URISchemes: http, https
127
+ Layout/LineLength:
128
+ Max: 210
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 4.0.0
data/AGENTS.md ADDED
@@ -0,0 +1,56 @@
1
+ # Agent Guide for callable_tree
2
+
3
+ ## Project Overview
4
+ `callable_tree` is a Ruby gem for building tree-structured executable workflows. It provides a framework for organizing complex logic into a tree of callable nodes, offering a structured, modular alternative to complex conditional logic. Nodes are matched against input (`match?`) and executed (`call`) in a chain from root to leaf.
5
+
6
+ ## Core Concepts
7
+ - **Nodes**:
8
+ - `Root`: The entry point of the tree.
9
+ - `Internal`: Branch nodes that contain child nodes. Strategies:
10
+ - `seekable`: Calls the first matching child (like `case`).
11
+ - `broadcastable`: Calls all matching children.
12
+ - `composable`: Pipes output from one child to the next.
13
+ - `External`: Leaf nodes that perform actual work.
14
+ - **Traversal**:
15
+ - `match?(input)`: Determines if a node should process the input.
16
+ - `call(input)`: Executes the node logic.
17
+ - `terminate?`: Controls when to stop traversal (mostly for `seekable`).
18
+ - **Hooks** (`hookable`):
19
+ - Enable instrumentation (logging, debugging) by adding callbacks.
20
+ - `before_matcher!`, `after_matcher!`: Hook into matching phase.
21
+ - `before_caller!`, `after_caller!`: Hook into call phase.
22
+ - `before_terminator!`, `after_terminator!`: Hook into termination phase.
23
+ - **Verbosify** (`verbosify`):
24
+ - Wraps External node output in `CallableTree::Node::External::Output` struct.
25
+ - Provides `value`, `options`, and `routes` (call path) for debugging.
26
+
27
+ ## Directory Structure
28
+ - `lib/`: Source code.
29
+ - `spec/`: RSpec tests.
30
+ - `examples/`: Usage examples.
31
+ - `examples/class/`: Class-style node definitions (using `include CallableTree::Node::*`).
32
+ - `examples/builder/`: Builder-style definitions (using `Builder.new.matcher { }.caller { }.build`).
33
+ - `examples/factory/`: Factory-style definitions (using `External.create(caller: ...)` or `External::Pod.new`).
34
+ - `examples/docs/`: Sample data files (JSON, XML) used by examples.
35
+
36
+ ## Development
37
+ - **Tool Version Manager**: mise
38
+ - **Language**: Ruby (>= 2.4.0)
39
+ - **Dependency Management**: Bundler
40
+ - Execute `bundle` commands via `mise` (e.g., `mise x -- bundle exec ...`)
41
+ - **Testing**: RSpec
42
+ - Run all tests: `mise x -- bundle exec rake` or `mise x -- bundle exec rake spec`
43
+ - **Commit Messages**: Follow the convention in [CONTRIBUTING.md](CONTRIBUTING.md).
44
+ - **Linter/Formatter**:
45
+ - Uses `rubocop`.
46
+ - Run checks: `mise x -- bundle exec rubocop`
47
+ - **CI/CD**:
48
+ - GitHub Actions: `.github/workflows/build.yml` runs tests and linter on push/PR.
49
+ - **Release Process**:
50
+ - Version: `lib/callable_tree/version.rb`
51
+ - Tagging: Create a git tag (e.g., `v0.4.0`) and push.
52
+
53
+ ## Architecture
54
+ - **Composite Pattern**: Used for `Internal` nodes to treat individual objects and compositions uniformly.
55
+ - **Builder Pattern**: `CallableTree::Node::Internal::Builder` and `CallableTree::Node::External::Builder` provide a fluent interface for constructing complex trees.
56
+ - **Pod Pattern**: `CallableTree::Node::Internal::Pod` and `CallableTree::Node::External::Pod` enable inline node creation via `External.create` / `Internal.create` factory methods with proc-based behaviors.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.12] - 2026-01-07
4
+
5
+ - Add Factory style for inline node creation as a third option alongside Class and Builder styles.
6
+ - `CallableTree::Node::External.create` and `CallableTree::Node::Internal.create` factory methods
7
+ - Supports `hookable: true` option for Hooks (before/around/after callbacks)
8
+ - See `examples/factory/*.rb` for details.
9
+
10
+ ## [0.3.11] - 2026-01-03
11
+
12
+ - Fix a typo in `Strategizable#strategize` where it incorrectly called `strategy!` instead of `strategize!`.`
13
+
3
14
  ## [0.3.10] - 2022-12-30
4
15
 
5
16
  - Change `CallableTree::Node::Internal#broadcastable` to take `matchable` keyword parameter as boolean. It defaults to `true`, which is the same behavior as before.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,68 @@
1
+ # Commit Message Convention
2
+
3
+ We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
4
+
5
+ ## Format
6
+
7
+ ```
8
+ <type>(<scope>): <subject>
9
+
10
+ <body>
11
+
12
+ <footer>
13
+ ```
14
+
15
+ ## Header
16
+
17
+ The header is mandatory and must not exceed 72 characters.
18
+
19
+ ### Type
20
+
21
+ Must be one of the following:
22
+
23
+ - **feat**: A new feature
24
+ - **fix**: A bug fix
25
+ - **docs**: Documentation only changes
26
+ - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
27
+ - **refactor**: A code change that neither fixes a bug nor adds a feature
28
+ - **perf**: A code change that improves performance
29
+ - **test**: Adding missing tests or correcting existing tests
30
+ - **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
31
+ - **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
32
+ - **chore**: Other changes that don't modify src or test files
33
+ - **revert**: Reverts a previous commit
34
+
35
+ ### Scope
36
+
37
+ The scope is optional and should be a phrase describing the section of the codebase affected.
38
+
39
+ ### Subject
40
+
41
+ The subject contains a succinct description of the change:
42
+
43
+ - Use the imperative, present tense: "change" not "changed" nor "changes"
44
+ - Don't capitalize the first letter
45
+ - No dot (.) at the end
46
+
47
+ ## Body
48
+
49
+ The body is optional and should include the motivation for the change and contrast this with previous behavior.
50
+
51
+ ## Footer
52
+
53
+ The footer is optional and should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit closes.
54
+
55
+ ## Branch Naming
56
+
57
+ We generally use the following prefixes for branch names:
58
+
59
+ - `feature/`: New features (e.g., `feature/login-screen`)
60
+ - `fix/`: Bug fixes (e.g., `fix/memory-leak`)
61
+ - `docs/`: Documentation only changes (e.g., `docs/update-readme`)
62
+ - `style/`: Changes that do not affect the meaning of the code (e.g., `style/rubocop-fixes`)
63
+ - `refactor/`: Code changes that neither fix a bug nor add a feature (e.g., `refactor/extract-method`)
64
+ - `test/`: Adding or correcting tests (e.g., `test/add-rspec-cases`)
65
+ - `chore/`: Changes to the build process or auxiliary tools and libraries (e.g., `chore/update-gems`)
66
+
67
+ Use kebab-case for the branch name (e.g., `feature/my-new-feature`).
68
+
data/Gemfile CHANGED
@@ -5,6 +5,8 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in callable_tree.gemspec
6
6
  gemspec
7
7
 
8
- gem 'rake', '~> 13.0'
8
+ gem 'rake', '~> 13.3'
9
9
 
10
- gem 'rspec', '~> 3.0'
10
+ gem 'rspec', '~> 3.13'
11
+
12
+ gem 'rubocop', require: false if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('4.0')