toys-core 0.22.0 → 0.23.0
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/CHANGELOG.md +89 -0
- data/README.md +13 -12
- data/docs/guide.md +356 -143
- data/lib/toys/acceptor.rb +1 -1
- data/lib/toys/arg_parser.rb +77 -45
- data/lib/toys/cli.rb +448 -336
- data/lib/toys/completion.rb +36 -45
- data/lib/toys/context.rb +80 -23
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/base.rb +22 -8
- data/lib/toys/dsl/internal.rb +29 -150
- data/lib/toys/dsl/tool.rb +176 -93
- data/lib/toys/errors.rb +142 -47
- data/lib/toys/flag_group.rb +16 -14
- data/lib/toys/input_file.rb +13 -9
- data/lib/toys/loader/load_state.rb +449 -0
- data/lib/toys/loader/tool_registry.rb +250 -0
- data/lib/toys/loader.rb +299 -631
- data/lib/toys/middleware.rb +1 -1
- data/lib/toys/runner.rb +542 -0
- data/lib/toys/source_info/origin.rb +228 -0
- data/lib/toys/source_info.rb +359 -222
- data/lib/toys/source_list.rb +89 -0
- data/lib/toys/source_spec.rb +480 -0
- data/lib/toys/standard_middleware/apply_config.rb +65 -13
- data/lib/toys/standard_middleware/show_help.rb +8 -9
- data/lib/toys/standard_middleware/show_root_version.rb +1 -1
- data/lib/toys/standard_mixins/bundler.rb +6 -3
- data/lib/toys/standard_mixins/exec.rb +8 -8
- data/lib/toys/standard_mixins/fileutils.rb +1 -1
- data/lib/toys/standard_mixins/gems.rb +1 -1
- data/lib/toys/standard_mixins/git_cache.rb +1 -1
- data/lib/toys/standard_mixins/highline.rb +2 -2
- data/lib/toys/standard_mixins/pager.rb +1 -1
- data/lib/toys/standard_mixins/terminal.rb +2 -2
- data/lib/toys/standard_mixins/xdg.rb +1 -1
- data/lib/toys/template.rb +10 -10
- data/lib/toys/tool_definition.rb +189 -93
- data/lib/toys/tool_name_splitter.rb +94 -0
- data/lib/toys/unique_key.rb +38 -0
- data/lib/toys/utils/completion_engine.rb +97 -22
- data/lib/toys/utils/gems.rb +251 -34
- data/lib/toys/utils/git_cache.rb +84 -16
- data/lib/toys/utils/help_text.rb +8 -5
- data/lib/toys/utils/standard_ui.rb +111 -34
- data/lib/toys/utils/terminal.rb +1 -1
- data/lib/toys-core.rb +64 -9
- metadata +13 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a09f479bfecbde001cec1076fdce57080117cbdeecfc2738879445a682b7363f
|
|
4
|
+
data.tar.gz: 277635722c80c2691759147cf7fc4b928755f0349a54fd970327c46bae443170
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6cfaa55d28079f651f9eab9d8a693af1386ddc6d08e58553ab457c4c669265902da5bba6e0f336faa8113bf0a92b2fbbe2bde4b49480118fe1d63b00a38ba791
|
|
7
|
+
data.tar.gz: f8b6b6efcc6c978d1a1ce7cece9040eb5475eb7baa58c6b1527ba2dc5bb780c8401f30917e62fcc43999224ecd755b136d03d05d79c03fdc26622efc306e02d3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.23.0 / 2026-09-09
|
|
4
|
+
|
|
5
|
+
This is a major release, with several new features, a number of fixes, and a significant refactor of the tool loading and execution layers, including several breaking interface changes. It is a release candidate for the upcoming version 1.0.
|
|
6
|
+
|
|
7
|
+
Highlights include:
|
|
8
|
+
|
|
9
|
+
* Major refactor of some of the larger classes that were doing too much: Extracted run logic out of `Toys::CLI` into `Toys::Runner`, extracted source configuration out of `Toys::Loader` into `Toys::SourceSpec` and `Toys::SourceList`, and extracted delimiter handling out of `Toys::Loader` into `Toys::ToolNameSplitter`. This cleaned up the architecture significantly, eliminating most back-references to the CLI object.
|
|
10
|
+
* Added the ability to treat unknown flags as positional args via the `treat_unknown_flags_as_args` DSL directive and corresponding methods on `Toys::ToolDefinition`. Useful for wrapping and delegating to other commands.
|
|
11
|
+
* Updates to error handling to improve the output and fix some long-standing issues related to tool delegation.
|
|
12
|
+
* Several fixes to bundler integration, especially with Bundler 4.
|
|
13
|
+
|
|
14
|
+
Details follow.
|
|
15
|
+
|
|
16
|
+
* Changes to tool definition:
|
|
17
|
+
* BREAKING CHANGE: In a tool that delegates, `Toys::ToolDefinition#run_handler` now returns the delegate target (the target's full name as a frozen array of strings) instead of a Proc. The `run_handler=` setter still accepts only a proc, symbol, or nil, so delegation must still be configured via `delegate_to`.
|
|
18
|
+
* BREAKING CHANGE: The `validation_errors` field in `Toys::FlagGroup` classes now return strings rather than `Toys::ArgParser::FlagGroupConstraintError` objects.
|
|
19
|
+
* Added the `treat_unknown_flags_as_args` DSL directive (and the corresponding `ToolDefinition#treat_unknown_flags_as_args` setter and `ToolDefinition#unknown_flags_are_args?` query), which redirects unrecognized flags to the tool's positional arguments instead of reporting a usage error. Useful for wrapping and delegating to other commands.
|
|
20
|
+
* When two or more flags are declared using the same context key, within a flag group, the flag group's requirements now evaluate correctly.
|
|
21
|
+
* A flag or optional/remaining positional argument declared with a `nil` default no longer clears default data already set for the same key by an earlier declaration.
|
|
22
|
+
* `ToolDefinition#custom_context_directory=` and the corresponding `set_context_directory` DSL directive can take Pathname arguments and can handle relative paths (by expanding them into absolute paths).
|
|
23
|
+
* Toys now raises `Toys::ToolDefinitionError` if you attempt to "reset" a tool previously defined using a `Toys::Tool` subclass.
|
|
24
|
+
* SECURITY FIX: Toys now raises `Toys::ToolSourceError` if a tool loaded from a RubyGem attempts to load tools from the local file system.
|
|
25
|
+
* `Toys::ToolDefinition#add_flag_group` and `Toys::ToolDefinition#completion=` now properly raise ToolDefinitionError if the definition has already been finished.
|
|
26
|
+
* BREAKING CHANGE: Toys now raises `Toys::ToolDefintionError` if you attempt to create or descend into a subtool from a `subtool_apply` block or a middleware-based config.
|
|
27
|
+
* Caught additional cases of incorrectly located `Toys::Tool` subclasses and raised `Toys::ToolDefinitionError`.
|
|
28
|
+
* If `Toys::ToolDefinition#finish_definition` raises an error (e.g. due to a middleware issue), the tool now goes into an error state and refuses any operation unless it is reset.
|
|
29
|
+
* The root tool's display_name is now "(root)" instead of the empty string.
|
|
30
|
+
|
|
31
|
+
* Changes to tool loading:
|
|
32
|
+
* BREAKING CHANGE: You no longer add sources to a `Toys::Loader` in place. Instead, construct a `Toys::SourceList` and add sources there, then pass it to the `Toys::Loader` constructor. (`Toys::CLI` has been modified to use this mechanism.)
|
|
33
|
+
* BREAKING CHANGE: Removed `Loader#split_path` and `Loader#split_partial_path` in favor of `Toys::ToolNameSplitter`. Use `Loader#tool_name_splitter` to get the name splitter object from a Loader. Related, `Loader.new` now takes a `tool_name_splitter` keyword argument in place of `extra_delimiters`. (`CLI.new` still takes `extra_delimiters`.)
|
|
34
|
+
* BREAKING CHANGE: `Toys::SourceInfo#apply_lib_paths` was replaced with `Toys::SourceInfo#find_lib_paths`, which returns a list of paths but does not actually `require` them. It is now the responsibility of the caller to do the `require`.
|
|
35
|
+
* BREAKING CHANGE: Removed git- and gem- specific attributes from `Toys::SourceInfo` and replaced with `Toys::SourceInfo#origin`.
|
|
36
|
+
* BREAKING CHANGE: `Toys::Loader#lookup_specific` now cannot take a tool name with delimiters. The tool name must be a string array.
|
|
37
|
+
* BREAKING CHANGE: Errors raised when the loader cannot open a tool source (e.g. a gem is missing) are now `Toys::ToolSourceError` instead of `Toys::ToolDefinitionError`.
|
|
38
|
+
* Added `Toys::SourceInfo#find_preload_files` which returns all files to preload.
|
|
39
|
+
* `Toys::Loader#list_subtools` now ensures returned tools have been finished by their middleware.
|
|
40
|
+
* The priority for the fallback root tool is now `-999_999_999` instead of `-999_999`. This value should still fit within the fixnum optimization.
|
|
41
|
+
* Directory contents are loaded in deterministic (sorted) order.
|
|
42
|
+
|
|
43
|
+
* Changes to tool execution:
|
|
44
|
+
* New class `Toys::Runner` now directs tool execution (which previously was available only from `Toys::CLI`.) `Toys::Runner` provides finer-grained control over error handling, allowing independent control over `Toys::ContextualError` wrapping and error handler usage.
|
|
45
|
+
* Added `Toys::Context#runner` and `Toys::Context#loader` for easy access to those facilities from a running tool.
|
|
46
|
+
* BREAKING CHANGE: `Toys::Context#context_directory`, and `Toys::Context#get` with the associated context key, now never return nil. If no context directory is set for the tool, these now return the current working directory.
|
|
47
|
+
* Standard context keys and sentinels use the new class `Toys::UniqueKey`, which displays useful names in diagnostic output.
|
|
48
|
+
* Delegation now propagates the caller's verbosity to the delegate target, rather than resetting it to zero.
|
|
49
|
+
* When a tool delegates, a `SignalException` unhandled by the inner tool now propagates outward so each tool in the delegation chain gets a chance at its own `on_interrupt` / `on_signal` handler.
|
|
50
|
+
* A nested run that shares a logger with the run that called it now uses the base level already in effect for that logger, so verbosity no longer compounds across nested runs.
|
|
51
|
+
* BREAKING CHANGE: Several internal `Toys::ArgParser` changes: The constructor now takes the tool and loader instead of the CLI and tool, and the `default_data` keyword argument was renamed to `common_data`. It also no longer populates context keys such as `LOGGER` and `TOOL` that are not directly related to parsing; those must be passed in via `common_data`, and are generally handled by `Tool::Runner`.
|
|
52
|
+
* `Toys::ArgParser#data` now reflects the args, unmatched args, unmatched flags, unmatched positional, and usage errors accumulated so far while parsing, instead of only after `finish` is called.
|
|
53
|
+
|
|
54
|
+
* Changes to the error handling system, particularly important if you provide a custom error handler:
|
|
55
|
+
* BREAKING CHANGE: `Toys::ContextualError` no longer wraps `SignalException`. Custom error handlers should be prepared to handle `SignalException` directly.
|
|
56
|
+
* BREAKING CHANGE: Custom error handlers should also be prepared to handle unwrapped `StandardError` and `ScriptError` exceptions directly, since it is now possible to disable `Toys::ContextualError` wrapping.
|
|
57
|
+
* An error raised by a delegated tool now raises nested `ContextualError` objects providing information about the inter-tool call sequence, one wrapper per tool in the chain. This might happen, for example, if a tool was delegated or if a subtool was called without an error handler. In normal runs, this displays both the delegating and original tools in the error message. It also means custom error handlers should use the new `Toys::ContextualError#root_cause` if they want the original exception.
|
|
58
|
+
* `ContextualError` now sets its backtrace from the wrapped error's backtrace locations when available (falling back to the backtrace strings), so an enclosing capture can still locate the config file line.
|
|
59
|
+
* Renamed `config_path` and `config_line` to `tool_file_path` and `tool_file_line`. The original names have been aliased for backward compatibility.
|
|
60
|
+
* `ContextualError` now captures tool names in more places, and captures the current phase (loading or running).
|
|
61
|
+
* Revamped the exception output provided by `Toys::Utils::StandardUI` so it's more compact and usable.
|
|
62
|
+
|
|
63
|
+
* Changes to the shell completion system:
|
|
64
|
+
* BREAKING CHANGE: Minor simplifications to the completion internals, notably `Toys::Completion::Context#fragment_prefix` has been removed, and `prefix_constraint` has been removed from several provided completions. A completion now always receives the entire word under the cursor as its `fragment`, and every candidate must be a replacement for the entire word. (The prefix handling requirements of the Bash completion have been moved up into `Toys::Utils::CompletionEngine`.)
|
|
65
|
+
* BREAKING CHANGE: The Bash and Zsh completion engine constructors now take a completion and a loader instead of a cli, and some of the internals and override points have been reorganized.
|
|
66
|
+
* Fixed shell completion for non-flag words containing `=` or `:`: completions are now computed against the whole word, and the engine trims candidates to the span the shell will actually replace.
|
|
67
|
+
* Zsh completion now replaces the entire word rather than only the text after an `=` or `:`, matching how zsh actually handles word breaks; bash continues to break at `=` and `:`.
|
|
68
|
+
* Word-break trimming for bash now ignores `=` and `:` characters that were quoted or backslash-escaped, since the shell does not break a word at a quoted character.
|
|
69
|
+
* Subtool completion now uses the loader's configured delimiters to decide where a tool path ends, rather than a separate regex over the fragment prefix, so completing a partially typed tool path works consistently with any configured extra delimiters.
|
|
70
|
+
|
|
71
|
+
* Changes to the CLI interfaces:
|
|
72
|
+
* Added `add_source` method that takes an instance of the new `Toys::SourceSpec` types that describe a tool source. This method replaces the now deprecated `add_config_*` methods (although those methods will continue to be supported for a time). In general, the "config" terminology is being retired in favor of "source" which I think better describes what is going on.
|
|
73
|
+
* BREAKING CHANGE: The `config_file_name` and `config_dir_name` arguments to CLI have been renamed to `toplevel_tool_file_name` and `toplevel_tool_dir_name`, respectively, as part of a general removal of the "config" term. The old names are not aliased.
|
|
74
|
+
* BREAKING CHANGE: It is no longer possible to configure the index file name, lib and data directory names, and preload names, via keyword arguments. This was done to simplify the CLI and Loader interfaces, and because I realized that if they ever were to be customizable, it would need to be per source directory rather than per-CLI.
|
|
75
|
+
* BREAKING CHANGE: `Toys::CLI#run` no longer accepts the `delegated_from:` keyword argument. Tool delegation is now handled internally, and its runtime is not exposed in the public interface.
|
|
76
|
+
* You can get the `Toys::Runner` for a CLI using `Toys::CLI#runner`, providing a way to customize the run process more closely than using `Toys::CLI#run`.
|
|
77
|
+
* Path parameters for the methods that add sources, such as `path` and `context_directory`, can now take Pathname objects and can handle relative paths properly (by expanding them into absolute paths).
|
|
78
|
+
* `Toys::CLI#child` accepts `copy_sources: true`, which populates the new CLI's loader with the same sources as the original. This makes it easy to create a new CLI with additional sources on top of the current.
|
|
79
|
+
* The `Toys::CLI` constructor now takes `git_cache` and `gems_util` keyword arguments letting you customize the objects used to resolve git and gem sources.
|
|
80
|
+
* Calling `Toys::CLI#add_search_path` with a nonexistent directory no longer raises.
|
|
81
|
+
* The ApplyConfig middleware now behaves correctly when configured globally in the CLI or loader.
|
|
82
|
+
|
|
83
|
+
* Rubygems/Bundler integration changes:
|
|
84
|
+
* Bundler integration requires Bundler 2.4 or later. This is the default on Ruby 3.2, but requires that Bundler is updated on earlier Rubies.
|
|
85
|
+
* Bundler integration properly detects provenance attributes such as `path:` and `git:`.
|
|
86
|
+
* Calling bundler integration with a nonempty group list no longer loses gems that were already loaded.
|
|
87
|
+
* Bundler integration now honors `BUNDLE_LOCKFILE` (new in Bundler 4) and stops leaking it from the modified bundle.
|
|
88
|
+
|
|
89
|
+
* Other fixes
|
|
90
|
+
* Terminal mixin now raises the correct ArgumentError (instead of NameError) if given an unknown style code.
|
|
91
|
+
|
|
3
92
|
### v0.22.0 / 2026-05-05
|
|
4
93
|
|
|
5
94
|
Toys-core 0.22 is a major release focused on polish and cleanup in preparation for version 1.0. It includes a number of small breaking changes where needed to clean up the interfaces.
|
data/README.md
CHANGED
|
@@ -87,7 +87,7 @@ require "toys-core"
|
|
|
87
87
|
cli = Toys::CLI.new
|
|
88
88
|
|
|
89
89
|
#### Insert the following block ...
|
|
90
|
-
cli.
|
|
90
|
+
cli.add_source do
|
|
91
91
|
desc "My first executable!"
|
|
92
92
|
flag :whom, default: "world"
|
|
93
93
|
def run
|
|
@@ -109,9 +109,9 @@ $ ./mycmd --help
|
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Notice that we did not create a `tool` block, but instead set up description,
|
|
112
|
-
flags, and functionality directly in the
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
flags, and functionality directly in the source block. This configures the
|
|
113
|
+
"root tool", i.e. what happens when you run the executable without passing a
|
|
114
|
+
tool name to it. (In fact, it's technically legal to do this in Toys as well,
|
|
115
115
|
by setting functionality at the "top level" of a `.toys.rb` file without any
|
|
116
116
|
`tool` block, although you probably won't actually want to do so.)
|
|
117
117
|
|
|
@@ -119,7 +119,8 @@ by setting functionality at the "top level" of a `.toys.rb` file without any
|
|
|
119
119
|
|
|
120
120
|
But perhaps you want your executable to have multiple "tools", similar to other
|
|
121
121
|
familiar executables like git or kubectl. You can define tools, including
|
|
122
|
-
nested tools, by writing `tool` blocks
|
|
122
|
+
nested tools, by writing `tool` sub-blocks within your source block. Here's an
|
|
123
|
+
example:
|
|
123
124
|
|
|
124
125
|
```ruby
|
|
125
126
|
#!/usr/bin/env ruby
|
|
@@ -128,8 +129,8 @@ require "toys-core"
|
|
|
128
129
|
|
|
129
130
|
cli = Toys::CLI.new
|
|
130
131
|
|
|
131
|
-
#### Change the
|
|
132
|
-
cli.
|
|
132
|
+
#### Change the source block as follows ...
|
|
133
|
+
cli.add_source do
|
|
133
134
|
# Things outside any tool block still apply to the root
|
|
134
135
|
desc "My first executable with several tools"
|
|
135
136
|
|
|
@@ -162,7 +163,7 @@ available tools.
|
|
|
162
163
|
$ ./mycmd
|
|
163
164
|
```
|
|
164
165
|
|
|
165
|
-
Notice that the description set at the "root" of the
|
|
166
|
+
Notice that the description set at the "root" of the source block (outside the
|
|
166
167
|
tool blocks) shows up here.
|
|
167
168
|
|
|
168
169
|
### Configuring the CLI
|
|
@@ -189,8 +190,8 @@ cli = Toys::CLI.new(
|
|
|
189
190
|
}
|
|
190
191
|
)
|
|
191
192
|
|
|
192
|
-
#### Change the
|
|
193
|
-
cli.
|
|
193
|
+
#### Change the source block as follows ...
|
|
194
|
+
cli.add_source do
|
|
194
195
|
tool "example" do
|
|
195
196
|
tool "greet" do
|
|
196
197
|
def run
|
|
@@ -238,8 +239,8 @@ middlewares = [
|
|
|
238
239
|
]
|
|
239
240
|
cli = Toys::CLI.new middleware_stack: middlewares
|
|
240
241
|
|
|
241
|
-
#### Use this
|
|
242
|
-
cli.
|
|
242
|
+
#### Use this source block ...
|
|
243
|
+
cli.add_source do
|
|
243
244
|
tool "greet" do
|
|
244
245
|
def run
|
|
245
246
|
puts "Hello, world!"
|