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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +155 -108
  5. data/docs/guide.md +698 -151
  6. data/lib/toys/acceptor.rb +35 -38
  7. data/lib/toys/arg_parser.rb +133 -82
  8. data/lib/toys/cli.rb +451 -332
  9. data/lib/toys/compat.rb +14 -0
  10. data/lib/toys/completion.rb +73 -88
  11. data/lib/toys/context.rb +85 -24
  12. data/lib/toys/core.rb +1 -1
  13. data/lib/toys/dsl/base.rb +24 -10
  14. data/lib/toys/dsl/flag.rb +6 -4
  15. data/lib/toys/dsl/flag_group.rb +1 -1
  16. data/lib/toys/dsl/internal.rb +31 -152
  17. data/lib/toys/dsl/positional_arg.rb +2 -2
  18. data/lib/toys/dsl/tool.rb +301 -161
  19. data/lib/toys/errors.rb +141 -91
  20. data/lib/toys/flag.rb +32 -27
  21. data/lib/toys/flag_group.rb +27 -18
  22. data/lib/toys/input_file.rb +20 -7
  23. data/lib/toys/loader/load_state.rb +449 -0
  24. data/lib/toys/loader/tool_registry.rb +250 -0
  25. data/lib/toys/loader.rb +316 -611
  26. data/lib/toys/middleware.rb +5 -5
  27. data/lib/toys/mixin.rb +15 -5
  28. data/lib/toys/module_lookup.rb +1 -1
  29. data/lib/toys/positional_arg.rb +12 -9
  30. data/lib/toys/runner.rb +542 -0
  31. data/lib/toys/source_info/origin.rb +228 -0
  32. data/lib/toys/source_info.rb +358 -220
  33. data/lib/toys/source_list.rb +89 -0
  34. data/lib/toys/source_spec.rb +480 -0
  35. data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -2
  36. data/lib/toys/standard_middleware/apply_config.rb +65 -13
  37. data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -1
  38. data/lib/toys/standard_middleware/show_help.rb +71 -58
  39. data/lib/toys/standard_middleware/show_root_version.rb +1 -1
  40. data/lib/toys/standard_mixins/bundler.rb +9 -4
  41. data/lib/toys/standard_mixins/exec.rb +16 -16
  42. data/lib/toys/standard_mixins/fileutils.rb +7 -6
  43. data/lib/toys/standard_mixins/gems.rb +51 -24
  44. data/lib/toys/standard_mixins/git_cache.rb +3 -3
  45. data/lib/toys/standard_mixins/highline.rb +17 -17
  46. data/lib/toys/standard_mixins/pager.rb +1 -1
  47. data/lib/toys/standard_mixins/terminal.rb +4 -4
  48. data/lib/toys/standard_mixins/xdg.rb +1 -1
  49. data/lib/toys/template.rb +28 -15
  50. data/lib/toys/tool_definition.rb +307 -129
  51. data/lib/toys/tool_name_splitter.rb +94 -0
  52. data/lib/toys/unique_key.rb +38 -0
  53. data/lib/toys/utils/completion_engine.rb +101 -26
  54. data/lib/toys/utils/exec.rb +1018 -604
  55. data/lib/toys/utils/gems.rb +290 -62
  56. data/lib/toys/utils/git_cache.rb +689 -606
  57. data/lib/toys/utils/help_text.rb +385 -25
  58. data/lib/toys/utils/standard_ui.rb +137 -59
  59. data/lib/toys/utils/terminal.rb +5 -5
  60. data/lib/toys/utils/xdg.rb +21 -12
  61. data/lib/toys/wrappable_string.rb +2 -2
  62. data/lib/toys-core.rb +64 -11
  63. metadata +13 -6
  64. data/lib/toys/settings.rb +0 -944
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2805bf71091b4ea8bf6f6a670281171dc6a5df4edec5196d80d64124ce2a199
4
- data.tar.gz: b5c860449af3a0566e9b1fe8dc527cba4df6750ccc3b259365faf99fa79682fb
3
+ metadata.gz: a09f479bfecbde001cec1076fdce57080117cbdeecfc2738879445a682b7363f
4
+ data.tar.gz: 277635722c80c2691759147cf7fc4b928755f0349a54fd970327c46bae443170
5
5
  SHA512:
6
- metadata.gz: 95490a978d1a3ae04be668a2fd318de2499e9610bb6072a053776fb47214d2444c0d93a73fa690b46a6d4706e2fec1f20b76f8d111100ec65216f0c274efe77a
7
- data.tar.gz: b9421ebfa989c45444024222cc3576a166d5797d7c982033265a11aa149c71ef0ab069441c70da490819e5bbafe95eb017c92289d6b46d7917c19634ee3eaf64
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-2023 Daniel Azuma and the Toys contributors
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
- $ gem install toys-core
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
- #!/usr/bin/env ruby
44
+ ```ruby
45
+ #!/usr/bin/env ruby
43
46
 
44
- require "toys-core"
47
+ require "toys-core"
45
48
 
46
- cli = Toys::CLI.new
49
+ cli = Toys::CLI.new
47
50
 
48
- exit(cli.run(*ARGV))
51
+ exit(cli.run(*ARGV))
52
+ ```
49
53
 
50
54
  Make sure the file's executable bit is set:
51
55
 
52
- $ chmod a+x mycmd
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
- $ ./mycmd
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
- #!/usr/bin/env ruby
82
+ ```ruby
83
+ #!/usr/bin/env ruby
75
84
 
76
- require "toys-core"
85
+ require "toys-core"
77
86
 
78
- cli = Toys::CLI.new
87
+ cli = Toys::CLI.new
79
88
 
80
- #### Insert the following block ...
81
- cli.add_config_block do
82
- desc "My first executable!"
83
- flag :whom, default: "world"
84
- def run
85
- puts "Hello, #{whom}!"
86
- end
87
- end
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
- exit(cli.run(*ARGV))
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
- $ ./mycmd
95
- $ ./mycmd --whom=ruby
96
- $ ./mycmd --bye
97
- $ ./mycmd --help
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 configuration block. This configures
101
- the "root tool", i.e. what happens when you run the executable without passing
102
- a tool name to it. (In fact, it's technically legal to do this in Toys as well,
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 in your config. Here's an example:
122
+ nested tools, by writing `tool` sub-blocks within your source block. Here's an
123
+ example:
111
124
 
112
- #!/usr/bin/env ruby
125
+ ```ruby
126
+ #!/usr/bin/env ruby
113
127
 
114
- require "toys-core"
128
+ require "toys-core"
115
129
 
116
- cli = Toys::CLI.new
130
+ cli = Toys::CLI.new
117
131
 
118
- #### Change the config block as follows ...
119
- cli.add_config_block do
120
- # Things outside any tool block still apply to the root
121
- desc "My first executable with several tools"
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
- # We'll put the greet function here
124
- tool "greet" do
125
- desc "My first tool!"
126
- flag :whom, default: "world"
127
- def run
128
- puts "Hello, #{whom}!"
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
- exit(cli.run(*ARGV))
150
+ exit(cli.run(*ARGV))
151
+ ```
137
152
 
138
153
  Now you can run `greet` as a tool:
139
154
 
140
- $ ./mycmd greet
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
- $ ./mycmd
162
+ ```
163
+ $ ./mycmd
164
+ ```
146
165
 
147
- Notice that the description set at the "root" of the config block (outside 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
- #!/usr/bin/env ruby
161
-
162
- require "toys-core"
163
-
164
- #### Pass some additional options to the CLI constructor ...
165
- cli = Toys::CLI.new(
166
- extra_delimiters: ":",
167
- error_handler: ->(err) {
168
- puts "Aww shucks, an error happened: #{err.message}"
169
- return 1
170
- }
171
- )
172
-
173
- #### Change the config block as follows ...
174
- cli.add_config_block do
175
- tool "example" do
176
- tool "greet" do
177
- def run
178
- puts "Hello, world!"
179
- end
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
- exit(cli.run(*ARGV))
209
+ exit(cli.run(*ARGV))
210
+ ```
190
211
 
191
212
  Try these runs. Do they behave as you expected?
192
213
 
193
- $ ./mycmd example greet
194
- $ ./mycmd example:greet
195
- $ ./mycmd example.greet
196
- $ ./mycmd example error
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
- #!/usr/bin/env ruby
230
+ ```ruby
231
+ #!/usr/bin/env ruby
208
232
 
209
- require "toys-core"
233
+ require "toys-core"
210
234
 
211
- #### Change the CLI construction again ...
212
- middlewares = [
213
- [:set_default_descriptions, default_tool_desc: "Hey look, a tool!"],
214
- [:show_help, help_flags: true]
215
- ]
216
- cli = Toys::CLI.new middleware_stack: middlewares
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
- #### Use this config block ...
219
- cli.add_config_block do
220
- tool "greet" do
221
- def run
222
- puts "Hello, world!"
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
- exit(cli.run(*ARGV))
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
- $ ./mycmd greet --help
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
- $ ./mycmd
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
- $ git clone https://github.com/dazuma/toys.git
262
- $ cd toys
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
- $ cd toys-core/examples/simple-gem
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
- $ toys install
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
- $ toys-core-simple-example --whom=Toys
318
+ ```
319
+ $ toys-core-simple-example --whom=Toys
320
+ ```
284
321
 
285
322
  Clean up by uninstalling the gem:
286
323
 
287
- $ gem uninstall toys-core-simple-example
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
- $ cd ../multi-file-gem
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
- $ toys install
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
- $ toys-core-multi-file-example greet
358
+ ```
359
+ $ toys-core-multi-file-example greet
360
+ ```
316
361
 
317
362
  Clean up by uninstalling the gem:
318
363
 
319
- $ gem uninstall toys-core-multi-file-example
364
+ ```
365
+ $ gem uninstall toys-core-multi-file-example
366
+ ```
320
367
 
321
368
  ### Learning more
322
369