toys 0.3.7.1 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +19 -22
- data/builtins/system.rb +1 -1
- data/docs/guide.md +830 -105
- data/lib/toys.rb +5 -0
- data/lib/toys/standard_cli.rb +6 -1
- data/lib/toys/templates/clean.rb +85 -0
- data/lib/toys/templates/gem_build.rb +125 -0
- data/lib/toys/templates/minitest.rb +125 -0
- data/lib/toys/templates/rubocop.rb +86 -0
- data/lib/toys/templates/yardoc.rb +101 -0
- data/lib/toys/version.rb +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9e8109a98db77604b1b398e113a6c8dff058645fe36f63dd08c013e92c06330
|
4
|
+
data.tar.gz: 9c706a637f851fa847fcc125b706499132d2fbdbbde12aaa6327f60bf95a1e3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6695774bf47169ccb18aa06fe45164229e75c8d20fcca24e79187d4f3e747b79adc80a24974dd8d7c0c2c3f4077225ce4362ee0d408bcb7e7213313ded926dfc
|
7
|
+
data.tar.gz: e04182467471e21017b659b0b5366f9850a42b61b350edc8c46472d171dad4db244e03e4abac76fe1328beab9fcfbb72c97f7ba135939c6acc1524003ecce9c2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.3.8 / 2018-06-10
|
4
|
+
|
5
|
+
* CHANGED: Renamed helpers to mixins.
|
6
|
+
* CHANGED: Renamed :in_from, :out_to, and :err_to exec options to :in, :out, :err
|
7
|
+
* IMPROVED: Exec raises an error if passed an unknown option.
|
8
|
+
* IMPROVED: Exec now accepts nearly all the same stream specifications as Process#spawn.
|
9
|
+
|
3
10
|
### 0.3.7.1 / 2018-05-30
|
4
11
|
|
5
12
|
* FIXED: Fix crash in system update.
|
data/README.md
CHANGED
@@ -30,10 +30,11 @@ tasks, can be invoked and passed arguments using familiar unix command line
|
|
30
30
|
arguments and flags. The Toys github repo itself comes with Toys configs
|
31
31
|
instead of Rakefiles.
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
Unlike most command line frameworks, Toys is *not primarily* designed to help
|
34
|
+
you build and ship a custom command line binary written in Ruby. However, you
|
35
|
+
*can* use it in that way by building witn the "toys-core" API, available as a
|
36
|
+
separate gem. For more info on using toys-core, see
|
37
|
+
https://ruby-doc.info/gems/toys-core
|
37
38
|
|
38
39
|
## Quick Start
|
39
40
|
|
@@ -85,7 +86,7 @@ The tool also recognizes a flag on the command line. Try this:
|
|
85
86
|
Toys provides a rich set of features for defining command line arguments and
|
86
87
|
flags. It can also validate arguments. Try this:
|
87
88
|
|
88
|
-
toys greet --
|
89
|
+
toys greet --bye
|
89
90
|
|
90
91
|
Notice that Toys automatically generated a usage summary for your tool. It can
|
91
92
|
also automatically generate a full help screen, which you can view using the
|
@@ -95,22 +96,19 @@ also automatically generate a full help screen, which you can view using the
|
|
95
96
|
|
96
97
|
### Next steps
|
97
98
|
|
98
|
-
You can add any number of additional tools to your `.toys.rb` file. Note
|
99
|
-
that the tools you create in
|
100
|
-
and its subdirectories. If you move
|
101
|
-
|
102
|
-
projects.
|
99
|
+
You can add any number of additional tools to your `.toys.rb` config file. Note
|
100
|
+
also that the tools you create in the config file are available only in this
|
101
|
+
directory and its subdirectories. If you move into a different directory tree,
|
102
|
+
Toys will instead look for a config file in that directory. Thus, you can
|
103
|
+
define tools scoped to particular projects. You can also define "global" tools
|
104
|
+
by creating a `.toys.rb` config in your home directory.
|
103
105
|
|
104
|
-
Toys
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
includes a simple library that you can use to produce styled output and basic
|
111
|
-
console-based interfaces, and another library that makes it easy to spawn and
|
112
|
-
control subprocesses. You can also take advantage of a variety of third-party
|
113
|
-
libraries such as Highline and TTY.
|
106
|
+
Toys provides a rich set of useful libraries for writing tools and subtools. It
|
107
|
+
gives you a logger and automatically provides flags to control verbosity of log
|
108
|
+
output. It includes a simple library that you can use to produce styled output
|
109
|
+
and basic console-based interfaces, and another library that makes it easy to
|
110
|
+
spawn and control subprocesses. You can also take advantage of a variety of
|
111
|
+
third-party libraries such as Highline and TTY.
|
114
112
|
|
115
113
|
For a more detailed look at Toys, see the
|
116
114
|
{file:docs/tutorial.md Extended Tutorial} and the
|
@@ -123,8 +121,7 @@ highly experimental, and the code is evolving very rapidly. Please contact the
|
|
123
121
|
author before embarking on a major pull request. More detailed contribution
|
124
122
|
guidelines will be provided when the software stabilizes further.
|
125
123
|
|
126
|
-
The source can be found on Github at
|
127
|
-
[https://github.com/dazuma/toys](https://github.com/dazuma/toys)
|
124
|
+
The source can be found on Github at https://github.com/dazuma/toys
|
128
125
|
|
129
126
|
## License
|
130
127
|
|
data/builtins/system.rb
CHANGED
@@ -65,7 +65,7 @@ tool "update" do
|
|
65
65
|
result = terminal.spinner(leading_text: "Installing Toys version #{latest_version}... ",
|
66
66
|
final_text: "Done.\n") do
|
67
67
|
exec(["gem", "install", "toys", "--version", latest_version.to_s],
|
68
|
-
|
68
|
+
out: :capture, err: :capture)
|
69
69
|
end
|
70
70
|
if result.error?
|
71
71
|
puts(result.captured_out + result.captured_err)
|
data/docs/guide.md
CHANGED
@@ -8,38 +8,45 @@ reporting, logging, help text, and many other details for you. Toys is designed
|
|
8
8
|
for software developers, IT specialists, and other power users who want to
|
9
9
|
write and organize scripts to automate their workflows.
|
10
10
|
|
11
|
+
Unlike most command line frameworks, Toys is *not primarily* designed to help
|
12
|
+
you build and ship a custom command line binary written in Ruby. Rather, it
|
13
|
+
provides a single multi-command binary called `toys`. You configure this binary
|
14
|
+
by writing configuration files that define the commands that Toys recongizes.
|
15
|
+
(You can, however, build your own custom command line binary using the separate
|
16
|
+
**toys-core** library.)
|
17
|
+
|
11
18
|
This user's guide covers everything you need to know to use Toys effectively.
|
12
19
|
|
13
20
|
## Conceptual overview
|
14
21
|
|
15
22
|
Toys is a command line *framework*. It provides a binary called `toys` along
|
16
23
|
with basic functions such as argument parsing and online help. You provide the
|
17
|
-
actual behavior of the
|
24
|
+
actual behavior of the Toys binary by writing **Toys files**.
|
18
25
|
|
19
|
-
Toys is a multi-command binary. You may define
|
26
|
+
Toys is a multi-command binary. You may define any number of commands, called
|
20
27
|
**tools**, which can be invoked by passing the tool name as an argument to the
|
21
28
|
`toys` binary. Tools are arranged in a hierarchy; a tool may be a **namespace**
|
22
|
-
that has
|
29
|
+
that has **subtools**.
|
23
30
|
|
24
31
|
Tools may recognize command line arguments in the form of **flags** and
|
25
32
|
**positional arguments**. Flags can optionally take **values**, while
|
26
33
|
positional arguments may be **required** or **optional**.
|
27
34
|
|
28
|
-
The configuration of a tool may
|
35
|
+
The configuration of a tool may include **descriptions**, for the tool itself,
|
29
36
|
and for each command line argument. These descriptions are displayed in the
|
30
37
|
tool's **online help** screen. Descriptions come in **long** and **short**
|
31
38
|
forms, which appear in different styles of help.
|
32
39
|
|
33
|
-
Toys searches for
|
34
|
-
|
35
|
-
|
40
|
+
Toys searches for tools in specifically-named **Toys files** and **Toys
|
41
|
+
directories**. It searches for these in the current directory, its ancestors,
|
42
|
+
and in the Toys **search path**.
|
36
43
|
|
37
44
|
Toys provides various features to help you write tools. This includes providing
|
38
45
|
a **logger** for each tool, **helper modules** that provide common functions a
|
39
|
-
tool can call, and **templates** which are prefabricated tools you can
|
40
|
-
your
|
46
|
+
tool can call, and **templates** which are prefabricated tools that you can
|
47
|
+
configure for your needs.
|
41
48
|
|
42
|
-
Finally, Toys provides
|
49
|
+
Finally, Toys provides useful **built-in behavior**, including automatically
|
43
50
|
providing flags to display help screens and set verbosity. It also includes a
|
44
51
|
built-in namespace of **system tools** that let you inspect and configure the
|
45
52
|
Toys system itself.
|
@@ -55,10 +62,10 @@ The general form of the `toys` command line is:
|
|
55
62
|
|
56
63
|
### Tools
|
57
64
|
|
58
|
-
The
|
59
|
-
argument that begins with a hyphen (which is interpreted as a
|
65
|
+
The **tool name** consists of all the command line arguments until the first
|
66
|
+
argument that begins with a hyphen (which is interpreted as a **flag**), until
|
60
67
|
no tool with that name exists (in which case the argument is treated as the
|
61
|
-
first
|
68
|
+
first **positional argument**), or until there are no more arguments.
|
62
69
|
|
63
70
|
For example, in the following command:
|
64
71
|
|
@@ -67,31 +74,31 @@ For example, in the following command:
|
|
67
74
|
|
68
75
|
The tool name is `system version`. Notice that the tool name may have multiple
|
69
76
|
words. Tools are arranged hierarchically. In this case, `system` is a
|
70
|
-
|
71
|
-
|
77
|
+
**namespace** for tools related to the Toys system, and `version` is one of its
|
78
|
+
**subtools**. It prints the current Toys version.
|
72
79
|
|
73
80
|
In the following command:
|
74
81
|
|
75
82
|
|TOOL| |ARG|
|
76
|
-
toys system
|
83
|
+
toys system frodo
|
77
84
|
|
78
|
-
There is no subtool `
|
79
|
-
until it finds an existing tool. In this case, the `system` namespace
|
80
|
-
does exist, so
|
81
|
-
argument
|
85
|
+
There is no subtool `frodo` under the `system` namespace, so Toys works
|
86
|
+
backward until it finds an existing tool. In this case, the `system` namespace
|
87
|
+
itself does exist, so Toys runs *it* as the tool, and passes it `frodo` as an
|
88
|
+
argument.
|
82
89
|
|
83
90
|
Namespaces such as `system` are themselves tools and can be executed like any
|
84
|
-
other tool. In the above case,
|
85
|
-
|
86
|
-
|
91
|
+
other tool. In the above case, it takes the argument `frodo`, determines it has
|
92
|
+
no subtool of that name, and prints an error message. Most commonly, though,
|
93
|
+
you might execute a namespace without arguments:
|
87
94
|
|
88
95
|
toys system
|
89
96
|
|
90
|
-
This displays the
|
97
|
+
This displays the **online help screen** for the `system` namespace, which
|
91
98
|
includes a list of all its subtools and what they do.
|
92
99
|
|
93
|
-
It is also legitimate for the tool name to be empty. This invokes the
|
94
|
-
tool
|
100
|
+
It is also legitimate for the tool name to be empty. This invokes the **root
|
101
|
+
tool**, the toplevel namespace:
|
95
102
|
|
96
103
|
toys
|
97
104
|
|
@@ -100,30 +107,30 @@ of its subtools.
|
|
100
107
|
|
101
108
|
One last example:
|
102
109
|
|
103
|
-
toys
|
110
|
+
toys frodo
|
104
111
|
|
105
|
-
If there is no tool called `
|
106
|
-
`
|
107
|
-
by printing an error message that the `
|
112
|
+
If there is no tool called `frodo` in the toplevel namespace, then once again,
|
113
|
+
`frodo` is interpreted as an argument to the root tool. The root tool responds
|
114
|
+
by printing an error message that the `frodo` tool does not exist.
|
108
115
|
|
109
116
|
### Flags
|
110
117
|
|
111
|
-
Flags are generally arguments that begin with a hyphen, and are used to set
|
118
|
+
**Flags** are generally arguments that begin with a hyphen, and are used to set
|
112
119
|
options for a tool.
|
113
120
|
|
114
121
|
Each tool recognizes a specific set of flags. If you pass an unknown flag to a
|
115
122
|
tool, the tool will generally display an error message.
|
116
123
|
|
117
|
-
Toys follows the typical unix conventions for flags
|
118
|
-
|
119
|
-
hyphen
|
120
|
-
examples.
|
124
|
+
Toys follows the typical unix conventions for flags, specifically those covered
|
125
|
+
by Ruby's OptionParser library. You can provide short (single-character) flags
|
126
|
+
with a single hyphen, or long flags with a double hyphen. You can also provide
|
127
|
+
optional **values** for flags. Following are a few examples.
|
121
128
|
|
122
129
|
Pass a single short flag (for verbose output).
|
123
130
|
|
124
131
|
toys -v
|
125
132
|
|
126
|
-
Pass multiple long flags (verbose output
|
133
|
+
Pass multiple long flags (for verbose output and recursive subtool search).
|
127
134
|
|
128
135
|
toys --verbose --recursive
|
129
136
|
|
@@ -150,7 +157,7 @@ arguments, even if they begin with hyphens. For example:
|
|
150
157
|
|
151
158
|
That will cause `--recursive` to be treated as a positional argument. (In this
|
152
159
|
case, as we saw earlier, the root tool will respond by printing an error
|
153
|
-
message that no
|
160
|
+
message that no tool named `--recursive` exists.)
|
154
161
|
|
155
162
|
Note that a single hyphen by itself `-` is not considered a flag, nor does it
|
156
163
|
disable flag parsing. It is treated as a normal positional argument.
|
@@ -188,59 +195,61 @@ Finally, the root tool also supports:
|
|
188
195
|
### Positional Arguments
|
189
196
|
|
190
197
|
Any arguments not recognized as flags or flag arguments, are interpreted as
|
191
|
-
positional arguments
|
192
|
-
required or optional.
|
198
|
+
**positional arguments**. Positional arguments are recognized in order and may
|
199
|
+
be required or optional.
|
193
200
|
|
194
201
|
Each tool recognizes a specific set of positional arguments. If you do not pass
|
195
202
|
a value for a required argument, or you pass too many arguments, the tool will
|
196
203
|
generally display an error message.
|
197
204
|
|
198
|
-
For example, the `do` tool runs multiple tools in sequence. It
|
199
|
-
number of positional arguments. Those arguments specify which
|
200
|
-
what arguments to pass to them. If, for example, you had a
|
201
|
-
`test` tool, you could run them in sequence with:
|
205
|
+
For example, the built-in `do` tool runs multiple tools in sequence. It
|
206
|
+
recognizes any number of positional arguments. Those arguments specify which
|
207
|
+
tools to run and what arguments to pass to them. If, for example, you had a
|
208
|
+
`build` tool and a `test` tool, you could run them in sequence with:
|
202
209
|
|
203
210
|
|---ARGS---|
|
204
211
|
toys do build , test
|
205
212
|
|
206
|
-
The arguments `build`, `,`, and `test` are positional arguments to the
|
207
|
-
tool. (The `do` tool
|
213
|
+
The three arguments `build`, `,`, and `test` are positional arguments to the
|
214
|
+
`do` tool. (The `do` tool uses `,` to delimit the tools that it should run.)
|
208
215
|
|
209
216
|
Here is a more complex example illustrating the interaction between flags and
|
210
217
|
positional arguments. Suppose we want to use `do` to display the help screens
|
211
218
|
for the root tool and the system tool in sequence. That is, we want to run
|
212
219
|
`toys --help` and `toys system --help` in sequence. We might start by trying:
|
213
220
|
|
221
|
+
|FLAG| |-ARGS-| |FLAG|
|
214
222
|
toys do --help , system --help
|
215
223
|
|
216
224
|
However, this simply displays the help for the `do` tool itself, because the
|
217
|
-
first `--help` is interpreted as a flag for `do
|
218
|
-
argument specifying the first tool
|
219
|
-
treat all its arguments as positional,
|
220
|
-
like so:
|
225
|
+
first `--help` is interpreted as a flag for `do`. What we actually want is for
|
226
|
+
`do` to treat it as a positional argument specifying the first tool to run. So
|
227
|
+
Let's force `do` to treat all its arguments as positional, by starting with
|
228
|
+
`--` like so:
|
221
229
|
|
222
230
|
|--------ARGS--------|
|
223
231
|
toys do -- --help , system --help
|
224
232
|
|
225
233
|
## Defining Tools
|
226
234
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
235
|
+
In this section, you will learn how to define tools by writing a **Toys file**.
|
236
|
+
|
237
|
+
A file named `.toys.rb` (note the leading period) in the current working
|
238
|
+
directory defines tools available in that directory and its subdirectories. We
|
239
|
+
will cover how to write tools, including specifying the functionality of the
|
240
|
+
tool, the flags and arguments it takes, and how its description appears in the
|
241
|
+
help screen.
|
234
242
|
|
235
|
-
### Basic
|
243
|
+
### Basic Toys Syntax
|
236
244
|
|
237
|
-
The format of a Toys
|
238
|
-
|
245
|
+
The format of a Toys file is a Ruby DSL that includes directives, methods, and
|
246
|
+
nested blocks. The actual DSL is specified in the
|
239
247
|
[Toys::DSL::Tool class](https://www.rubydoc.info/gems/toys-core/Toys/DSL/Tool).
|
240
248
|
|
241
249
|
To create a tool, write a `tool` block, giving the tool a name. Within the
|
242
|
-
block, set the properties of the tool, including
|
243
|
-
arguments recognized by the tool
|
250
|
+
block, use directives to set the properties of the tool, including descriptions
|
251
|
+
and the flags and arguments recognized by the tool. The actual functionality of
|
252
|
+
the tool is set by defining a `run` method.
|
244
253
|
|
245
254
|
Consider the following example:
|
246
255
|
|
@@ -267,16 +276,16 @@ details below.
|
|
267
276
|
|
268
277
|
### Descriptions
|
269
278
|
|
270
|
-
Each tool may have a short and a long description
|
271
|
-
generally a single string that is displayed with the
|
272
|
-
its help page
|
273
|
-
strings, which are displayed in
|
274
|
-
the tool's help page. Long
|
275
|
-
paragraphs visually.
|
279
|
+
Each tool may have a **short description** and/or a **long description**. The
|
280
|
+
short description is a generally a single string that is displayed with the
|
281
|
+
tool name, at the top of its help page or in a subtool list. The long
|
282
|
+
description typically includes multiple strings, which are displayed in
|
283
|
+
multiple lines in the "description" section of the tool's help page. Long
|
284
|
+
descriptions may include blank lines to separate paragraphs visually.
|
276
285
|
|
277
286
|
Each description string/line is word-wrapped by default when displayed. In the
|
278
|
-
long description above, the first line is a bit longer than 80
|
279
|
-
may be word-wrapped if displayed on an 80-character terminal.
|
287
|
+
long description example above, the first line is a bit longer than 80
|
288
|
+
characters, and may be word-wrapped if displayed on an 80-character terminal.
|
280
289
|
|
281
290
|
If you need to control the wrapping behavior, pass an array of strings for that
|
282
291
|
line. Each array element will be considered a unit for wrapping purposes, and
|
@@ -285,18 +294,21 @@ illustrates how to prevent a line from being word-wrapped. This is also a
|
|
285
294
|
useful technique for preserving spaces and indentation.
|
286
295
|
|
287
296
|
For more details, see the reference documentation for
|
288
|
-
[DSL::Tool#desc](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:desc)
|
297
|
+
[Toys::DSL::Tool#desc](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:desc)
|
289
298
|
and
|
290
|
-
[DSL::Tool#long_desc](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:long_desc).
|
299
|
+
[Toys::DSL::Tool#long_desc](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:long_desc).
|
291
300
|
|
292
301
|
### Positional Arguments
|
293
302
|
|
294
|
-
Tools may recognize
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
303
|
+
Tools may recognize any number of **positional arguments**. Each argument must
|
304
|
+
have a name, which is a key that the tool can use to obtain the argument's
|
305
|
+
value at execution time. Arguments may also have various properties controlling
|
306
|
+
how values are validated and expressed.
|
307
|
+
|
308
|
+
The above example uses the directive
|
309
|
+
[Toys::DSL::Tool#optional_arg](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:optional_arg)
|
310
|
+
to declare an **optional argument** named `:whom`. If the argument is provided
|
311
|
+
on the command line e.g.
|
300
312
|
|
301
313
|
toys greet ruby
|
302
314
|
|
@@ -308,108 +320,821 @@ argument is omitted, e.g.
|
|
308
320
|
|
309
321
|
Then the option `:whom` is set to the default value `"world"`.
|
310
322
|
|
311
|
-
|
323
|
+
An argument may also be **required**, which means it must be provided on the
|
312
324
|
command line; otherwise the tool will report a usage error. You may declare a
|
313
|
-
required argument using the
|
314
|
-
[DSL::Tool#required_arg](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:required_arg).
|
325
|
+
required argument using the directive
|
326
|
+
[Toys::DSL::Tool#required_arg](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:required_arg).
|
327
|
+
|
328
|
+
#### Parsing Required and Optional Arguments
|
315
329
|
|
316
330
|
When command line arguments are parsed, the required arguments are matched
|
317
331
|
first, in order, followed by the optional arguments. For example:
|
318
332
|
|
319
|
-
tool "
|
333
|
+
tool "args-demo" do
|
320
334
|
optional_arg :arg2
|
321
335
|
required_arg :arg1
|
322
336
|
# ...
|
323
337
|
|
324
338
|
If a user runs
|
325
339
|
|
326
|
-
toys
|
340
|
+
toys args-demo foo
|
327
341
|
|
328
342
|
Then the required argument `:arg1` will be set to `"foo"`, and the optional
|
329
343
|
argument `:arg2` will not be set (i.e. it will remain `nil`).
|
330
344
|
|
331
345
|
If the user runs:
|
332
346
|
|
333
|
-
toys
|
347
|
+
toys args-demo foo bar
|
334
348
|
|
335
349
|
Then `:arg1` is set to `"foo"`, and `:arg2` is set to `"bar"`.
|
336
350
|
|
337
351
|
Running the following:
|
338
352
|
|
339
|
-
toys
|
353
|
+
toys args-demo
|
340
354
|
|
341
355
|
Will produce a usage error, because no value is set for the required argument
|
342
356
|
`:arg1`. Similarly, running:
|
343
357
|
|
344
|
-
toys
|
358
|
+
toys args-demo foo bar baz
|
345
359
|
|
346
|
-
Will also produce an error, since the tool does not
|
360
|
+
Will also produce an error, since the tool does not define an argument to
|
347
361
|
match `"baz"`.
|
348
362
|
|
349
|
-
|
350
|
-
|
351
|
-
[DSL::Tool#remaining_args](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:remaining_args). For example:
|
363
|
+
Optional arguments may declare a default value to be used if the argument is
|
364
|
+
not provided on the command line. For example:
|
352
365
|
|
353
|
-
tool "
|
354
|
-
|
366
|
+
tool "args-demo" do
|
367
|
+
required_arg :arg1
|
368
|
+
optional_arg :arg2, default: "the-default"
|
369
|
+
# ...
|
370
|
+
|
371
|
+
Now running the following:
|
372
|
+
|
373
|
+
toys args-demo foo
|
374
|
+
|
375
|
+
Will set the required argument to `"foo"` as usual, and the optional argument,
|
376
|
+
because it is not provided, will default to `"the-default"` instead of `nil`.
|
377
|
+
|
378
|
+
#### Remaining Arguments
|
379
|
+
|
380
|
+
Normally, unmatched arguments will result in an error message. However, you can
|
381
|
+
provide an "argument" to match all **remaining** unmatched arguments at the
|
382
|
+
end, using the directive
|
383
|
+
[Toys::DSL::Tool#remaining_args](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:remaining_args).
|
384
|
+
For example:
|
385
|
+
|
386
|
+
tool "args-demo" do
|
355
387
|
required_arg :arg1
|
388
|
+
optional_arg :arg2
|
356
389
|
remaining_args :arg3
|
357
390
|
# ...
|
358
391
|
|
359
392
|
Now, running:
|
360
393
|
|
361
|
-
toys
|
394
|
+
toys args-demo foo bar baz bey
|
362
395
|
|
363
396
|
Sets the following option data:
|
364
397
|
|
365
398
|
{arg1: "foo", arg2: "bar", arg3: ["baz", "bey"]}
|
366
399
|
|
400
|
+
If instead you run:
|
401
|
+
|
402
|
+
toys args-demo foo
|
403
|
+
|
404
|
+
This sets the following option data:
|
405
|
+
|
406
|
+
{arg1: "foo", arg2: nil, arg3: []}
|
407
|
+
|
408
|
+
Whereas your tool can include any number of `required_arg` and `optional_arg`
|
409
|
+
directives, declaring any number of required and optional arguments, it can
|
410
|
+
have only at most a single `remaining_args` directive.
|
411
|
+
|
412
|
+
#### Descriptions and the Args DSL
|
413
|
+
|
367
414
|
Positional arguments may also have short and long descriptions, which are
|
368
|
-
displayed in online help.
|
415
|
+
displayed in online help. Set descriptions via the `desc:` and `long_desc:`
|
416
|
+
arguments to the argument directive. The `desc:` argument takes a single string
|
417
|
+
description, while the `long_desc:` argument takes an array of strings. Here is
|
418
|
+
an example:
|
419
|
+
|
420
|
+
required_arg :arg,
|
421
|
+
desc: "This is a short description for the arg",
|
422
|
+
long_desc: ["Long descriptions may have multiple lines.",
|
423
|
+
"This is the second line."]
|
424
|
+
|
425
|
+
See the above section on Descriptions for more information on how descriptions
|
426
|
+
are rendered and word wrapped.
|
427
|
+
|
428
|
+
Long descriptions may be unwieldly to write as a hash argument in this way. So
|
429
|
+
Toys provides an alternate syntax for defining arguments using a block.
|
430
|
+
|
431
|
+
required_arg :arg do
|
432
|
+
desc "This is a short description for the arg"
|
433
|
+
long_desc "Long desc can be set as multiple lines together,",
|
434
|
+
"like this second line."
|
435
|
+
long_desc "Or you can call long_desc again to add more lines."
|
436
|
+
end
|
437
|
+
|
438
|
+
For detailed info on configuring an argument using a block, see the
|
439
|
+
[Toys::DSL::Arg class](https://www.rubydoc.info/gems/toys-core/Toys/DSL/Arg).
|
440
|
+
|
441
|
+
#### Acceptors
|
442
|
+
|
443
|
+
Finally, positional arguments may use **acceptors** to define how to validate
|
444
|
+
arguments and convert them to Ruby objects for your tool to consume. By
|
445
|
+
default, Toys will accept an argument string in any form, and expose it to your
|
446
|
+
tool as a raw string. However, you may provide an acceptor to change this
|
447
|
+
behavior.
|
448
|
+
|
449
|
+
Acceptors are part of the OptionParser interface, and are described under the
|
450
|
+
[type coercion](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#class-OptionParser-label-Type+Coercion)
|
451
|
+
section. For example, you can provide the `Integer` class as an acceptor, which
|
452
|
+
will validate that the argument is a well-formed integer, and convert it to an
|
453
|
+
integer during parsing:
|
454
|
+
|
455
|
+
tool "args-demo" do
|
456
|
+
required_arg :age, accept: Integer
|
457
|
+
def run
|
458
|
+
age = option(:age) # This is an integer
|
459
|
+
...
|
460
|
+
|
461
|
+
If you pass a non-integer for this argument, Toys will report a usage error.
|
462
|
+
|
463
|
+
You may use any of the ready-to-use coercion types provided by OptionParser,
|
464
|
+
including the special types such as
|
465
|
+
[OptionParser::DecimalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#DecimalInteger)
|
466
|
+
and
|
467
|
+
[OptionParser::OctalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#OctalInteger).
|
468
|
+
|
469
|
+
You may also create **custom acceptors**. See the section below on Custom
|
470
|
+
Acceptors for more information.
|
369
471
|
|
370
472
|
### Flags
|
371
473
|
|
372
|
-
Tools may also recognize flags on the command line. In our "greet" example,
|
373
|
-
declared a flag named `:shout`:
|
474
|
+
Tools may also recognize **flags** on the command line. In our "greet" example,
|
475
|
+
we declared a flag named `:shout`:
|
374
476
|
|
375
477
|
flag :shout, "-s", "--shout", desc: "Greet loudly."
|
376
478
|
|
377
|
-
|
479
|
+
Like a positional argument, a flag sets an option based on the command line
|
480
|
+
arguments passed to the tool. In the case above, the `:shout` option is set to
|
481
|
+
`true` if either `-s` or `--shout` is provided on the command line; otherwise
|
482
|
+
it is set to `false`. Any number of short or long flags can be declared; they
|
483
|
+
will be synonyms and have the same effect.
|
484
|
+
|
485
|
+
#### Flag Types
|
486
|
+
|
487
|
+
Toys recognizes the same syntax used by the standard OptionParser library. This
|
488
|
+
means you can also declare a flag that can be both set and unset:
|
489
|
+
|
490
|
+
flag :shout, "--[no-]shout"
|
491
|
+
|
492
|
+
If you do not provide any actual flags, Toys will infer a long flag from the
|
493
|
+
name of the option. Hence, the following two definitions are equivalent:
|
494
|
+
|
495
|
+
flag :shout
|
496
|
+
flag :shout, "--shout"
|
497
|
+
|
498
|
+
You can declare that a short or long flag takes a value:
|
499
|
+
|
500
|
+
flag :whom, "--whom=VALUE"
|
501
|
+
flag :whom, "--whom VALUE"
|
502
|
+
flag :whom, "-wVALUE"
|
503
|
+
flag :whom, "-w VALUE"
|
504
|
+
|
505
|
+
You can also declare the value to be optional:
|
506
|
+
|
507
|
+
flag :whom, "--whom[=VALUE]"
|
508
|
+
flag :whom, "--whom [VALUE]"
|
509
|
+
flag :whom, "-wVALUE"
|
510
|
+
flag :whom, "-w VALUE"
|
511
|
+
|
512
|
+
Note that if you define multiple flags together, they will all be coerced to
|
513
|
+
the same "type". That is, if one takes a value, they all will implicitly take
|
514
|
+
a value. (This is the same behavior as OptionParser.) In this example:
|
515
|
+
|
516
|
+
flag :whom, "-w", "--whom=VALUE"
|
517
|
+
|
518
|
+
The `-w` flag will also implicitly take a value, because it is defined as an
|
519
|
+
alias with another flag that takes a value.
|
520
|
+
|
521
|
+
Note also that Toys will raise an error if those flags are incompatible. For
|
522
|
+
example:
|
523
|
+
|
524
|
+
flag :whom, "-w[VALUE]", "--whom=VALUE"
|
525
|
+
|
526
|
+
Raises an error because one flag's value is optional while the other is
|
527
|
+
required. (Again, this is consistent with OptionParser's behavior.)
|
528
|
+
|
529
|
+
#### Custom Acceptors
|
530
|
+
|
531
|
+
Flags may use **acceptors** to define how to validate values and convert them
|
532
|
+
to Ruby objects for your tool to consume. By default, Toys will accept a flag
|
533
|
+
value string in any form, and expose it to your tool as a raw string. However,
|
534
|
+
you may provide an acceptor to change this behavior.
|
378
535
|
|
379
|
-
|
536
|
+
Acceptors are part of the OptionParser interface, and are described under the
|
537
|
+
[type coercion](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#class-OptionParser-label-Type+Coercion)
|
538
|
+
section. For example, you can provide the `Integer` class as an acceptor, which
|
539
|
+
will validate that the argument is a well-formed integer, and convert it to an
|
540
|
+
integer during parsing:
|
380
541
|
|
381
|
-
|
542
|
+
tool "args-demo" do
|
543
|
+
flag :age, accept: Integer
|
544
|
+
def run
|
545
|
+
option(:age) # This is an integer
|
546
|
+
...
|
547
|
+
|
548
|
+
If you pass a non-integer for this flag value, Toys will report a usage error.
|
549
|
+
|
550
|
+
You may use any of the ready-to-use coercion types provided by OptionParser,
|
551
|
+
including the special types such as
|
552
|
+
[OptionParser::DecimalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#DecimalInteger)
|
553
|
+
and
|
554
|
+
[OptionParser::OctalInteger](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/OptionParser.html#OctalInteger).
|
555
|
+
|
556
|
+
You may also create **custom acceptors**. See the section below on Custom
|
557
|
+
Acceptors for more information.
|
558
|
+
|
559
|
+
#### Defaults and Handlers
|
560
|
+
|
561
|
+
Currently, flags are always optional and a flag can appear in a command line
|
562
|
+
zero, one, or any number of times. If a flag is not passed in the command line
|
563
|
+
arguments for a tool, by default its corresponding option value will be `nil`.
|
564
|
+
|
565
|
+
You may change this by providing a default value for a flag:
|
566
|
+
|
567
|
+
flag :age, accept: Integer, default: 21
|
568
|
+
|
569
|
+
If you pass a flag multiple times on the command line, by default the *last*
|
570
|
+
appearance of the flag will take effect. That is, suppose you define this flag:
|
571
|
+
|
572
|
+
flag :shout, "--[no-]shout"
|
573
|
+
|
574
|
+
Now if you pass `--shout --no-shout`, then the value of the `:shout` option
|
575
|
+
will be `false`, i.e. the last value set on the command line. This is because a
|
576
|
+
flag *sets* its option value, replacing any previous value. You may change this
|
577
|
+
behavior by providing a **handler**.
|
578
|
+
|
579
|
+
A handler is a proc that governs what a flag does to its option value. It takes
|
580
|
+
two arguments, the new value given, and the previously set value (which might
|
581
|
+
be the default value if this is the first appearance of the flag), and returns
|
582
|
+
the new value that should be set. So effectively, the default behavior is
|
583
|
+
equivalent to the following handler:
|
584
|
+
|
585
|
+
flag :shout, "--[no-]shout", handler: proc { | val, _prev| val }
|
586
|
+
|
587
|
+
For example, most tools automatically get a "--verbose" flag. This flag may
|
588
|
+
appear any number of times, and each appearance increases the verbosity. The
|
589
|
+
value of this verbosity is an integer. This flag is actually implemented by
|
590
|
+
Toys, as follows:
|
591
|
+
|
592
|
+
flag Toys::Tool::Keys::VERBOSITY, "-v", "--verbose",
|
593
|
+
default: 0,
|
594
|
+
handler: proc { |_val, prev| prev + 1 }
|
595
|
+
|
596
|
+
Similarly, the "--quiet" flag, which decreases the verbosity, is implemented
|
597
|
+
as follows:
|
598
|
+
|
599
|
+
flag Toys::Tool::Keys::VERBOSITY, "-q", "--quiet",
|
600
|
+
default: 0,
|
601
|
+
handler: proc { |_val, prev| prev - 1 }
|
602
|
+
|
603
|
+
Note that both flags affect the same option value, `VERBOSITY`. The first
|
604
|
+
increments it each time it appears, and the second decrements it. A tool can
|
605
|
+
query this option and get an integer telling the requested verbosity level, as
|
606
|
+
you will see below in the section on execution environment.
|
607
|
+
|
608
|
+
#### Descriptions and the Flags DSL
|
609
|
+
|
610
|
+
Flags may also have short and long descriptions, which are displayed in online
|
611
|
+
help. Set descriptions via the `desc:` and `long_desc:` arguments to the flag
|
612
|
+
directive. The `desc:` argument takes a single string description, while the
|
613
|
+
`long_desc:` argument takes an array of strings. Here is an example:
|
614
|
+
|
615
|
+
flag :my_flag, "--my-flag",
|
616
|
+
desc: "This is a short description for the arg",
|
617
|
+
long_desc: ["Long descriptions may have multiple lines.",
|
618
|
+
"This is the second line."]
|
619
|
+
|
620
|
+
See the above section on Descriptions for more information on how descriptions
|
621
|
+
are rendered and word wrapped.
|
622
|
+
|
623
|
+
Long descriptions may be unwieldly to write as a hash argument in this way. So
|
624
|
+
Toys provides an alternate syntax for defining flags using a block.
|
382
625
|
|
383
|
-
|
626
|
+
flag :my_flag do
|
627
|
+
flags "--my-flag"
|
628
|
+
desc "This is a short description for the flag"
|
629
|
+
long_desc "Long desc can be set as multiple lines together,",
|
630
|
+
"like this second line."
|
631
|
+
long_desc "Or you can call long_desc again to add more lines."
|
632
|
+
end
|
633
|
+
|
634
|
+
For detailed info on configuring an flag using a block, see the
|
635
|
+
[Toys::DSL::Flag class](https://www.rubydoc.info/gems/toys-core/Toys/DSL/Flag).
|
636
|
+
|
637
|
+
### Tool Execution Basics
|
638
|
+
|
639
|
+
When you run a tool from the command line, Toys will build the tool based on
|
640
|
+
its definition in a Toys file, and then it will attempt to execute it by
|
641
|
+
calling the `run` method. Normally, you should define this method in each of
|
642
|
+
your tools.
|
643
|
+
|
644
|
+
Note: If you do not define the `run` method for a tool, Toys provides a default
|
645
|
+
implementation that displays the tool's help screen. This is typically used for
|
646
|
+
namespaces, as we shall see below. Most tools, however, should define `run`.
|
647
|
+
|
648
|
+
Let's revisit the "greet" example we covered earlier.
|
649
|
+
|
650
|
+
tool "greet" do
|
651
|
+
optional_arg :whom, default: "world"
|
652
|
+
flag :shout, "-s", "--shout"
|
653
|
+
|
654
|
+
def run
|
655
|
+
greeting = "Hello, #{option(:whom)}!"
|
656
|
+
greeting.upcase! if option(:shout)
|
657
|
+
puts greeting
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
Note how the `run` method uses the
|
662
|
+
[Toys::Tool#option](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:option)
|
663
|
+
method to access values that were assigned by flags or positional arguments.
|
664
|
+
Note also that you can produce output or interact with the console using the
|
665
|
+
normal Ruby `$stdout`, `$stderr`, and `$stdin` streams.
|
666
|
+
|
667
|
+
If a tool's `run` method finishes normally, Toys will exit with a result code
|
668
|
+
of 0, indicating success. You may exit immediately and/or provide a nonzero
|
669
|
+
result using the
|
670
|
+
[Toys::Tool#exit](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:exit)
|
671
|
+
method:
|
672
|
+
|
673
|
+
def run
|
674
|
+
puts "Exiting with an error..."
|
675
|
+
exit(1)
|
676
|
+
puts "Will never get here."
|
677
|
+
end
|
678
|
+
|
679
|
+
If your `run` method raises an exception, Toys will display the exception and
|
680
|
+
exit with a nonzero code.
|
384
681
|
|
385
|
-
|
682
|
+
Finally, you may also define additional methods within the tool. These are
|
683
|
+
available to be called by your `run` method, and can be used to decompose your
|
684
|
+
tool implementation.
|
386
685
|
|
387
|
-
|
686
|
+
This should be enough to get you started implementing tools. A variety of
|
687
|
+
additional features are available for your tool implementation and will be
|
688
|
+
discussed further below. But first we will cover a few important topics.
|
689
|
+
|
690
|
+
### Namespaces and Subtools
|
691
|
+
|
692
|
+
Like many command line frameworks, Toys supports **subtools**. You may, for
|
693
|
+
example create a tool called "test" that runs your tests for a particular
|
694
|
+
project, but you might also want "test unit" and "test integration" tools to
|
695
|
+
run specific subsets of the test suite. One way to do this, of course, is for
|
696
|
+
the "test" tool to parse "unit" and "integration" as arguments. However, you
|
697
|
+
could also define them as separate tools, subtools of "test".
|
698
|
+
|
699
|
+
To define a subtool, create nested `tool` directives. Here's a simple example:
|
700
|
+
|
701
|
+
tool "test" do
|
702
|
+
tool "unit" do
|
703
|
+
def run
|
704
|
+
puts "run unit tests here..."
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
tool "integration" do
|
709
|
+
def run
|
710
|
+
puts "run integration tests here..."
|
711
|
+
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
You can now invoke them like this:
|
716
|
+
|
717
|
+
toys test unit
|
718
|
+
toys test integration
|
719
|
+
|
720
|
+
Notice in this case, the parent "test" tool itself has no `run` method. This is
|
721
|
+
a common pattern: "test" is just a "container" for tools, a way of organizing
|
722
|
+
your tools. In Toys terminology, it is called a **namespace**. But it is still
|
723
|
+
a tool, and it can still be run:
|
724
|
+
|
725
|
+
toys test
|
726
|
+
|
727
|
+
As discussed earlier, Toys provides a default implementation that displays the
|
728
|
+
help screen, which includes a list of the subtools and their descriptions.
|
729
|
+
|
730
|
+
As another example, the "root" tool is also normally a namespace. If you just
|
731
|
+
run Toys with no arguments:
|
732
|
+
|
733
|
+
toys
|
734
|
+
|
735
|
+
The root tool will display the overall help screen for Toys.
|
736
|
+
|
737
|
+
Although it is a less common pattern, it is possible for a tool that has
|
738
|
+
subtools to have its own `run` method:
|
739
|
+
|
740
|
+
tool "test" do
|
741
|
+
def run
|
742
|
+
puts "run all tests here..."
|
743
|
+
end
|
744
|
+
|
745
|
+
tool "unit" do
|
746
|
+
def run
|
747
|
+
puts "run only unit tests here..."
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
tool "integration" do
|
752
|
+
def run
|
753
|
+
puts "run only integration tests here..."
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
Now running `toys test` will run its own implementation.
|
759
|
+
|
760
|
+
(Yes, it is even possible to write a `run` method for the root tool. I don't
|
761
|
+
recommend doing so, because then you lose the root tool's useful default
|
762
|
+
implementation that lists all your tools.)
|
763
|
+
|
764
|
+
Toys allows subtools to be nested arbitrarily deep. Although in practice, more
|
765
|
+
than two or three levels of hierarchy can be confusing to use.
|
766
|
+
|
767
|
+
## Understanding Toys Files
|
768
|
+
|
769
|
+
Toys commands are defined in Toys files. We covered the basic syntax for these
|
770
|
+
files in the above section on defining tools. In this section, we will take a
|
771
|
+
deeper look at Toys files, including:
|
772
|
+
|
773
|
+
* Defining subtools in their own files
|
774
|
+
* Global and local Toys files
|
775
|
+
* The `TOYS_PATH`
|
776
|
+
* Overriding tools
|
777
|
+
|
778
|
+
### Toys Directories
|
779
|
+
|
780
|
+
So far we have been defining tools by writing a Toys file named `.toys.rb`
|
781
|
+
located in the current working directory. This works great if you have a small
|
782
|
+
number of fairly simple tools, but if you are defining many tools or tools with
|
783
|
+
long or complex implementations, you may find it better to define some tools in
|
784
|
+
separate files. You can have Toys load tools from multiple files by creating a
|
785
|
+
**Toys directory**.
|
786
|
+
|
787
|
+
A Toys directory is a directory called `.toys` located in the current working
|
788
|
+
directory. Ruby files inside a Toys directory (or any of its subdirectories)
|
789
|
+
are loaded when Toys looks for tool definitions. Furthermore, the name of the
|
790
|
+
Ruby file (and indeed its path relative to the Toys directory) determines which
|
791
|
+
tool it defines.
|
792
|
+
|
793
|
+
For example, one way to create a "greet" tool, as we saw before, is to write a
|
794
|
+
`.toys.rb` file in the current directory, and populate it like this:
|
795
|
+
|
796
|
+
tool "greet" do
|
797
|
+
optional_arg :whom, default: "world"
|
798
|
+
def run
|
799
|
+
puts "Hello, #{option(:whom)}"
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
You could also create the same tool by creating a `.toys` directory, and then
|
804
|
+
creating a file `greet.rb` inside that directory. The contents would be:
|
805
|
+
|
806
|
+
optional_arg :whom, default: "world"
|
807
|
+
def run
|
808
|
+
puts "Hello, #{option(:whom)}"
|
809
|
+
end
|
810
|
+
|
811
|
+
Notice that we did not use a `tool "greet"` block here. That is because the
|
812
|
+
name of the file `greet.rb` already provides a tool context: Toys already knows
|
813
|
+
that we are defining a "greet" tool.
|
814
|
+
|
815
|
+
If you do include a `tool` block inside the `greet.rb` file, it will create a
|
816
|
+
*subtool* of `greet`. So, inside a Toys directory, the path to each Ruby file
|
817
|
+
defines the "starting point" namespace for the tools defined in that file.
|
818
|
+
|
819
|
+
If you create subdirectories inside a Toys directory, their names also
|
820
|
+
contribute to the namespace of created tools. For example, if you create a
|
821
|
+
directory `.toys/test` and a file `unit.rb` under that directory, it will
|
822
|
+
create the tool `test unit`.
|
823
|
+
|
824
|
+
(current directory)
|
825
|
+
|
|
826
|
+
+- .toys/
|
827
|
+
|
|
828
|
+
+- greet.rb <-- defines "greet" (and subtools)
|
829
|
+
|
|
830
|
+
+- test/
|
831
|
+
|
|
832
|
+
+- unit.rb <-- defines "test unit" (and its subtools)
|
833
|
+
|
834
|
+
Once again, `test unit` is the "starting point" for tools defined in the
|
835
|
+
`.toys/test/unit.rb` file. Declarations and methods at the top level of that
|
836
|
+
file will define the `test unit` tool. Any `tool` blocks you add to that file
|
837
|
+
will define subtools of `test unit`.
|
838
|
+
|
839
|
+
#### Index Files
|
840
|
+
|
841
|
+
The file name `.toys.rb` can also be used inside Toys directories and
|
842
|
+
subdirectories. Such files are called **index files**, and they create tools
|
843
|
+
with the directory as the "starting point" namespace. For example, if you
|
844
|
+
create an index file directly underneath a `.toys` directory, it will define
|
845
|
+
top level tools (just like a `.toys.rb` file in the current directory.) An
|
846
|
+
index file located inside `.toys/test` will define tools with `test` as the
|
847
|
+
"starting point" namespace.
|
848
|
+
|
849
|
+
(current directory)
|
850
|
+
|
|
851
|
+
+- .toys/
|
852
|
+
|
|
853
|
+
+- .toys.rb <-- index file, defines tools at the top level
|
854
|
+
|
|
855
|
+
+- greet.rb <-- defines "greet" (and subtools)
|
856
|
+
|
|
857
|
+
+- test/
|
858
|
+
|
|
859
|
+
+- .toys.rb <-- index file, defines "test" (and its subtools)
|
860
|
+
|
|
861
|
+
+- unit.rb <-- defines "test unit" (and its subtools)
|
862
|
+
|
863
|
+
Index files give you some flexibility for organizing your tools. For example,
|
864
|
+
if you have a number of subtools of `test`, including a lot of small tools and
|
865
|
+
one big complex subtool called `unit`, you might define all the simple tools in
|
866
|
+
the index file `.toys/test/.toys.rb`, while defining the large `test unit` tool
|
867
|
+
in the separate file `.toys/test/unit.rb`.
|
868
|
+
|
869
|
+
Toys also loads index files first before other files in the directory. This
|
870
|
+
means they are convenient places to define shared code that can be used by all
|
871
|
+
the subtools, as we shall see later in the section on helpers.
|
872
|
+
|
873
|
+
### The Toys Search Path
|
874
|
+
|
875
|
+
So far we have seen how to define tools by writing a `.toys.rb` file in the
|
876
|
+
current directory, or by writing files inside a `.toys` directory in the
|
877
|
+
current directory. These tools are "scoped" to the current directory. If you
|
878
|
+
move to a different directory, they may not be available.
|
879
|
+
|
880
|
+
When Toys runs, it looks for tools in a **search path**. Specifically:
|
881
|
+
|
882
|
+
(1) It looks for a `.toys.rb` file and/or a `.toys` directory in the *current
|
883
|
+
working directory*.
|
884
|
+
(2) It does the same in the *parent directory* of the current directory, and
|
885
|
+
its parent, all the way up to the root of the file system.
|
886
|
+
(3) It does the same in the current user's *home directory*.
|
887
|
+
(4) It does the same in the system configuration directory (i.e. `/etc` on unix
|
888
|
+
systems)
|
889
|
+
|
890
|
+
It uses the *first* implementation that it finds for the requested tool. For
|
891
|
+
example, if the tool `greet` is defined in the `.toys.rb` file in the current
|
892
|
+
working directory, and also in the `.toys/greet.rb` file of the parent
|
893
|
+
directory, it will use the version in the current directory.
|
894
|
+
|
895
|
+
This means you could write a default implementation for a tool in your home
|
896
|
+
directory, and override it in the current directory. For example, you could
|
897
|
+
define a tool `get-credentials` in your home directory that gets credentials
|
898
|
+
you need for *most* of your projects. But maybe on particular project requires
|
899
|
+
different credentials, so you could define a different `get-credentials` tool
|
900
|
+
in that project's directory.
|
901
|
+
|
902
|
+
While a tool can be overridden when it is defined at different points in the
|
903
|
+
search path, it is *not* allowed to provide multiple definitions of a tool at
|
904
|
+
the *same* point in the search path. For example, if you define the `greet`
|
905
|
+
tool twice in the same `.toys.rb` file, Toys will report an error. Perhaps less
|
906
|
+
obviously, if you define `greet` in the `.toys.rb` file in the current
|
907
|
+
directory, and you also define it in the `.toys/greet.rb` file in the same
|
908
|
+
current directory, Toys will also report an error, since both are defined at
|
909
|
+
the same point (the current directory) in the search path.
|
910
|
+
|
911
|
+
#### Global Toys
|
912
|
+
|
913
|
+
Note that in the search path above, steps (1) and (2) are *context-dependent*.
|
914
|
+
That is, they may be different depending on what directory you are in. However,
|
915
|
+
steps (3) and (4) are *not* context-dependent, and are searched regardless of
|
916
|
+
where you are located. Toys defined here are **global**, available everywhere.
|
917
|
+
|
918
|
+
By default, global tools are defined in your home directory and the system
|
919
|
+
configuration directory. However, you can change this by defining the
|
920
|
+
environment variable `TOYS_PATH`. This environment variable should contain a
|
921
|
+
colon-delimited list of paths that should be searched for global toys. If you
|
922
|
+
do define it, it replaces (3) and (4) with the paths you specify.
|
388
923
|
|
389
924
|
## The Execution Environment
|
390
925
|
|
926
|
+
This section describes the context and resources available to your tool when it
|
927
|
+
is running; that is, what you can call from your tool's `run` method.
|
928
|
+
|
929
|
+
Generally, your tool is executed in an object of type
|
930
|
+
[Toys::Tool](https://www.rubydoc.info/gems/toys-core/Toys/Tool). This class
|
931
|
+
defines a number of methods, and provides access to a variety of data and
|
932
|
+
objects relevant to your tool. We have already seen earlier how to use the
|
933
|
+
[Toys::Tool#option](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:option)
|
934
|
+
method to retrieve option values, and how to use the
|
935
|
+
[Toys::Tool#exit](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:exit)
|
936
|
+
method to exit immediately and return an exit code. Now we will cover other
|
937
|
+
resources available to your tool.
|
938
|
+
|
391
939
|
### Built-in Context
|
392
940
|
|
941
|
+
The options set by your tool's flags and command line arguments are only a
|
942
|
+
subset of the data you can access. A variety of other data and objects are
|
943
|
+
also accessible using the
|
944
|
+
[Toys::Tool#get method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:get)
|
945
|
+
For example, you can get the full name of the tool being executed like this:
|
946
|
+
|
947
|
+
def run
|
948
|
+
puts "Current tool is #{get(TOOL_NAME)}"
|
949
|
+
end
|
950
|
+
|
951
|
+
The `TOOL_NAME` constant above is a well-known key that corresponds to the full
|
952
|
+
name (as an array of strings) of the running tool. A variety of well-known keys
|
953
|
+
are defined in the
|
954
|
+
[Toys::Tool::Keys module](https://www.rubydoc.info/gems/toys-core/Toys/Tool/Keys).
|
955
|
+
They range from the tool name and the original command line arguments passed to
|
956
|
+
it (before they were parsed), to references to some of the Toys objects that
|
957
|
+
can be used to do things like write to the log or look up and call other tools.
|
958
|
+
|
959
|
+
(The `get` method also returns the options that were set by flags or command
|
960
|
+
line arguments if you pass the corresponding option keys. However, it is
|
961
|
+
generally good practice to use
|
962
|
+
[Toys::Tool#option](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:option)
|
963
|
+
to get option values.)
|
964
|
+
|
965
|
+
Most of the important context also can be accessed from convenience methods.
|
966
|
+
For example, the `TOOL_NAME` is also available from the
|
967
|
+
[Toys::Tool#tool_name method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:tool_name):
|
968
|
+
|
969
|
+
def run
|
970
|
+
puts "Current tool is #{tool_name}"
|
971
|
+
end
|
972
|
+
|
973
|
+
Let's take a look at a few things your tool can do with the objects you can
|
974
|
+
access from built-in context.
|
975
|
+
|
393
976
|
### Logging and Verbosity
|
394
977
|
|
978
|
+
Toys provides a Logger (a simple instance of the Ruby standard library logger
|
979
|
+
that writes to standard error) for your tool to use to report status
|
980
|
+
information. You can access this logger via the `LOGGER` context key, or the
|
981
|
+
[Toys::Tool#logger method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:logger).
|
982
|
+
For example:
|
983
|
+
|
984
|
+
def run
|
985
|
+
logger.warn "Danger Will Robinson!"
|
986
|
+
end
|
987
|
+
|
988
|
+
The current logger level is controlled by the verbosity. Verbosity is an
|
989
|
+
integer context value that you can retrieve using the `VERBOSITY` context key
|
990
|
+
or the
|
991
|
+
[Toys::Tool#verbosity method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:verbosity).
|
992
|
+
The verbosity is set to 0 by default. This corresponds to a logger level of
|
993
|
+
`WARN`. That is, warnings, errors, and fatals are displayed, while infos and
|
994
|
+
debugs are not. However, as we saw earlier, tools automatically respond to the
|
995
|
+
`--verbose` and `--quiet` flags, (or `-v` and `-q`), which increment and
|
996
|
+
decrement the verbosity value, respectively. If you run a tool with `-v`, the
|
997
|
+
verbosity is incremented to 1, and the logger level is set to `INFO`. If you
|
998
|
+
set `-q`, the verbosity is decremented to -1, and the logger level is set to
|
999
|
+
`ERROR`. So by using the provided logger, a tool can easily provide command
|
1000
|
+
line based control of the output verbosity.
|
1001
|
+
|
395
1002
|
### Running Tools from Tools
|
396
1003
|
|
1004
|
+
A common operation a tool might want to do is "call" another tool. This can be
|
1005
|
+
done via the CLI object, which you can retrieve using the `CLI` key or the
|
1006
|
+
[Toys::Tool#cli method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:cli).
|
1007
|
+
These return the current instance of
|
1008
|
+
[Toys::CLI](https://www.rubydoc.info/gems/toys-core/Toys/CLI) which is the
|
1009
|
+
"main" interface to Toys. In particular, it provides the
|
1010
|
+
[Toys::CLI#run method](https://www.rubydoc.info/gems/toys-core/Toys%2FCLI:run)
|
1011
|
+
which can be used to call another tool:
|
1012
|
+
|
1013
|
+
def run
|
1014
|
+
status = cli.run("greet", "rubyists", "-v")
|
1015
|
+
exit(status) unless status.zero?
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
Pass the tool name and arguments as arguments to the run method. It will
|
1019
|
+
execute, and return a process status object (i.e. 0 for success, and nonzero
|
1020
|
+
for error). Make sure you handle the exit status. For example, in most cases,
|
1021
|
+
you should probably exit if the tool you are calling returns a nonzero code.
|
1022
|
+
|
1023
|
+
### Helper Methods and Mixins
|
1024
|
+
|
1025
|
+
The methods of [Toys::Tool](https://www.rubydoc.info/gems/toys-core/Toys/Tool)
|
1026
|
+
are not the only methods available for your tool to call. We saw earlier that
|
1027
|
+
a tool can define additional methods that you can use as helpers.
|
1028
|
+
|
1029
|
+
You can also include **mixins**, which are modules that bring in a whole set of
|
1030
|
+
helper methods. Include a mixin using the `include` directive:
|
1031
|
+
|
1032
|
+
tool "greet" do
|
1033
|
+
include :terminal
|
1034
|
+
def run
|
1035
|
+
puts "This is a bold line.", :bold
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
Th mixins may be specified by providing a module itself, or by providing a
|
1040
|
+
**mixin name**. In the above example, we used `:terminal`, which is the name
|
1041
|
+
of a built-in mixin that Toys provides. Among other things, it defines a
|
1042
|
+
special `puts` method that lets you include style information such as `:bold`,
|
1043
|
+
which affects the display on ANSI-capable terminals.
|
1044
|
+
|
1045
|
+
For details on the standard mixins provided by Toys, see the modules under
|
1046
|
+
[Toys::StandardMixins](https://www.rubydoc.info/gems/toys-core/Toys/StandardMixins).
|
1047
|
+
We will look at a few examples of the use of these mixins below. You can also
|
1048
|
+
define your own mixins, as we will see in the next section on sharing code.
|
1049
|
+
|
397
1050
|
### Executing Subprocesses
|
398
1051
|
|
1052
|
+
Another common operation you might do in a tool is to execute other binaries.
|
1053
|
+
For example, you might write a tool that shells out to `scp` to copy files to
|
1054
|
+
a remote server.
|
1055
|
+
|
1056
|
+
Ruby itself provides a few convenient methods for simple execution, such as the
|
1057
|
+
[Kernel#system](http://ruby-doc.org/core/Kernel.html#method-i-system) method.
|
1058
|
+
However, these typically provide limited ability to control or interact with
|
1059
|
+
subprocess streams, and you need to remember to handle the exit status
|
1060
|
+
yourself. If you do want to exert any control over subprocesses, you can use
|
1061
|
+
[Process.spawn](http://ruby-doc.org/core/Process.html#method-c-spawn), or a
|
1062
|
+
higher-level wrapper such as the
|
1063
|
+
[open3 library](http://ruby-doc.org/stdlib/libdoc/open3/rdoc/index.html).
|
1064
|
+
|
1065
|
+
Another alternative is to use the `:exec` built-in mixin. This mixin provides
|
1066
|
+
convenient methods for the common cases of executing subprocesses and capturing
|
1067
|
+
their output, and a powerful block-based interface for controlling streams. The
|
1068
|
+
exec mixin also lets you set a useful default option that causes the tool to
|
1069
|
+
exit automatically if one of its subprocesses exits abnormally.
|
1070
|
+
|
1071
|
+
For more information, see the
|
1072
|
+
[Toys::StandardMixins::Exec mixin module](https://www.rubydoc.info/gems/toys-core/Toys/StandardMixins/Exec)
|
1073
|
+
and the underyling library
|
1074
|
+
[Toys::Utils::Exec](https://www.rubydoc.info/gems/toys-core/Toys/Utils/Exec).
|
1075
|
+
|
399
1076
|
### Formatting Output
|
400
1077
|
|
401
|
-
|
1078
|
+
Interacting with the user is a very common function of a command line tool, and
|
1079
|
+
many modern tools include intricately designed and styled output, and terminal
|
1080
|
+
effects such as progress bars and spinners. Toys provides several mixins that
|
1081
|
+
can help create nicer interfaces.
|
1082
|
+
|
1083
|
+
First, there is `:terminal`, which provides some basic terminal features such
|
1084
|
+
as styled output and simple spinners. For information, see the
|
1085
|
+
[Toys::StandardMixins::Terminal mixin module](https://www.rubydoc.info/gems/toys-core/Toys/StandardMixins/Terminal)
|
1086
|
+
and the underyling library
|
1087
|
+
[Toys::Utils::Terminal](https://www.rubydoc.info/gems/toys-core/Toys/Utils/Terminal).
|
1088
|
+
|
1089
|
+
If you prefer the venerable Highline library interface, Toys provides a mixin
|
1090
|
+
that makes Highline available. It also automatically installs the highline
|
1091
|
+
gem if it is not available. For more information, see the
|
1092
|
+
[Toys::StandardMixins::Highline mixin module](https://www.rubydoc.info/gems/toys-core/Toys/StandardMixins/Highline).
|
1093
|
+
|
1094
|
+
Additional mixins are forthcoming...
|
1095
|
+
|
1096
|
+
## Sharing Code
|
1097
|
+
|
1098
|
+
|
1099
|
+
|
1100
|
+
### Defining Mixins
|
1101
|
+
|
1102
|
+
|
1103
|
+
|
1104
|
+
### Using Constants
|
1105
|
+
|
1106
|
+
|
1107
|
+
|
1108
|
+
### Expanding Templates
|
1109
|
+
|
1110
|
+
|
1111
|
+
|
1112
|
+
#### Defining Templates
|
1113
|
+
|
402
1114
|
|
403
|
-
### Defining Templates
|
404
1115
|
|
405
1116
|
## Advanced Tool Definition Techniques
|
406
1117
|
|
1118
|
+
|
1119
|
+
|
407
1120
|
### Aliases
|
408
1121
|
|
409
|
-
|
1122
|
+
|
1123
|
+
|
1124
|
+
### Custom Acceptors
|
1125
|
+
|
1126
|
+
|
410
1127
|
|
411
1128
|
### Controlling Built-in Flags
|
412
1129
|
|
413
|
-
|
1130
|
+
|
1131
|
+
|
1132
|
+
### Middleware
|
1133
|
+
|
1134
|
+
|
1135
|
+
|
1136
|
+
## Toys Administration using the System Tools
|
1137
|
+
|
1138
|
+
|
414
1139
|
|
415
1140
|
## Embedding Toys
|