toys-core 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +98 -0
- data/LICENSE.md +16 -24
- data/README.md +307 -59
- data/docs/guide.md +44 -4
- data/lib/toys-core.rb +58 -49
- data/lib/toys/acceptor.rb +672 -0
- data/lib/toys/alias.rb +106 -0
- data/lib/toys/arg_parser.rb +624 -0
- data/lib/toys/cli.rb +422 -181
- data/lib/toys/compat.rb +83 -0
- data/lib/toys/completion.rb +442 -0
- data/lib/toys/context.rb +354 -0
- data/lib/toys/core_version.rb +18 -26
- data/lib/toys/dsl/flag.rb +213 -56
- data/lib/toys/dsl/flag_group.rb +237 -51
- data/lib/toys/dsl/positional_arg.rb +210 -0
- data/lib/toys/dsl/tool.rb +968 -317
- data/lib/toys/errors.rb +46 -28
- data/lib/toys/flag.rb +821 -0
- data/lib/toys/flag_group.rb +282 -0
- data/lib/toys/input_file.rb +18 -26
- data/lib/toys/loader.rb +110 -100
- data/lib/toys/middleware.rb +24 -31
- data/lib/toys/mixin.rb +90 -59
- data/lib/toys/module_lookup.rb +125 -0
- data/lib/toys/positional_arg.rb +184 -0
- data/lib/toys/source_info.rb +192 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
- data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
- data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
- data/lib/toys/standard_middleware/show_help.rb +130 -113
- data/lib/toys/standard_middleware/show_root_version.rb +29 -35
- data/lib/toys/standard_mixins/exec.rb +116 -78
- data/lib/toys/standard_mixins/fileutils.rb +16 -24
- data/lib/toys/standard_mixins/gems.rb +29 -30
- data/lib/toys/standard_mixins/highline.rb +34 -41
- data/lib/toys/standard_mixins/terminal.rb +72 -26
- data/lib/toys/template.rb +51 -35
- data/lib/toys/tool.rb +1161 -206
- data/lib/toys/utils/completion_engine.rb +171 -0
- data/lib/toys/utils/exec.rb +279 -182
- data/lib/toys/utils/gems.rb +58 -49
- data/lib/toys/utils/help_text.rb +117 -111
- data/lib/toys/utils/terminal.rb +69 -62
- data/lib/toys/wrappable_string.rb +162 -0
- metadata +24 -22
- data/lib/toys/definition/acceptor.rb +0 -191
- data/lib/toys/definition/alias.rb +0 -112
- data/lib/toys/definition/arg.rb +0 -140
- data/lib/toys/definition/flag.rb +0 -370
- data/lib/toys/definition/flag_group.rb +0 -205
- data/lib/toys/definition/source_info.rb +0 -190
- data/lib/toys/definition/tool.rb +0 -842
- data/lib/toys/dsl/arg.rb +0 -132
- data/lib/toys/runner.rb +0 -188
- data/lib/toys/standard_middleware.rb +0 -47
- data/lib/toys/utils/module_lookup.rb +0 -135
- data/lib/toys/utils/wrappable_string.rb +0 -165
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c599749b4dfa556b393f532a666455ced20062d746a8e9156aa472bdcbe5c6a2
|
4
|
+
data.tar.gz: 303c5615df653c4ca0fcb7646a413630fd74db0c28518ff2d8baae9f6ba80ccb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b92268de87c8555a113e2e9804d4a4b3489cbe3c216dc1056e45595214297e4aaf4b19a95da9db5c024bfa41336d8d25f6df591b1861fb85c3f552a69cf3b3a
|
7
|
+
data.tar.gz: 860c322590729d3a111b3e205197eb42110cc8dd455dc8c6c5ff7ba042f69dd25583f3d720e9ccc3104fe9bd021999a655639c15fcec185b9b4fdd0126052aca
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,103 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.8.0 / 2019-06-20
|
4
|
+
|
5
|
+
This is a major update with significant new features and a bunch of fixes. It also includes a significant amount of internal reorganization and cleanup, some of which resulted in backward incompatible changes. Basic use should not be affected. All signifiant features planned for beta are now implemented.
|
6
|
+
|
7
|
+
Major changes and features:
|
8
|
+
|
9
|
+
* CHANGED: Relicensed under the MIT License.
|
10
|
+
* ADDED: Tab completion for bash. Added APIs and DSL constructs for tools to customize completions.
|
11
|
+
* ADDED: The usage error screen displays suggestions when an argument is misspelled. (Requires Ruby 2.4 or later.)
|
12
|
+
* ADDED: Tools can provide an interrupt handler and a custom usage error handler. Added appropriate APIs and DSL methods.
|
13
|
+
* ADDED: Tools can enforce that flags must be given before positional args, and can control whether partial flags are accepted.
|
14
|
+
|
15
|
+
Other notable changes:
|
16
|
+
|
17
|
+
* ADDED: Flag handlers can accept the symbolic names `:set` and `:push` for common cases.
|
18
|
+
* ADDED: Function and range based acceptors.
|
19
|
+
* ADDED: The `acceptor` directive takes an optional `type_desc` argument.
|
20
|
+
* ADDED: The `accept` directives under flag and positional arg blocks in the DSL can now take blocks and `type_desc` values.
|
21
|
+
* ADDED: Context keys `UNMATCHED_ARGS`, `UNMATCHED_POSITIONAL`, and `UNMATCHED_FLAGS` that provide arguments that were not handled during arg parsing.
|
22
|
+
* ADDED: The Exec util and mixin support specifying a callback for process results.
|
23
|
+
* ADDED: The Exec util and mixin provide a way to identify processes by name.
|
24
|
+
* CHANGED: Implemented custom argument parsing and custom implementations of the standard OptionParser acceptors, rather than relying on OptionParser itself. For the most part, OptionParser behavior is preserved, except in cases where there is clearly a bug.
|
25
|
+
* CHANGED: Flags create a short form flag by default if the name has one character.
|
26
|
+
* CHANGED: Flags with explicit value-less syntax are no longer given a value if they specify a default or an acceptor.
|
27
|
+
* CHANGED: Renamed the `TOOL_DEFINITION` context key to `TOOL`, and removed the `tool_definition` convenience method.
|
28
|
+
* CHANGED: Removed the `BINARY_NAME` and `LOADER` context keys, and removed the corresponding convenience methods. Get these values from the CLI if needed.
|
29
|
+
* CHANGED: Renamed the `USAGE_ERROR` context key to `USAGE_ERRORS`, and the corresponding convenience method to `usage_errors`. The value is now a (possibly empty) array of `Toys::ArgParser::UsageError` objects rather than a string that isn't machine-parseable.
|
30
|
+
* CHANGED: The help middleware no longer defines remaining_args on the root tool.
|
31
|
+
* CHANGED: Renamed `to_expand` to `on_expand` in template definitions.
|
32
|
+
* CHANGED: Renamed `to_initialize` to `on_initialize`, and `to_include` to `on_include` in mixin definitions.
|
33
|
+
* CHANGED: The CLI options `preload_directory_name` and `data_directory_name` renamed to `preload_dir_name` and `data_dir_name`.
|
34
|
+
* CHANGED: Default descriptions for flag groups is now handled by the `set_default_descriptions` middleware rather than hard-coded in FlagGroup.
|
35
|
+
* CHANGED: Exec reports failure to start processes in the result object rather than, e.g. raising ENOENT.
|
36
|
+
* IMPROVED: Default error handler no longer displays a stack trace if a tool is interrupted.
|
37
|
+
* IMPROVED: Error messages for flag groups are more complete.
|
38
|
+
* IMPROVED: All context data, including well-known data, is available to be modified by flags and args.
|
39
|
+
* FIXED: Flags with optional values are properly set to `true` (rather than left at `nil`) if no value is provided.
|
40
|
+
* FIXED: Acceptors no longer raise errors when run on missing optional values.
|
41
|
+
* FIXED: When reporting errors in toys files, the line number was off by 2.
|
42
|
+
* FIXED: The `--usage` help flag now honors `--all` and `--no-recursive`.
|
43
|
+
* FIXED: The terminal now handles nil streams, as advertised.
|
44
|
+
|
45
|
+
Changes to internal interfaces:
|
46
|
+
|
47
|
+
* General changes:
|
48
|
+
* CHANGED: Renamed `Toys::Tool` to `Toys::Context`, and the `Keys` submodule to `Key`.
|
49
|
+
* CHANGED: Moved the `ModuleLookup` and `WrappableString` out of the `Utils` module to be located directly under `Toys`. Other modules remain under `Utils`. The remaining files under "toys/utils" must now be required explicitly. This directory is now specifically for modules that are not part of the "core" interface.
|
50
|
+
* CHANGED: All the classes under `Toys::Definition` are now located directly under `Toys`. For example, `Toys::Definition::Tool` is now `Toys::Tool`.
|
51
|
+
* CHANGED: Generally removed the term "definition" from interfaces. For example, an accessor method called `tool_definition` is now just called `tool`.
|
52
|
+
* CHANGED: Renamed `Toys::DSL::Arg` to `Toys::DSL::PositionalArg`
|
53
|
+
* CHANGED: Removed `Toys::Runner` and folded its functionality into `Toys::CLI`.
|
54
|
+
* CHANGED: The fallback execution feature of the show_help middleware is implemented by catching an exception afterward rather than detecting non-runnable up front. This lets us remove the second copy of show_help from the middleware stack.
|
55
|
+
* ADDED: Functionality dependent on Ruby version is kept in `Toys::Compat`.
|
56
|
+
* Changes related to the tool classes:
|
57
|
+
* CHANGED: Moved `Toys::Definition::Tool` to `Toys::Tool`.
|
58
|
+
* CHANGED: Removed the term "definition" from accessors. Specifically `flag_definitions` renamed to `flags`, `required_arg_definitions` renamed to `required_args`, `optional_arg_definitions` renamed to `optional_args`, `remaining_args_definition` renamed to `remaining_arg`, and `arg_definitions` renamed to `positional_args`.
|
59
|
+
* CHANGED: Renamed `Tool#runnable=` to `Tool#run_handler=`.
|
60
|
+
* CHANGED: `Tool#add_acceptor` takes the name as a separate argument, for consistency with `add_mixin` and `add_template`.
|
61
|
+
* CHANGED: Removed `Tool#custom_acceptors` method.
|
62
|
+
* CHANGED: Removed `Tool#resolve_acceptor` and replaced with `lookup_acceptor` which only looks up names.
|
63
|
+
* CHANGED: Renamed `Tool#resolve_mixin` to `lookup_mixin` and `Tool#resolve_template` to `lookup_template`.
|
64
|
+
* ADDED: Added `resolve_flag` method to look up flags by syntax.
|
65
|
+
* ADDED: Accessor for interrupt handler.
|
66
|
+
* ADDED: `enforce_flags_before_args` setting and `flags_before_args_enforced?` query.
|
67
|
+
* ADDED: Completion accessor, and options to the various flag and positional arg methods to set their completion strategies.
|
68
|
+
* ADDED: Added `Tool::DefaultCompletion` class.
|
69
|
+
* IMPROVED: `add_mixin`, `add_template`, and `add_acceptor` support all the specs understood by their create methods.
|
70
|
+
* Changes related to the flag classes:
|
71
|
+
* CHANGED: Moved `Toys::Definition::Flag` to `Toys::Flag`
|
72
|
+
* CHANGED: `FlagSyntax` is now `Flag::Syntax`.
|
73
|
+
* CHANGED: `Flag::Syntax#flag_style` now has values `:short` and `:long` instead of `"-"` and `"--"`.
|
74
|
+
* CHANGED: `Flag#single_flag_syntax` renamed to `Flag#short_flag_syntax`, and `Flag#double_flag_syntax` renamed to `Flag#long_flag_syntax`.
|
75
|
+
* CHANGED: Renamed `Flag#accept` to `Flag#acceptor` which now always returns an acceptor object (even for well-known acceptors such as `Integer`).
|
76
|
+
* CHANGED: Removed `Flag#optparser_info` and replaced with `Flag#canonical_syntax_strings`.
|
77
|
+
* ADDED: `Flag#create` class method providing a sane construction interface.
|
78
|
+
* ADDED: `Flag#resolve` method to look up flag syntax.
|
79
|
+
* ADDED: `Flag#completion` field.
|
80
|
+
* ADDED: Added `Flag::Resolution` and `Flag::DefaultCompletion` classes.
|
81
|
+
* Changes related to the positional arg classes:
|
82
|
+
* CHANGED: Moved `Toys::Definition::Arg` to `Toys::PositionalArg`.
|
83
|
+
* CHANGED: Renamed `Arg#accept` to `PositionalArg#acceptor` which now always returns an acceptor object (even for well-known acceptors such as `Integer`).
|
84
|
+
* ADDED: `PositionalArg#create` class method providing a sane construction interface.
|
85
|
+
* ADDED: `PositionalArg#completion` field
|
86
|
+
* Changes related to the flag group classes:
|
87
|
+
* CHANGED: Moved `Toys::Definition::FlagGroup` to `Toys::FlagGroup`
|
88
|
+
* CHANGED: The base class is now `FlagGroup::Base` instead of `FlagGroup` itself.
|
89
|
+
* ADDED: `FlagGroup#create` class method providing a sane construction interface.
|
90
|
+
* Changes related to acceptors:
|
91
|
+
* CHANGED: Moved `Toys::Definition::Acceptor` to `Toys::Acceptor`
|
92
|
+
* CHANGED: The base ciass is now `Acceptor::Base` instead of `Acceptor` itself.
|
93
|
+
* CHANGED: Subclasses are now submodules under `Acceptor`. For example, moved `Toys::Definition::PatternAcceptor` to `Toys::Acceptor::Pattern`.
|
94
|
+
* CHANGED: Replaced `name` field with separate `type_desc` and `well_known_spec` fields.
|
95
|
+
* CHANGED: The base class no longer takes a conversion proc. It is always a no-op. `Acceptor::Pattern`, however, does take a converter so it can continue to handle custom OptionParser acceptors.
|
96
|
+
* ADDED: Acceptors may define `suggestions` which returns results from did-you-mean.
|
97
|
+
* ADDED: Simple acceptor (`Acceptor::Simple`) which uses a single function to validate and convert input.
|
98
|
+
* ADDED: Range acceptor (`Acceptor::Range`) which validates against a range.
|
99
|
+
* ADDED: Class methods `Acceptor.create` and `Acceptor.lookup_well_known`.
|
100
|
+
|
3
101
|
### 0.7.0 / 2019-01-23
|
4
102
|
|
5
103
|
* ADDED: Flag groups, which enforce policies around which flags are required.
|
data/LICENSE.md
CHANGED
@@ -1,29 +1,21 @@
|
|
1
1
|
# License
|
2
2
|
|
3
|
-
Copyright
|
3
|
+
Copyright 2019 Daniel Azuma
|
4
4
|
|
5
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
6
11
|
|
7
|
-
|
8
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
derived from this software without specific prior written permission.
|
18
|
-
|
19
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
POSSIBILITY OF SUCH DAMAGE.
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,108 +1,356 @@
|
|
1
|
-
# Toys
|
1
|
+
# Toys-Core
|
2
2
|
|
3
3
|
Toys is a configurable command line tool. Write commands in config files using
|
4
|
-
a simple DSL, and Toys will provide the command line
|
5
|
-
all the details such as argument parsing, online help, and error reporting.
|
4
|
+
a simple DSL, and Toys will provide the command line executable and take care
|
5
|
+
of all the details such as argument parsing, online help, and error reporting.
|
6
|
+
|
6
7
|
Toys-Core is the command line tool framework underlying Toys. It can be used
|
7
|
-
to
|
8
|
+
to write command line executables using the Toys DSL and the power of the Toys
|
9
|
+
classes.
|
8
10
|
|
9
11
|
For more detailed information about Toys-Core, see the
|
10
12
|
[Toys-Core User's Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
|
11
13
|
For background information about Toys itself, see the
|
14
|
+
[Toys README](https://www.rubydoc.info/gems/toys) and the
|
12
15
|
[Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
|
13
16
|
|
14
|
-
##
|
17
|
+
## Introductory tutorial
|
18
|
+
|
19
|
+
Here's a tutorial to help you get a feel for how to write a basic command line
|
20
|
+
executable using Toys-Core.
|
15
21
|
|
16
|
-
|
17
|
-
|
22
|
+
It assumes basic familiarity with Toys, so, if you have not done so, I
|
23
|
+
recommend first walking through the tutorial in the
|
24
|
+
[Toys README](https://www.rubydoc.info/gems/toys). It also assumes you are
|
25
|
+
running a unix-like system such as Linux or macOS. Some commands might need to
|
26
|
+
be modified if you're running on Windows.
|
18
27
|
|
19
28
|
### Install Toys
|
20
29
|
|
30
|
+
Toys requires Ruby 2.3 or later. (JRuby is not currently supported.)
|
31
|
+
|
21
32
|
Install the **toys-core** gem using:
|
22
33
|
|
23
|
-
gem install toys-core
|
34
|
+
$ gem install toys-core
|
24
35
|
|
25
36
|
You may also install the **toys** gem, which brings in **toys-core** as a
|
26
37
|
dependency.
|
27
38
|
|
28
|
-
### Create a
|
39
|
+
### Create a new executable
|
40
|
+
|
41
|
+
We'll start by creating an executable Ruby script. Using your favorite text
|
42
|
+
editor, create new a file called `mycmd` with the following contents:
|
43
|
+
|
44
|
+
#!/usr/bin/env ruby
|
45
|
+
|
46
|
+
require "toys-core"
|
47
|
+
|
48
|
+
cli = Toys::CLI.new
|
49
|
+
|
50
|
+
exit(cli.run(*ARGV))
|
29
51
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
52
|
+
Make sure the file's executable bit is set:
|
53
|
+
|
54
|
+
$ chmod a+x mycmd
|
55
|
+
|
56
|
+
That's it! This is a fully-functional Toys-based executable! Let's see what
|
57
|
+
happens when you run it:
|
58
|
+
|
59
|
+
$ ./mycmd
|
60
|
+
|
61
|
+
Just as with Toys itself, you get a help screen by default (since we haven't
|
62
|
+
yet actually implemented any behavior.) As you can see, some of the same
|
63
|
+
features from Toys are present already: online help, and `--verbose` and
|
64
|
+
`--quiet` flags. These features can of course all be customized, but they're
|
65
|
+
useful to have to start off.
|
66
|
+
|
67
|
+
### Add some functionality
|
68
|
+
|
69
|
+
You implement the functionality of your executable using the same DSL that you
|
70
|
+
use to write Toys files. You could point your executable at a directory
|
71
|
+
containing actual Toys files, but the simplest option is to provide the
|
72
|
+
information to the Toys CLI object in a block.
|
73
|
+
|
74
|
+
Let's add some functionality.
|
75
|
+
|
76
|
+
#!/usr/bin/env ruby
|
77
|
+
|
78
|
+
require "toys-core"
|
34
79
|
|
35
|
-
|
36
|
-
editor, create a file called `tools.rb`. Copy the following into the file, and
|
37
|
-
save it:
|
80
|
+
cli = Toys::CLI.new
|
38
81
|
|
39
|
-
|
40
|
-
|
82
|
+
#### Insert the following block ...
|
83
|
+
cli.add_config_block do
|
84
|
+
desc "My first executable!"
|
41
85
|
flag :whom, default: "world"
|
42
86
|
def run
|
43
87
|
puts "Hello, #{whom}!"
|
44
88
|
end
|
45
89
|
end
|
46
90
|
|
47
|
-
|
48
|
-
|
91
|
+
exit(cli.run(*ARGV))
|
92
|
+
|
93
|
+
If you went through the tutorial in the README for the Toys gem, this should
|
94
|
+
look familiar. Let's run it now, and experiment with passing flags to it.
|
95
|
+
|
96
|
+
$ ./mycmd
|
97
|
+
$ ./mycmd --whom=ruby
|
98
|
+
$ ./mycmd --bye
|
99
|
+
$ ./mycmd --help
|
49
100
|
|
50
|
-
|
101
|
+
Notice that we did not create a `tool` block, but instead set up description,
|
102
|
+
flags, and functionality directly in the configuration block. This configures
|
103
|
+
the "root tool", i.e. what happens when you run the executable without passing
|
104
|
+
a tool name to it. (Note, it's legal to do this in Toys as well, by setting
|
105
|
+
functionality at the "top level" of a `.toys.rb` file without including any
|
106
|
+
`tool` block.)
|
51
107
|
|
52
|
-
|
53
|
-
|
108
|
+
### Tool-based executables
|
109
|
+
|
110
|
+
But perhaps you want your executable to have multiple "tools", similar to other
|
111
|
+
familiar executables like git or kubectl. You can define tools, including
|
112
|
+
nested tools, by writing `tool` blocks in your config. Here's an example:
|
54
113
|
|
55
114
|
#!/usr/bin/env ruby
|
115
|
+
|
56
116
|
require "toys-core"
|
117
|
+
|
57
118
|
cli = Toys::CLI.new
|
58
|
-
cli.add_config_path(File.join(__dir__, "tools.rb"))
|
59
|
-
exit(cli.run(ARGV))
|
60
119
|
|
61
|
-
|
120
|
+
#### Change the config block as follows ...
|
121
|
+
cli.add_config_block do
|
122
|
+
# Things outside any tool block still apply to the root
|
123
|
+
desc "My first executable with several tools"
|
124
|
+
|
125
|
+
# We'll put the greet function here
|
126
|
+
tool "greet" do
|
127
|
+
desc "My first tool!"
|
128
|
+
flag :whom, default: "world"
|
129
|
+
def run
|
130
|
+
puts "Hello, #{whom}!"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Try writing a second tool here. You could use the "new-repo"
|
135
|
+
# example from the Toys tutorial.
|
136
|
+
end
|
137
|
+
|
138
|
+
exit(cli.run(*ARGV))
|
139
|
+
|
140
|
+
Now you can run `greet` as a tool:
|
141
|
+
|
142
|
+
$ ./mycmd greet
|
143
|
+
|
144
|
+
The "root" functionality once again shows global help, including a list of the
|
145
|
+
available tools.
|
146
|
+
|
147
|
+
$ ./mycmd
|
148
|
+
|
149
|
+
Notice that the description set at the "root" of the config block (outside the
|
150
|
+
tool blocks) shows up here.
|
151
|
+
|
152
|
+
### Configuring the CLI
|
153
|
+
|
154
|
+
So far, our executable behaves very similarly to Toys itself. Help screens are
|
155
|
+
shown by default, flags for help and verbosity are provided automatically, and
|
156
|
+
any exceptions are displayed to the terminal.
|
157
|
+
|
158
|
+
These and many more aspects of the behavior of our executable can be customized
|
159
|
+
by passing options to the `Toys::CLI` constructor. Here's an example that
|
160
|
+
modifies error handling and delimiter parsing.
|
161
|
+
|
162
|
+
#!/usr/bin/env ruby
|
163
|
+
|
164
|
+
require "toys-core"
|
165
|
+
|
166
|
+
#### Pass some additional options to the CLI constructor ...
|
167
|
+
cli = Toys::CLI.new(
|
168
|
+
extra_delimiters: ":",
|
169
|
+
error_handler: ->(e) {
|
170
|
+
puts "Dude, an error happened..."
|
171
|
+
return 1
|
172
|
+
}
|
173
|
+
)
|
174
|
+
|
175
|
+
#### Change the config block as follows ...
|
176
|
+
cli.add_config_block do
|
177
|
+
tool "example" do
|
178
|
+
tool "greet" do
|
179
|
+
def run
|
180
|
+
puts "Hello, world!"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
tool "error" do
|
184
|
+
def run
|
185
|
+
raise "Whoops!"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
exit(cli.run(*ARGV))
|
192
|
+
|
193
|
+
Try these runs. Do they behave as you expected?
|
194
|
+
|
195
|
+
$ ./mycmd example greet
|
196
|
+
$ ./mycmd example:greet
|
197
|
+
$ ./mycmd example.greet
|
198
|
+
$ ./mycmd example error
|
199
|
+
|
200
|
+
### Configuring middleware
|
201
|
+
|
202
|
+
Toys _middleware_ are objects that provide common functionality for all the
|
203
|
+
tools in your executable. For example, a middleware adds the `--help` flag to
|
204
|
+
your tools by default.
|
205
|
+
|
206
|
+
The next example modifies the middleware stack to alter this common tool
|
207
|
+
functionality.
|
208
|
+
|
209
|
+
#!/usr/bin/env ruby
|
210
|
+
|
211
|
+
require "toys-core"
|
212
|
+
|
213
|
+
#### Change the CLI construction again ...
|
214
|
+
middlewares = [
|
215
|
+
[:set_default_descriptions, default_tool_desc: "Hey look, a tool!"],
|
216
|
+
[:show_help, help_flags: true]
|
217
|
+
]
|
218
|
+
cli = Toys::CLI.new middleware_stack: middlewares
|
219
|
+
|
220
|
+
#### Use this config block ...
|
221
|
+
cli.add_config_block do
|
222
|
+
tool "greet" do
|
223
|
+
def run
|
224
|
+
puts "Hello, world!"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
exit(cli.run(*ARGV))
|
230
|
+
|
231
|
+
We've now modified the default description applied to tools that don't provide
|
232
|
+
their own description. See the effect with:
|
233
|
+
|
234
|
+
$ ./mycmd greet --help
|
62
235
|
|
63
|
-
|
236
|
+
We've also omitted some of the default middleware, including the one that adds
|
237
|
+
the `--verbose` and `--quiet` flags to all your tools. Notice those flags are
|
238
|
+
no longer present.
|
64
239
|
|
65
|
-
|
66
|
-
|
240
|
+
We've also omitted the middleware that provides default execution behavior
|
241
|
+
(i.e. displaying the help screen) when there is no `run` method. Now, since we
|
242
|
+
haven't defined a toplevel `run` method in this last example, invoking the root
|
243
|
+
tool will cause an error:
|
67
244
|
|
68
|
-
./mycmd
|
69
|
-
./mycmd greet --whom=Ruby
|
70
|
-
./mycmd greet --help
|
71
|
-
./mycmd
|
72
|
-
./mycmd foo
|
245
|
+
$ ./mycmd
|
73
246
|
|
74
|
-
|
247
|
+
It is even possible to write your own middleware. In general, while the
|
248
|
+
`Toys::CLI` constructor provides defaults that should work for many use cases,
|
249
|
+
you can also customize it heavily to suit the needs of your executable.
|
75
250
|
|
76
|
-
|
77
|
-
itself, and a Toys File (or directory) defining the commands to run. All the
|
78
|
-
features of Toys, described in the
|
79
|
-
[Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md),
|
80
|
-
are at your disposal for writing tools for your binary. Or, if you want your
|
81
|
-
binary to have a single function rather than support a set of tools, you can
|
82
|
-
just write a toplevel tool in your Toys File.
|
251
|
+
### Packaging as a gem
|
83
252
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
middleware, but you can customize and change them for your own binary.
|
253
|
+
So far we've created simple one-file executables that you could distribute by
|
254
|
+
itself. However, the `toys-core` gem is a dependency, and your users will need
|
255
|
+
to have it installed. You could alleviate this by wrapping your executable in a
|
256
|
+
gem that can declare `toys-core` as a dependency explicitly.
|
89
257
|
|
90
|
-
|
91
|
-
|
92
|
-
`Toys::CLI` with the correct config path. The Toys File does not need to be in
|
93
|
-
the require path (i.e. in the `lib` directory), and indeed it is probably best
|
94
|
-
for it not to be, to prevent users of your gem from requiring it accidentally.
|
258
|
+
The [examples directory](https://github.com/dazuma/toys/tree/master/toys-core/examples)
|
259
|
+
includes a few simple examples that you can use as a starting point.
|
95
260
|
|
96
|
-
|
97
|
-
|
98
|
-
|
261
|
+
To experiment with the examples, clone the Toys repo from GitHub:
|
262
|
+
|
263
|
+
$ git clone https://github.com/dazuma/toys.git
|
264
|
+
$ cd toys
|
265
|
+
|
266
|
+
Navigate to the simple-gem example:
|
267
|
+
|
268
|
+
$ cd toys-core/examples/simple-gem
|
269
|
+
|
270
|
+
This example wraps the simple "greet" executable that we
|
271
|
+
[covered earlier](#Add_some_functionality) in a gem. You can see the
|
272
|
+
[executable file](https://github.com/dazuma/toys/tree/master/toys-core/examples/simple-gem/bin/toys-core-simple-example)
|
273
|
+
in the bin directory.
|
274
|
+
|
275
|
+
Try it out by building and installing the gem. From the `examples/simple-gem`
|
276
|
+
directory, run:
|
277
|
+
|
278
|
+
$ toys install
|
279
|
+
|
280
|
+
Once the gem has successfully installed, you can run the executable, which
|
281
|
+
Rubygems should have added to your path. (Note: if you are using a ruby
|
282
|
+
installation manager, you may need to "rehash" or "reshim" to gain access to
|
283
|
+
the executable.)
|
284
|
+
|
285
|
+
$ toys-core-simple-example --whom=Toys
|
286
|
+
|
287
|
+
Clean up by uninstalling the gem:
|
288
|
+
|
289
|
+
$ gem uninstall toys-core-simple-example
|
290
|
+
|
291
|
+
If the implementation of your executable is more complex, you might want to
|
292
|
+
break it up into multiple files. The multi-file gem example demonstrates this.
|
293
|
+
|
294
|
+
$ cd ../multi-file-gem
|
295
|
+
|
296
|
+
This executable's implementation resides in its
|
297
|
+
[lib directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib),
|
298
|
+
a technique that may be familiar to writers of command line executables. More
|
299
|
+
interestingly, the tools themselves are no longer defined in a block passed to
|
300
|
+
the CLI object, but have been moved into a separate
|
301
|
+
["tools" directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/tools).
|
302
|
+
This directory has the same structure and supports the same features that are
|
303
|
+
available when writing complex sets of tools in a `.toys` directory. You then
|
304
|
+
configure the CLI object to look in this directory for its tools definitions,
|
305
|
+
as you can see in
|
306
|
+
[the code](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib/toys-core-multi-gem-example.rb).
|
307
|
+
|
308
|
+
Try it out now. From the `examples/multi-file-gem` directory, run:
|
309
|
+
|
310
|
+
$ toys install
|
311
|
+
|
312
|
+
Once the gem has successfully installed, you can run the executable, which
|
313
|
+
Rubygems should have added to your path. (Note: if you are using a ruby
|
314
|
+
installation manager, you may need to "rehash" or "reshim" to gain access to
|
315
|
+
the executable.)
|
316
|
+
|
317
|
+
$ toys-core-multi-file-example greet
|
318
|
+
|
319
|
+
Clean up by uninstalling the gem:
|
320
|
+
|
321
|
+
$ gem uninstall toys-core-multi-file-example
|
322
|
+
|
323
|
+
### Learning more
|
324
|
+
|
325
|
+
This introduction should be enough to get you started. However, Toys-Core is a
|
326
|
+
deep framework with many more features. Learn about how to write tools using
|
327
|
+
the Toys DSL, including validating and interpreting command line arguments,
|
328
|
+
using templates and mixins, controlling subprocesses, and producing nice styled
|
329
|
+
output, in the
|
330
|
+
[Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
|
331
|
+
Learn more about how to customize and package your own executable, including
|
332
|
+
handling errors, controlling log output, and providing your own mixins,
|
333
|
+
templates, and middleware, in the
|
334
|
+
[Toys-Core User Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
|
99
335
|
|
100
336
|
## License
|
101
337
|
|
102
|
-
Copyright
|
338
|
+
Copyright 2019 Daniel Azuma
|
103
339
|
|
104
|
-
|
340
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
341
|
+
of this software and associated documentation files (the "Software"), to deal
|
342
|
+
in the Software without restriction, including without limitation the rights
|
343
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
344
|
+
copies of the Software, and to permit persons to whom the Software is
|
345
|
+
furnished to do so, subject to the following conditions:
|
105
346
|
|
106
|
-
|
347
|
+
The above copyright notice and this permission notice shall be included in
|
348
|
+
all copies or substantial portions of the Software.
|
107
349
|
|
108
|
-
|
350
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
351
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
352
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
353
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
354
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
355
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
356
|
+
IN THE SOFTWARE.
|