toys 0.7.0 → 0.8.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 +54 -0
- data/LICENSE.md +16 -24
- data/README.md +263 -57
- data/bin/toys +17 -25
- data/builtins/do.rb +38 -38
- data/builtins/system.rb +111 -30
- data/docs/guide.md +783 -321
- data/lib/toys.rb +32 -34
- data/lib/toys/standard_cli.rb +69 -90
- data/lib/toys/templates/clean.rb +19 -27
- data/lib/toys/templates/gem_build.rb +78 -41
- data/lib/toys/templates/minitest.rb +25 -31
- data/lib/toys/templates/rake.rb +21 -29
- data/lib/toys/templates/rdoc.rb +27 -35
- data/lib/toys/templates/rspec.rb +30 -36
- data/lib/toys/templates/rubocop.rb +21 -29
- data/lib/toys/templates/yardoc.rb +36 -44
- data/lib/toys/version.rb +18 -26
- data/share/bash-completion-remove.sh +26 -0
- data/share/bash-completion.sh +26 -0
- metadata +14 -12
data/bin/toys
CHANGED
@@ -1,33 +1,25 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# Copyright
|
4
|
+
# Copyright 2019 Daniel Azuma
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
11
|
+
# furnished to do so, subject to the following conditions:
|
7
12
|
#
|
8
|
-
#
|
9
|
-
#
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
14
|
+
# all copies or substantial portions of the Software.
|
10
15
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# derived from this software without specific prior written permission.
|
19
|
-
#
|
20
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
24
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
22
|
+
# IN THE SOFTWARE.
|
31
23
|
;
|
32
24
|
|
33
25
|
::ENV["TOYS_BIN_PATH"] ||= ::File.absolute_path(__FILE__)
|
@@ -35,4 +27,4 @@
|
|
35
27
|
$LOAD_PATH.unshift(::File.absolute_path(::File.join(::File.dirname(__dir__), "lib")))
|
36
28
|
require "toys"
|
37
29
|
|
38
|
-
exit(::Toys::StandardCLI.
|
30
|
+
exit(::Toys::StandardCLI.new.run(::ARGV))
|
data/builtins/do.rb
CHANGED
@@ -1,33 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
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.
|
30
|
-
;
|
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.
|
31
22
|
|
32
23
|
desc "Run multiple tools in order"
|
33
24
|
|
@@ -39,25 +30,34 @@ long_desc \
|
|
39
30
|
"",
|
40
31
|
"Example: Suppose you have a \"rails build\" tool and a \"deploy\" tool. You could run them" \
|
41
32
|
" in order like this:",
|
42
|
-
[" toys do rails build , deploy"],
|
43
|
-
"",
|
44
|
-
"However, if you want to pass flags to the tools to run, you need to preface the arguments" \
|
45
|
-
" with \"--\" in order to prevent \"do\" from trying to use them as its own flags. That" \
|
46
|
-
" might look something like this:",
|
47
|
-
[" toys do -- rails build --staging , deploy --migrate"],
|
33
|
+
[" toys do rails build --staging , deploy --migrate"],
|
48
34
|
"",
|
49
35
|
"You may change the delimiter using the --delim flag. For example:",
|
50
|
-
[" toys do --delim=/
|
36
|
+
[" toys do --delim=/ rails build --staging / deploy --migrate"],
|
37
|
+
"The --delim flag must appear first before the tools to run. Any flags that appear later in" \
|
38
|
+
" the command line will be passed to the tools themselves."
|
51
39
|
|
52
|
-
flag :delim
|
53
|
-
|
54
|
-
|
55
|
-
|
40
|
+
flag :delim do
|
41
|
+
flags "-d", "--delim=VALUE"
|
42
|
+
default ","
|
43
|
+
desc "Set the delimiter"
|
44
|
+
long_desc "Sets the delimiter that separates tool invocations. The default value is \",\"."
|
45
|
+
end
|
46
|
+
|
47
|
+
remaining_args :commands do
|
48
|
+
complete do |context|
|
49
|
+
commands = context.arg_parser.data[:commands]
|
50
|
+
last_command = commands.inject([]) { |acc, arg| arg == "," ? [] : (acc << arg) }
|
51
|
+
new_context = context.with(previous_words: last_command, disable_flags: commands.empty?)
|
52
|
+
new_context.tool.completion.call(new_context)
|
53
|
+
end
|
54
|
+
desc "A series of tools to run, separated by the delimiter"
|
55
|
+
end
|
56
56
|
|
57
|
-
|
57
|
+
enforce_flags_before_args
|
58
58
|
|
59
59
|
def run
|
60
|
-
|
60
|
+
commands
|
61
61
|
.chunk { |arg| arg == delim ? :_separator : true }
|
62
62
|
.each do |_, action|
|
63
63
|
code = cli.run(action)
|
data/builtins/system.rb
CHANGED
@@ -1,33 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
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.
|
30
|
-
;
|
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.
|
31
22
|
|
32
23
|
desc "A set of system commands for Toys"
|
33
24
|
|
@@ -54,18 +45,18 @@ tool "update" do
|
|
54
45
|
|
55
46
|
def run
|
56
47
|
configure_exec(exit_on_nonzero_status: true)
|
57
|
-
version_info =
|
58
|
-
|
48
|
+
version_info = spinner(leading_text: "Checking rubygems for the latest release... ",
|
49
|
+
final_text: "Done.\n") do
|
59
50
|
capture(["gem", "query", "-q", "-r", "-e", "toys"])
|
60
51
|
end
|
61
52
|
if version_info =~ /toys\s\((.+)\)/
|
62
|
-
latest_version = ::Gem::Version.new(
|
53
|
+
latest_version = ::Gem::Version.new(::Regexp.last_match(1))
|
63
54
|
cur_version = ::Gem::Version.new(::Toys::VERSION)
|
64
55
|
if latest_version > cur_version
|
65
56
|
prompt = "Update Toys from #{cur_version} to #{latest_version}? "
|
66
57
|
exit(1) unless yes || confirm(prompt, default: true)
|
67
|
-
result =
|
68
|
-
|
58
|
+
result = spinner(leading_text: "Installing Toys version #{latest_version}... ",
|
59
|
+
final_text: "Done.\n") do
|
69
60
|
exec(["gem", "install", "toys", "--version", latest_version.to_s],
|
70
61
|
out: :capture, err: :capture)
|
71
62
|
end
|
@@ -88,3 +79,93 @@ tool "update" do
|
|
88
79
|
end
|
89
80
|
end
|
90
81
|
end
|
82
|
+
|
83
|
+
tool "bash-completion" do
|
84
|
+
desc "Bash tab completion for Toys"
|
85
|
+
|
86
|
+
long_desc \
|
87
|
+
"Tools that manage tab completion for Toys in the bash shell.",
|
88
|
+
"",
|
89
|
+
"To install tab completion for Toys, execute the following line in a bash shell, or" \
|
90
|
+
" include it in an init file such as your .bashrc:",
|
91
|
+
[" $(toys system bash-completion install)"],
|
92
|
+
"",
|
93
|
+
"To remove tab completion, execute:",
|
94
|
+
[" $(toys system bash-completion remove)"],
|
95
|
+
"",
|
96
|
+
"It is also possible to install completions for different executable names if you have" \
|
97
|
+
" aliases for Toys. See the help for the \"install\" and \"remove\" tools for details.",
|
98
|
+
"",
|
99
|
+
"The \"eval\" tool is the actual completion command invoked by bash when it needs to" \
|
100
|
+
" complete a toys command line. You shouldn't need to invoke it directly."
|
101
|
+
|
102
|
+
tool "eval" do
|
103
|
+
desc "Tab completion command (executed by bash)"
|
104
|
+
|
105
|
+
long_desc \
|
106
|
+
"Completion command invoked by bash to compete a toys command line. Generally you do not" \
|
107
|
+
" need to invoke this directly. It reads the command line context from the COMP_LINE" \
|
108
|
+
" and COMP_POINT environment variables, and outputs completion candidates to stdout."
|
109
|
+
|
110
|
+
disable_argument_parsing
|
111
|
+
|
112
|
+
def run
|
113
|
+
require "toys/utils/completion_engine"
|
114
|
+
result = ::Toys::Utils::CompletionEngine::Bash.new(cli).run
|
115
|
+
if result > 1
|
116
|
+
logger.fatal("This tool must be invoked as a bash completion command.")
|
117
|
+
end
|
118
|
+
exit(result)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
tool "install" do
|
123
|
+
desc "Install bash tab completion"
|
124
|
+
|
125
|
+
long_desc \
|
126
|
+
"Outputs a command to set up Toys tab completion in the current bash shell.",
|
127
|
+
"",
|
128
|
+
"To use, execute the following line in a bash shell, or include it in an init file" \
|
129
|
+
" such as your .bashrc:",
|
130
|
+
[" $(toys system bash-completion install)"],
|
131
|
+
"",
|
132
|
+
"This will associate the toys tab completion logic with the `toys` executable by default." \
|
133
|
+
" If you have aliases for the toys executable, pass them as arguments. e.g.",
|
134
|
+
[" $(toys system bash-completion install my-toys-alias another-alias)"]
|
135
|
+
|
136
|
+
remaining_args :executable_names,
|
137
|
+
desc: "Names of executables for which to set up tab completion" \
|
138
|
+
" (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
|
139
|
+
|
140
|
+
def run
|
141
|
+
require "shellwords"
|
142
|
+
path = ::File.join(::File.dirname(__dir__), "share", "bash-completion.sh")
|
143
|
+
exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
|
144
|
+
puts Shellwords.join(["source", path] + exes)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
tool "remove" do
|
149
|
+
desc "Remove bash tab completion"
|
150
|
+
|
151
|
+
long_desc \
|
152
|
+
"Outputs a command to remove Toys tab completion from the current bash shell.",
|
153
|
+
"",
|
154
|
+
"To use, execute the following line in a bash shell:",
|
155
|
+
[" $(toys system bash-completion remove)"],
|
156
|
+
"",
|
157
|
+
"If you have other names or aliases for the toys executable, pass them as arguments. e.g.",
|
158
|
+
[" $(toys system bash-completion remove my-toys-alias another-alias)"]
|
159
|
+
|
160
|
+
remaining_args :executable_names,
|
161
|
+
desc: "Names of executables for which to set up tab completion" \
|
162
|
+
" (default: #{::Toys::StandardCLI::EXECUTABLE_NAME})"
|
163
|
+
|
164
|
+
def run
|
165
|
+
require "shellwords"
|
166
|
+
path = ::File.join(::File.dirname(__dir__), "share", "bash-completion-remove.sh")
|
167
|
+
exes = executable_names.empty? ? [::Toys::StandardCLI::EXECUTABLE_NAME] : executable_names
|
168
|
+
puts Shellwords.join(["source", path] + exes)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/docs/guide.md
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# Toys User Guide
|
4
4
|
|
5
5
|
Toys is a configurable command line tool. Write commands in config files using
|
6
|
-
a simple DSL, and Toys will provide the command line
|
7
|
-
all the details such as argument parsing, online help, and error reporting.
|
6
|
+
a simple DSL, and Toys will provide the command line executable and take care
|
7
|
+
of all the details such as argument parsing, online help, and error reporting.
|
8
8
|
|
9
9
|
Toys is designed for software developers, IT professionals, and other power
|
10
10
|
users who want to write and organize scripts to automate their workflows. It
|
@@ -12,23 +12,33 @@ can also be used as a Rake replacement, providing a more natural command line
|
|
12
12
|
interface for your project's build tasks.
|
13
13
|
|
14
14
|
Unlike most command line frameworks, Toys is *not primarily* designed to help
|
15
|
-
you build and ship a custom command line
|
16
|
-
provides a single
|
17
|
-
the Toys
|
18
|
-
own custom command line
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Toys
|
15
|
+
you build and ship a custom command line executable written in Ruby. Rather, it
|
16
|
+
provides a single executable called `toys`. You define the commands recognized
|
17
|
+
by the Toys executable by writing configuration files. (You can, however, build
|
18
|
+
your own custom command line executable using the related **toys-core**
|
19
|
+
library.)
|
20
|
+
|
21
|
+
If this is your first time using Toys, we recommend starting with the
|
22
|
+
[README](https://www.rubydoc.info/gems/toys/file/README.md), which includes a
|
23
|
+
tutorial that introduces how to install Toys, write and execute tools, and even
|
24
|
+
use Toys to replace Rake. The tutorial will likely give you enough information
|
25
|
+
to start using Toys effectively.
|
26
|
+
|
27
|
+
This user's guide is also structured like an extended tutorial, but it is much
|
28
|
+
longer and covers all the features of Toys in much more depth. Read it when
|
29
|
+
you're ready to unlock all the capabilities of Toys to create sophisticated
|
30
|
+
command line tools.
|
31
|
+
|
32
|
+
## Conceptual overview
|
33
|
+
|
34
|
+
Toys is a command line *framework*. It provides an executable called `toys`
|
25
35
|
with basic functions such as argument parsing and online help. You provide the
|
26
|
-
actual behavior of the Toys
|
36
|
+
actual behavior of the Toys executable by writing **Toys files**.
|
27
37
|
|
28
|
-
Toys is a multi-command
|
29
|
-
**tools**, which can be invoked by passing the tool name as an argument
|
30
|
-
`toys`
|
31
|
-
that have **subtools**.
|
38
|
+
Toys is a multi-command executable. You may define any number of commands,
|
39
|
+
called **tools**, which can be invoked by passing the tool name as an argument
|
40
|
+
to the `toys` executable. Tools are arranged in a hierarchy; you may define
|
41
|
+
**namespaces** that have **subtools**.
|
32
42
|
|
33
43
|
Tools may recognize command line arguments in the form of **flags** and
|
34
44
|
**positional arguments**. Flags can optionally take **values**, while
|
@@ -42,12 +52,12 @@ tool's **online help** screen. Descriptions come in **long** and **short**
|
|
42
52
|
forms, which appear in different styles of help.
|
43
53
|
|
44
54
|
Toys searches for tools in specifically-named **Toys files** and **Toys
|
45
|
-
directories**. It searches for these in the current directory, its
|
46
|
-
and in the Toys **search path**.
|
55
|
+
directories**. It searches for these in the current directory, in its
|
56
|
+
ancestors, and in the Toys **search path**.
|
47
57
|
|
48
58
|
Toys provides various features to help you write tools. This includes providing
|
49
59
|
a **logger** for each tool, **mixins** that provide common functions a tool can
|
50
|
-
call (such as
|
60
|
+
call (such as to control subprocesses and style output), and **templates**
|
51
61
|
which are prefabricated tools that you can configure for your needs.
|
52
62
|
|
53
63
|
Finally, Toys provides useful **built-in behavior**, including automatically
|
@@ -55,7 +65,7 @@ providing flags to display help screens and set verbosity. It also includes a
|
|
55
65
|
built-in namespace of **system tools** that let you inspect and configure the
|
56
66
|
Toys system itself.
|
57
67
|
|
58
|
-
## The Toys
|
68
|
+
## The Toys command line
|
59
69
|
|
60
70
|
In this section, you will learn how Toys parses its command line, identifies a
|
61
71
|
tool to run, and interprets flags and other command line arguments.
|
@@ -73,8 +83,8 @@ first **positional argument**), or until there are no more arguments.
|
|
73
83
|
|
74
84
|
For example, in the following command:
|
75
85
|
|
76
|
-
|
77
|
-
toys system version
|
86
|
+
|----TOOL----|
|
87
|
+
$ toys system version
|
78
88
|
|
79
89
|
The tool name is `system version`. Notice that the tool name may have multiple
|
80
90
|
words. Tools are arranged hierarchically. In this case, `system` is a
|
@@ -85,13 +95,13 @@ The words in a tool name can be delimited with spaces as shown above, or
|
|
85
95
|
alternately periods or colons. The following commands also invoke the tool
|
86
96
|
`system version`:
|
87
97
|
|
88
|
-
toys system.version
|
89
|
-
toys system:version
|
98
|
+
$ toys system.version
|
99
|
+
$ toys system:version
|
90
100
|
|
91
101
|
In the following command:
|
92
102
|
|
93
|
-
|
94
|
-
toys system frodo
|
103
|
+
|TOOL| |ARG|
|
104
|
+
$ toys system frodo
|
95
105
|
|
96
106
|
There is no subtool `frodo` under the `system` namespace, so Toys works
|
97
107
|
backward until it finds an existing tool. In this case, the `system` namespace
|
@@ -103,7 +113,7 @@ other tool. In the above case, it takes the argument `frodo`, determines it has
|
|
103
113
|
no subtool of that name, and prints an error message. More commonly, though,
|
104
114
|
you might execute a namespace without arguments:
|
105
115
|
|
106
|
-
toys system
|
116
|
+
$ toys system
|
107
117
|
|
108
118
|
This displays the **online help screen** for the `system` namespace, which
|
109
119
|
includes a list of all its subtools and what they do.
|
@@ -111,14 +121,14 @@ includes a list of all its subtools and what they do.
|
|
111
121
|
It is also legitimate for the tool name to be empty. This invokes the **root
|
112
122
|
tool**, the toplevel namespace:
|
113
123
|
|
114
|
-
toys
|
124
|
+
$ toys
|
115
125
|
|
116
126
|
Like any namespace, invoking the root tool displays its help screen, including
|
117
127
|
showing the list of all its subtools.
|
118
128
|
|
119
129
|
One last example:
|
120
130
|
|
121
|
-
toys frodo
|
131
|
+
$ toys frodo
|
122
132
|
|
123
133
|
If there is no tool called `frodo` in the toplevel namespace, then once again,
|
124
134
|
`frodo` is interpreted as an argument to the root tool. The root tool responds
|
@@ -134,38 +144,51 @@ tool, the tool will generally display an error message.
|
|
134
144
|
|
135
145
|
Toys follows the typical unix conventions for flags, specifically those covered
|
136
146
|
by Ruby's OptionParser library. You can provide short (single-character) flags
|
137
|
-
with a single hyphen, or long flags with a double hyphen.
|
138
|
-
|
147
|
+
with a single hyphen, or long flags with a double hyphen. Some flags can also
|
148
|
+
take **values**. Following are a few examples.
|
149
|
+
|
150
|
+
Here we pass a single short flag (for verbose output).
|
151
|
+
|
152
|
+
$ toys system -v
|
139
153
|
|
140
|
-
|
154
|
+
Here we pass multiple long flags (for verbose output and recursive subtool
|
155
|
+
search).
|
141
156
|
|
142
|
-
toys system
|
157
|
+
$ toys system --verbose --recursive
|
143
158
|
|
144
|
-
|
159
|
+
You can combine short flags. The following passes both the `-v` and `-r` flags
|
160
|
+
(i.e. it has the same effect as the previous example.)
|
145
161
|
|
146
|
-
toys system
|
162
|
+
$ toys system -vr
|
147
163
|
|
148
|
-
|
164
|
+
Long flags can be abbreviated, as long as the abbreviation is not ambiguous.
|
165
|
+
For example, there is only one flag (`--recursive`) beginning with the string
|
166
|
+
`--rec`, so you can use the shortened form.
|
149
167
|
|
150
|
-
toys
|
168
|
+
$ toys --rec
|
151
169
|
|
152
|
-
|
153
|
-
|
170
|
+
However, there are two flags (`--version` and `--verbose`) beginning with
|
171
|
+
`--ver`, so it cannot be used as an abbreviation. This will cause an error:
|
154
172
|
|
155
|
-
toys --
|
156
|
-
toys --search build
|
173
|
+
$ toys --ver
|
157
174
|
|
158
|
-
|
175
|
+
Some flags take values. The root tool supports the `--search` flag to search
|
176
|
+
for tools that have the given keyword.
|
159
177
|
|
160
|
-
toys
|
161
|
-
toys
|
178
|
+
$ toys --search=build
|
179
|
+
$ toys --search build
|
180
|
+
|
181
|
+
The short form of the search flag `-s` also takes a value.
|
182
|
+
|
183
|
+
$ toys -s build
|
184
|
+
$ toys -sbuild
|
162
185
|
|
163
186
|
If a double hyphen `--` appears by itself in the arguments, it disables flag
|
164
187
|
parsing from that point. Any further arguments are treated as positional
|
165
188
|
arguments, even if they begin with hyphens. For example:
|
166
189
|
|
167
|
-
|
168
|
-
toys --verbose -- --recursive
|
190
|
+
|--FLAG--| |---ARG---|
|
191
|
+
$ toys --verbose -- --recursive
|
169
192
|
|
170
193
|
That will cause `--recursive` to be treated as a positional argument. (In this
|
171
194
|
case, as we saw earlier, the root tool will respond by printing an error
|
@@ -174,7 +197,7 @@ message that no tool named `--recursive` exists.)
|
|
174
197
|
Note that a single hyphen by itself `-` is not considered a flag, nor does it
|
175
198
|
disable flag parsing. It is treated as a normal positional argument.
|
176
199
|
|
177
|
-
#### Standard
|
200
|
+
#### Standard flags
|
178
201
|
|
179
202
|
For the most part, each tool specifies which flags and arguments it recognizes.
|
180
203
|
However, Toys adds a few standard flags globally to every tool. (It is possible
|
@@ -196,18 +219,19 @@ flag, but it has no additional effect.) Namespaces also support the following
|
|
196
219
|
additional flags:
|
197
220
|
|
198
221
|
* `--all` which displays all subtools, including
|
199
|
-
[hidden subtools](#
|
200
|
-
* `--
|
201
|
-
|
222
|
+
[hidden subtools](#Hidden_tools) and namespaces.
|
223
|
+
* `--no-recursive` which displays only immediate subtools, instead of the
|
224
|
+
default behavior of showing all subtools recursively.
|
202
225
|
* `--search=TERM` which displays only subtools whose name or description
|
203
226
|
contain the specified search term.
|
204
|
-
* `--tools` which displays just the list of subtools
|
227
|
+
* `--tools` which displays just the list of subtools rather than the entire
|
228
|
+
help screen.
|
205
229
|
|
206
230
|
Finally, the root tool also supports:
|
207
231
|
|
208
232
|
* `--version` which displays the current Toys version.
|
209
233
|
|
210
|
-
### Positional
|
234
|
+
### Positional arguments
|
211
235
|
|
212
236
|
Any arguments not recognized as flags or flag arguments, are interpreted as
|
213
237
|
**positional arguments**. Positional arguments are recognized in order and may
|
@@ -222,32 +246,70 @@ recognizes any number of positional arguments. Those arguments specify which
|
|
222
246
|
tools to run and what arguments to pass to them. If, for example, you had a
|
223
247
|
`build` tool and a `test` tool, you could run them in sequence with:
|
224
248
|
|
225
|
-
|
226
|
-
toys do build , test
|
249
|
+
|---ARGS---|
|
250
|
+
$ toys do build , test
|
227
251
|
|
228
|
-
The three arguments `build
|
252
|
+
The three arguments `build` and `,` and `test` are positional arguments to the
|
229
253
|
`do` tool. (The `do` tool uses `,` to delimit the tools that it should run.)
|
230
254
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
255
|
+
Most tools allow flags and positional arguments to be interspersed. A flag will
|
256
|
+
be recognized even if it appears after some of the positional arguments.
|
257
|
+
|
258
|
+
However, this approach would not work for the `do` tool because its common case
|
259
|
+
is to pass flags down to the steps it runs. (That is, `do` wants most arguments
|
260
|
+
to be treated as positional even if they look like flags.) So `do` stops
|
261
|
+
recognizing flags once it encounters its first positional argument. That is,
|
262
|
+
you could do this:
|
263
|
+
|
264
|
+
|------------ARGS-----------|
|
265
|
+
$ toys do build --staging , test --help
|
266
|
+
|
267
|
+
Each tool can choose which behavior it will support, whether or not to enforce
|
268
|
+
[flags before positional args](#Enforcing_flags_before_args).
|
235
269
|
|
236
|
-
|
237
|
-
|
270
|
+
You can also, of course, stop recognizing flags on the command line by passing
|
271
|
+
`--` as an argument.
|
238
272
|
|
239
|
-
|
240
|
-
first `--help` is interpreted as a flag for `do`. What we actually want is for
|
241
|
-
`do` to treat it as a positional argument specifying the first tool to run. So
|
242
|
-
Let's force `do` to treat all its arguments as positional, by starting with
|
243
|
-
`--` like so:
|
273
|
+
### Tab completion
|
244
274
|
|
245
|
-
|
246
|
-
|
275
|
+
If you are using the Bash shell, Toys provides custom tab completion. See
|
276
|
+
[this section](#Installing_tab_completion_for_Bash) for instructions on
|
277
|
+
installing tab completion.
|
247
278
|
|
248
|
-
|
279
|
+
Toys will complete tool and subtool names, flags, values passed to flags, and
|
280
|
+
positional argument values, and it will respect the current context. For
|
281
|
+
example, if you type:
|
249
282
|
|
250
|
-
|
283
|
+
$ toys <TAB><TAB>
|
284
|
+
|
285
|
+
The tab completion will show you a list of reasonable things that could appear
|
286
|
+
next, including the defined tool names (such as `system` and `do`) as well as
|
287
|
+
all the flags supported by the root tool (such as `--help` and `-v`). And of
|
288
|
+
course, if you start typing something, tab completion will limit the display to
|
289
|
+
matching completions. The following displays only flags, i.e. completions that
|
290
|
+
begin with a hyphen:
|
291
|
+
|
292
|
+
$ toys -<TAB><TAB>
|
293
|
+
|
294
|
+
And if you type the following:
|
295
|
+
|
296
|
+
$ toys sys<TAB>
|
297
|
+
|
298
|
+
It is likely only one tool name starts with `sys`, so completion will
|
299
|
+
automatically type the rest of `system` for you.
|
300
|
+
|
301
|
+
The tab completion for Toys also supports values passed to flags and positional
|
302
|
+
args. As we shall see later, when you define a flag or a positional argument,
|
303
|
+
you can specify how completions are computed.
|
304
|
+
|
305
|
+
**Note:** Because of the highly dynamic nature of Toys in which tools, flags,
|
306
|
+
and arguments can be highly customized, the completion implementation actually
|
307
|
+
requires *executing Toys* so it can analyze your tool configurations. This
|
308
|
+
unfortunately means paying some upfront latency as the Ruby interpreter starts
|
309
|
+
up. So you can expect a slight pause when evaluating tab completion for Toys,
|
310
|
+
at least in comparison with most other tab completions.
|
311
|
+
|
312
|
+
## Defining tools
|
251
313
|
|
252
314
|
So far we've been experimenting only with the built-in tools provided by Toys.
|
253
315
|
In this section, you will learn how to define tools by writing a **Toys file**.
|
@@ -255,7 +317,7 @@ We will cover how to write tools, including specifying the functionality of the
|
|
255
317
|
tool, the flags and arguments it takes, and how its description appears in the
|
256
318
|
help screen.
|
257
319
|
|
258
|
-
### Basic Toys
|
320
|
+
### Basic Toys syntax
|
259
321
|
|
260
322
|
A file named `.toys.rb` (note the leading period) in the current working
|
261
323
|
directory is called a **Toys file**. It defines tools available in that
|
@@ -285,14 +347,14 @@ Let's start with an example:
|
|
285
347
|
|
286
348
|
def run
|
287
349
|
greeting = "Hello, #{whom}!"
|
288
|
-
greeting.upcase
|
350
|
+
greeting = greeting.upcase if shout
|
289
351
|
puts greeting
|
290
352
|
end
|
291
353
|
end
|
292
354
|
|
293
355
|
Its results should be mostly self-evident. But let's unpack a few details.
|
294
356
|
|
295
|
-
### Tool
|
357
|
+
### Tool descriptions
|
296
358
|
|
297
359
|
Each tool may have a **short description** and/or a **long description**. The
|
298
360
|
short description is a generally a single string that is displayed with the
|
@@ -316,7 +378,7 @@ For more details, see the reference documentation for
|
|
316
378
|
and
|
317
379
|
[Toys::DSL::Tool#long_desc](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:long_desc).
|
318
380
|
|
319
|
-
### Positional
|
381
|
+
### Positional arguments
|
320
382
|
|
321
383
|
Tools may recognize any number of **positional arguments**. Each argument must
|
322
384
|
have a name, which is a key that the tool can use to obtain the argument's
|
@@ -328,12 +390,14 @@ The above example uses the directive
|
|
328
390
|
to declare an **optional argument** named `:whom`. If the argument is provided
|
329
391
|
on the command line e.g.
|
330
392
|
|
331
|
-
toys greet ruby
|
393
|
+
$ toys greet ruby
|
394
|
+
Hello, ruby!
|
332
395
|
|
333
396
|
Then the option `:whom` is set to the string `"ruby"`. Otherwise, if the
|
334
397
|
argument is omitted, e.g.
|
335
398
|
|
336
|
-
toys greet
|
399
|
+
$ toys greet
|
400
|
+
Hello, world!
|
337
401
|
|
338
402
|
Then the option `:whom` is set to the default value `"world"`.
|
339
403
|
|
@@ -341,14 +405,14 @@ If the option name is a valid method name, Toys will provide a method that you
|
|
341
405
|
can use to retrieve the value. In the above example, we retrieve the value for
|
342
406
|
the option `:whom` by calling the method `whom`. If the option name cannot be
|
343
407
|
made into a method, you can retrieve the value by calling
|
344
|
-
[Toys::
|
408
|
+
[Toys::Context#get](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:get).
|
345
409
|
|
346
410
|
An argument may also be **required**, which means it must be provided on the
|
347
411
|
command line; otherwise the tool will report a usage error. You may declare a
|
348
412
|
required argument using the directive
|
349
413
|
[Toys::DSL::Tool#required_arg](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:required_arg).
|
350
414
|
|
351
|
-
#### Parsing
|
415
|
+
#### Parsing required and optional arguments
|
352
416
|
|
353
417
|
When command line arguments are parsed, the required arguments are matched
|
354
418
|
first, in order, followed by the optional arguments. For example:
|
@@ -356,29 +420,35 @@ first, in order, followed by the optional arguments. For example:
|
|
356
420
|
tool "args-demo" do
|
357
421
|
optional_arg :arg2
|
358
422
|
required_arg :arg1
|
359
|
-
|
423
|
+
|
424
|
+
def run
|
425
|
+
puts "options data is #{options.inspect}"
|
426
|
+
end
|
427
|
+
end
|
360
428
|
|
361
429
|
If a user runs
|
362
430
|
|
363
|
-
toys args-demo foo
|
431
|
+
$ toys args-demo foo
|
432
|
+
Options data is {arg1: "foo", arg2: nil}
|
364
433
|
|
365
434
|
Then the required argument `:arg1` will be set to `"foo"`, and the optional
|
366
435
|
argument `:arg2` will not be set (i.e. it will remain `nil`).
|
367
436
|
|
368
437
|
If the user runs:
|
369
438
|
|
370
|
-
toys args-demo foo bar
|
439
|
+
$ toys args-demo foo bar
|
440
|
+
Options data is {arg1: "foo", arg2: "bar"}
|
371
441
|
|
372
442
|
Then `:arg1` is set to `"foo"`, and `:arg2` is set to `"bar"`.
|
373
443
|
|
374
444
|
Running the following:
|
375
445
|
|
376
|
-
toys args-demo
|
446
|
+
$ toys args-demo
|
377
447
|
|
378
448
|
Will produce a usage error, because no value is set for the required argument
|
379
449
|
`:arg1`. Similarly, running:
|
380
450
|
|
381
|
-
toys args-demo foo bar baz
|
451
|
+
$ toys args-demo foo bar baz
|
382
452
|
|
383
453
|
Will also produce an error, since the tool does not define an argument to
|
384
454
|
match `"baz"`.
|
@@ -389,16 +459,21 @@ not provided on the command line. For example:
|
|
389
459
|
tool "args-demo" do
|
390
460
|
required_arg :arg1
|
391
461
|
optional_arg :arg2, default: "the-default"
|
392
|
-
|
462
|
+
|
463
|
+
def run
|
464
|
+
puts "options data is #{options.inspect}"
|
465
|
+
end
|
466
|
+
end
|
393
467
|
|
394
468
|
Now running the following:
|
395
469
|
|
396
|
-
toys args-demo foo
|
470
|
+
$ toys args-demo foo
|
471
|
+
Options data is {arg1: "foo", arg2: "the-default"}
|
397
472
|
|
398
473
|
Will set the required argument to `"foo"` as usual, and the optional argument,
|
399
474
|
because it is not provided, will default to `"the-default"` instead of `nil`.
|
400
475
|
|
401
|
-
#### Remaining
|
476
|
+
#### Remaining arguments
|
402
477
|
|
403
478
|
Normally, unmatched arguments will result in an error message. However, you can
|
404
479
|
provide an "argument" to match all **remaining** unmatched arguments at the
|
@@ -410,29 +485,25 @@ For example:
|
|
410
485
|
required_arg :arg1
|
411
486
|
optional_arg :arg2
|
412
487
|
remaining_args :arg3
|
413
|
-
# ...
|
414
488
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
Sets the following option data:
|
420
|
-
|
421
|
-
{arg1: "foo", arg2: "bar", arg3: ["baz", "bey"]}
|
422
|
-
|
423
|
-
If instead you run:
|
489
|
+
def run
|
490
|
+
puts "Options data is #{options.inspect}"
|
491
|
+
end
|
492
|
+
end
|
424
493
|
|
425
|
-
|
494
|
+
Now, we can see how the remaining arguments (if any) are collected by `:arg3`:
|
426
495
|
|
427
|
-
|
496
|
+
$ toys args-demo foo bar baz qux
|
497
|
+
Options data is {arg1: "foo", arg2: "bar", arg3: ["baz", "qux"]}
|
428
498
|
|
429
|
-
|
499
|
+
$ toys args-demo foo
|
500
|
+
Options data is {arg1: "foo", arg2: nil, arg3: []}
|
430
501
|
|
431
502
|
Tools can include any number of `required_arg` and `optional_arg` directives,
|
432
|
-
declaring any number of required and optional arguments
|
433
|
-
|
503
|
+
declaring any number of required and optional arguments. But tools can have at
|
504
|
+
most only one `remaining_args` directive.
|
434
505
|
|
435
|
-
#### Descriptions and the
|
506
|
+
#### Descriptions and the args DSL
|
436
507
|
|
437
508
|
Positional arguments may also have short and long descriptions, which are
|
438
509
|
displayed in online help. Set descriptions via the `desc:` and `long_desc:`
|
@@ -445,7 +516,7 @@ an example:
|
|
445
516
|
long_desc: ["Long descriptions may have multiple lines.",
|
446
517
|
"This is the second line."]
|
447
518
|
|
448
|
-
See the [above section on Descriptions](#
|
519
|
+
See the [above section on Descriptions](#Tool_descriptions) for more
|
449
520
|
information on how descriptions are rendered and word wrapped.
|
450
521
|
|
451
522
|
Because long descriptions may be unwieldly to write as a hash argument in this
|
@@ -459,9 +530,9 @@ way, Toys provides an alternate syntax for defining arguments using a block.
|
|
459
530
|
end
|
460
531
|
|
461
532
|
For detailed info on configuring an argument using a block, see the
|
462
|
-
[Toys::DSL::
|
533
|
+
[Toys::DSL::PositionalArg class](https://www.rubydoc.info/gems/toys-core/Toys/DSL/PositionalArg).
|
463
534
|
|
464
|
-
#### Argument
|
535
|
+
#### Argument acceptors
|
465
536
|
|
466
537
|
Finally, positional arguments may use **acceptors** to define how to validate
|
467
538
|
arguments and convert them to Ruby objects for your tool to consume. By
|
@@ -478,7 +549,8 @@ integer during parsing:
|
|
478
549
|
required_arg :age, accept: Integer
|
479
550
|
def run
|
480
551
|
puts "Next year I will be #{age + 1}" # Age is an integer
|
481
|
-
|
552
|
+
end
|
553
|
+
end
|
482
554
|
|
483
555
|
If you pass a non-integer for this argument, Toys will report a usage error.
|
484
556
|
|
@@ -489,7 +561,31 @@ and
|
|
489
561
|
[OptionParser::OctalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#OctalInteger).
|
490
562
|
|
491
563
|
You may also create **custom acceptors**. See the
|
492
|
-
[section below on Custom Acceptors](#
|
564
|
+
[section below on Custom Acceptors](#Custom_acceptors) for more information.
|
565
|
+
|
566
|
+
#### Argument completions
|
567
|
+
|
568
|
+
Shell tab completion supports positional arguments, and arguments can be
|
569
|
+
configured to present a set of completion candidates for themselves.
|
570
|
+
|
571
|
+
By default, an argument does not provide any completions for itself. To change
|
572
|
+
that, set the `completion` option. Currently there are three ways to set the
|
573
|
+
completion:
|
574
|
+
|
575
|
+
* Provide a static set of possible values, as an array of strings.
|
576
|
+
* Specify that values should be paths in the file system by setting the
|
577
|
+
symbol `:file_system`.
|
578
|
+
* Provide a `Proc` that returns possible values.
|
579
|
+
|
580
|
+
The following are two example arguments, one that supports a static set of
|
581
|
+
completions and the other that supports file paths.
|
582
|
+
|
583
|
+
required_arg :language, complete: ["ruby", "elixir", "rust"]
|
584
|
+
required_arg :path, complete: :file_system
|
585
|
+
|
586
|
+
Completions are somewhat related to acceptors, and it is a common pattern to
|
587
|
+
set both in concert. But they perform distinct functions. Acceptors affect
|
588
|
+
argument parsing, whereas completions affect tab completion in the shell.
|
493
589
|
|
494
590
|
### Flags
|
495
591
|
|
@@ -508,21 +604,15 @@ As with arguments, Toys will provide a method that you can call to retrieve the
|
|
508
604
|
option value set by a flag. In this case, a method called `shout` will be
|
509
605
|
available, and will return either true or false. If the option name cannot be
|
510
606
|
made into a method, you can retrieve the value by calling
|
511
|
-
[Toys::
|
607
|
+
[Toys::Context#get](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:get).
|
512
608
|
|
513
|
-
#### Flag
|
609
|
+
#### Flag types
|
514
610
|
|
515
611
|
Toys recognizes the same syntax used by the standard OptionParser library. This
|
516
612
|
means you can also declare a flag that can be set either to true or false:
|
517
613
|
|
518
614
|
flag :shout, "--[no-]shout"
|
519
615
|
|
520
|
-
If you do not provide any actual flags, Toys will infer a long flag from the
|
521
|
-
name of the option. Hence, the following two definitions are equivalent:
|
522
|
-
|
523
|
-
flag :shout
|
524
|
-
flag :shout, "--shout"
|
525
|
-
|
526
616
|
You can declare that a short or long flag takes a value:
|
527
617
|
|
528
618
|
flag :whom, "--whom=VALUE"
|
@@ -554,7 +644,106 @@ example:
|
|
554
644
|
Raises an error because one flag's value is optional while the other is
|
555
645
|
required. (Again, this is consistent with OptionParser's behavior.)
|
556
646
|
|
557
|
-
####
|
647
|
+
#### Inferred flags
|
648
|
+
|
649
|
+
If you do not provide any actual flags, Toys will attempt to infer one from the
|
650
|
+
name of the option. A one-character name will yield a short flag, and a longer
|
651
|
+
name a long flag. Hence, the following two definitions are equivalent:
|
652
|
+
|
653
|
+
flag :shout
|
654
|
+
flag :shout, "--shout"
|
655
|
+
|
656
|
+
And the following two are equivalent:
|
657
|
+
|
658
|
+
flag :S
|
659
|
+
flag :S, "-S"
|
660
|
+
|
661
|
+
Inferred flags will convert underscores to hyphens. So the following two
|
662
|
+
definitions are also equivalent:
|
663
|
+
|
664
|
+
flag :call_out
|
665
|
+
flag :call_out, "--call-out
|
666
|
+
|
667
|
+
#### Handling optional values
|
668
|
+
|
669
|
+
There are some subtleties in how the Ruby OptionParser library treats flags
|
670
|
+
with optional values. Although Toys does not use OptionParser interally, it
|
671
|
+
does, for the most part, replicate OptionParser's behavior. It is thus
|
672
|
+
important to understand that behavior if you use optional values.
|
673
|
+
|
674
|
+
First, if a flag has an optional value that is not provided on the command
|
675
|
+
line, then the option is set to `true`, as if it were a normal boolean flag
|
676
|
+
that didn't take a value. Consider this example:
|
677
|
+
|
678
|
+
tool "flags-demo" do
|
679
|
+
flag :output, "--output [DIRECTORY]", default: "."
|
680
|
+
def run
|
681
|
+
puts "output is #{output.inspect}"
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
If a user executes this without passing the `--output` flag, the default will
|
686
|
+
be printed as we expect.
|
687
|
+
|
688
|
+
$ toys flags-demo
|
689
|
+
output is "."
|
690
|
+
|
691
|
+
If a user executes this and provides a value for `--output`, it will show up:
|
692
|
+
|
693
|
+
$ toys flags-demo --output /etc
|
694
|
+
output is "/etc"
|
695
|
+
|
696
|
+
If a user provides `--output` but omits the value, it displays `true`:
|
697
|
+
|
698
|
+
$ toys flags-demo --output
|
699
|
+
output is true
|
700
|
+
|
701
|
+
Second, if the following argument looks like a flag (i.e. it begins with a
|
702
|
+
hyphen), it is not treated as an optional value. In this example, the argument
|
703
|
+
`--verbose` is not treated as the value of `--output` but as a separate flag.
|
704
|
+
(If `--output` had a *required* value, then `--verbose` would have been treated
|
705
|
+
as the value.)
|
706
|
+
|
707
|
+
$ toys flags-demo --output --verbose
|
708
|
+
output is true
|
709
|
+
|
710
|
+
Finally, there is an important difference between the syntax
|
711
|
+
`"--output [DIRECTORY]"` and `"--output=[DIRECTORY]"`. In the former case, the
|
712
|
+
following argument (as long as it doesn't look like a flag) will be treated as
|
713
|
+
the value. In the latter case, however, the following argument is *never*
|
714
|
+
treated as the value. In that latter case, you *must* use the equals sign
|
715
|
+
syntax to provide a value.
|
716
|
+
|
717
|
+
To illustrate, consider two flags with optional values, one using space and the
|
718
|
+
other using equals.
|
719
|
+
|
720
|
+
tool "flags-demo-space" do
|
721
|
+
flag :output, "--output [DIRECTORY]", default: "."
|
722
|
+
set_remaining_args :remaining
|
723
|
+
def run
|
724
|
+
puts "output is #{output.inspect}"
|
725
|
+
end
|
726
|
+
end
|
727
|
+
tool "flags-demo-equals" do
|
728
|
+
flag :output, "--output=[DIRECTORY]", default: "."
|
729
|
+
set_remaining_args :remaining
|
730
|
+
def run
|
731
|
+
puts "output is #{output.inspect}"
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
Here is the behavior:
|
736
|
+
|
737
|
+
$ toys flags-demo-space --output=/etc
|
738
|
+
output is "/etc"
|
739
|
+
$ toys flags-demo-space --output /etc
|
740
|
+
output is "/etc"
|
741
|
+
$ toys flags-demo-equals --output=/etc
|
742
|
+
output is "/etc"
|
743
|
+
$ toys flags-demo-equals --output /etc
|
744
|
+
output is true
|
745
|
+
|
746
|
+
#### Flag acceptors
|
558
747
|
|
559
748
|
Flags may use **acceptors** to define how to validate values and convert them
|
560
749
|
to Ruby objects for your tool to consume. By default, Toys will accept a flag
|
@@ -567,11 +756,12 @@ section. For example, you can provide the `Integer` class as an acceptor, which
|
|
567
756
|
will validate that the argument is a well-formed integer, and convert it to an
|
568
757
|
integer during parsing:
|
569
758
|
|
570
|
-
tool "
|
759
|
+
tool "flags-demo" do
|
571
760
|
flag :age, accept: Integer
|
572
761
|
def run
|
573
762
|
puts "Next year I will be #{age + 1}" # Age is an integer
|
574
|
-
|
763
|
+
end
|
764
|
+
end
|
575
765
|
|
576
766
|
If you pass a non-integer for this flag value, Toys will report a usage error.
|
577
767
|
|
@@ -582,15 +772,16 @@ and
|
|
582
772
|
[OptionParser::OctalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#OctalInteger).
|
583
773
|
|
584
774
|
You may also create **custom acceptors**. See the
|
585
|
-
[section below on Custom Acceptors](#
|
775
|
+
[section below on Custom Acceptors](#Custom_acceptors) for more information.
|
586
776
|
|
587
|
-
#### Defaults and
|
777
|
+
#### Defaults and handlers
|
588
778
|
|
589
|
-
|
590
|
-
|
591
|
-
arguments for a tool, by default its corresponding option value will be `nil`.
|
779
|
+
Flags are usually optional; a flag can appear in a command line zero, one, or
|
780
|
+
any number of times.
|
592
781
|
|
593
|
-
|
782
|
+
If a flag is not passed in the command line arguments for a tool, by default
|
783
|
+
its corresponding option value will be `nil`. You may change this by providing
|
784
|
+
a default value for a flag:
|
594
785
|
|
595
786
|
flag :age, accept: Integer, default: 21
|
596
787
|
|
@@ -601,39 +792,61 @@ appearance of the flag will take effect. That is, suppose you define this flag:
|
|
601
792
|
|
602
793
|
Now if you pass `--shout --no-shout`, then the value of the `:shout` option
|
603
794
|
will be `false`, i.e. the last value set on the command line. This is because a
|
604
|
-
flag *sets* its option value, replacing any previously set value.
|
605
|
-
|
795
|
+
flag normally *sets* its option value, replacing any previously set value.
|
796
|
+
|
797
|
+
You can, however, change this behavior by providing a **handler**. A handler is
|
798
|
+
a Ruby Proc that defines what a flag does to its option value. It takes two
|
799
|
+
arguments, the new value given, and the previously set value (which might be
|
800
|
+
the default value if this is the first appearance of the flag), and returns the
|
801
|
+
new value that should be set.
|
606
802
|
|
607
|
-
|
608
|
-
|
609
|
-
be the default value if this is the first appearance of the flag), and returns
|
610
|
-
the new value that should be set. So effectively, the default behavior is
|
611
|
-
equivalent to the following handler:
|
803
|
+
Effectively, the default behavior (setting the value and ignoring the previous
|
804
|
+
value) is equivalent to the following handler:
|
612
805
|
|
613
806
|
flag :shout, "--[no-]shout", handler: proc { | val, _prev| val }
|
614
807
|
|
615
|
-
|
616
|
-
|
617
|
-
value of this verbosity is an integer. This flag is provided automatically by
|
618
|
-
Toys, and its implementation looks something like this:
|
808
|
+
Toys gives the default handler the special name `:set`. So the above is also
|
809
|
+
equivalent to:
|
619
810
|
|
620
|
-
flag
|
811
|
+
flag :shout, "--[no-]shout", handler: :set
|
812
|
+
|
813
|
+
The `--verbose` flag, provided automatically by Toys for most tools, shows an
|
814
|
+
example of an alternate handler. Verbosity is represented by an integer value,
|
815
|
+
defaulting to 0. The `--verbose` flag may appear any number of times, and
|
816
|
+
*each* appearance increases the verbosity. Its implementation is internal to
|
817
|
+
Toys, but looks something like this:
|
818
|
+
|
819
|
+
flag Toys::Context::Key::VERBOSITY, "-v", "--verbose",
|
621
820
|
default: 0,
|
622
821
|
handler: proc { |_val, prev| prev + 1 }
|
623
822
|
|
624
823
|
Similarly, the "--quiet" flag, which decreases the verbosity, is implemented
|
625
824
|
like this:
|
626
825
|
|
627
|
-
flag Toys::
|
826
|
+
flag Toys::Context::Key::VERBOSITY, "-q", "--quiet",
|
628
827
|
default: 0,
|
629
828
|
handler: proc { |_val, prev| prev - 1 }
|
630
829
|
|
631
|
-
Note that both flags affect the same option
|
830
|
+
Note that both flags affect the same option name, `VERBOSITY`. The first
|
632
831
|
increments it each time it appears, and the second decrements it. A tool can
|
633
832
|
query this option and get an integer telling the requested verbosity level, as
|
634
|
-
you will see [below](#
|
833
|
+
you will see [below](#Logging_and_verbosity).
|
834
|
+
|
835
|
+
Toys provides a few built-in handlers that can be specified by name. We already
|
836
|
+
discussed the default handler that can be specified by its name `:set` or by
|
837
|
+
simply omitting the `handler:` option. Another named handler is `:push`. This
|
838
|
+
handler is intended for flags that take values and can be provided more than
|
839
|
+
once. The final value is then an array of values.
|
635
840
|
|
636
|
-
|
841
|
+
In the following example, an invocation can provide any number of `--include`
|
842
|
+
flags, and the `:include` option will be set to an array of the given paths.
|
843
|
+
|
844
|
+
flag :include, "-I", "--include PATH", default: [], handler: :push
|
845
|
+
|
846
|
+
The `:push` handler is equivalent to
|
847
|
+
`proc { |val, array| array.nil? ? [val] : array << val }`.
|
848
|
+
|
849
|
+
#### Descriptions and the flags DSL
|
637
850
|
|
638
851
|
Flags may also have short and long descriptions, which are displayed in online
|
639
852
|
help. Set descriptions via the `desc:` and `long_desc:` arguments to the flag
|
@@ -645,7 +858,7 @@ directive. The `desc:` argument takes a single string description, while the
|
|
645
858
|
long_desc: ["Long descriptions may have multiple lines.",
|
646
859
|
"This is the second line."]
|
647
860
|
|
648
|
-
See the [above section on Descriptions](#
|
861
|
+
See the [above section on Descriptions](#Tool_descriptions) for more information on
|
649
862
|
how descriptions are rendered and word wrapped.
|
650
863
|
|
651
864
|
Because long descriptions may be unwieldly to write as a hash argument in this
|
@@ -662,7 +875,31 @@ way, Toys provides an alternate syntax for defining flags using a block.
|
|
662
875
|
For detailed info on configuring an flag using a block, see the
|
663
876
|
[Toys::DSL::Flag class](https://www.rubydoc.info/gems/toys-core/Toys/DSL/Flag).
|
664
877
|
|
665
|
-
#### Flag
|
878
|
+
#### Flag completions
|
879
|
+
|
880
|
+
Shell tab completion supports flag values, and flags can be configured to
|
881
|
+
present a set of completion candidates for themselves.
|
882
|
+
|
883
|
+
By default, a flag does not provide any completions for itself. To change that,
|
884
|
+
set the `completion` option. Currently there are three ways to set the
|
885
|
+
completion:
|
886
|
+
|
887
|
+
* Provide a static set of possible values, as an array of strings.
|
888
|
+
* Specify that values should be paths in the file system by setting the
|
889
|
+
symbol `:file_system`.
|
890
|
+
* Provide a `Proc` that returns possible values.
|
891
|
+
|
892
|
+
The following are two example flags, one that supports a static set of
|
893
|
+
completions and the other that supports file paths.
|
894
|
+
|
895
|
+
flag :language, "--lang=VAL", complete_values: ["ruby", "elixir", "rust"]
|
896
|
+
flag :path, "--path=VAL", complete_values: :file_system
|
897
|
+
|
898
|
+
Completions are somewhat related to acceptors, and it is a common pattern to
|
899
|
+
set both in concert. But they perform distinct functions. Acceptors affect
|
900
|
+
option parsing, whereas completions affect tab completion in the shell.
|
901
|
+
|
902
|
+
#### Flag groups
|
666
903
|
|
667
904
|
Flags may be organized into groups. This serves two functions:
|
668
905
|
|
@@ -699,8 +936,11 @@ The `all_required` directive is actually just shorthand for passing
|
|
699
936
|
flag :password, "--password=VAL", desc: "Set the password (required)"
|
700
937
|
end
|
701
938
|
|
702
|
-
|
939
|
+
The following are the supported types of flag groups:
|
703
940
|
|
941
|
+
* The `:required` type, which you can create using the directive
|
942
|
+
`all_required`. All flags from the group are required and must be provided
|
943
|
+
on the command line to avoid an error.
|
704
944
|
* The `:exactly_one` type, which you can create using the directive
|
705
945
|
`exactly_one_required`. Exactly one, and no more than one, flag from the
|
706
946
|
group must be provided on the command line to avoid an error.
|
@@ -710,6 +950,9 @@ There are several other types of flag groups:
|
|
710
950
|
* The `:at_least_one` type, which you can create using the directive
|
711
951
|
`at_least_one_required`. At least one flag from the group must be provided
|
712
952
|
on the command line to avoid an error.
|
953
|
+
* The `:optional` type is the default created using the directive
|
954
|
+
`flag_group` when no type is specified. Flags in the group are ordinary
|
955
|
+
optional flags.
|
713
956
|
|
714
957
|
Flag group types are useful for a variety of tools. For example, suppose you
|
715
958
|
are writing a tool that deploys an app to one of several different kinds of
|
@@ -724,11 +967,12 @@ configuration for your tool with a flag group:
|
|
724
967
|
end
|
725
968
|
|
726
969
|
def run
|
727
|
-
# Now exactly one of server, vm, or container will be set.
|
970
|
+
# Now exactly one of server, vm, or container will be set. The other
|
971
|
+
# two options will be their default value, nil.
|
728
972
|
end
|
729
973
|
end
|
730
974
|
|
731
|
-
### Tool
|
975
|
+
### Tool execution basics
|
732
976
|
|
733
977
|
When you run a tool from the command line, Toys will build the tool based on
|
734
978
|
its definition in a Toys file, and then it will attempt to execute it by
|
@@ -737,7 +981,7 @@ your tools.
|
|
737
981
|
|
738
982
|
Note: If you do not define the `run` method for a tool, Toys provides a default
|
739
983
|
implementation that displays the tool's help screen. This is typically used for
|
740
|
-
namespaces, as we shall see [below](#
|
984
|
+
namespaces, as we shall see [below](#Namespaces_and_subtools). Most tools,
|
741
985
|
however, should define `run`.
|
742
986
|
|
743
987
|
Let's revisit the "greet" example we covered earlier.
|
@@ -748,7 +992,7 @@ Let's revisit the "greet" example we covered earlier.
|
|
748
992
|
|
749
993
|
def run
|
750
994
|
greeting = "Hello, #{whom}!"
|
751
|
-
greeting.upcase
|
995
|
+
greeting = greeting.upcase if shout
|
752
996
|
puts greeting
|
753
997
|
end
|
754
998
|
end
|
@@ -759,12 +1003,12 @@ Ruby `$stdout`, `$stderr`, and `$stdin` streams.
|
|
759
1003
|
Note also how the `run` method can access values that were assigned by flags or
|
760
1004
|
positional arguments by just calling a method with that flag or argument name.
|
761
1005
|
When you declare a flag or argument, if the option name is a symbol that is a
|
762
|
-
valid Ruby method name, Toys will provide a method
|
763
|
-
|
1006
|
+
valid Ruby method name, Toys will provide a method that you can call to get the
|
1007
|
+
value. In the above example, `whom` and `shout` are such methods.
|
764
1008
|
|
765
|
-
If you create a flag or argument whose option name is not a symbol
|
1009
|
+
If you create a flag or argument whose option name is not a symbol *or* is not
|
766
1010
|
a valid method name, you can still get the value by calling the
|
767
|
-
[Toys::
|
1011
|
+
[Toys::Context#get](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:get)
|
768
1012
|
method. For example:
|
769
1013
|
|
770
1014
|
tool "greet" do
|
@@ -775,7 +1019,7 @@ method. For example:
|
|
775
1019
|
def run
|
776
1020
|
# We can access the "whom-to-greet" option using the "get" method.
|
777
1021
|
greeting = "Hello, #{get('whom-to-greet')}!"
|
778
|
-
greeting.upcase
|
1022
|
+
greeting = greeting.upcase if shout
|
779
1023
|
puts greeting
|
780
1024
|
end
|
781
1025
|
end
|
@@ -783,7 +1027,7 @@ method. For example:
|
|
783
1027
|
If a tool's `run` method finishes normally, Toys will exit with a result code
|
784
1028
|
of 0, indicating success. You may exit immediately and/or provide a nonzero
|
785
1029
|
result by calling the
|
786
|
-
[Toys::
|
1030
|
+
[Toys::Context#exit](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:exit)
|
787
1031
|
method:
|
788
1032
|
|
789
1033
|
def run
|
@@ -797,7 +1041,8 @@ exit with a nonzero code.
|
|
797
1041
|
|
798
1042
|
Finally, you may also define additional methods within the tool. These are
|
799
1043
|
available to be called by your `run` method, and can be used to decompose your
|
800
|
-
tool implementation.
|
1044
|
+
tool implementation. Indeed, a tool is actually a class under the hood, and
|
1045
|
+
you can define methods as with any other class. Here's a contrived example:
|
801
1046
|
|
802
1047
|
tool "greet-many" do
|
803
1048
|
# Support any number of arguments on the command line
|
@@ -807,7 +1052,7 @@ tool implementation. Here's a contrived example:
|
|
807
1052
|
# You can define helper methods like this.
|
808
1053
|
def greet(name)
|
809
1054
|
greeting = "Hello, #{name}!"
|
810
|
-
greeting.upcase
|
1055
|
+
greeting = greeting.upcase if shout
|
811
1056
|
puts greeting
|
812
1057
|
end
|
813
1058
|
|
@@ -822,14 +1067,14 @@ This should be enough to get you started implementing tools. A variety of
|
|
822
1067
|
additional features are available for your tool implementation and will be
|
823
1068
|
discussed further below. But first we will cover a few important topics.
|
824
1069
|
|
825
|
-
### Namespaces and
|
1070
|
+
### Namespaces and subtools
|
826
1071
|
|
827
1072
|
Like many command line frameworks, Toys supports **subtools**. You may, for
|
828
1073
|
example create a tool called "test" that runs your tests for a particular
|
829
1074
|
project, but you might also want "test unit" and "test integration" tools to
|
830
1075
|
run specific subsets of the test suite. One way to do this, of course, is for
|
831
|
-
the "test" tool to parse "unit" or "integration" as arguments. However,
|
832
|
-
|
1076
|
+
the "test" tool to parse "unit" or "integration" as arguments. However, it's
|
1077
|
+
often easier to define them as separate tools, subtools of "test".
|
833
1078
|
|
834
1079
|
To define a subtool, create nested `tool` directives. Here's a simple example:
|
835
1080
|
|
@@ -849,15 +1094,17 @@ To define a subtool, create nested `tool` directives. Here's a simple example:
|
|
849
1094
|
|
850
1095
|
You can now invoke them like this:
|
851
1096
|
|
852
|
-
toys test unit
|
853
|
-
|
1097
|
+
$ toys test unit
|
1098
|
+
run unit tests here...
|
1099
|
+
$ toys test integration
|
1100
|
+
run integration tests here...
|
854
1101
|
|
855
1102
|
Notice in this case, the parent "test" tool itself has no `run` method. This is
|
856
1103
|
a common pattern: "test" is just a "container" for tools, a way of organizing
|
857
1104
|
your tools. In Toys terminology, it is called a **namespace**. But it is still
|
858
1105
|
a tool, and it can still be run:
|
859
1106
|
|
860
|
-
toys test
|
1107
|
+
$ toys test
|
861
1108
|
|
862
1109
|
As discussed earlier, Toys provides a default implementation that displays the
|
863
1110
|
help screen, which includes a list of the subtools and their descriptions.
|
@@ -865,7 +1112,7 @@ help screen, which includes a list of the subtools and their descriptions.
|
|
865
1112
|
As another example, the "root" tool is also normally a namespace. If you just
|
866
1113
|
run Toys with no arguments:
|
867
1114
|
|
868
|
-
toys
|
1115
|
+
$ toys
|
869
1116
|
|
870
1117
|
The root tool will display the overall help screen for Toys.
|
871
1118
|
|
@@ -899,13 +1146,13 @@ implementation that lists all your tools.)
|
|
899
1146
|
Toys allows subtools to be nested arbitrarily deep. In practice, however, more
|
900
1147
|
than two or three levels of hierarchy can be confusing to use.
|
901
1148
|
|
902
|
-
## Understanding Toys
|
1149
|
+
## Understanding Toys files
|
903
1150
|
|
904
1151
|
Toys commands are defined in Toys files. We covered the basic syntax for these
|
905
|
-
files in the [above section on defining tools](#
|
1152
|
+
files in the [above section on defining tools](#Defining_tools). In this
|
906
1153
|
section, we will take a deeper look at what you can do with Toys files.
|
907
1154
|
|
908
|
-
### Toys
|
1155
|
+
### Toys directories
|
909
1156
|
|
910
1157
|
So far we have been defining tools by writing a Toys file named `.toys.rb`
|
911
1158
|
located in the current working directory. This works great if you have a small
|
@@ -947,8 +1194,8 @@ The contents of `greet.rb` would be:
|
|
947
1194
|
end
|
948
1195
|
|
949
1196
|
Notice that we did not use a `tool "greet"` block here. That is because the
|
950
|
-
name of the file `greet.rb` already provides a
|
951
|
-
that we are defining a "greet" tool.
|
1197
|
+
name of the file `greet.rb` already provides a naming context: Toys already
|
1198
|
+
knows that we are defining a "greet" tool.
|
952
1199
|
|
953
1200
|
If you do include a `tool` block inside the `greet.rb` file, it will create a
|
954
1201
|
*subtool* of `greet`. In other words, the path to the Ruby file defines a
|
@@ -974,7 +1221,7 @@ Once again, `test unit` is the "starting point" for tools defined in the
|
|
974
1221
|
file will define the `test unit` tool. Any `tool` blocks you add to that file
|
975
1222
|
will define subtools of `test unit`.
|
976
1223
|
|
977
|
-
#### Index
|
1224
|
+
#### Index files
|
978
1225
|
|
979
1226
|
The file name `.toys.rb` can also be used inside Toys directories and
|
980
1227
|
subdirectories. Such files are called **index files**, and they create tools
|
@@ -1007,9 +1254,9 @@ in the separate file `.toys/test/unit.rb`.
|
|
1007
1254
|
Toys also loads index files first before other files in the directory. This
|
1008
1255
|
means they are convenient places to define shared code that can be used by all
|
1009
1256
|
the subtools defined in that directory, as we shall see later in the
|
1010
|
-
[section on sharing code](#
|
1257
|
+
[section on sharing code](#Sharing_code).
|
1011
1258
|
|
1012
|
-
### The Toys
|
1259
|
+
### The Toys search path
|
1013
1260
|
|
1014
1261
|
So far we have seen how to define tools by writing a `.toys.rb` file in the
|
1015
1262
|
current directory, or by writing files inside a `.toys` directory in the
|
@@ -1018,14 +1265,16 @@ move to a different directory, they may not be available.
|
|
1018
1265
|
|
1019
1266
|
When Toys runs, it looks for tools in a **search path**. Specifically:
|
1020
1267
|
|
1021
|
-
|
1268
|
+
1. It looks for a `.toys.rb` file and/or a `.toys` directory in the *current
|
1022
1269
|
working directory*.
|
1023
|
-
|
1024
|
-
then its parent,
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1270
|
+
2. It does the same in the *parent directory* of the current directory, and
|
1271
|
+
then its parent, and so on until it hits either the root of the file system
|
1272
|
+
or one of the global directories described in (3).
|
1273
|
+
3. It looks in a list of *global directories*, specified in the environment
|
1274
|
+
variable `TOYS_PATH`. This variable can contain a colon-delimited list of
|
1275
|
+
directory paths. If the variable is not set, the current user's *home
|
1276
|
+
directory*, and the system configuration directory (`/etc` on unix systems)
|
1277
|
+
are used by default. Toys does *not* search parents of global directories.
|
1029
1278
|
|
1030
1279
|
It uses the *first* implementation that it finds for the requested tool. For
|
1031
1280
|
example, if the tool `greet` is defined in the `.toys.rb` file in the current
|
@@ -1048,40 +1297,31 @@ directory, and you also define it in the `.toys/greet.rb` file in the same
|
|
1048
1297
|
current directory, Toys will also report an error, since both are defined at
|
1049
1298
|
the same point (the current directory) in the search path.
|
1050
1299
|
|
1051
|
-
#### Global Tools
|
1052
|
-
|
1053
1300
|
Note that in the search path above, steps (1) and (2) are *context-dependent*.
|
1054
1301
|
That is, they may be different depending on what directory you are in. However,
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
By default, global tools are defined in your home directory and the system
|
1059
|
-
configuration directory. However, you can change this by defining the
|
1060
|
-
environment variable `TOYS_PATH`. This environment variable should contain a
|
1061
|
-
colon-delimited list of paths that should be searched for global tools. If you
|
1062
|
-
do define it, it replaces (3) and (4) with the paths you specify.
|
1302
|
+
step (3) is *not* context-dependent, and is searched regardless of where you
|
1303
|
+
are located. Tools defined here are **global**, available everywhere.
|
1063
1304
|
|
1064
|
-
## The
|
1305
|
+
## The execution environment
|
1065
1306
|
|
1066
1307
|
This section describes the context and resources available to your tool when it
|
1067
1308
|
is running; that is, what you can call from your tool's `run` method.
|
1068
1309
|
|
1069
|
-
|
1070
|
-
[Toys::
|
1071
|
-
defines a number of methods, and provides access to a variety of data and
|
1310
|
+
Each tool is defined as a class that subclasses
|
1311
|
+
[Toys::Context](https://www.rubydoc.info/gems/toys-core/Toys/Context). The base
|
1312
|
+
class defines a number of methods, and provides access to a variety of data and
|
1072
1313
|
objects relevant to your tool. We have already seen earlier how to use the
|
1073
|
-
[Toys::
|
1314
|
+
[Toys::Context#get](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:get)
|
1074
1315
|
method to retrieve option values, and how to use the
|
1075
|
-
[Toys::
|
1316
|
+
[Toys::Context#exit](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:exit)
|
1076
1317
|
method to exit immediately and return an exit code. Now we will cover other
|
1077
1318
|
resources available to your tool.
|
1078
1319
|
|
1079
|
-
### Built-in
|
1320
|
+
### Built-in context
|
1080
1321
|
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
[Toys::Tool#get method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:get)
|
1322
|
+
In addition to the options set by your tool's flags and command line arguments,
|
1323
|
+
a variety of other data and objects are also accessible using the
|
1324
|
+
[Toys::Context#get method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:get)
|
1085
1325
|
For example, you can get the full name of the tool being executed like this:
|
1086
1326
|
|
1087
1327
|
def run
|
@@ -1091,15 +1331,15 @@ For example, you can get the full name of the tool being executed like this:
|
|
1091
1331
|
The `TOOL_NAME` constant above is a well-known key that corresponds to the full
|
1092
1332
|
name (as an array of strings) of the running tool. A variety of well-known keys
|
1093
1333
|
are defined in the
|
1094
|
-
[Toys::
|
1334
|
+
[Toys::Context::Key module](https://www.rubydoc.info/gems/toys-core/Toys/Context/Key).
|
1095
1335
|
They include information about the current execution, such as the tool name and
|
1096
1336
|
the original command line arguments passed to it (before they were parsed).
|
1097
1337
|
They also include some internal Toys objects, which can be used to do things
|
1098
|
-
like write to the
|
1338
|
+
like write to the logger or look up and call other tools.
|
1099
1339
|
|
1100
1340
|
Most of the important context also can be accessed from convenience methods.
|
1101
1341
|
For example, the `TOOL_NAME` is also available from the
|
1102
|
-
[Toys::
|
1342
|
+
[Toys::Context#tool_name method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:tool_name):
|
1103
1343
|
|
1104
1344
|
def run
|
1105
1345
|
puts "Current tool is #{tool_name}"
|
@@ -1108,12 +1348,12 @@ For example, the `TOOL_NAME` is also available from the
|
|
1108
1348
|
Let's take a look at a few things your tool can do with the objects you can
|
1109
1349
|
access from built-in context.
|
1110
1350
|
|
1111
|
-
### Logging and
|
1351
|
+
### Logging and verbosity
|
1112
1352
|
|
1113
1353
|
Toys provides a Logger (a simple instance of the Ruby standard library logger
|
1114
1354
|
that writes to standard error) for your tool to use to report status
|
1115
1355
|
information. You can access this logger via the `LOGGER` context key, or the
|
1116
|
-
[Toys::
|
1356
|
+
[Toys::Context#logger method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:logger).
|
1117
1357
|
For example:
|
1118
1358
|
|
1119
1359
|
def run
|
@@ -1123,10 +1363,10 @@ For example:
|
|
1123
1363
|
The current logger level is controlled by the verbosity. Verbosity is an
|
1124
1364
|
integer context value that you can retrieve using the `VERBOSITY` context key
|
1125
1365
|
or the
|
1126
|
-
[Toys::
|
1366
|
+
[Toys::Context#verbosity method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:verbosity).
|
1127
1367
|
The verbosity is set to 0 by default. This corresponds to a logger level of
|
1128
1368
|
`WARN`. That is, warnings, errors, and fatals are displayed, while infos and
|
1129
|
-
debugs are not. However, [as we saw earlier](#
|
1369
|
+
debugs are not. However, [as we saw earlier](#Standard_flags), most tools
|
1130
1370
|
automatically respond to the `--verbose` and `--quiet` flags, (or `-v` and
|
1131
1371
|
`-q`), which increment and decrement the verbosity value, respectively. If you
|
1132
1372
|
run a tool with `-v`, the verbosity is incremented to 1, and the logger level
|
@@ -1134,11 +1374,11 @@ is set to `INFO`. If you set `-q`, the verbosity is decremented to -1, and the
|
|
1134
1374
|
logger level is set to `ERROR`. So by using the provided logger, a tool can
|
1135
1375
|
easily provide command line based control of the output verbosity.
|
1136
1376
|
|
1137
|
-
### Running
|
1377
|
+
### Running tools from tools
|
1138
1378
|
|
1139
1379
|
A common operation a tool might want to do is "call" another tool. This can be
|
1140
1380
|
done via the CLI object, which you can retrieve using the `CLI` key or the
|
1141
|
-
[Toys::
|
1381
|
+
[Toys::Context#cli method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:cli).
|
1142
1382
|
These return the current instance of
|
1143
1383
|
[Toys::CLI](https://www.rubydoc.info/gems/toys-core/Toys/CLI) which is the
|
1144
1384
|
"main" interface to Toys. In particular, it provides the
|
@@ -1155,15 +1395,15 @@ execute, and return a process status code (i.e. 0 for success, and nonzero for
|
|
1155
1395
|
error). Make sure you handle the exit status. For example, in most cases, you
|
1156
1396
|
should probably exit if the tool you are calling returns a nonzero code.
|
1157
1397
|
|
1158
|
-
You may also use the `exec` mixin [described below](#
|
1398
|
+
You may also use the `exec` mixin [described below](#Executing_subprocesses) to
|
1159
1399
|
run a tool in a separate process. This is particularly useful if you need to
|
1160
1400
|
capture or manipulate that tool's input or output stream.
|
1161
1401
|
|
1162
|
-
### Helper
|
1402
|
+
### Helper methods and mixins
|
1163
1403
|
|
1164
|
-
The methods of [Toys::
|
1404
|
+
The methods of [Toys::Context](https://www.rubydoc.info/gems/toys-core/Toys/Context)
|
1165
1405
|
are not the only methods available for your tool to call. We
|
1166
|
-
[saw earlier](#
|
1406
|
+
[saw earlier](#Tool_execution_basics) that a tool can define additional methods
|
1167
1407
|
that you can use as helpers.
|
1168
1408
|
|
1169
1409
|
You can also include **mixins**, which are modules that bring in a whole set of
|
@@ -1188,9 +1428,9 @@ We will look at a few examples of the use of these mixins below. Built-in
|
|
1188
1428
|
mixins have names that are symbols.
|
1189
1429
|
|
1190
1430
|
You can also define your own mixins, as we will see in the
|
1191
|
-
[upcoming section on defining mixins](#
|
1431
|
+
[upcoming section on defining mixins](#Defining_mixins).
|
1192
1432
|
|
1193
|
-
### Executing
|
1433
|
+
### Executing subprocesses
|
1194
1434
|
|
1195
1435
|
Another common operation you might do in a tool is to execute other binaries.
|
1196
1436
|
For example, you might write a tool that shells out to `scp` to copy files to
|
@@ -1199,8 +1439,8 @@ a remote server.
|
|
1199
1439
|
Ruby itself provides a few convenient methods for simple execution, such as the
|
1200
1440
|
[Kernel#system](http://ruby-doc.org/core/Kernel.html#method-i-system) method.
|
1201
1441
|
However, these typically provide limited ability to control or interact with
|
1202
|
-
subprocess streams, and you need to remember to handle the exit status
|
1203
|
-
yourself. If you do want to exert
|
1442
|
+
subprocess streams, and you also need to remember to handle the exit status
|
1443
|
+
yourself. If you do want to exert more control over subprocesses, you can use
|
1204
1444
|
[Process.spawn](http://ruby-doc.org/core/Process.html#method-c-spawn), or a
|
1205
1445
|
higher-level wrapper such as the
|
1206
1446
|
[open3 library](http://ruby-doc.org/stdlib/libdoc/open3/rdoc/index.html).
|
@@ -1225,7 +1465,7 @@ For more information, see the
|
|
1225
1465
|
and the underyling library
|
1226
1466
|
[Toys::Utils::Exec](https://www.rubydoc.info/gems/toys-core/Toys/Utils/Exec).
|
1227
1467
|
|
1228
|
-
### Formatting
|
1468
|
+
### Formatting output
|
1229
1469
|
|
1230
1470
|
Interacting with the user is a very common function of a command line tool, and
|
1231
1471
|
many modern tools include intricately designed and styled output, and terminal
|
@@ -1236,7 +1476,7 @@ First, there is `:terminal`, which provides some basic terminal features such
|
|
1236
1476
|
as styled output and simple spinners. For information, see the
|
1237
1477
|
[Toys::StandardMixins::Terminal mixin module](https://www.rubydoc.info/gems/toys-core/Toys/StandardMixins/Terminal)
|
1238
1478
|
and the underyling library
|
1239
|
-
[Toys::
|
1479
|
+
[Toys::Terminal](https://www.rubydoc.info/gems/toys-core/Toys/Terminal).
|
1240
1480
|
|
1241
1481
|
If you prefer the venerable Highline library interface, Toys provides a mixin
|
1242
1482
|
called `:highline` that automatically installs the highline gem (version 2.x)
|
@@ -1246,9 +1486,9 @@ more information, see the
|
|
1246
1486
|
|
1247
1487
|
You may also use other third-party gems such as
|
1248
1488
|
[tty](https://github.com/piotrmurach/tty). The section below on
|
1249
|
-
[useful gems](#
|
1489
|
+
[useful gems](#Useful_gems) provides some examples.
|
1250
1490
|
|
1251
|
-
## Sharing
|
1491
|
+
## Sharing code
|
1252
1492
|
|
1253
1493
|
As you accumulate additional and more complex tools, you may find that some of
|
1254
1494
|
your tools need to share some common configuration, data, or logic. You might,
|
@@ -1257,9 +1497,9 @@ authentication. This section describes several techniques for sharing code
|
|
1257
1497
|
between tools, and describes the scope of Ruby structures, such as methods,
|
1258
1498
|
classes, and constants, that you might define in your tools.
|
1259
1499
|
|
1260
|
-
### Defining
|
1500
|
+
### Defining mixins
|
1261
1501
|
|
1262
|
-
We [saw earlier](#
|
1502
|
+
We [saw earlier](#Helper_methods_and_mixins) that you can mix a module (with
|
1263
1503
|
all its methods) into your tool using the `include` directive. You can specify
|
1264
1504
|
a module itself, or the name of a built-in mixin such as `:exec` or
|
1265
1505
|
`:terminal`. But you can also define your own mixin using the `mixin`
|
@@ -1274,7 +1514,7 @@ includes the mixin, in the same way that you can include a Ruby module.
|
|
1274
1514
|
|
1275
1515
|
(Note that, unlike full modules, mixins allow only methods to be shared. Mixins
|
1276
1516
|
do not support constants. See the next section on
|
1277
|
-
[using constants](#
|
1517
|
+
[using constants](#Using_constants) to learn how Toys handles constants.)
|
1278
1518
|
|
1279
1519
|
Here's an example. Suppose you had common setup code that you wanted to share
|
1280
1520
|
among your testing tools.
|
@@ -1312,15 +1552,15 @@ define a mixin in a file located in a `.toys` directory, it will be visible to
|
|
1312
1552
|
descendant tools defined in that same directory, but not in a different `.toys`
|
1313
1553
|
directory.
|
1314
1554
|
|
1315
|
-
A common technique, for example, would be to define a mixin in the
|
1316
|
-
in a Toys directory. You can then include it from
|
1317
|
-
files in that same directory.
|
1555
|
+
A common technique, for example, would be to define a mixin in the
|
1556
|
+
[index file](#Index_files) in a Toys directory. You can then include it from
|
1557
|
+
any subtools defined in other files in that same directory.
|
1318
1558
|
|
1319
1559
|
#### Mixin initializers
|
1320
1560
|
|
1321
1561
|
Sometimes a mixin will want to initialize some state before the tool executes.
|
1322
1562
|
For example, the `:highline` mixin creates an instance of Highline during tool
|
1323
|
-
initialization. To do so, provide
|
1563
|
+
initialization. To do so, provide an `on_initialize` block in the mixin block.
|
1324
1564
|
The initializer block is called within the context of the tool before it
|
1325
1565
|
initializes, so it has access to the tool's built-in context and options.
|
1326
1566
|
|
@@ -1333,8 +1573,9 @@ pass a value to the mixin's initializer:
|
|
1333
1573
|
|
1334
1574
|
tool "test" do
|
1335
1575
|
mixin "common_test_code" do
|
1336
|
-
# Initialize the mixin, and receive the argument passed to
|
1337
|
-
|
1576
|
+
# Initialize the mixin, and receive the argument passed to the
|
1577
|
+
# include directive
|
1578
|
+
on_initialize do |type|
|
1338
1579
|
# Initializers are called in the context of the tool, and so can
|
1339
1580
|
# affect the tool's state.
|
1340
1581
|
set(:test_type, type)
|
@@ -1363,7 +1604,7 @@ pass a value to the mixin's initializer:
|
|
1363
1604
|
end
|
1364
1605
|
end
|
1365
1606
|
|
1366
|
-
### Using
|
1607
|
+
### Using constants
|
1367
1608
|
|
1368
1609
|
You can define and use Ruby constants, i.e. names beginning with a capital
|
1369
1610
|
letter, in a Toys file. However, they are subject to Ruby's rules regarding
|
@@ -1375,7 +1616,7 @@ Toys file), it is important to understand how they work.
|
|
1375
1616
|
Constants in Toys are visible only within the Toys file in which they are
|
1376
1617
|
defined. They normally behave as though they are defined at the "top level" of
|
1377
1618
|
the file. Even if you define a constant lexically "inside" a tool or a mixin,
|
1378
|
-
the constant does
|
1619
|
+
the constant does *not* end up connected to that tool or mixin; it is defined
|
1379
1620
|
at the file level.
|
1380
1621
|
|
1381
1622
|
tool "test" do
|
@@ -1397,7 +1638,8 @@ at the file level.
|
|
1397
1638
|
end
|
1398
1639
|
|
1399
1640
|
(Note it is still possible to attach constants to a tool or mixin by defining
|
1400
|
-
them with `self::`. However, this
|
1641
|
+
them with `self::`. However, this is uncommon Ruby practice and is mildly
|
1642
|
+
discouraged.)
|
1401
1643
|
|
1402
1644
|
Because of this, it is highly recommended that you define constants only at the
|
1403
1645
|
top level of a Toys file, so it doesn't "look" like it is scoped to something
|
@@ -1437,11 +1679,11 @@ constants, and thus follow the same rules. So you could, for example, define a
|
|
1437
1679
|
The difference between this technique and using the `mixin` directive we saw
|
1438
1680
|
earlier, is the scope. The module here is accessed via a constant, and so, like
|
1439
1681
|
any constant, it is visible only in the same file it is defined in. The `mixin`
|
1440
|
-
directive creates mixins that are visible from
|
1682
|
+
directive creates mixins that are visible from *all* files at the same point in
|
1441
1683
|
the search path.
|
1442
1684
|
|
1443
1685
|
Not also, when you define a mixin in this way, you should include `Toys::Mixin`
|
1444
|
-
in the module, as illustrated above. This makes `
|
1686
|
+
in the module, as illustrated above. This makes `on_initialize` available in
|
1445
1687
|
the module.
|
1446
1688
|
|
1447
1689
|
### Templates
|
@@ -1481,11 +1723,11 @@ development, including templates that generate build, test, and documentation
|
|
1481
1723
|
tools. The `:minitest` template illustrated above is one of these built-in
|
1482
1724
|
templates. Like built-in mixins, built-in template names are always symbols.
|
1483
1725
|
You can read more about them in the next section on using
|
1484
|
-
[Toys as a Rake replacement](#
|
1726
|
+
[Toys as a Rake replacement](#Toys_as_a_Rake_replacement).
|
1485
1727
|
|
1486
1728
|
You may also write your own templates. Here's how...
|
1487
1729
|
|
1488
|
-
#### Defining
|
1730
|
+
#### Defining templates
|
1489
1731
|
|
1490
1732
|
One way to define a template is to use the `template` directive. Like the
|
1491
1733
|
`mixin` directive, this creates a named template that you can access inside the
|
@@ -1502,7 +1744,7 @@ Following is a simple template example:
|
|
1502
1744
|
attr_accessor :name
|
1503
1745
|
attr_accessor :whom
|
1504
1746
|
|
1505
|
-
|
1747
|
+
on_expand do |template|
|
1506
1748
|
tool template.name do
|
1507
1749
|
desc "A greeting tool generated from a template"
|
1508
1750
|
to_run do
|
@@ -1521,15 +1763,15 @@ will typically have a constructor, and methods to access configuration
|
|
1521
1763
|
properties. When the template is expanded, the class gets instantiated, and you
|
1522
1764
|
can set those properties.
|
1523
1765
|
|
1524
|
-
Next, a template has
|
1766
|
+
Next, a template has an `on_expand` block. This block contains the Toys file
|
1525
1767
|
directives that should be generated by the template. The template object is
|
1526
1768
|
passed to the block, so it can access the template configuration when
|
1527
1769
|
generating directives. The "greet" template in the above example generates a
|
1528
1770
|
tool whose name is set by the template's `name` property.
|
1529
1771
|
|
1530
|
-
Notice that in the above example, we used `to_run do`, providing a
|
1772
|
+
Notice that in the above example, we used `to_run do`, providing a *block* for
|
1531
1773
|
the tool's execution, rather than `def run`, providing a method. Both forms are
|
1532
|
-
valid and will work in a template (as well in a normal Toys file), but the
|
1774
|
+
valid and will work in a template (as well as in a normal Toys file), but the
|
1533
1775
|
block form is often useful in a template because you can access the `template`
|
1534
1776
|
variable inside the block, whereas it would not be accessible if you defined a
|
1535
1777
|
method. Similarly, if your template generates helper methods, and the body of
|
@@ -1543,7 +1785,7 @@ properties. In this way, when you expand the template, options can be provided
|
|
1543
1785
|
either as arguments to the `expand` directive, or in a block passed to the
|
1544
1786
|
directive by setting properties on the template object.
|
1545
1787
|
|
1546
|
-
#### Template
|
1788
|
+
#### Template classes
|
1547
1789
|
|
1548
1790
|
Finally, templates are classes, and you can create a template directly as a
|
1549
1791
|
class by including the
|
@@ -1560,7 +1802,7 @@ in your class definition.
|
|
1560
1802
|
attr_accessor :name
|
1561
1803
|
attr_accessor :whom
|
1562
1804
|
|
1563
|
-
|
1805
|
+
on_expand do |template|
|
1564
1806
|
tool template.name do
|
1565
1807
|
desc "A greeting tool generated from a template"
|
1566
1808
|
to_run do
|
@@ -1576,7 +1818,8 @@ Remember that classes created this way are constants, and so the name
|
|
1576
1818
|
`GreetTemplate` is available only inside the Toys file where it was defined.
|
1577
1819
|
|
1578
1820
|
You must `include Toys::Template` if you define a template directly as a class,
|
1579
|
-
but you can omit it if you use the `template` directive to define the template
|
1821
|
+
but you can omit it if you use the `template` directive to define the template
|
1822
|
+
in a block.
|
1580
1823
|
|
1581
1824
|
Defining templates as classes is also a useful way for third-party gems to
|
1582
1825
|
provide Toys integration. For example, suppose you are writing a code analysis
|
@@ -1588,7 +1831,7 @@ following in their Toys file:
|
|
1588
1831
|
require "my_analysis"
|
1589
1832
|
expand MyAnalysis::ToysTemplate
|
1590
1833
|
|
1591
|
-
### Preloading Ruby
|
1834
|
+
### Preloading Ruby files
|
1592
1835
|
|
1593
1836
|
For more complicated tools, you might want to write normal Ruby modules and
|
1594
1837
|
classes as helpers. Toys provides a way to write Ruby code outside of its DSL
|
@@ -1599,7 +1842,7 @@ tools are defined. You can use such files to define Ruby classes, modules, and
|
|
1599
1842
|
other code that may be used and shared by your tools.
|
1600
1843
|
|
1601
1844
|
To use preloaded files, you must define your tools inside a
|
1602
|
-
[Toys directory](#
|
1845
|
+
[Toys directory](#Toys_directories). Before any tools inside a directory are
|
1603
1846
|
loaded, any file named `.preload.rb` in the directory is automatically
|
1604
1847
|
required. Additionally, any Ruby files inside a subdirectory called `.preload`
|
1605
1848
|
are also automatically required.
|
@@ -1641,10 +1884,10 @@ first before loading any of the tools in the `test` subdirectory. Thus, any
|
|
1641
1884
|
additional classes needed by `test unit` can be defined in these files.
|
1642
1885
|
|
1643
1886
|
Either a single `.preload.rb` file or a `.preload` directory, or both, may be
|
1644
|
-
used. If both are present, the `.preload.rb` file is
|
1645
|
-
`.preload` directory contents.
|
1887
|
+
used. If both are present in the same directory, the `.preload.rb` file is
|
1888
|
+
loaded first before the `.preload` directory contents.
|
1646
1889
|
|
1647
|
-
## Using
|
1890
|
+
## Using third-party gems
|
1648
1891
|
|
1649
1892
|
The Ruby community has developed many resources for building command line
|
1650
1893
|
tools, including a variety of gems that provide alternate command line parsing,
|
@@ -1654,12 +1897,17 @@ forth.
|
|
1654
1897
|
|
1655
1898
|
This section describes how to use a third-party gem in your tool.
|
1656
1899
|
|
1657
|
-
### Activating
|
1900
|
+
### Activating gems
|
1658
1901
|
|
1659
|
-
The toys
|
1660
|
-
no other gem dependencies. However, if you want to use a third-party gem in
|
1902
|
+
The toys executable itself uses only two gems: **toys** and **toys-core**. It
|
1903
|
+
has no other gem dependencies. However, if you want to use a third-party gem in
|
1661
1904
|
your tool, Toys provides a convenient mechanism to ensure the gem is installed.
|
1662
1905
|
|
1906
|
+
(Note that you generally do not use bundler when running Toys; i.e. you do not
|
1907
|
+
normally run `bundle exec toys`. This is because Toys is intended as a
|
1908
|
+
general-purpose tool that can be run anywhere. It would be inconvenient to have
|
1909
|
+
to include Gemfiles in every directory where you might want to run it.)
|
1910
|
+
|
1663
1911
|
To access the gem services, include the `:gems` mixin. This mixin adds a `gem`
|
1664
1912
|
directive to ensure a gem is installed and activated when you're defining a
|
1665
1913
|
tool, and a `gem` method to ensure a gem is available when you're running a
|
@@ -1686,6 +1934,8 @@ defined.
|
|
1686
1934
|
end
|
1687
1935
|
def run
|
1688
1936
|
# ...
|
1937
|
+
end
|
1938
|
+
end
|
1689
1939
|
|
1690
1940
|
Here's an example tool that just runs `rake`. Because it requires rake to be
|
1691
1941
|
installed in order to *run* the tool, we call the
|
@@ -1708,8 +1958,13 @@ exception is raised.
|
|
1708
1958
|
If you are not in the Toys DSL context—for example from a class-based
|
1709
1959
|
mixin—you should use
|
1710
1960
|
[Toys::Utils::Gems.activate](https://www.rubydoc.info/gems/toys-core/Toys%2FUtils%2FGems.activate)
|
1711
|
-
instead.
|
1961
|
+
instead. (Note that you must `require "toys/utils/gems"` explicitly before
|
1962
|
+
invoking the
|
1963
|
+
[Toys::Utils::Gems](https://www.rubydoc.info/gems/toys-core/Toys/Utils/Gems)
|
1964
|
+
class because, like all classes under `Toys::Utils`, Toys does not load it
|
1965
|
+
automatically.) For example:
|
1712
1966
|
|
1967
|
+
require "toys/utils/gems"
|
1713
1968
|
Toys::Utils::Gems.activate("highline", "~> 2.0")
|
1714
1969
|
|
1715
1970
|
Note these methods are a bit different from the
|
@@ -1717,7 +1972,7 @@ Note these methods are a bit different from the
|
|
1717
1972
|
provided by Rubygems. The Toys version attempts to install a missing gem for
|
1718
1973
|
you, whereas Rubygems will just throw an exception.
|
1719
1974
|
|
1720
|
-
### Useful
|
1975
|
+
### Useful gems
|
1721
1976
|
|
1722
1977
|
Now that you know how to ensure a gem is installed, let's look at some third-
|
1723
1978
|
party gems that you might find useful when writing tools.
|
@@ -1790,14 +2045,14 @@ non-deterministic.
|
|
1790
2045
|
A variety of other useful gems can also be found in
|
1791
2046
|
[this article](https://lab.hookops.com/ruby-cli-gems.html).
|
1792
2047
|
|
1793
|
-
## Toys as a Rake
|
2048
|
+
## Toys as a Rake replacement
|
1794
2049
|
|
1795
2050
|
Toys was designed to organize scripts that may be "scoped" to a project or
|
1796
2051
|
directory. Rake is also commonly used for this purpose: you can write a
|
1797
2052
|
"Rakefile" that defines rake tasks scoped to a directory. In many cases, Toys
|
1798
2053
|
can be used as a replacement for Rake. Indeed, the Toys repository itself
|
1799
|
-
contains a `.toys.rb` file instead of a Rakefile for running tests, builds,
|
1800
|
-
so forth.
|
2054
|
+
contains a `.toys.rb` file instead of a Rakefile, for running tests, builds,
|
2055
|
+
and so forth.
|
1801
2056
|
|
1802
2057
|
This section will explore the differences between Toys and Rake, and describe
|
1803
2058
|
how to use Toys for some of the things traditionally done with Rake.
|
@@ -1808,7 +2063,7 @@ Although Toys and Rake serve many of the same use cases, they have very
|
|
1808
2063
|
different design goals, and it is useful to understand them.
|
1809
2064
|
|
1810
2065
|
Rake's design is based on the classic "make" tool often provided in unix
|
1811
|
-
development environments. This design focuses on
|
2066
|
+
development environments. This design focuses on *targets* and *dependencies*,
|
1812
2067
|
and is meant for a world where you invoke an external compiler tool whenever
|
1813
2068
|
changes are made to an individual source file or any of its dependencies. This
|
1814
2069
|
"declarative" approach expresses very well the build process for programs
|
@@ -1818,8 +2073,8 @@ Ruby, however, does not have an external compiler, and certainly not one that
|
|
1818
2073
|
requires separate invocation for each source file as does the C compiler. So
|
1819
2074
|
although Rake does support file dependencies, they are much less commonly used
|
1820
2075
|
than in their Makefile cousins. Instead, in practice, most Rake tasks are not
|
1821
|
-
connected to a dependency at all; they are simply standalone
|
1822
|
-
be called "phony" targets in Makefile
|
2076
|
+
connected to a dependency at all; they are simply standalone scripts, what
|
2077
|
+
would be called "phony" targets in a Makefile. Such tasks are more imperative
|
1823
2078
|
than declarative.
|
1824
2079
|
|
1825
2080
|
The Toys approach to build tools simply embraces the fact that our build
|
@@ -1831,7 +2086,7 @@ For example, Rake provides a primitive mechanism for passing arguments to a
|
|
1831
2086
|
task, but it is clumsy and quite different from most unix programs. However, to
|
1832
2087
|
do otherwise would clash with Rake's design goal of treating tasks as targets
|
1833
2088
|
and dependencies. Toys does not have those design goals, so it is able to
|
1834
|
-
embrace the familiar
|
2089
|
+
embrace the familiar unix conventions for command line arguments.
|
1835
2090
|
|
1836
2091
|
Toys actually borrows some of its design from the "mix" build tool used for
|
1837
2092
|
Elixir and Erlang programs. Unlike C, the Erlang and Elixir compilers do their
|
@@ -1844,7 +2099,7 @@ your build tasks. Rake will continue to be your friend in those cases. However,
|
|
1844
2099
|
for imperative tasks such as "run my tests", "build my YARD documentation", or
|
1845
2100
|
"release my gem", you may find Toys easier to use.
|
1846
2101
|
|
1847
|
-
### Using Toys to
|
2102
|
+
### Using Toys to invoke Rake tasks
|
1848
2103
|
|
1849
2104
|
If you've already written a Rakefile for your project, Toys provides a
|
1850
2105
|
convenient way to invoke your existing Rake tasks using Toys. The built-in
|
@@ -1857,16 +2112,16 @@ following contents:
|
|
1857
2112
|
# In .toys.rb
|
1858
2113
|
expand :rake
|
1859
2114
|
|
1860
|
-
Now within that directory, if you had a task called `test`, you can invoke
|
1861
|
-
with:
|
2115
|
+
Now within that directory, if you had a Rake task called `test`, you can invoke
|
2116
|
+
it with:
|
1862
2117
|
|
1863
|
-
toys test
|
2118
|
+
$ toys test
|
1864
2119
|
|
1865
|
-
Similarly, a task named `test:integration` can be invoked with either of
|
1866
|
-
following:
|
2120
|
+
Similarly, a Rake task named `test:integration` can be invoked with either of
|
2121
|
+
the following:
|
1867
2122
|
|
1868
|
-
toys test integration
|
1869
|
-
toys test:integration
|
2123
|
+
$ toys test integration
|
2124
|
+
$ toys test:integration
|
1870
2125
|
|
1871
2126
|
Rake tasks with arguments are mapped to tool arguments, making it easier to
|
1872
2127
|
invoke those tasks using Toys. For example, consider a Rake task with two
|
@@ -1880,12 +2135,12 @@ arguments, defined as follows:
|
|
1880
2135
|
|
1881
2136
|
would have to be invoked as follows using rake:
|
1882
2137
|
|
1883
|
-
rake my_task[value1,value2]
|
2138
|
+
$ rake my_task[value1,value2]
|
1884
2139
|
|
1885
2140
|
You may even need to escape the brackets if you are using a shell that treats
|
1886
2141
|
them specially. Toys will let you pass them as normal command line arguments:
|
1887
2142
|
|
1888
|
-
toys my_task value1 value2
|
2143
|
+
$ toys my_task value1 value2
|
1889
2144
|
|
1890
2145
|
The `:rake` template provides several options. If your Rakefile is named
|
1891
2146
|
something other than `Rakefile` or isn't in the current directory, you can
|
@@ -1903,9 +2158,9 @@ arguments. Set `:use_flags` when expanding the template:
|
|
1903
2158
|
Now with this option, to pass arguments to the tool, use the argument names as
|
1904
2159
|
flags:
|
1905
2160
|
|
1906
|
-
toys my_task --first=value1 --second=value2
|
2161
|
+
$ toys my_task --first=value1 --second=value2
|
1907
2162
|
|
1908
|
-
### From Rakefiles to Toys
|
2163
|
+
### From Rakefiles to Toys files
|
1909
2164
|
|
1910
2165
|
Invoking Rake tasks using Toys is an easy first step, but eventually you will
|
1911
2166
|
likely want to migrate some of your project's build tasks from Rake to Toys.
|
@@ -1933,7 +2188,7 @@ Toys to generate common build tools.
|
|
1933
2188
|
Note that Rakefiles and Toys files can coexist in the same directory, so you
|
1934
2189
|
can use either or both tools, depending on your needs.
|
1935
2190
|
|
1936
|
-
### Running
|
2191
|
+
### Running tests
|
1937
2192
|
|
1938
2193
|
Toys provides a built-in template called `:minitest` for running unit tests
|
1939
2194
|
with [minitest](https://github.com/seattlerb/minitest). The following example
|
@@ -1963,7 +2218,7 @@ tool called `rubocop`:
|
|
1963
2218
|
See the {Toys::Templates::Rubocop} documentation for details on the available
|
1964
2219
|
options.
|
1965
2220
|
|
1966
|
-
### Building and
|
2221
|
+
### Building and releasing gems
|
1967
2222
|
|
1968
2223
|
The `:gem_build` built-in template can generate a variety of build and release
|
1969
2224
|
tools for gems, and is a useful alternative to the Rake tasks provided by
|
@@ -1994,7 +2249,7 @@ example:
|
|
1994
2249
|
See the {Toys::Templates::Clean} documentation for details on the various
|
1995
2250
|
options for clean.
|
1996
2251
|
|
1997
|
-
### Building
|
2252
|
+
### Building documentation
|
1998
2253
|
|
1999
2254
|
Toys provides an `:rdoc` template for creating tools that generate RDoc
|
2000
2255
|
documentation, and a `:yardoc` template for creating tools that generate YARD.
|
@@ -2006,7 +2261,7 @@ Here's an example for YARD, creating a tool called `yardoc`:
|
|
2006
2261
|
|
2007
2262
|
expand :yardoc, protected: true, markup: "markdown"
|
2008
2263
|
|
2009
|
-
### Gem
|
2264
|
+
### Gem example
|
2010
2265
|
|
2011
2266
|
Let's look at a complete example that combines the techniques above to provide
|
2012
2267
|
all the basic tools for a Ruby gem. It includes:
|
@@ -2021,9 +2276,9 @@ all the basic tools for a Ruby gem. It includes:
|
|
2021
2276
|
actually build) the documentation for warnings and completeness.
|
2022
2277
|
|
2023
2278
|
Below is the full annotated `.toys.rb` file. For many gems, you could drop this
|
2024
|
-
into the gem source repo with minimal or no modifications. Indeed, it is
|
2025
|
-
|
2026
|
-
|
2279
|
+
into the gem source repo with minimal or no modifications. Indeed, it is very
|
2280
|
+
similar to the Toys files provided for the **toys** and **toys-core** gems
|
2281
|
+
themselves.
|
2027
2282
|
|
2028
2283
|
# This file is .toys.rb
|
2029
2284
|
|
@@ -2051,14 +2306,16 @@ gems themselves.
|
|
2051
2306
|
# The normal "build" tool that just builds a gem into the pkg directory.
|
2052
2307
|
expand :gem_build
|
2053
2308
|
|
2309
|
+
# An "install" tool that builds the gem and installs it locally.
|
2310
|
+
expand :gem_build, name: "install", install_gem: true
|
2311
|
+
|
2054
2312
|
# A full gem "release" tool that builds the gem, and pushes it to rubygems.
|
2055
2313
|
# This assumes your local rubygems configuration is set up with the proper
|
2056
2314
|
# credentials.
|
2057
2315
|
expand :gem_build, name: "release", push_gem: true
|
2058
2316
|
|
2059
2317
|
# Now we create a full CI tool. It runs the test, rubocop, and yardoc tools
|
2060
|
-
# and checks for errors. This tool could be invoked from
|
2061
|
-
# similar CI system.
|
2318
|
+
# and checks for errors. This tool could be invoked from a CI system.
|
2062
2319
|
tool "ci" do
|
2063
2320
|
# The :exec mixin provides the exec_tool() method that we will use to run
|
2064
2321
|
# other tools and check their exit status.
|
@@ -2088,7 +2345,7 @@ gems themselves.
|
|
2088
2345
|
end
|
2089
2346
|
end
|
2090
2347
|
|
2091
|
-
## Advanced
|
2348
|
+
## Advanced tool definition techniques
|
2092
2349
|
|
2093
2350
|
This section covers some additional features that are often useful for writing
|
2094
2351
|
tools. I've labeled them "advanced", but all that really means is that this
|
@@ -2112,7 +2369,7 @@ To define an alias, use the `alias_tool` directive:
|
|
2112
2369
|
alias_tool "t", "test"
|
2113
2370
|
|
2114
2371
|
You may create an alias of a subtool, but the alias must have the same parent
|
2115
|
-
(namespace)
|
2372
|
+
(namespace) as the target tool. For example:
|
2116
2373
|
|
2117
2374
|
tool "gem" do
|
2118
2375
|
tool "test" do
|
@@ -2123,7 +2380,7 @@ You may create an alias of a subtool, but the alias must have the same parent
|
|
2123
2380
|
alias_tool "t", "test"
|
2124
2381
|
end
|
2125
2382
|
|
2126
|
-
### Custom
|
2383
|
+
### Custom acceptors
|
2127
2384
|
|
2128
2385
|
We saw earlier that flags and positional arguments can have acceptors, which
|
2129
2386
|
control the allowed format, and may also convert the string argument to a Ruby
|
@@ -2167,7 +2424,7 @@ A common technique, for example, would be to define an acceptor in the index
|
|
2167
2424
|
file in a Toys directory. You can then include it from any subtools defined in
|
2168
2425
|
other files in that same directory.
|
2169
2426
|
|
2170
|
-
### Controlling
|
2427
|
+
### Controlling built-in flags
|
2171
2428
|
|
2172
2429
|
Earlier we saw that certain flags are added automatically to every tool:
|
2173
2430
|
`--verbose`, `--quiet`, `--help`, and so forth. You may occasionally want to
|
@@ -2178,14 +2435,14 @@ the flag as you choose. Flags explicitly defined by your tool take precedence
|
|
2178
2435
|
over the built-ins.
|
2179
2436
|
|
2180
2437
|
For example, normally two built-in flags are provided to decrease the verbosity
|
2181
|
-
level: `-q` and `--quiet`. If you define `-q` yourself
|
2182
|
-
a "quick" mode
|
2438
|
+
level: `-q` and `--quiet`. If you define `-q` yourself (for example to activate
|
2439
|
+
a "quick" mode) then `-q` will be repurposed for your flag, but `--quiet` will
|
2183
2440
|
still be present to decrease verbosity.
|
2184
2441
|
|
2185
2442
|
# Repurposes -q to set the "quick" option instead of "quiet"
|
2186
2443
|
flag :quick, "-q"
|
2187
2444
|
|
2188
|
-
You may also completely disable a flag, and
|
2445
|
+
You may also completely disable a flag, and *not* repurpose it, using the
|
2189
2446
|
`disable_flag` directive. It lets you mark one or more flags as "never use".
|
2190
2447
|
|
2191
2448
|
For example, if you disable the `-q` flag, then `-q` will no longer be a
|
@@ -2198,7 +2455,83 @@ completely disable decreasing the verbosity, disable both `-q` and `--quiet`.
|
|
2198
2455
|
# Completely disables decreasing verbosity
|
2199
2456
|
disable_flag "-q", "--quiet"
|
2200
2457
|
|
2201
|
-
###
|
2458
|
+
### Enforcing flags before args
|
2459
|
+
|
2460
|
+
By default, tools allow flags and positional arguments to be interspersed when
|
2461
|
+
command line arguments are parsed. This matches the behavior of most common
|
2462
|
+
command line binaries.
|
2463
|
+
|
2464
|
+
However, some tools prefer to follow the convention that all flags must appear
|
2465
|
+
first, followed by positional arguments. In such a tool, once a non-flag
|
2466
|
+
argument appears on the command line, all remaining arguments are treated as
|
2467
|
+
positional, even if they look like a flag and start with a hyphen.
|
2468
|
+
|
2469
|
+
You may configure a tool to follow this alternate parsing strategy using the
|
2470
|
+
`enforce_flags_before_args` directive.
|
2471
|
+
|
2472
|
+
The built-in tool `toys do` is an example of a tool that does this. It
|
2473
|
+
recognizes its own flags (such as `--help` and `--delim`) but once positional
|
2474
|
+
arguments start appearing, it wants further flags to be treated as positional
|
2475
|
+
so it can pass them down to the different steps it is executing. Here is a
|
2476
|
+
simplified excerpt from the implementation that tool:
|
2477
|
+
|
2478
|
+
tool "do" do
|
2479
|
+
flag :delim, default: ","
|
2480
|
+
remaining_args :commands # the commands to execute
|
2481
|
+
enforce_flags_before_args
|
2482
|
+
def run
|
2483
|
+
# Now commands includes both the commands to run and
|
2484
|
+
# the "flags" to pass to them.
|
2485
|
+
commands.each do
|
2486
|
+
# ...
|
2487
|
+
end
|
2488
|
+
end
|
2489
|
+
end
|
2490
|
+
|
2491
|
+
### Requiring exact flag matches
|
2492
|
+
|
2493
|
+
By default, tools will recognized "shortened" forms of long flags. For example,
|
2494
|
+
most suppose you are defining a tool with long flags:
|
2495
|
+
|
2496
|
+
tool "my-tool" do
|
2497
|
+
flag :long_flag_name, "--long-flag-name"
|
2498
|
+
flag :another_long_flag, "--another-long-flag"
|
2499
|
+
def run
|
2500
|
+
# ...
|
2501
|
+
end
|
2502
|
+
end
|
2503
|
+
|
2504
|
+
When you invoke this tool, you do not need to type the entire flag names.
|
2505
|
+
Abbreviations will also work:
|
2506
|
+
|
2507
|
+
$ toys my-tool --long --an
|
2508
|
+
|
2509
|
+
As long as the abbreviation is unambiguous (i.e. there is no other flag that
|
2510
|
+
begins with the same string), the Toys argument parser will recognize the flag.
|
2511
|
+
This is consistent with the behavior of most command line tools (and is also
|
2512
|
+
the behavior of Ruby's OptionParser library.)
|
2513
|
+
|
2514
|
+
However, it is possible to disable this behavior and require that flags be
|
2515
|
+
presented in their entirety, using the `require_exact_flag_match` directive.
|
2516
|
+
|
2517
|
+
tool "my-tool" do
|
2518
|
+
require_exact_flag_match
|
2519
|
+
flag :long_flag_name, "--long-flag-name"
|
2520
|
+
flag :another_long_flag, "--another-long-flag"
|
2521
|
+
def run
|
2522
|
+
# ...
|
2523
|
+
end
|
2524
|
+
end
|
2525
|
+
|
2526
|
+
Now, all flags for this tool must be presented in their entirety. Abbreviations
|
2527
|
+
are not allowed.
|
2528
|
+
|
2529
|
+
$ toys my-tool --long-flag-name --another-long-flag
|
2530
|
+
|
2531
|
+
Currently you can require exact flag matches only at the tool level, applied to
|
2532
|
+
all flags for that tool. You cannot set this option for individual flags.
|
2533
|
+
|
2534
|
+
### Disabling argument parsing
|
2202
2535
|
|
2203
2536
|
Normally Toys handles parsing command line arguments for you. This makes
|
2204
2537
|
writing tools easier, and also allows Toys to generate documentation
|
@@ -2211,7 +2544,7 @@ To disable argument parsing, use the `disable_argument_parsing` directive. This
|
|
2211
2544
|
directive disables parsing and validation of flags and positional arguments.
|
2212
2545
|
(Thus, it is incompatible with specifying any flags or arguments for the tool.)
|
2213
2546
|
Instead, you can retrieve the raw arguments using the
|
2214
|
-
[Toys::
|
2547
|
+
[Toys::Context#args method](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:args).
|
2215
2548
|
|
2216
2549
|
Here is an example that wraps calls to git:
|
2217
2550
|
|
@@ -2224,24 +2557,123 @@ Here is an example that wraps calls to git:
|
|
2224
2557
|
end
|
2225
2558
|
end
|
2226
2559
|
|
2227
|
-
###
|
2560
|
+
### Handling interrupts
|
2561
|
+
|
2562
|
+
If you interrupt a running tool, say, by hitting `CTRL`-`C`, Toys will normally
|
2563
|
+
terminate execution and display the message `INTERRUPTED` on the standard error
|
2564
|
+
stream.
|
2565
|
+
|
2566
|
+
If your tool needs to handle interrupts itself, you have several options. You
|
2567
|
+
can rescue the `Interrupt` exception or call `Signal.trap`. Or you can provide
|
2568
|
+
an *interrupt handler* in your tool using the `on_interrupt` directive. This
|
2569
|
+
directive either provides a block to handle interrupts, or designates a named
|
2570
|
+
method as the handler. If an interrupt handler is present, Toys will handle
|
2571
|
+
interrupts as follows:
|
2572
|
+
|
2573
|
+
1. Toys will terminate the tool's `run` method by raising an `Interrupt`
|
2574
|
+
exception. Any `ensure` blocks will be called.
|
2575
|
+
2. Toys will call the interrupt handler. If this method or block takes an
|
2576
|
+
argument, Toys will pass it the `Interrupt` exception object.
|
2577
|
+
3. The interrupt handler is then responsible for tool execution from that
|
2578
|
+
point. It may terminate execution by returning or calling `exit`, or it may
|
2579
|
+
restart or resume processing (perhaps by calling the `run` method again).
|
2580
|
+
Or it may invoke the normal Toys interrupt handling (i.e. terminating
|
2581
|
+
execution, displaying the message `INTERRUPTED`) by re-raising *the same*
|
2582
|
+
interrupt exception object.
|
2583
|
+
4. If another interrupt takes place during the execution of the interrupt
|
2584
|
+
handler, Toys will terminate it by raising a *second* `Interrupt` exception
|
2585
|
+
(calling any `ensure` blocks). Then, the interrupt handler will be called
|
2586
|
+
*again* and passed the new exception. Any additional interrupts will be
|
2587
|
+
handled similarly.
|
2588
|
+
|
2589
|
+
Because the interrupt handler is called again even if it is itself interrupted,
|
2590
|
+
you might consider detecting this case if your interrupt handler might be
|
2591
|
+
long-running. You can tell how many interrupts have taken place by looking at
|
2592
|
+
the `Exception#cause` property of the exception. The first interrupt will have
|
2593
|
+
a cause of `nil`. The second interrupt (i.e. the interrupt raised the first
|
2594
|
+
time the interrupt handler is itself interrupted) will have its cause point to
|
2595
|
+
the first interrupt (which in turn has a `nil` cause.) The third interrupt's
|
2596
|
+
cause will point to the second interrupt, and so on. So you can determine the
|
2597
|
+
interrupt "depth" by counting the length of the cause chain.
|
2598
|
+
|
2599
|
+
Here is an example that performs a long-running task. The first two times the
|
2600
|
+
task is interrupted, it is restarted. The third time, it is terminated.
|
2601
|
+
|
2602
|
+
tool "long-running" do
|
2603
|
+
def long_task(is_restart)
|
2604
|
+
puts "task #{is_restart ? 're' : ''}starting..."
|
2605
|
+
sleep 10
|
2606
|
+
puts "task finished!"
|
2607
|
+
end
|
2608
|
+
|
2609
|
+
def run
|
2610
|
+
long_task(false)
|
2611
|
+
end
|
2612
|
+
|
2613
|
+
on_interrupt do |ex|
|
2614
|
+
# The third interrupt will have a non-nil ex.cause.cause.
|
2615
|
+
# At that time, just give up and re-raise the exception, which causes
|
2616
|
+
# it to propagate out and invoke the standard Toys interrupt handler.
|
2617
|
+
raise ex if ex.cause&.cause
|
2618
|
+
# Otherwise, restart the long task.
|
2619
|
+
long_task(true)
|
2620
|
+
end
|
2621
|
+
end
|
2622
|
+
|
2623
|
+
### Handling usage errors
|
2624
|
+
|
2625
|
+
Normally, if Toys detects a usage error (such as an unrecognized flag) while
|
2626
|
+
parsing arguments, it will respond by aborting the tool and displaying the
|
2627
|
+
usage error. It is possible to override this behavior by providing your own
|
2628
|
+
usage error handler using the `on_usage_error` directive. This directive either
|
2629
|
+
provides a block to handle usage errors, or designates a named method as the
|
2630
|
+
handler.
|
2631
|
+
|
2632
|
+
If your handler block or method takes a parameter, Toys will pass it the array
|
2633
|
+
of usage errors. Otherwise, you can get the array by calling
|
2634
|
+
[Toys::Context#usage_errors](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:usage_errors).
|
2635
|
+
This array will provide you with a list of the usage errors encountered.
|
2636
|
+
|
2637
|
+
You can also get information about the arguments that could not be parsed from
|
2638
|
+
the context. For example, the list of unrecognized flags is available from the
|
2639
|
+
context key `UNMATCHED_FLAGS`.
|
2640
|
+
|
2641
|
+
One common technique is to redirect usage errors back to the `run` method. In
|
2642
|
+
this way, `run` is called regardless of whether argument parsing succeeded or
|
2643
|
+
failed.
|
2644
|
+
|
2645
|
+
tool "lenient-parser" do
|
2646
|
+
flag :abc
|
2647
|
+
|
2648
|
+
on_usage_error :run
|
2649
|
+
|
2650
|
+
def run
|
2651
|
+
if usage_errors.empty?
|
2652
|
+
puts "Usage was correct"
|
2653
|
+
else
|
2654
|
+
puts "Usage was not correct"
|
2655
|
+
end
|
2656
|
+
end
|
2657
|
+
end
|
2658
|
+
|
2659
|
+
### Data files
|
2228
2660
|
|
2229
2661
|
If your tools require images, archives, keys, or other such static data, Toys
|
2230
2662
|
provides a convenient place to put data files that can be looked up by tools
|
2231
2663
|
either during definition or runtime.
|
2232
2664
|
|
2233
2665
|
To use data files, you must define your tools inside a
|
2234
|
-
[Toys directory](#
|
2666
|
+
[Toys directory](#Toys_directories). Within the Toys directory, create a
|
2235
2667
|
directory named `.data` and copy your data files there.
|
2236
2668
|
|
2237
2669
|
You may then "find" a data file by providing the relative path to the file from
|
2238
2670
|
the `.data` directory. When defining a tool, use the
|
2239
2671
|
[Toys::DSL::Tool#find_data](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:find_data)
|
2240
2672
|
directive in a Toys file. Or, at tool execution time, call
|
2241
|
-
[Toys::
|
2673
|
+
[Toys::Context#find_data](https://www.rubydoc.info/gems/toys-core/Toys%2FContext:find_data)
|
2242
2674
|
(which is a convenience method for getting the tool source object using the
|
2243
2675
|
`TOOL_SOURCE` key, and calling
|
2244
|
-
[Toys::
|
2676
|
+
[Toys::SourceInfo#find_data](https://www.rubydoc.info/gems/toys-core/Toys%2FSourceInfo:find_data)
|
2245
2677
|
on it). In either case, `find_data` locates a matching file (or directory)
|
2246
2678
|
among the data files, and returns the full path to that file system object. You
|
2247
2679
|
may then read the file or perform any other operation on it.
|
@@ -2321,7 +2753,7 @@ If, however, you find `greeting.txt` from a tool under `test`, it will still
|
|
2321
2753
|
find the more general `.toys/.data/greeting.txt` file because there is no
|
2322
2754
|
overriding file under `.toys/test/.data`.
|
2323
2755
|
|
2324
|
-
### The
|
2756
|
+
### The context directory
|
2325
2757
|
|
2326
2758
|
The **context directory** for a tool is the directory containing the toplevel
|
2327
2759
|
`.toys.rb` file or the `.toys` directory within which the tool is defined. It
|
@@ -2352,8 +2784,8 @@ This tool assumes it will be run from the main project directory (`my-project`
|
|
2352
2784
|
in the above case). However, Toys lets you invoke tools even if you are in a
|
2353
2785
|
subdirectory:
|
2354
2786
|
|
2355
|
-
cd lib
|
2356
|
-
toys list-tests # Does not work
|
2787
|
+
$ cd lib
|
2788
|
+
$ toys list-tests # Does not work
|
2357
2789
|
|
2358
2790
|
Rake handles this by actually changing the current working directory to the
|
2359
2791
|
directory containing the active Rakefile. Toys, however, does not change the
|
@@ -2388,10 +2820,10 @@ the entire toys directory structure. So if your tool definition is inside a
|
|
2388
2820
|
|
2389
2821
|
This behavior is particularly useful for build tools. Indeed, all the build
|
2390
2822
|
tools described in the section on
|
2391
|
-
[Toys as a Rake Replacement](#
|
2823
|
+
[Toys as a Rake Replacement](#Toys_as_a_Rake_replacement) automatically move
|
2392
2824
|
into the context directory when they execute.
|
2393
2825
|
|
2394
|
-
#### Changing the
|
2826
|
+
#### Changing the context directory
|
2395
2827
|
|
2396
2828
|
It is even possible to modify the context directory, causing tools that use the
|
2397
2829
|
context directory (such as the standard build tools) to run in a different
|
@@ -2450,7 +2882,7 @@ context directory to `my-repo/gem1`. Here's what that could look like:
|
|
2450
2882
|
# etc.
|
2451
2883
|
end
|
2452
2884
|
|
2453
|
-
### Hidden
|
2885
|
+
### Hidden tools
|
2454
2886
|
|
2455
2887
|
Tools whose name begins with an underscore (e.g. `_foo`) are called "hidden"
|
2456
2888
|
tools. They can be executed the same as any other tool, but are normally
|
@@ -2461,12 +2893,12 @@ the implementation of other tools.
|
|
2461
2893
|
If you pass the `--all` flag when displaying help, the help screen will include
|
2462
2894
|
hidden tools in the subtools list.
|
2463
2895
|
|
2464
|
-
## Toys
|
2896
|
+
## Toys administration using the system tools
|
2465
2897
|
|
2466
2898
|
Toys comes with a few built-in tools, including some that let you administer
|
2467
2899
|
Toys itself. These tools live in the `system` namespace.
|
2468
2900
|
|
2469
|
-
### Getting the Toys
|
2901
|
+
### Getting the Toys version
|
2470
2902
|
|
2471
2903
|
You can get the current version of Toys by running:
|
2472
2904
|
|
@@ -2489,39 +2921,69 @@ installation if it is not already current.
|
|
2489
2921
|
Normall it asks you for confirmation before downloading. To disable interactive
|
2490
2922
|
confirmation, pass the `--yes` flag.
|
2491
2923
|
|
2492
|
-
A similar effect can of course be obtained
|
2924
|
+
A similar effect can of course be obtained by running `gem install toys`.
|
2925
|
+
|
2926
|
+
### Installing tab completion for Bash
|
2927
|
+
|
2928
|
+
Toys provides tab completion for the bash shell, and lets tools customize the
|
2929
|
+
completions for their arguments. However, you need to install the Toys
|
2930
|
+
completion tool into your shell. The following command sets up tab completion
|
2931
|
+
the current shell:
|
2932
|
+
|
2933
|
+
$(toys system bash-completion install)
|
2934
|
+
|
2935
|
+
Typically, you will want to include the above in your `.bashrc` or other bash
|
2936
|
+
initialization file.
|
2937
|
+
|
2938
|
+
By default, this associates the Toys tab completion logic with the `toys`
|
2939
|
+
executable. If you have other names or aliases for the executable, pass them as
|
2940
|
+
arguments. For example, I use `t` as an alias for `toys`, and I therefore
|
2941
|
+
install Toys's completion logic for `t`:
|
2942
|
+
|
2943
|
+
$(toys system bash-completion install t)
|
2944
|
+
|
2945
|
+
You can also remove the completion logic from the current shell:
|
2946
|
+
|
2947
|
+
$(toys system bash-completion remove)
|
2948
|
+
$(toys system bash-completion remove t)
|
2949
|
+
|
2950
|
+
At this time, bash is the only shell that is supported directly. If you are
|
2951
|
+
using zsh, however, you can use the `bashcompinit` function to load the toys
|
2952
|
+
bash completion (as well as other bash-based completions). This *mostly* works,
|
2953
|
+
with a few caveats. Native zsh completion is on the future roadmap.
|
2493
2954
|
|
2494
|
-
## Writing
|
2955
|
+
## Writing your own CLI using Toys
|
2495
2956
|
|
2496
2957
|
Although Toys is not primarily designed to help you write a custom command-line
|
2497
|
-
|
2958
|
+
executable, you can use it in that way. Toys is factored into two gems:
|
2498
2959
|
**toys-core**, which includes all the underlying machinery for creating
|
2499
|
-
command-line
|
2500
|
-
provides the `toys`
|
2501
|
-
write your own command line
|
2502
|
-
**toys-core** gem and configure your
|
2960
|
+
command-line executables, and **toys**, which is really just a wrapper that
|
2961
|
+
provides the `toys` executable itself and its built-in commands and behavior.
|
2962
|
+
To write your own command line executable based on the Toys system, just
|
2963
|
+
require the **toys-core** gem and configure your executable the way you want.
|
2503
2964
|
|
2504
2965
|
Toys-Core is modular and lets you customize much of the behavior of a command
|
2505
|
-
line
|
2966
|
+
line executable, simply by setting options or adding plugins. For example:
|
2506
2967
|
|
2507
2968
|
* Toys itself automatically adds a number of flags, such as `--verbose` and
|
2508
|
-
`--help`, to each tool. Toys-Core lets you customize what flags
|
2509
|
-
automatically added for your own command line
|
2510
|
-
* Toys itself provides a default way to run tools that have no `run` method
|
2511
|
-
|
2969
|
+
`--help`, to each tool. Toys-Core lets you customize what flags (if any)
|
2970
|
+
are automatically added for your own command line executable.
|
2971
|
+
* Toys itself provides a default way to run tools that have no `run` method.
|
2972
|
+
It assumes such tools are namespaces, and displays the online help screen.
|
2512
2973
|
Toys-Core lets you provide an alternate default run method for your own
|
2513
|
-
command line
|
2974
|
+
command line executable.
|
2514
2975
|
* Toys itself provides several built-in tools, such as `do`, and `system`.
|
2515
|
-
Toys-Core lets you write your own command line
|
2516
|
-
tools.
|
2976
|
+
Toys-Core lets you write your own command line executable with its own
|
2977
|
+
built-in tools.
|
2517
2978
|
* Toys itself implements a particular search path for user-provided Toys
|
2518
2979
|
files, and looks for specific file and directory names such as `.toys.rb`.
|
2519
2980
|
Toys-Core lets you change the search path, the file/directory names, or
|
2520
2981
|
disable user-provided Toys files altogether for your own command line
|
2521
|
-
|
2522
|
-
tools, and can ship with only built-in tools.
|
2523
|
-
* Toys itself has a particular way of displaying online help and
|
2524
|
-
errors. Toys-Core lets your own command line
|
2982
|
+
executable. Indeed, most command line executables do not need
|
2983
|
+
user-customizable tools, and can ship with only built-in tools.
|
2984
|
+
* Toys itself has a particular way of displaying online help and reporting
|
2985
|
+
errors. Toys-Core lets your own command line executable customize these and
|
2986
|
+
many other features.
|
2525
2987
|
|
2526
2988
|
For more information, see the
|
2527
2989
|
[Toys-Core documentation](https://www.rubydoc.info/gems/toys-core/).
|