toys 0.3.8 → 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +0 -1
- data/CHANGELOG.md +10 -0
- data/README.md +7 -9
- data/docs/guide.md +729 -19
- data/lib/toys/standard_cli.rb +32 -14
- data/lib/toys/templates/clean.rb +1 -1
- data/lib/toys/templates/gem_build.rb +8 -9
- data/lib/toys/templates/minitest.rb +18 -6
- data/lib/toys/templates/rdoc.rb +145 -0
- data/lib/toys/templates/rubocop.rb +13 -2
- data/lib/toys/templates/yardoc.rb +132 -10
- data/lib/toys/version.rb +1 -1
- metadata +5 -5
- data/docs/tutorial.md +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c487937ebe79d0f42a022cc6bab700a49e684e5ec2c4dbfb6af52acb60b4876e
|
4
|
+
data.tar.gz: 65c95d766c8b2172099ed6be196a46740b4e05753bb3df455daf54c88ab4e6fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13c9507f71773f10696ba52f017975b87204329b1e8ec54fc2c6fdf4ec788469badde38b71573766e425bddd0cdd409ce80e76aa77ad0169e7d15a6e1fb38c4e
|
7
|
+
data.tar.gz: b7aaacef8a2017439390647ce2d519081ccda7ee27f34a3d06881040d2ebb6d906ea4e06d64dbc24179ed6642cb0cf2c1651facfb616a97fb8951db0e045a6fc
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.3.9 / 2018-06-24
|
4
|
+
|
5
|
+
* CHANGED: Removed alias_as directive since it's incompatible with selective loading.
|
6
|
+
* ADDED: Ability to define named templates in Toys files
|
7
|
+
* ADDED: Ability to disable argument parsing
|
8
|
+
* ADDED: Rdoc template
|
9
|
+
* ADDED: Exec#exec_proc and Exec#exec_tool that supports all the stream redirects
|
10
|
+
* IMPROVED: Acceptors can be looked up recursively in the same way as mixins and templates
|
11
|
+
* FIXED: Templates were not activating needed gems
|
12
|
+
|
3
13
|
### 0.3.8 / 2018-06-10
|
4
14
|
|
5
15
|
* CHANGED: Renamed helpers to mixins.
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ them. Furthermore, when writing new scripts, I was repeating the same
|
|
16
16
|
OptionParser boilerplate and common functionality.
|
17
17
|
|
18
18
|
Toys was designed to address those problems by providing a framework for
|
19
|
-
writing and organizing command line scripts. You provide the actual
|
19
|
+
writing and organizing your own command line scripts. You provide the actual
|
20
20
|
functionality, and Toys takes care of all the other details expected from a
|
21
21
|
good command line tool. It provides a streamlined interface for defining and
|
22
22
|
handling command line flags and positional arguments, and sensible ways to
|
@@ -25,14 +25,14 @@ usage information at a glance, and it also provides a search feature to help
|
|
25
25
|
you find the script you need.
|
26
26
|
|
27
27
|
Toys can also be used to share scripts. For example, it can be used instead of
|
28
|
-
Rake to provide build and test scripts for a project
|
28
|
+
Rake to provide build and test scripts for a project—tools that, unlike Rake
|
29
29
|
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
33
|
Unlike most command line frameworks, Toys is *not primarily* designed to help
|
34
34
|
you build and ship a custom command line binary written in Ruby. However, you
|
35
|
-
*can* use it in that way by building
|
35
|
+
*can* use it in that way by building with the "toys-core" API, available as a
|
36
36
|
separate gem. For more info on using toys-core, see
|
37
37
|
https://ruby-doc.info/gems/toys-core
|
38
38
|
|
@@ -63,9 +63,9 @@ The "system version" tool displays the current version of the toys gem.
|
|
63
63
|
|
64
64
|
### Write your first tool
|
65
65
|
|
66
|
-
You can define tools by creating
|
67
|
-
editor, create a new file called `.toys.rb` (note the
|
68
|
-
|
66
|
+
You can define tools by creating a *Toys file*. Go into any directory, and,
|
67
|
+
using your favorite editor, create a new file called `.toys.rb` (note the
|
68
|
+
leading period). Copy the following into the file, and save it:
|
69
69
|
|
70
70
|
tool "greet" do
|
71
71
|
desc "My first tool!"
|
@@ -110,9 +110,7 @@ and basic console-based interfaces, and another library that makes it easy to
|
|
110
110
|
spawn and control subprocesses. You can also take advantage of a variety of
|
111
111
|
third-party libraries such as Highline and TTY.
|
112
112
|
|
113
|
-
For a more detailed look at Toys, see the
|
114
|
-
{file:docs/tutorial.md Extended Tutorial} and the
|
115
|
-
{file:docs/guide.md User Guide}.
|
113
|
+
For a more detailed look at Toys, see the {file:docs/guide.md User Guide}.
|
116
114
|
|
117
115
|
## Contributing
|
118
116
|
|
data/docs/guide.md
CHANGED
@@ -10,14 +10,13 @@ write and organize scripts to automate their workflows.
|
|
10
10
|
|
11
11
|
Unlike most command line frameworks, Toys is *not primarily* designed to help
|
12
12
|
you build and ship a custom command line binary written in Ruby. Rather, it
|
13
|
-
provides a single
|
14
|
-
by writing configuration files
|
15
|
-
|
16
|
-
**toys-core** library.)
|
13
|
+
provides a single binary called `toys`. You define the commands recognized by
|
14
|
+
the Toys binary by writing configuration files. (You can, however, build your
|
15
|
+
own custom command line binary using the related **toys-core** library.)
|
17
16
|
|
18
17
|
This user's guide covers everything you need to know to use Toys effectively.
|
19
18
|
|
20
|
-
## Conceptual
|
19
|
+
## Conceptual Overview
|
21
20
|
|
22
21
|
Toys is a command line *framework*. It provides a binary called `toys` along
|
23
22
|
with basic functions such as argument parsing and online help. You provide the
|
@@ -42,9 +41,9 @@ directories**. It searches for these in the current directory, its ancestors,
|
|
42
41
|
and in the Toys **search path**.
|
43
42
|
|
44
43
|
Toys provides various features to help you write tools. This includes providing
|
45
|
-
a **logger** for each tool, **
|
46
|
-
|
47
|
-
configure for your needs.
|
44
|
+
a **logger** for each tool, **mixins** that provide common functions a tool can
|
45
|
+
call (such as controlling subprocesses and styling output), and **templates**
|
46
|
+
which are prefabricated tools that you can configure for your needs.
|
48
47
|
|
49
48
|
Finally, Toys provides useful **built-in behavior**, including automatically
|
50
49
|
providing flags to display help screens and set verbosity. It also includes a
|
@@ -89,7 +88,7 @@ argument.
|
|
89
88
|
|
90
89
|
Namespaces such as `system` are themselves tools and can be executed like any
|
91
90
|
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.
|
91
|
+
no subtool of that name, and prints an error message. More commonly, though,
|
93
92
|
you might execute a namespace without arguments:
|
94
93
|
|
95
94
|
toys system
|
@@ -128,17 +127,18 @@ optional **values** for flags. Following are a few examples.
|
|
128
127
|
|
129
128
|
Pass a single short flag (for verbose output).
|
130
129
|
|
131
|
-
toys -v
|
130
|
+
toys system version -v
|
132
131
|
|
133
132
|
Pass multiple long flags (for verbose output and recursive subtool search).
|
134
133
|
|
135
|
-
toys --verbose --recursive
|
134
|
+
toys system version --verbose --recursive
|
136
135
|
|
137
136
|
You can combine short flags. This does the same as the previous example.
|
138
137
|
|
139
|
-
toys -rv
|
138
|
+
toys system version -rv
|
140
139
|
|
141
|
-
Pass a value using a long flag.
|
140
|
+
Pass a value using a long flag. The root tool supports the `--search` flag to
|
141
|
+
search for tools that have the given keyword.
|
142
142
|
|
143
143
|
toys --search=build
|
144
144
|
toys --search build
|
@@ -1095,46 +1095,756 @@ Additional mixins are forthcoming...
|
|
1095
1095
|
|
1096
1096
|
## Sharing Code
|
1097
1097
|
|
1098
|
-
|
1098
|
+
As you accumulate additional and more complex tools, you may find that some of
|
1099
|
+
your tools need to share some common configuration, data, or logic. You might,
|
1100
|
+
for example, have a set of admin scripts that need to do some common
|
1101
|
+
authentication. This section describes several techniques for sharing code
|
1102
|
+
between tools, and describes the scope of Ruby structures, such as methods,
|
1103
|
+
classes, and constants, that you might define in your tools.
|
1099
1104
|
|
1100
1105
|
### Defining Mixins
|
1101
1106
|
|
1107
|
+
We saw earlier that you can mix a module (with all its methods) into your tool
|
1108
|
+
using the `include` directive. You can specify a module itself, or the name of
|
1109
|
+
a built-in mixin such as `:exec` or `:terminal`. But you can also define your
|
1110
|
+
own mixin using the `mixin` directive. A mixin defined in a tool can be
|
1111
|
+
`include`d in that tool or any of its subtools or their subtools, recursively,
|
1112
|
+
so it's a useful way to share code. Here's how that works.
|
1113
|
+
|
1114
|
+
Define a mixin using the `mixin` directive, and give it a name and a block. In
|
1115
|
+
the block, you can define methods that will be made available to any tool that
|
1116
|
+
includes the mixin, in the same way that you can include a Ruby module.
|
1117
|
+
|
1118
|
+
(Unlike full modules, however, mixins allow only methods to be shared. Mixins
|
1119
|
+
do not support constants. See the next section on using constants to learn how
|
1120
|
+
constants are treated by Toys.)
|
1102
1121
|
|
1122
|
+
Here's an example. Suppose you had common setup code that you wanted to share
|
1123
|
+
among your testing tools.
|
1124
|
+
|
1125
|
+
tool "test" do
|
1126
|
+
# Define a mixin, which is just a collection of methods.
|
1127
|
+
mixin "common_test_code" do
|
1128
|
+
def setup
|
1129
|
+
# Do setup here
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
tool "unit" do
|
1134
|
+
# Include the mixin by name
|
1135
|
+
include "common_test_code"
|
1136
|
+
def run
|
1137
|
+
setup # Mixin methods are made available
|
1138
|
+
puts "run only unit tests here..."
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
tool "integration" do
|
1143
|
+
include "common_test_code"
|
1144
|
+
def run
|
1145
|
+
setup
|
1146
|
+
puts "run only integration tests here..."
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
A mixin is available to the tool in which it is defined, and any subtools and
|
1152
|
+
descendants defined at the same point in the Toys search path, but not from
|
1153
|
+
tools defined in a different point in the search path. For example, if you
|
1154
|
+
define a mixin in a file located in a `.toys` directory, it will be visible to
|
1155
|
+
descendant tools defined in that same directory, but not in a different `.toys`
|
1156
|
+
directory.
|
1157
|
+
|
1158
|
+
A common technique, for example, would be to define a mixin in the index file
|
1159
|
+
in a Toys directory. You can then include it from any subtools defined in other
|
1160
|
+
files in that same directory.
|
1103
1161
|
|
1104
1162
|
### Using Constants
|
1105
1163
|
|
1164
|
+
You can define and use Ruby cconstants, i.e. names beginning with a capital
|
1165
|
+
letter, in a Toys file. However, they are subject to Ruby's rules regarding
|
1166
|
+
constant scope and lookup, which can be confusing, especially in a DSL. Toys
|
1167
|
+
tries to simplify those rules and make constant behavior somewhat tractable,
|
1168
|
+
but if you do use constants (which includes modules and classes defined in a
|
1169
|
+
Toys file), it is important to understand how they work.
|
1170
|
+
|
1171
|
+
Constants in Toys are visible only within the Toys file in which they are
|
1172
|
+
defined. They always behave as though they are defined at the "top level" of
|
1173
|
+
the file. Even if you define a constant lexically "inside" a tool or a mixin,
|
1174
|
+
the constant does _not_ end up connected to that tool or mixin; it is always
|
1175
|
+
defined at the file level.
|
1106
1176
|
|
1177
|
+
tool "test" do
|
1178
|
+
tool "unit" do
|
1179
|
+
# This constant is now usable for the rest of the file
|
1180
|
+
API_KEY_FOR_TESTING = "12345"
|
1181
|
+
def run
|
1182
|
+
# It is visible here
|
1183
|
+
puts API_KEY_FOR_TESTING
|
1184
|
+
end
|
1185
|
+
end
|
1107
1186
|
|
1108
|
-
|
1187
|
+
tool "integration" do
|
1188
|
+
def run
|
1189
|
+
# And it is still visible here
|
1190
|
+
puts API_KEY_FOR_TESTING
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
end
|
1109
1194
|
|
1195
|
+
Because of this, it is highly recommended that you define constants only at the
|
1196
|
+
top level of a Toys file, so it doesn't "look" like it is scoped to something
|
1197
|
+
smaller. In particular, do not attempt to define constants in a mixin. The
|
1198
|
+
constants will not actually be connected to the mixin, and will not be
|
1199
|
+
available to tools that include the mixin.
|
1110
1200
|
|
1201
|
+
Modules and classes defined using the `module` or `class` keyword, are also
|
1202
|
+
constants, and thus follow the same rules. So you could, for example, define a
|
1203
|
+
"mixin" module like this:
|
1204
|
+
|
1205
|
+
module CommonTestCode
|
1206
|
+
def setup
|
1207
|
+
# Do setup here
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
tool "test" do
|
1212
|
+
tool "unit" do
|
1213
|
+
# Include the modules as a mixin
|
1214
|
+
include CommonTestCode
|
1215
|
+
def run
|
1216
|
+
setup # Module methods are made available
|
1217
|
+
puts "run only unit tests here..."
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
tool "integration" do
|
1222
|
+
include CommonTestCode
|
1223
|
+
def run
|
1224
|
+
setup
|
1225
|
+
puts "run only integration tests here..."
|
1226
|
+
end
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
|
1230
|
+
The difference between this technique and using the `mixin` directive we saw
|
1231
|
+
earlier, is the scope. The module here is accessed via a constant, and so, like
|
1232
|
+
any constant, it is visible only in the same file it is defined in. The `mixin`
|
1233
|
+
directive creates mixins that are visible from _all_ files at the same point in
|
1234
|
+
the search path.
|
1235
|
+
|
1236
|
+
### Templates
|
1237
|
+
|
1238
|
+
One final way to share code is to expand a **template**.
|
1239
|
+
|
1240
|
+
A template is a class that inserts a bunch of lines into a Toys file. It is
|
1241
|
+
often used to "instantiate" prefabricated tools. For instance, Toys comes with
|
1242
|
+
a template called "minitest" that can generate a test tool for you. You
|
1243
|
+
instantiate it using the `expand` directive in your Toys file, like this:
|
1244
|
+
|
1245
|
+
expand :minitest
|
1246
|
+
|
1247
|
+
And it will generate a tool called "test" that runs your test suite.
|
1248
|
+
|
1249
|
+
Most templates generate one or more complete tools. However, it is possible for
|
1250
|
+
a template to generate just part of a tool, such as one or more description
|
1251
|
+
directives. In general, expanding a template simply adds directives to your
|
1252
|
+
Toys file.
|
1253
|
+
|
1254
|
+
Many templates can be configured with options such as the name of the tool to
|
1255
|
+
generate, or details of the tool's behavior. This is done by passing additional
|
1256
|
+
arguments to the `expand` directive, such as:
|
1257
|
+
|
1258
|
+
expand :minitest, name: "unit-test", warnings: true
|
1259
|
+
|
1260
|
+
Alternatively, you may provide a block to `expand`. It will yield the template
|
1261
|
+
to your block, letting you modify its properties:
|
1262
|
+
|
1263
|
+
expand :minitest do |tmpl|
|
1264
|
+
tmpl.name "unit-test"
|
1265
|
+
tmpl.warnings = true
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
Toys provides several built-in templates that are useful for project and gem
|
1269
|
+
development, including templates that generate build, test, and documentation
|
1270
|
+
tools. You can read more about these templates in the next section on using
|
1271
|
+
Toys as a Rake replacement.
|
1272
|
+
|
1273
|
+
You may also write your own templates. Here's how...
|
1111
1274
|
|
1112
1275
|
#### Defining Templates
|
1113
1276
|
|
1277
|
+
One way to define a template is to use the `template` directive. Like the
|
1278
|
+
`mixin` directive, this creates a named template that you can access inside the
|
1279
|
+
current tool and any of its subtools.
|
1114
1280
|
|
1281
|
+
Following is a simple template example:
|
1115
1282
|
|
1116
|
-
|
1283
|
+
template "greet" do
|
1284
|
+
def initialize(name: "greet", whom: "world")
|
1285
|
+
@name = name
|
1286
|
+
@whom = whom
|
1287
|
+
end
|
1288
|
+
attr_accessor :name
|
1289
|
+
attr_accessor :whom
|
1290
|
+
|
1291
|
+
to_expand do |template|
|
1292
|
+
tool template.name do
|
1293
|
+
desc "A greeting tool generated from a template"
|
1294
|
+
run do
|
1295
|
+
puts "Hello, #{template.whom}!"
|
1296
|
+
end
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
expand "greet"
|
1302
|
+
|
1303
|
+
expand "greet", name: "greet-ruby", whom: "ruby"
|
1304
|
+
|
1305
|
+
Above we created a template called "greet". A template is simply a class. It
|
1306
|
+
will typically have a constructor, and methods to access configuration
|
1307
|
+
properties. When the template is expanded, the class gets instantiated, and you
|
1308
|
+
can set those properties.
|
1309
|
+
|
1310
|
+
Next, a template has a `to_expand` block. This block contains the Toys file
|
1311
|
+
directives that should be generated by the template. The template object is
|
1312
|
+
passed to the block, so it can access the template configuration when
|
1313
|
+
generating directives. The "greet" template in the above example generates a
|
1314
|
+
tool whose name is set by the template's `name` property.
|
1315
|
+
|
1316
|
+
Notice that in the above example, we used `run do`, providing a _block_ for the
|
1317
|
+
tool's execution, rather than `def run`, providing a method. Both forms are
|
1318
|
+
valid and will work in a template (as well in a normal Toys file), but the
|
1319
|
+
block form is often useful in a template because you can access the `template`
|
1320
|
+
variable inside the block, whereas it would not be accessible if you defined a
|
1321
|
+
method. Similarly, if your template generates helper methods, and the body of
|
1322
|
+
those methods need access to the `template` variable, you can use
|
1323
|
+
[Module#define_method](http://ruby-doc.org/core/Module.html#method-i-define_method)
|
1324
|
+
instead of `def`.
|
1325
|
+
|
1326
|
+
By convention, it is a good idea for configuration options for your template to
|
1327
|
+
be settable either as arguments to the constructor, or as `attr_accessor`
|
1328
|
+
properties. In this way, when you expand the template, options can be provided
|
1329
|
+
either as arguments to the `expand` directive, or in a block passed to the
|
1330
|
+
directive by setting properties on the template object.
|
1331
|
+
|
1332
|
+
#### Template Classes
|
1333
|
+
|
1334
|
+
Finally, templates are classes, and you can create a template directly as a
|
1335
|
+
class by including the
|
1336
|
+
[Toys::Template](https://www.rubydoc.info/gems/toys-core/Toys/Template) module
|
1337
|
+
in your class definition.
|
1338
|
+
|
1339
|
+
class GreetTemplate
|
1340
|
+
include Toys::Template
|
1341
|
+
|
1342
|
+
def initialize(name: "greet", whom: "world")
|
1343
|
+
@name = name
|
1344
|
+
@whom = whom
|
1345
|
+
end
|
1346
|
+
attr_accessor :name
|
1347
|
+
attr_accessor :whom
|
1348
|
+
|
1349
|
+
to_expand do |template|
|
1350
|
+
tool template.name do
|
1351
|
+
desc "A greeting tool generated from a template"
|
1352
|
+
run do
|
1353
|
+
puts "Hello, #{template.whom}!"
|
1354
|
+
end
|
1355
|
+
end
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
expand GreetTemplate, name: "greet-ruby", whom: "ruby"
|
1360
|
+
|
1361
|
+
Remember that classes created this way are constants, and so the name
|
1362
|
+
`GreetTemplate` is available only inside the Toys file where it was defined.
|
1363
|
+
|
1364
|
+
You must `include Toys::Template` if you define a template directly as a class,
|
1365
|
+
but you can omit it if you use the `template` directive to define the template.
|
1366
|
+
|
1367
|
+
Defining templates as classes is also a useful way for third-party gems to
|
1368
|
+
provide Toys integration. For example, suppose you are writing a code analysis
|
1369
|
+
gem, and you want to make it easy for your users to create a Toys tool that
|
1370
|
+
invokes your analysis. Just write a template class in your gem, maybe named
|
1371
|
+
`MyAnalysis::ToysTemplate`. Now, just instruct your users to include the
|
1372
|
+
following in their Toys file:
|
1373
|
+
|
1374
|
+
require "my_analysis"
|
1375
|
+
expand MyAnalysis::ToysTemplate
|
1376
|
+
|
1377
|
+
## Toys as a Rake Replacement
|
1378
|
+
|
1379
|
+
Toys was designed to organize scripts that may be "scoped" to a project or
|
1380
|
+
directory. Rake is also commonly used for this purpose: you can write a
|
1381
|
+
"Rakefile" that defines rake tasks scoped to a directory. In many cases, Toys
|
1382
|
+
can be used as a replacement for Rake. Indeed, the Toys repository itself,
|
1383
|
+
rather than a Rakefile, contains a `.toys.rb` file that defines tools for
|
1384
|
+
running tests, builds, and so forth.
|
1385
|
+
|
1386
|
+
This section will explore the differences between Toys and Rake, and describe
|
1387
|
+
how to use Toys for some of the things traditionally done with Rake.
|
1388
|
+
|
1389
|
+
### Comparing Toys and Rake
|
1390
|
+
|
1391
|
+
Although Toys and Rake serve many of the same use cases, they have very
|
1392
|
+
different design goals, and it is useful to understand them.
|
1393
|
+
|
1394
|
+
Rake's design is based on the classic "make" tool often provided in unix
|
1395
|
+
development environments. This design focuses on _targets_ and _dependencies_,
|
1396
|
+
and is meant for a world where you invoke an external compiler tool whenever
|
1397
|
+
changes are made to an individual source file or any of its dependencies. This
|
1398
|
+
"declarative" approach expresses very well the build process for programs
|
1399
|
+
written in C and similar compiled languages.
|
1400
|
+
|
1401
|
+
Ruby, however, does not have an external compiler, and certainly not one that
|
1402
|
+
requires separate invocation for each source file as does the C compiler. So
|
1403
|
+
although Rake does support file dependencies, they are much less commonly used
|
1404
|
+
than in their Makefile cousins. Instead, in practice, most Rake tasks are not
|
1405
|
+
connected to a dependency at all; they are simply standalone tasks, what would
|
1406
|
+
be called "phony" targets in Makefile parlance. Such tasks are more imperative
|
1407
|
+
than declarative.
|
1408
|
+
|
1409
|
+
The Toys approach to build tools simply embraces the fact that our build
|
1410
|
+
processes already tend to be imperative. So unlike Rake, Toys does not provide
|
1411
|
+
syntax for describing targets and dependencies, since we generally don't have
|
1412
|
+
them in Ruby programs. Instead, it is optimized for writing tools.
|
1413
|
+
|
1414
|
+
For example, Rake provides a primitive mechanism for passing arguments to a
|
1415
|
+
task, but it is clumsy and quite different from most unix programs. However, to
|
1416
|
+
do otherwise would clash with Rake's design goal of treating tasks as targets
|
1417
|
+
and dependencies. Toys does not have those design goals, so it is able to
|
1418
|
+
embrace the familiar ways to pass command line arguments.
|
1419
|
+
|
1420
|
+
Toys actually borrows some of its design from the "mix" build tool used for
|
1421
|
+
Elixir and Erlang programs. Unlike C, the Erlang and Elixir compilers do their
|
1422
|
+
own dependency management, so mix does not require those capabilities. Instead,
|
1423
|
+
it focuses on making it easy to define imperative tasks.
|
1424
|
+
|
1425
|
+
All told, this boils down to the principle of using the best tool for the job.
|
1426
|
+
There will be times when you need to express file-based dependencies in some of
|
1427
|
+
your build tasks. Rake will continue to be your friend in those cases. However,
|
1428
|
+
for high level tasks such as "run my tests", "build my YARD documentation", or
|
1429
|
+
"release my gem", you may find Toys easier to use.
|
1430
|
+
|
1431
|
+
### From Rakefiles to Toys Files
|
1432
|
+
|
1433
|
+
If you want to migrate some of your project's build tasks from Rake to Toys,
|
1434
|
+
there are some common patterns.
|
1435
|
+
|
1436
|
+
When you use Rake for these tasks, you will typically require a particular file
|
1437
|
+
from your Rakefile, and/or write some code. Different tools will have different
|
1438
|
+
mechanisms for generating tasks. For example, a test task might be defined like
|
1439
|
+
this:
|
1440
|
+
|
1441
|
+
require "rake/testtask"
|
1442
|
+
Rake::TestTask.new do |t|
|
1443
|
+
t.test_files = FileList["test/test*.rb"]
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
In Toys, templates are the standard mechanism for generating tools.
|
1447
|
+
|
1448
|
+
expand :minitest do |t|
|
1449
|
+
t.files = ["test/test*.rb"]
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
The following sections will describe some of the built-in templates provided by
|
1453
|
+
Toys to generate common build tools.
|
1454
|
+
|
1455
|
+
Note that Rakefiles and Toys files can coexist in the same directory, so you
|
1456
|
+
can use either or both tools, depending on your needs.
|
1457
|
+
|
1458
|
+
### Running Tests
|
1459
|
+
|
1460
|
+
Toys provides a built-in template for minitest, called `:minitest`. It is
|
1461
|
+
implemented by the template class {Toys::Templates::Minitest}, and it uses the
|
1462
|
+
minitest gem, which is provided with most recent versions of Ruby.
|
1463
|
+
|
1464
|
+
expand :minitest, files: ["test/test*.rb"], libs: ["lib", "ext"]
|
1465
|
+
|
1466
|
+
See the {Toys::Templates::Minitest} documentation for details on the various
|
1467
|
+
options.
|
1468
|
+
|
1469
|
+
If you want to enforce code style using the "rubocop" gem, you can use the
|
1470
|
+
built-in `:rubocop` template:
|
1471
|
+
|
1472
|
+
expand :rubocop
|
1473
|
+
|
1474
|
+
See the {Toys::Templates::Rubocop} documentation for details on the available
|
1475
|
+
options.
|
1476
|
+
|
1477
|
+
### Building and Releasing Gems
|
1478
|
+
|
1479
|
+
The `:gem_build` built-in template can generate a variety of build and release
|
1480
|
+
tools for gems, and is a useful alternative to the Rake tasks provided by
|
1481
|
+
bundler. It is implemented by {Toys::Templates::GemBuild}.
|
1482
|
+
|
1483
|
+
Expanding `:gem_build` by default looks for a gemspec file in the current
|
1484
|
+
directory, and builds that gem into a `pkg` directory. You can also build a
|
1485
|
+
specific gem if you have multiple gemspec files.
|
1117
1486
|
|
1487
|
+
You may also configure the template so it also releases the gem to Rubygems
|
1488
|
+
(using your stored Rubygems credentials), by setting the `push_gem` option.
|
1489
|
+
For example, here is how to generate a "release" tool that builds and releases
|
1490
|
+
your gem:
|
1118
1491
|
|
1492
|
+
expand :gem_build, name: "release", push_gem: true
|
1493
|
+
|
1494
|
+
See the {Toys::Templates::GemBuild} documentation for details on the various
|
1495
|
+
options for build tools.
|
1496
|
+
|
1497
|
+
To generate a "clean" tool, you can use the `:clean` built-in template. For
|
1498
|
+
example:
|
1499
|
+
|
1500
|
+
expand :clean, paths: ["pkg", "doc", "tmp"]
|
1501
|
+
|
1502
|
+
See the {Toys::Templates::Clean} documentation for details on the various
|
1503
|
+
options for clean tools.
|
1504
|
+
|
1505
|
+
### Building Documentation
|
1506
|
+
|
1507
|
+
Toys provides an `:rdoc` template for creating tools that generate RDoc
|
1508
|
+
documentation, and a `:yardoc` template for creating tools that generate YARD.
|
1509
|
+
Both templates provide a variety of options for controlling documentation
|
1510
|
+
generation. See {Toys::Templates::Rdoc} and {Toys::Templates::Yardoc} for
|
1511
|
+
detailed information.
|
1512
|
+
|
1513
|
+
Here's an example for YARD:
|
1514
|
+
|
1515
|
+
expand :yardoc, protected: true, markup: "markdown"
|
1516
|
+
|
1517
|
+
### Gem Example
|
1518
|
+
|
1519
|
+
Let's look at a complete example that combines the techniques above to provide
|
1520
|
+
all the basic tools for a Ruby gem. It includes:
|
1521
|
+
|
1522
|
+
* A testing tool that can be run with `toys test`
|
1523
|
+
* Code style checking using Rubocop, run with `toys rubocop`
|
1524
|
+
* Documentation building using Yardoc, run with `toys yardoc`
|
1525
|
+
* Gem building, run with `toys build`
|
1526
|
+
* Gem build and release to Rubygems.org, run with `toys release`
|
1527
|
+
* A full CI tool, run with `toys ci`, that can be run from your favorite CI
|
1528
|
+
system. It runs the tests and style checks, and checks (but does not
|
1529
|
+
actually build) the documentation for warnings and completeness.
|
1530
|
+
|
1531
|
+
Below is the full annotated `.toys.rb` file. For many gems, you could drop this
|
1532
|
+
into the gem source repo with minimal or no modifications. Indeed, this is
|
1533
|
+
essentially identical to the Toys files provided for the **toys** and
|
1534
|
+
**toys-core** gems themselves.
|
1535
|
+
|
1536
|
+
# A "clean" tool that cleans out gem builds (from the pkg directory), and
|
1537
|
+
# documentation builds (from doc and .yardoc)
|
1538
|
+
expand :clean, paths: ["pkg", "doc", ".yardoc"]
|
1539
|
+
|
1540
|
+
# This is the "test" tool.
|
1541
|
+
expand :minitest, libs: ["lib", "test"]
|
1542
|
+
|
1543
|
+
# This is the "rubocop" tool.
|
1544
|
+
expand :rubocop
|
1545
|
+
|
1546
|
+
# This is the "yardoc" tool. We cause it to fail on warnings and if there
|
1547
|
+
# are any undocumented objects, which is useful for CI. We also configure
|
1548
|
+
# the tool so it recognizes the "--no-output" flag. The CI tool will use
|
1549
|
+
# this flag to invoke yardoc but suppress output, because it just wants to
|
1550
|
+
# check for warnings.
|
1551
|
+
expand :yardoc do |t|
|
1552
|
+
t.generate_output_flag = true
|
1553
|
+
t.fail_on_warning = true
|
1554
|
+
t.fail_on_undocumented_objects = true
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
# The normal "build" tool that just builds a gem into the pkg directory.
|
1558
|
+
expand :gem_build
|
1559
|
+
|
1560
|
+
# A full gem "release" tool that builds the gem, and pushes it to rubygems.
|
1561
|
+
# This assumes your local rubygems configuration is set up with the proper
|
1562
|
+
# credentials.
|
1563
|
+
expand :gem_build, name: "release", push_gem: true
|
1564
|
+
|
1565
|
+
# Now we have a full CI tool. It runs the test, rubocop, and yardoc tools
|
1566
|
+
# and checks for errors. This tool could be invoked from Travis-CI or
|
1567
|
+
# similar CI system.
|
1568
|
+
tool "ci" do
|
1569
|
+
# The :exec mixin provides the exec_tool() method that we will use to run
|
1570
|
+
# other tools and check their exit status.
|
1571
|
+
include :exec
|
1572
|
+
# The :terminal mixin provides an enhanced "puts" method that lets you
|
1573
|
+
# write styled text to the terminal.
|
1574
|
+
include :terminal
|
1575
|
+
|
1576
|
+
# A helper method, that runs a tool and outputs the result. It also
|
1577
|
+
# terminates if the tool reported an error.
|
1578
|
+
def run_stage(name, tool)
|
1579
|
+
if exec_tool(tool).success?
|
1580
|
+
puts("** #{name} passed", :green, :bold)
|
1581
|
+
puts
|
1582
|
+
else
|
1583
|
+
puts("** CI terminated: #{name} failed!", :red, :bold)
|
1584
|
+
exit(1)
|
1585
|
+
end
|
1586
|
+
end
|
1587
|
+
|
1588
|
+
# The main run method. It just calls the above helper method for the
|
1589
|
+
# three tools we want to run for CI
|
1590
|
+
def run
|
1591
|
+
run_stage("Tests", ["test"])
|
1592
|
+
run_stage("Style checker", ["rubocop"])
|
1593
|
+
run_stage("Docs generation", ["yardoc", "--no-output"])
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
## Advanced Tool Definition Techniques
|
1598
|
+
|
1599
|
+
This section covers some additional features that are often useful for writing
|
1600
|
+
tools. I've labeled them "advanced", but all that really means is that this
|
1601
|
+
user's guide didn't happen to have covered them until this section. Each of
|
1602
|
+
these features is very useful for certain types of tools, and it is good at
|
1603
|
+
least to know that you *can* do these things, even if you don't use them
|
1604
|
+
regularly.
|
1119
1605
|
|
1120
1606
|
### Aliases
|
1121
1607
|
|
1608
|
+
An **alias** is simply an alternate name for a tool. For example, suppose you
|
1609
|
+
have a tool called `test` that you run with `toys test`. You could define an
|
1610
|
+
alias `t` that points to `test`; then you can run the same tool with `toys t`.
|
1611
|
+
|
1612
|
+
To define an alias, use the `alias_tool` directive:
|
1613
|
+
|
1614
|
+
tool "test" do
|
1615
|
+
# Define test tool here...
|
1616
|
+
end
|
1617
|
+
|
1618
|
+
alias_tool "t", "test"
|
1619
|
+
|
1620
|
+
You may create an alias of a subtool, but the alias must have the same parent
|
1621
|
+
(namespace) tool as the target tool. For example:
|
1622
|
+
|
1623
|
+
tool "gem" do
|
1624
|
+
tool "test" do
|
1625
|
+
# Define test tool here...
|
1626
|
+
end
|
1122
1627
|
|
1628
|
+
# Allows you to invoke `toys gem t`
|
1629
|
+
alias_tool "t", "test"
|
1630
|
+
end
|
1123
1631
|
|
1124
1632
|
### Custom Acceptors
|
1125
1633
|
|
1634
|
+
We saw earlier that flags and positional arguments can have acceptors, which
|
1635
|
+
control the allowed format, and may also convert the string argument to a Ruby
|
1636
|
+
object. By default, Toys supports the same acceptors recognized by Ruby's
|
1637
|
+
OptionParser library. And like OptionParser, Toys also lets you define your own
|
1638
|
+
acceptors.
|
1639
|
+
|
1640
|
+
Define an acceptor using the `acceptor` directive. You provide a name for the
|
1641
|
+
acceptor, and specify how to validate input strings and how to convert input
|
1642
|
+
strings to Ruby objects. You may then reference the acceptor in that tool or
|
1643
|
+
any of its subtools or their subtools, recursively.
|
1644
|
+
|
1645
|
+
There are several ways to define an acceptor.
|
1646
|
+
|
1647
|
+
You may validate input strings against a regular expression, by passing the
|
1648
|
+
regex to the `acceptor` directive. You may also optionally provide a block to
|
1649
|
+
convert input strings to objects (or omit the block to use the original string
|
1650
|
+
as the option value.) For example, a simple hexadecimal input acceptor might
|
1651
|
+
look like this:
|
1652
|
+
|
1653
|
+
acceptor("hex", /^[0-9a-fA-F]+$/) { |input| input.to_i(16) }
|
1654
|
+
|
1655
|
+
You may also accept enum values by passing an array of valid values to the
|
1656
|
+
`acceptor` directive. Inputs will be matched against the `to_s` form of the
|
1657
|
+
given values, and will be converted to the value itself. For example, one way
|
1658
|
+
to accept integers from 1 to 5 is:
|
1659
|
+
|
1660
|
+
acceptor("1to5", [1, 2, 3, 4, 5])
|
1126
1661
|
|
1662
|
+
There are various other options. See the reference documentation for
|
1663
|
+
[Toys::DSL::Tool#acceptor](https://www.rubydoc.info/gems/toys-core/Toys%2FDSL%2FTool:acceptor).
|
1664
|
+
|
1665
|
+
An acceptor is available to the tool in which it is defined, and any subtools
|
1666
|
+
and descendants defined at the same point in the Toys search path, but not from
|
1667
|
+
tools defined in a different point in the search path. For example, if you
|
1668
|
+
define an acceptor in a file located in a `.toys` directory, it will be visible
|
1669
|
+
to descendant tools defined in that same directory, but not in a different
|
1670
|
+
`.toys` directory.
|
1671
|
+
|
1672
|
+
A common technique, for example, would be to define an acceptor in the index
|
1673
|
+
file in a Toys directory. You can then include it from any subtools defined in
|
1674
|
+
other files in that same directory.
|
1127
1675
|
|
1128
1676
|
### Controlling Built-in Flags
|
1129
1677
|
|
1678
|
+
Earlier we saw that certain flags are added automatically to every tool:
|
1679
|
+
`--verbose`, `--quiet`, `--help`, and so forth. You may occasionally want to
|
1680
|
+
disable some of these "built-in" flags. There are two ways to do so:
|
1681
|
+
|
1682
|
+
If you want to use one of the built-in flags for another purpose, simply define
|
1683
|
+
the flag as you choose. Flags explicitly defined by your tool take precedence
|
1684
|
+
over the built-ins.
|
1685
|
+
|
1686
|
+
For example, normally two built-in flags are provided to decrease the verbosity
|
1687
|
+
level: `-q` and `--quiet`. If you define `-q` yourself—for example to activate
|
1688
|
+
a "quick" mode—then `-q` will be repurposed for your flag, but `--quiet` will
|
1689
|
+
still be present to decrease verbosity.
|
1690
|
+
|
1691
|
+
# Repurposes -q to set the "quick" option instead of "quiet"
|
1692
|
+
flag :quick, "-q"
|
1693
|
+
|
1694
|
+
You may also completely disable a flag, and _not_ repurpose it, using the
|
1695
|
+
`disable_flag` directive. It lets you mark one or more flags as "never use".
|
1130
1696
|
|
1697
|
+
For example, if you disable the `-q` flag, then `-q` will no longer be a
|
1698
|
+
built-in flag that decreases the verbosity, but `--quiet` will remain. To
|
1699
|
+
completely disable decreasing the verbosity, disable both `-q` and `--quiet`.
|
1131
1700
|
|
1132
|
-
|
1701
|
+
# Disables -q but leaves --quiet
|
1702
|
+
disable_flag "-q"
|
1133
1703
|
|
1704
|
+
# Completely disables decreasing verbosity
|
1705
|
+
disable_flag "-q", "--quiet"
|
1134
1706
|
|
1707
|
+
### Disabling Argument Parsing
|
1135
1708
|
|
1136
|
-
|
1709
|
+
Normally Toys handles parsing command line arguments for you. This makes
|
1710
|
+
writing tools easier, but also allows Toys to generate documentation
|
1711
|
+
automatically for flags and arguments. However, occasionally you'll not want
|
1712
|
+
Toys to perform any parsing, but just to give you the command line arguments
|
1713
|
+
raw. One common case is if your tool turns around and passes its arguments to
|
1714
|
+
another subprocess.
|
1715
|
+
|
1716
|
+
To disable argument parsing, use the `disable_argument_parsing` directive. This
|
1717
|
+
directive disables parsing and validation of flags and positional arguments.
|
1718
|
+
(Thus, it is incompatible with specifying any flags or arguments for the tool.)
|
1719
|
+
Instead, you can retrieve the raw arguments using the
|
1720
|
+
[Toys::Tool#args method](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:args).
|
1721
|
+
|
1722
|
+
Here is an example that wraps calls to git:
|
1723
|
+
|
1724
|
+
tool "my-git" do
|
1725
|
+
desc "Prints a message, and then calls git normally"
|
1726
|
+
disable_argument_parsing
|
1727
|
+
def run
|
1728
|
+
puts "Calling my-git!"
|
1729
|
+
Kernel.exec(["git"] + args)
|
1730
|
+
end
|
1731
|
+
end
|
1137
1732
|
|
1733
|
+
### Activating Gems
|
1734
|
+
|
1735
|
+
Sometimes implementing a tool will require a third-party gem. Some mixins also
|
1736
|
+
utilize a gem. Toys provides a way to help the user install such gems if
|
1737
|
+
needed.
|
1738
|
+
|
1739
|
+
If the gem is needed to *define* the tool, use the `gem` directive to ensure
|
1740
|
+
the gem is installed and activated. This takes the name of the gem, and an
|
1741
|
+
optional set of version requirements. If a gem matching the given version
|
1742
|
+
requirements is installed, it is activated. If not, the gem is installed (which
|
1743
|
+
the user can confirm or abort). Or, if Toys is being run in a bundle, a message
|
1744
|
+
is printed informing the user that they need to add the gem to their Gemfile.
|
1745
|
+
|
1746
|
+
For example, here's a way to configure a tool with flags for each of the
|
1747
|
+
HighLine styles:
|
1748
|
+
|
1749
|
+
tool "highline-styles-demo" do
|
1750
|
+
gem "highline", "~> 2.0"
|
1751
|
+
require "highline"
|
1752
|
+
HighLine::BuiltinStyles::STYLES.each do |style|
|
1753
|
+
style = style.downcase
|
1754
|
+
flag style.to_sym, "--#{style}", "Apply #{style} to the text"
|
1755
|
+
end
|
1756
|
+
def run
|
1757
|
+
# ...
|
1758
|
+
|
1759
|
+
If the gem is *not* needed to define the tool, but is needed to *run* the tool,
|
1760
|
+
then you can call
|
1761
|
+
[Toys::Tool#gem](https://www.rubydoc.info/gems/toys-core/Toys%2FTool:gem) from
|
1762
|
+
your `run` method. Here's an example:
|
1763
|
+
|
1764
|
+
tool "rake" do
|
1765
|
+
disable_argument_passing
|
1766
|
+
def run
|
1767
|
+
gem "rake", "~> 12.0"
|
1768
|
+
Kernel.exec(["rake"] + args)
|
1769
|
+
end
|
1770
|
+
end
|
1138
1771
|
|
1772
|
+
If a gem satisfying the given version constraints is already activated, it
|
1773
|
+
remains active. If a gem with a conflicting version is already activated, an
|
1774
|
+
exception is raised.
|
1139
1775
|
|
1140
|
-
|
1776
|
+
If you are not in the Toys DSL context—e.g. you are writing a class-based
|
1777
|
+
mixin—you should use
|
1778
|
+
[Toys::Utils::Gems.activate](https://www.rubydoc.info/gems/toys-core/Toys%2FUtils%2FGems:activate)
|
1779
|
+
instead. For example:
|
1780
|
+
|
1781
|
+
Toys::Utils::Gems.activate("highline", "~> 2.0")
|
1782
|
+
|
1783
|
+
Note these methods are a bit different from the
|
1784
|
+
[gem method](http://ruby-doc.org/stdlib-2.5.1/libdoc/rubygems/rdoc/Kernel.html)
|
1785
|
+
provided by Rubygems. The Toys version attempts to install a missing gem for
|
1786
|
+
you, whereas Rubygems will just throw an exception.
|
1787
|
+
|
1788
|
+
## Toys Administration Using the System Tools
|
1789
|
+
|
1790
|
+
Toys comes with a few built-in tools, including some that let you administer
|
1791
|
+
Toys itself. These tools live in the `system` namespace.
|
1792
|
+
|
1793
|
+
### Getting the Toys Version
|
1794
|
+
|
1795
|
+
You can get the current version of Toys by running:
|
1796
|
+
|
1797
|
+
toys system version
|
1798
|
+
|
1799
|
+
Note that the same output can be obtained by passing the `--version` flag to
|
1800
|
+
the root tool:
|
1801
|
+
|
1802
|
+
toys --version
|
1803
|
+
|
1804
|
+
### Upgrading Toys
|
1805
|
+
|
1806
|
+
To update Toys to the latest released version, run:
|
1807
|
+
|
1808
|
+
toys system update
|
1809
|
+
|
1810
|
+
This will determine the latest version from Rubygems, and update your Toys
|
1811
|
+
installation if it is not already current.
|
1812
|
+
|
1813
|
+
Normall it asks you for confirmation before downloading. To disable interactive
|
1814
|
+
confirmation, pass the `--yes` flag.
|
1815
|
+
|
1816
|
+
A similar effect can of course be obtained simply by `gem install toys`.
|
1817
|
+
|
1818
|
+
## Writing Your Own CLI Using Toys
|
1819
|
+
|
1820
|
+
Toys is not primarily designed to help you write a custom command-line binary,
|
1821
|
+
but you can use it in that fashion. Toys is factored into two gems:
|
1822
|
+
**toys-core**, which includes all the underlying machinery for creating
|
1823
|
+
command-line binaries, and **toys**, which is really just a wrapper that
|
1824
|
+
provides the `toys` binary itself and its built-in commands and behavior. To
|
1825
|
+
write your own command line binary based on the Toys system, just require the
|
1826
|
+
**toys-core** gem and configure your binary the way you want.
|
1827
|
+
|
1828
|
+
Toys-Core is modular and lets you customize much of the behavior of a command
|
1829
|
+
line binary. For example:
|
1830
|
+
|
1831
|
+
* Toys itself automatically adds a number of flags, such as `--verbose` and
|
1832
|
+
`--help`, to each tool. Toys-Core lets you customize what flags are
|
1833
|
+
automatically added for your own command line binary.
|
1834
|
+
* Toys itself provides a default way to run tools that have no `run` method:
|
1835
|
+
it assumes such tools are namespaces, and displays the online help screen.
|
1836
|
+
Toys-Core lets you provide an alternate default run method for your own
|
1837
|
+
command line binary.
|
1838
|
+
* Toys itself provides several built-in tools, such as `do`, and `system`.
|
1839
|
+
Toys-Core lets your own command line binary define its own built-in tools.
|
1840
|
+
* Toys itself implements a particular search path for user-provided Toys
|
1841
|
+
files, and looks for specific file and directory names such as `.toys.rb`.
|
1842
|
+
Toys-Core lets you change the search path, the file/directory names, or
|
1843
|
+
disable user-provided Toys files altogether for your own command line
|
1844
|
+
binary. Indeed, most command line binaries do not need user-customizable
|
1845
|
+
tools, and can ship with only built-in tools.
|
1846
|
+
* Toys itself has a particular way of displaying online help and displaying
|
1847
|
+
errors. Toys-Core lets your own command line binary customize these.
|
1848
|
+
|
1849
|
+
For more information, see the
|
1850
|
+
[Toys-Core documentation](https://www.rubydoc.info/gems/toys-core/).
|