toys 0.3.7.1 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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
|