toys 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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/).
|