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