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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 712c3527fa2521408e41064789e32d6af61c71d6b1552059cbd076d3460ab93d
4
- data.tar.gz: 3434cd2b2486b7c296e3cedc4bed96ac12b68110e4f5a94ceca4885251469754
3
+ metadata.gz: c9e8109a98db77604b1b398e113a6c8dff058645fe36f63dd08c013e92c06330
4
+ data.tar.gz: 9c706a637f851fa847fcc125b706499132d2fbdbbde12aaa6327f60bf95a1e3d
5
5
  SHA512:
6
- metadata.gz: 88d801b601cd339ad650dd94814e5b49e26d0df8358b80fc93857571c09fb9e981c4a0f06e3751adf6fbfd5137812f07ed8f74f7dea33f3ce786be75b298727a
7
- data.tar.gz: 562e0dd6893833a2cf9132986d8ea272308a554cccfca64bfbe1a9c06c69018157f6914e9958218e2486d6be8a794aef272246a9e03e932db3d4f8c5fa29697f
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
- You can also use the core functionality of Toys to create your own command line
34
- binaries, by using the *toys-core* API, which is available as a separate gem.
35
- For more info on using toys-core, see
36
- [https://ruby-doc.info/gems/toys-core](https://ruby-doc.info/gems/toys-core).
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 --hello
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 also
99
- that the tools you create in that file are available only in this directory
100
- and its subdirectories. If you move outside the directory tree, Toys will not
101
- use that file. Thus, you can define tools scoped to particular directories and
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 also lets you create hierarchies of tools. The "system version" tool you
105
- tried earlier is an example. The "system" tool is treated as a namespace, and
106
- various subtools, such as "version", are available under that namespace.
107
-
108
- Toys provides a rich set of useful libraries for writing tools. It gives you a
109
- logger and automatically provides flags to control verbosity of log output. It
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
- out_to: :capture, err_to: :capture)
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 toys binary by writing **configuration files**.
24
+ actual behavior of the Toys binary by writing **Toys files**.
18
25
 
19
- Toys is a multi-command binary. You may define a collection of commands, called
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 *subtools*.
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 define **descriptions**, for the tool itself,
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 configuration in specifically-named **configuration files**
34
- and **configuration directories**. It searches for these in the current
35
- directory, its ancestors, and in a **configuration search path**.
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 add to
40
- your configuration.
46
+ tool can call, and **templates** which are prefabricated tools that you can
47
+ configure for your needs.
41
48
 
42
- Finally, Toys provides certain **built-in behavior**, including automatically
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 *tool name* consists of all the command line arguments until the first
59
- argument that begins with a hyphen (which is interpreted as a *flag*), until
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 *positional argument*), or until there are no more arguments.
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
- *namespace* for tools related to the Toys system, and `version` is one of its
71
- *subtools*. It prints the current Toys version.
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 blah
83
+ toys system frodo
77
84
 
78
- There is no subtool `blah` under the `system` namespace, so Toys works backward
79
- until it finds an existing tool. In this case, the `system` namespace itself
80
- does exist, so it is interpreted as the tool, and `blah` is interpreted as an
81
- argument passed to it.
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, its function is to take the argument `blah`,
85
- note that it has no subtool of that name, and print an error message. Most
86
- commonly, though, you might execute a namespace without arguments:
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 *online help screen* for the `system` namespace, which
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 "root"
94
- tool, the toplevel namespace:
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 blah
110
+ toys frodo
104
111
 
105
- If there is no tool called `blah` in the toplevel namespace, then once again,
106
- `blah` is interpreted as an argument to the root tool. The root tool responds
107
- by printing an error message that the `blah` tool does not exist.
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: you can provide short
118
- (single-character) flags with a single hyphen, or long flags with a double
119
- hyphen. You can also provide optional values for flags. Following are a few
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, and recursive subtool search).
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 subtool named `--recursive` exists.)
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. Positional arguments are recognized in order and may be
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 recognizes any
199
- number of positional arguments. Those arguments specify which tools to run and
200
- what arguments to pass to them. If, for example, you had a `build` tool and 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 `do`
207
- tool. (The `do` tool splits them using `,` as the delimiter.)
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` instead of a positional
218
- argument specifying the first tool for `do` to run. We need to force `do` to
219
- treat all its arguments as positional, and we can do that by starting with `--`
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
- Tools are defined by writing Toys *configuration files*. The simplest form of a
228
- configuration file is a file named `.toys.rb` (note the leading period) in the
229
- current working directory. Such a file may define tools that are available in
230
- the current directory, and for this section we will assume we are writing such
231
- a file. The following section on "Understanding Configurations" will cover the
232
- larger concerns of how configuration files are looked up and how multiple
233
- configurations interact.
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 Config Syntax
243
+ ### Basic Toys Syntax
236
244
 
237
- The format of a Toys configuration file is a Ruby DSL including method calls
238
- and nested blocks. The actual DSL is specified in the
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 a description, the flags and
243
- arguments recognized by the tool, and the actual functionality of 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. The short description is a
271
- generally a single string that is displayed with the tool name at the top of
272
- its help page, or in a subtool list. The long description can include multiple
273
- strings, which are displayed in multiple lines in the "description" section of
274
- the tool's help page. Long descriptions may include blank lines to separate
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 characters, and
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 required and optional positional arguments. Each argument
295
- must provide a name, which defines how the argument value is exposed to the
296
- tool at execution time. The above example uses the DSL method
297
- [DSL::Tool#optional_arg](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:optional_arg)
298
- to declare an optional argument named `:whom`. If the argument is provided on
299
- the command line e.g.
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
- Arguments may also be **required** which means they must be provided on the
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 DSL method
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 "arguments" do
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 arguments foo
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 arguments foo bar
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 arguments
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 arguments foo bar baz
358
+ toys args-demo foo bar baz
345
359
 
346
- Will also produce an error, since the tool does not provide an argument to
360
+ Will also produce an error, since the tool does not define an argument to
347
361
  match `"baz"`.
348
362
 
349
- You can also provide an "argument" to match all remaining unmatched arguments
350
- at the end, using the DSL method
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 "arguments" do
354
- optional_arg :arg2
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 arguments foo bar baz bey
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, we
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
- (guid is still incomplete)
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
- ### The Execution Script
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
- ### Namespaces
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
- ## Understanding Configurations
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
- ## Helper Methods and Modules
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
- ### The Standard Helpers
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
- ## Prefabricated Tools with Templates
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
- ### Includes
1122
+
1123
+
1124
+ ### Custom Acceptors
1125
+
1126
+
410
1127
 
411
1128
  ### Controlling Built-in Flags
412
1129
 
413
- ## The System Tools
1130
+
1131
+
1132
+ ### Middleware
1133
+
1134
+
1135
+
1136
+ ## Toys Administration using the System Tools
1137
+
1138
+
414
1139
 
415
1140
  ## Embedding Toys