1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
|
# Toys-Core
Toys is a configurable command line tool. Write commands in Ruby using a simple
DSL, and Toys will provide the command line executable and take care of all the
details such as argument parsing, online help, and error reporting.
Toys-Core is the command line tool framework underlying Toys. It can be used
to write command line executables using the Toys DSL and the power of the Toys
classes.
For more detailed information about Toys-Core, see the
[Toys-Core User's Guide](https://dazuma.github.io/toys/gems/toys-core/latest/file.guide.html).
For background information about Toys itself, see the
[Toys README](https://dazuma.github.io/toys/gems/toys/latest) and the
[Toys User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).
## Introductory tutorial
Here's a tutorial to help you get a feel for how to write a basic command line
executable using Toys-Core.
It assumes basic familiarity with Toys, so, if you have not done so, I
recommend first walking through the tutorial in the
[Toys README](https://dazuma.github.io/toys/gems/toys/latest). It also assumes
you are running a Unix-like system such as Linux or macOS. Some commands might
need to be modified if you're running on Windows.
### Install Toys-Core
Install the **toys-core** gem using:
$ gem install toys-core
You can also install the **toys** gem, which brings in **toys-core** as a
dependency.
### Create a new executable
We'll start by creating an executable Ruby script. Using your favorite text
editor, create a new file called `mycmd` with the following contents:
#!/usr/bin/env ruby
require "toys-core"
cli = Toys::CLI.new
exit(cli.run(*ARGV))
Make sure the file's executable bit is set:
$ chmod a+x mycmd
That's it! This is a fully-functional Toys-based executable! Let's see what
happens when you run it:
$ ./mycmd
Just as with Toys itself, you get a help screen by default (since we haven't
yet actually implemented any behavior.) As you can see, some of the same
features from Toys are present already: online help, and `--verbose` and
`--quiet` flags. These features can of course all be customized or disabled,
but they're often useful to have to start off.
### Add some functionality
You implement the functionality of your executable using the same DSL that you
use to write Toys files. You could point your executable at a directory
containing actual Toys files, but the simplest option is to provide the
information to the Toys CLI object in a block.
Let's add some functionality to `mycmd`.
#!/usr/bin/env ruby
require "toys-core"
cli = Toys::CLI.new
#### Insert the following block ...
cli.add_config_block do
desc "My first executable!"
flag :whom, default: "world"
def run
puts "Hello, #{whom}!"
end
end
exit(cli.run(*ARGV))
If you went through the tutorial in the README for the Toys gem, this should
look familiar. Let's run it now, and experiment with passing flags to it.
$ ./mycmd
$ ./mycmd --whom=ruby
$ ./mycmd --bye
$ ./mycmd --help
Notice that we did not create a `tool` block, but instead set up description,
flags, and functionality directly in the configuration block. This configures
the "root tool", i.e. what happens when you run the executable without passing
a tool name to it. (In fact, it's technically legal to do this in Toys as well,
by setting functionality at the "top level" of a `.toys.rb` file without any
`tool` block, although you probably won't actually want to do so.)
### Tool-based executables
But perhaps you want your executable to have multiple "tools", similar to other
familiar executables like git or kubectl. You can define tools, including
nested tools, by writing `tool` blocks in your config. Here's an example:
#!/usr/bin/env ruby
require "toys-core"
cli = Toys::CLI.new
#### Change the config block as follows ...
cli.add_config_block do
# Things outside any tool block still apply to the root
desc "My first executable with several tools"
# We'll put the greet function here
tool "greet" do
desc "My first tool!"
flag :whom, default: "world"
def run
puts "Hello, #{whom}!"
end
end
# Try writing a second tool here. You could use the "new-repo"
# example from the Toys tutorial.
end
exit(cli.run(*ARGV))
Now you can run `greet` as a tool:
$ ./mycmd greet
The "root" functionality once again shows global help, including a list of the
available tools.
$ ./mycmd
Notice that the description set at the "root" of the config block (outside the
tool blocks) shows up here.
### Configuring the CLI
So far, our executable behaves very similarly to Toys itself. Help screens are
shown by default, flags for help and verbosity are provided automatically, and
any exceptions are displayed to the terminal.
These and many more aspects of the behavior of our executable can be customized
by passing options to the `Toys::CLI` constructor. Here's an example that
modifies error handling and delimiter parsing.
#!/usr/bin/env ruby
require "toys-core"
#### Pass some additional options to the CLI constructor ...
cli = Toys::CLI.new(
extra_delimiters: ":",
error_handler: ->(err) {
puts "Aww shucks, an error happened: #{err.message}"
return 1
}
)
#### Change the config block as follows ...
cli.add_config_block do
tool "example" do
tool "greet" do
def run
puts "Hello, world!"
end
end
tool "error" do
def run
raise "Whoops!"
end
end
end
end
exit(cli.run(*ARGV))
Try these runs. Do they behave as you expected?
$ ./mycmd example greet
$ ./mycmd example:greet
$ ./mycmd example.greet
$ ./mycmd example error
### Configuring middleware
Toys _middleware_ are objects that provide common functionality for all the
tools in your executable. For example, a middleware adds the `--help` flag to
your tools by default.
The next example provides a custom middleware stack, resulting in a different
set of common tool functionality.
#!/usr/bin/env ruby
require "toys-core"
#### Change the CLI construction again ...
middlewares = [
[:set_default_descriptions, default_tool_desc: "Hey look, a tool!"],
[:show_help, help_flags: true]
]
cli = Toys::CLI.new middleware_stack: middlewares
#### Use this config block ...
cli.add_config_block do
tool "greet" do
def run
puts "Hello, world!"
end
end
end
exit(cli.run(*ARGV))
We've now modified the default description applied to tools that don't provide
their own description. See the effect with:
$ ./mycmd greet --help
We've also omitted some of the default middleware, including the one that adds
the `--verbose` and `--quiet` flags to all your tools. Notice those flags are
no longer present.
We've also omitted the middleware that provides default execution behavior
(i.e. displaying the help screen) when there is no `run` method. Now, since we
haven't defined a top-level `run` method in this last example, invoking the
root tool will cause an error:
$ ./mycmd
It is even possible to write your own middleware. In general, while the
`Toys::CLI` constructor provides defaults that should work for many use cases,
you can also customize it heavily to suit the needs of your executable.
### Packaging as a gem
So far we've created simple one-file executables that you could distribute by
itself. However, the `toys-core` gem is a dependency, and your users will need
to have it installed. You could alleviate this by wrapping your executable in a
gem that can declare `toys-core` as a dependency explicitly.
The [examples directory](https://github.com/dazuma/toys/tree/main/toys-core/examples)
includes a few simple examples that you can use as a starting point.
To experiment with the examples, clone the Toys repo from GitHub:
$ git clone https://github.com/dazuma/toys.git
$ cd toys
Navigate to the simple-gem example:
$ cd toys-core/examples/simple-gem
This example wraps the simple "greet" executable that we
[covered earlier](#Add_some_functionality) in a gem. You can see the
[executable file](https://github.com/dazuma/toys/tree/main/toys-core/examples/simple-gem/bin/toys-core-simple-example)
in the bin directory.
Try it out by building and installing the gem. From the `examples/simple-gem`
directory, run:
$ toys install
Once the gem has successfully installed, you can run the executable, which
RubyGems should have added to your path. (Note: if you are using a ruby
installation manager, you may need to "rehash" or "reshim" to gain access to
the executable.)
$ toys-core-simple-example --whom=Toys
Clean up by uninstalling the gem:
$ gem uninstall toys-core-simple-example
If the implementation of your executable is more complex, you might want to
break it up into multiple files. The multi-file gem example demonstrates this.
$ cd ../multi-file-gem
This executable's implementation resides in its
[lib directory](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/lib),
a technique that may be familiar to writers of command line executables. More
interestingly, the tools themselves are no longer defined in a block passed to
the CLI object, but have been moved into a separate
["tools" directory](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/tools).
This directory has the same structure and supports the same features that are
available when writing complex sets of tools in a `.toys` directory. You then
configure the CLI object to look in this directory for its tools definitions,
as you can see in
[the code](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/lib/toys-core-multi-gem-example.rb).
Try it out now. From the `examples/multi-file-gem` directory, run:
$ toys install
Once the gem has successfully installed, you can run the executable, which
RubyGems should have added to your path. (Note: if you are using a ruby
installation manager, you may need to "rehash" or "reshim" to gain access to
the executable.)
$ toys-core-multi-file-example greet
Clean up by uninstalling the gem:
$ gem uninstall toys-core-multi-file-example
### Learning more
This introduction should be enough to get you started. However, Toys-Core is a
deep framework with many more features.
Learn about how to write tools using the Toys DSL, including validating and
interpreting command line arguments, using templates and mixins, controlling
subprocesses, and producing nice styled output, in the
[Toys User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).
Learn more about how to customize and package your own executable, including
handling errors, controlling log output, and providing your own mixins,
templates, and middleware, in the
[Toys-Core User Guide](https://dazuma.github.io/toys/gems/toys-core/latest/file.guide.html).
Detailed usage information can be found in the
[class reference documentation](https://dazuma.github.io/toys/gems/toys-core/latest/Toys.html)
## System requirements
Toys-Core requires Ruby 2.7 or later.
Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
because of JVM boot latency, lack of support for Kernel#fork, and other issues.
Most parts of Toys-Core work on TruffleRuby. However, TruffleRuby is not
recommended because it has a few known bugs that affect Toys.
## License
Copyright 2019-2025 Daniel Azuma and the Toys contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
|