toys-core 0.21.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 +144 -0
- data/LICENSE.md +1 -1
- data/README.md +155 -108
- data/docs/guide.md +698 -151
- data/lib/toys/acceptor.rb +35 -38
- data/lib/toys/arg_parser.rb +133 -82
- data/lib/toys/cli.rb +451 -332
- data/lib/toys/compat.rb +14 -0
- data/lib/toys/completion.rb +73 -88
- data/lib/toys/context.rb +85 -24
- data/lib/toys/core.rb +1 -1
- data/lib/toys/dsl/base.rb +24 -10
- data/lib/toys/dsl/flag.rb +6 -4
- data/lib/toys/dsl/flag_group.rb +1 -1
- data/lib/toys/dsl/internal.rb +31 -152
- data/lib/toys/dsl/positional_arg.rb +2 -2
- data/lib/toys/dsl/tool.rb +301 -161
- data/lib/toys/errors.rb +141 -91
- data/lib/toys/flag.rb +32 -27
- data/lib/toys/flag_group.rb +27 -18
- data/lib/toys/input_file.rb +20 -7
- data/lib/toys/loader/load_state.rb +449 -0
- data/lib/toys/loader/tool_registry.rb +250 -0
- data/lib/toys/loader.rb +316 -611
- data/lib/toys/middleware.rb +5 -5
- data/lib/toys/mixin.rb +15 -5
- data/lib/toys/module_lookup.rb +1 -1
- data/lib/toys/positional_arg.rb +12 -9
- data/lib/toys/runner.rb +542 -0
- data/lib/toys/source_info/origin.rb +228 -0
- data/lib/toys/source_info.rb +358 -220
- data/lib/toys/source_list.rb +89 -0
- data/lib/toys/source_spec.rb +480 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -2
- data/lib/toys/standard_middleware/apply_config.rb +65 -13
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -1
- data/lib/toys/standard_middleware/show_help.rb +71 -58
- data/lib/toys/standard_middleware/show_root_version.rb +1 -1
- data/lib/toys/standard_mixins/bundler.rb +9 -4
- data/lib/toys/standard_mixins/exec.rb +16 -16
- data/lib/toys/standard_mixins/fileutils.rb +7 -6
- data/lib/toys/standard_mixins/gems.rb +51 -24
- data/lib/toys/standard_mixins/git_cache.rb +3 -3
- data/lib/toys/standard_mixins/highline.rb +17 -17
- data/lib/toys/standard_mixins/pager.rb +1 -1
- data/lib/toys/standard_mixins/terminal.rb +4 -4
- data/lib/toys/standard_mixins/xdg.rb +1 -1
- data/lib/toys/template.rb +28 -15
- data/lib/toys/tool_definition.rb +307 -129
- data/lib/toys/tool_name_splitter.rb +94 -0
- data/lib/toys/unique_key.rb +38 -0
- data/lib/toys/utils/completion_engine.rb +101 -26
- data/lib/toys/utils/exec.rb +1018 -604
- data/lib/toys/utils/gems.rb +290 -62
- data/lib/toys/utils/git_cache.rb +689 -606
- data/lib/toys/utils/help_text.rb +385 -25
- data/lib/toys/utils/standard_ui.rb +137 -59
- data/lib/toys/utils/terminal.rb +5 -5
- data/lib/toys/utils/xdg.rb +21 -12
- data/lib/toys/wrappable_string.rb +2 -2
- data/lib/toys-core.rb +64 -11
- metadata +13 -6
- data/lib/toys/settings.rb +0 -944
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,149 @@
|
|
|
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
|
+
|
|
92
|
+
### v0.22.0 / 2026-05-05
|
|
93
|
+
|
|
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.
|
|
95
|
+
|
|
96
|
+
Breaking interface changes:
|
|
97
|
+
|
|
98
|
+
* The `static` directive no longer "forces" creating of helper methods, instead using the same logic as the flag and argument directives. In particular, by default it will not create a method starting with underscore.
|
|
99
|
+
* Removed `Toys::Settings` and moved inheritable_helper_methods to a dedicated tool attribute. Use the `basic_settings` gem if you need the old settings class.
|
|
100
|
+
* `Toys::Acceptor#create` no longer takes a hash as the spec. (This was undocumented but worked previously.) To construct an object, pass the class as the main spec object, and the keyword arguments in the options.
|
|
101
|
+
* `Toys::Acceptor::Enum#values` now returns the values themselves instead of a zipped array that includes strings.
|
|
102
|
+
* `Toys::FlagGroup::Base` is no longer instantiable; use `Toys::FlagGroup::Optional` instead.
|
|
103
|
+
* The `acceptor` and `display_name` attributes of `Toys::PositionalArg` are no longer writable and must be set in the constructor.
|
|
104
|
+
* `Toys::Completion#create` no longer takes a hash as the spec. (This was undocumented but worked previously.) To construct an object, pass the class as the main spec object, and the keyword arguments in the options.
|
|
105
|
+
* The `delegation_target` attribute of `Toys::ToolDefinition::DefaultCompletion` is no longer writable and must be set in the constructor.
|
|
106
|
+
* You can no longer override a completion class using the `:""` key. (This was undocumented but worked previously.) Just pass a fully constructed completion object if you need a custom object.
|
|
107
|
+
* The original `Module#include` method is now available in the DSL as `include_module` instead of `super_include`.
|
|
108
|
+
* Template classes no longer automatically include `Toys::Context::Key`. This behavior was undocumented and inconsistent between different ways of defining templates.
|
|
109
|
+
* Made `Toys::CLI#load_tool` return the block value rather than the exit_code.
|
|
110
|
+
* Dropped undocumented `Loader#add_path_set` option to include individual source names in the relative_paths argument.
|
|
111
|
+
* Renamed `use_less` in the `Toys::StandardMiddleware::ShowHelp` constructor to `use_pager`, and added support for custom pagers.
|
|
112
|
+
* Renamed many of the methods in `StandardUI` to reduce confusion.
|
|
113
|
+
* Removed `LoaderError` and used `ToolDefinitionError` for those cases, as it was decided that the distinction was ambiguous and dependent on internal details.
|
|
114
|
+
* Removed `ContextualError#underlying_error` as it was synonymous with `cause`.
|
|
115
|
+
* Consolidated `ContextualError` public interface into a single `capture` method.
|
|
116
|
+
* `ArgParser::ExtraArgumentsError` now takes `:arguments` instead of `:values`, and provides an accessor for the same.
|
|
117
|
+
* `ArgParser::ToolUnrecognizedError` now takes `:full_name` instead of `:values`, and provides an accessor for the same.
|
|
118
|
+
* Renamed `ArgParser::UsageError#full_message` to `message_with_suggestions` to avoid overriding `Exception#full_message`.
|
|
119
|
+
|
|
120
|
+
New features:
|
|
121
|
+
|
|
122
|
+
* The range acceptor supports beginless and endless ranges.
|
|
123
|
+
* A custom source_name can be provided to `Loader#add_path_set`, `Loader#add_git`, and `Loader#add_gem`.
|
|
124
|
+
* The `:gems` mixin provides a context key for retrieving the underlying `Toys::Utils::Gems` service object.
|
|
125
|
+
* The `:gems` mixin provides an explicit `Toys::Utils::Gems::ClassMethods` module defining the directives added to the tool class.
|
|
126
|
+
* The `activate` and `bundle` methods in the Gems utility now return useful results.
|
|
127
|
+
|
|
128
|
+
Other fixes:
|
|
129
|
+
|
|
130
|
+
* The `:update` argument to the `load_git` directive had no effect if the `:as` argument was provided.
|
|
131
|
+
* `FlagValueUnacceptableError` and `ArgValueUnacceptableError` have their value set correctly.
|
|
132
|
+
* If a CLI has a static (shared) logger, creating a child with `logger: nil` actually clears it.
|
|
133
|
+
* Removed unused `Toys::StandardMiddleware::ShowHelp::TOOL_NAME_KEY`.
|
|
134
|
+
* ShowHelp middleware displays an error message instead of raising an exception when help cannot be generated due to a bad search regex.
|
|
135
|
+
* `ArgParser::UsageError` is now a proper exception.
|
|
136
|
+
* `ContextualError` does a better job of capturing relevant syntax errors.
|
|
137
|
+
* Various minor completion system fixes.
|
|
138
|
+
|
|
139
|
+
Several libraries under `toys/utils` were extracted into their own gems. The current code remains under `Toys::Utils` as a vendored copy of the external gem, so the extracted gems are not actually dependencies of Toys. The affected libraries are:
|
|
140
|
+
|
|
141
|
+
* `Toys::Utils::Exec` which was extracted to the gem `exec_service`.
|
|
142
|
+
* `Toys::Utils::XDG` which was extracted to the gem `simple_xdg`.
|
|
143
|
+
* `Toys::Utils::GitCache` which was extracted to the gem `git_cache`.
|
|
144
|
+
|
|
145
|
+
Finally, a variety of clarifications and fixes were made to the reference documentation, readme, and user's guide.
|
|
146
|
+
|
|
3
147
|
### v0.21.0 / 2026-03-23
|
|
4
148
|
|
|
5
149
|
This release includes a variety of small fixes and updates toward improving product polish in preparation for a 1.0 release. It focuses on the following areas:
|
data/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# License
|
|
2
2
|
|
|
3
|
-
Copyright 2019-
|
|
3
|
+
Copyright 2019-2026 Daniel Azuma and the Toys contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -29,7 +29,9 @@ need to be modified if you're running on Windows.
|
|
|
29
29
|
|
|
30
30
|
Install the **toys-core** gem using:
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
```
|
|
33
|
+
$ gem install toys-core
|
|
34
|
+
```
|
|
33
35
|
|
|
34
36
|
You can also install the **toys** gem, which brings in **toys-core** as a
|
|
35
37
|
dependency.
|
|
@@ -39,22 +41,28 @@ dependency.
|
|
|
39
41
|
We'll start by creating an executable Ruby script. Using your favorite text
|
|
40
42
|
editor, create a new file called `mycmd` with the following contents:
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
```ruby
|
|
45
|
+
#!/usr/bin/env ruby
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
require "toys-core"
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
cli = Toys::CLI.new
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
exit(cli.run(*ARGV))
|
|
52
|
+
```
|
|
49
53
|
|
|
50
54
|
Make sure the file's executable bit is set:
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
```
|
|
57
|
+
$ chmod a+x mycmd
|
|
58
|
+
```
|
|
53
59
|
|
|
54
60
|
That's it! This is a fully-functional Toys-based executable! Let's see what
|
|
55
61
|
happens when you run it:
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
```
|
|
64
|
+
$ ./mycmd
|
|
65
|
+
```
|
|
58
66
|
|
|
59
67
|
Just as with Toys itself, you get a help screen by default (since we haven't
|
|
60
68
|
yet actually implemented any behavior.) As you can see, some of the same
|
|
@@ -71,35 +79,39 @@ information to the Toys CLI object in a block.
|
|
|
71
79
|
|
|
72
80
|
Let's add some functionality to `mycmd`.
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
```ruby
|
|
83
|
+
#!/usr/bin/env ruby
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
require "toys-core"
|
|
77
86
|
|
|
78
|
-
|
|
87
|
+
cli = Toys::CLI.new
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
#### Insert the following block ...
|
|
90
|
+
cli.add_source do
|
|
91
|
+
desc "My first executable!"
|
|
92
|
+
flag :whom, default: "world"
|
|
93
|
+
def run
|
|
94
|
+
puts "Hello, #{whom}!"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
exit(cli.run(*ARGV))
|
|
99
|
+
```
|
|
90
100
|
|
|
91
101
|
If you went through the tutorial in the README for the Toys gem, this should
|
|
92
102
|
look familiar. Let's run it now, and experiment with passing flags to it.
|
|
93
103
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
```
|
|
105
|
+
$ ./mycmd
|
|
106
|
+
$ ./mycmd --whom=ruby
|
|
107
|
+
$ ./mycmd --bye
|
|
108
|
+
$ ./mycmd --help
|
|
109
|
+
```
|
|
98
110
|
|
|
99
111
|
Notice that we did not create a `tool` block, but instead set up description,
|
|
100
|
-
flags, and functionality directly in the
|
|
101
|
-
|
|
102
|
-
|
|
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,
|
|
103
115
|
by setting functionality at the "top level" of a `.toys.rb` file without any
|
|
104
116
|
`tool` block, although you probably won't actually want to do so.)
|
|
105
117
|
|
|
@@ -107,44 +119,51 @@ by setting functionality at the "top level" of a `.toys.rb` file without any
|
|
|
107
119
|
|
|
108
120
|
But perhaps you want your executable to have multiple "tools", similar to other
|
|
109
121
|
familiar executables like git or kubectl. You can define tools, including
|
|
110
|
-
nested tools, by writing `tool` blocks
|
|
122
|
+
nested tools, by writing `tool` sub-blocks within your source block. Here's an
|
|
123
|
+
example:
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
```ruby
|
|
126
|
+
#!/usr/bin/env ruby
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
require "toys-core"
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
cli = Toys::CLI.new
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
#### Change the source block as follows ...
|
|
133
|
+
cli.add_source do
|
|
134
|
+
# Things outside any tool block still apply to the root
|
|
135
|
+
desc "My first executable with several tools"
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Try writing a second tool here. You could use the "new-repo"
|
|
133
|
-
# example from the Toys tutorial.
|
|
137
|
+
# We'll put the greet function here
|
|
138
|
+
tool "greet" do
|
|
139
|
+
desc "My first tool!"
|
|
140
|
+
flag :whom, default: "world"
|
|
141
|
+
def run
|
|
142
|
+
puts "Hello, #{whom}!"
|
|
134
143
|
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Try writing a second tool here. You could use the "new-repo"
|
|
147
|
+
# example from the Toys tutorial.
|
|
148
|
+
end
|
|
135
149
|
|
|
136
|
-
|
|
150
|
+
exit(cli.run(*ARGV))
|
|
151
|
+
```
|
|
137
152
|
|
|
138
153
|
Now you can run `greet` as a tool:
|
|
139
154
|
|
|
140
|
-
|
|
155
|
+
```
|
|
156
|
+
$ ./mycmd greet
|
|
157
|
+
```
|
|
141
158
|
|
|
142
159
|
The "root" functionality once again shows global help, including a list of the
|
|
143
160
|
available tools.
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
```
|
|
163
|
+
$ ./mycmd
|
|
164
|
+
```
|
|
146
165
|
|
|
147
|
-
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
|
|
148
167
|
tool blocks) shows up here.
|
|
149
168
|
|
|
150
169
|
### Configuring the CLI
|
|
@@ -157,43 +176,47 @@ These and many more aspects of the behavior of our executable can be customized
|
|
|
157
176
|
by passing options to the `Toys::CLI` constructor. Here's an example that
|
|
158
177
|
modifies error handling and delimiter parsing.
|
|
159
178
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
end
|
|
181
|
-
tool "error" do
|
|
182
|
-
def run
|
|
183
|
-
raise "Whoops!"
|
|
184
|
-
end
|
|
185
|
-
end
|
|
179
|
+
```ruby
|
|
180
|
+
#!/usr/bin/env ruby
|
|
181
|
+
|
|
182
|
+
require "toys-core"
|
|
183
|
+
|
|
184
|
+
#### Pass some additional options to the CLI constructor ...
|
|
185
|
+
cli = Toys::CLI.new(
|
|
186
|
+
extra_delimiters: ":",
|
|
187
|
+
error_handler: ->(err) {
|
|
188
|
+
puts "Aww shucks, an error happened: #{err.message}"
|
|
189
|
+
return 1
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
#### Change the source block as follows ...
|
|
194
|
+
cli.add_source do
|
|
195
|
+
tool "example" do
|
|
196
|
+
tool "greet" do
|
|
197
|
+
def run
|
|
198
|
+
puts "Hello, world!"
|
|
186
199
|
end
|
|
187
200
|
end
|
|
201
|
+
tool "error" do
|
|
202
|
+
def run
|
|
203
|
+
raise "Whoops!"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
188
208
|
|
|
189
|
-
|
|
209
|
+
exit(cli.run(*ARGV))
|
|
210
|
+
```
|
|
190
211
|
|
|
191
212
|
Try these runs. Do they behave as you expected?
|
|
192
213
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
214
|
+
```
|
|
215
|
+
$ ./mycmd example greet
|
|
216
|
+
$ ./mycmd example:greet
|
|
217
|
+
$ ./mycmd example.greet
|
|
218
|
+
$ ./mycmd example error
|
|
219
|
+
```
|
|
197
220
|
|
|
198
221
|
### Configuring middleware
|
|
199
222
|
|
|
@@ -204,32 +227,36 @@ your tools by default.
|
|
|
204
227
|
The next example provides a custom middleware stack, resulting in a different
|
|
205
228
|
set of common tool functionality.
|
|
206
229
|
|
|
207
|
-
|
|
230
|
+
```ruby
|
|
231
|
+
#!/usr/bin/env ruby
|
|
208
232
|
|
|
209
|
-
|
|
233
|
+
require "toys-core"
|
|
210
234
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
#### Change the CLI construction again ...
|
|
236
|
+
middlewares = [
|
|
237
|
+
[:set_default_descriptions, default_tool_desc: "Hey look, a tool!"],
|
|
238
|
+
[:show_help, help_flags: true]
|
|
239
|
+
]
|
|
240
|
+
cli = Toys::CLI.new middleware_stack: middlewares
|
|
217
241
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
end
|
|
224
|
-
end
|
|
242
|
+
#### Use this source block ...
|
|
243
|
+
cli.add_source do
|
|
244
|
+
tool "greet" do
|
|
245
|
+
def run
|
|
246
|
+
puts "Hello, world!"
|
|
225
247
|
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
226
250
|
|
|
227
|
-
|
|
251
|
+
exit(cli.run(*ARGV))
|
|
252
|
+
```
|
|
228
253
|
|
|
229
254
|
We've now modified the default description applied to tools that don't provide
|
|
230
255
|
their own description. See the effect with:
|
|
231
256
|
|
|
232
|
-
|
|
257
|
+
```
|
|
258
|
+
$ ./mycmd greet --help
|
|
259
|
+
```
|
|
233
260
|
|
|
234
261
|
We've also omitted some of the default middleware, including the one that adds
|
|
235
262
|
the `--verbose` and `--quiet` flags to all your tools. Notice those flags are
|
|
@@ -240,7 +267,9 @@ We've also omitted the middleware that provides default execution behavior
|
|
|
240
267
|
haven't defined a top-level `run` method in this last example, invoking the
|
|
241
268
|
root tool will cause an error:
|
|
242
269
|
|
|
243
|
-
|
|
270
|
+
```
|
|
271
|
+
$ ./mycmd
|
|
272
|
+
```
|
|
244
273
|
|
|
245
274
|
It is even possible to write your own middleware. In general, while the
|
|
246
275
|
`Toys::CLI` constructor provides defaults that should work for many use cases,
|
|
@@ -258,12 +287,16 @@ includes a few simple examples that you can use as a starting point.
|
|
|
258
287
|
|
|
259
288
|
To experiment with the examples, clone the Toys repo from GitHub:
|
|
260
289
|
|
|
261
|
-
|
|
262
|
-
|
|
290
|
+
```
|
|
291
|
+
$ git clone https://github.com/dazuma/toys.git
|
|
292
|
+
$ cd toys
|
|
293
|
+
```
|
|
263
294
|
|
|
264
295
|
Navigate to the simple-gem example:
|
|
265
296
|
|
|
266
|
-
|
|
297
|
+
```
|
|
298
|
+
$ cd toys-core/examples/simple-gem
|
|
299
|
+
```
|
|
267
300
|
|
|
268
301
|
This example wraps the simple "greet" executable that we covered earlier, in a
|
|
269
302
|
gem. You can see the
|
|
@@ -273,23 +306,31 @@ in the bin directory.
|
|
|
273
306
|
Try it out by building and installing the gem. From the `examples/simple-gem`
|
|
274
307
|
directory, run:
|
|
275
308
|
|
|
276
|
-
|
|
309
|
+
```
|
|
310
|
+
$ toys install
|
|
311
|
+
```
|
|
277
312
|
|
|
278
313
|
Once the gem has successfully installed, you can run the executable, which
|
|
279
314
|
RubyGems should have added to your path. (Note: if you are using a ruby
|
|
280
315
|
installation manager, you may need to "rehash" or "reshim" to gain access to
|
|
281
316
|
the executable.)
|
|
282
317
|
|
|
283
|
-
|
|
318
|
+
```
|
|
319
|
+
$ toys-core-simple-example --whom=Toys
|
|
320
|
+
```
|
|
284
321
|
|
|
285
322
|
Clean up by uninstalling the gem:
|
|
286
323
|
|
|
287
|
-
|
|
324
|
+
```
|
|
325
|
+
$ gem uninstall toys-core-simple-example
|
|
326
|
+
```
|
|
288
327
|
|
|
289
328
|
If the implementation of your executable is more complex, you might want to
|
|
290
329
|
break it up into multiple files. The multi-file gem example demonstrates this.
|
|
291
330
|
|
|
292
|
-
|
|
331
|
+
```
|
|
332
|
+
$ cd ../multi-file-gem
|
|
333
|
+
```
|
|
293
334
|
|
|
294
335
|
This executable's implementation resides in its
|
|
295
336
|
[lib directory](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/lib),
|
|
@@ -305,18 +346,24 @@ as you can see in
|
|
|
305
346
|
|
|
306
347
|
Try it out now. From the `examples/multi-file-gem` directory, run:
|
|
307
348
|
|
|
308
|
-
|
|
349
|
+
```
|
|
350
|
+
$ toys install
|
|
351
|
+
```
|
|
309
352
|
|
|
310
353
|
Once the gem has successfully installed, you can run the executable, which
|
|
311
354
|
RubyGems should have added to your path. (Note: if you are using a ruby
|
|
312
355
|
installation manager, you may need to "rehash" or "reshim" to gain access to
|
|
313
356
|
the executable.)
|
|
314
357
|
|
|
315
|
-
|
|
358
|
+
```
|
|
359
|
+
$ toys-core-multi-file-example greet
|
|
360
|
+
```
|
|
316
361
|
|
|
317
362
|
Clean up by uninstalling the gem:
|
|
318
363
|
|
|
319
|
-
|
|
364
|
+
```
|
|
365
|
+
$ gem uninstall toys-core-multi-file-example
|
|
366
|
+
```
|
|
320
367
|
|
|
321
368
|
### Learning more
|
|
322
369
|
|