{"id":15634871,"url":"https://github.com/dazuma/toys","last_synced_at":"2026-02-11T02:55:31.758Z","repository":{"id":29523763,"uuid":"121931434","full_name":"dazuma/toys","owner":"dazuma","description":"A configurable command line tool for builds and workflow automation","archived":false,"fork":false,"pushed_at":"2025-03-03T05:00:13.000Z","size":14838,"stargazers_count":129,"open_issues_count":10,"forks_count":7,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-31T22:18:20.749Z","etag":null,"topics":["build-tool","command-line-tool","ruby","scripts"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dazuma.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-18T08:03:40.000Z","updated_at":"2025-03-03T20:48:18.000Z","dependencies_parsed_at":"2023-10-13T08:17:38.524Z","dependency_job_id":"f9f6f611-818b-45bd-bfd0-8a342c5b9807","html_url":"https://github.com/dazuma/toys","commit_stats":{"total_commits":551,"total_committers":4,"mean_commits":137.75,"dds":"0.31215970961887474","last_synced_commit":"b25a8ef93e253021e7dd07936125a80a51038215"},"previous_names":[],"tags_count":96,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dazuma%2Ftoys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dazuma%2Ftoys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dazuma%2Ftoys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dazuma%2Ftoys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dazuma","download_url":"https://codeload.github.com/dazuma/toys/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247744332,"owners_count":20988783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build-tool","command-line-tool","ruby","scripts"],"created_at":"2024-10-03T10:58:15.750Z","updated_at":"2026-01-06T20:31:53.138Z","avatar_url":"https://github.com/dazuma.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Toys\n\n[![Gem Version](https://badge.fury.io/rb/toys.svg)](https://badge.fury.io/rb/toys)\n[![Documentation](https://img.shields.io/badge/docs-Toys-red.svg)](https://dazuma.github.io/toys/gems/toys/latest)\n\nToys is a configurable command line tool. Write commands in Ruby using a simple\nDSL, and Toys will provide the command line executable and take care of all the\ndetails such as argument parsing, online help, and error reporting.\n\nToys is designed for software developers, IT professionals, and other power\nusers who want to write and organize scripts to automate their workflows. It\ncan also be used as a replacement for Rake, providing a more natural command\nline interface for your project's build tasks.\n\nThis repository includes the source for two gems:\n\n*   **[toys](https://rubygems.org/gems/toys)** provides the Toys executable\n    itself and all its capabilities.\n*   **[toys-core](https://rubygems.org/gems/toys-core)** provides the\n    underlying command line framework, and can be used to build other command\n    line executables.\n\n## Introductory tutorial\n\nHere's a tutorial to help you get a feel of what Toys can do.\n\n### Install and try out Toys\n\nInstall the **toys** gem using:\n\n    $ gem install toys\n\nThis installs the `toys` executable, along with some built-in tools and\nlibraries. You can run the executable immediately:\n\n    $ toys\n\nThis displays overall help for Toys. If you have `less` installed, Toys will\nuse it to display the help screen. Press `q` to exit.\n\nYou may notice that the help screen lists some tools that are pre-installed.\nLet's run one of them:\n\n    $ toys system version\n\nThe `system version` tool displays the current version of the toys gem.\n\nToys also provides optional tab completion for bash. To install it, execute the\nfollowing command in your shell, or add it to your bash configuration file\n(e.g. `~/.bashrc`).\n\n    $(toys system bash-completion install)\n\nToys does not yet specially implement tab completion for zsh or other shells.\nHowever, if you are using zsh, installing bash completion using `bashcompinit`\n*mostly* works.\n\n### Write your first tool\n\nYou can define tools by creating a *Toys file*. Go into any directory, and,\nusing your favorite editor, create a new file called `.toys.rb` (note the\nleading period). Copy the following text into the file, and save it:\n\n    tool \"greet\" do\n      desc \"My first tool!\"\n      flag :whom, default: \"world\"\n      def run\n        puts \"Hello, #{whom}!\"\n      end\n    end\n\nThis defines a tool named \"greet\". Try running it:\n\n    $ toys greet\n\nThe tool also recognizes a flag on the command line. Try this:\n\n    $ toys greet --whom=ruby\n\nToys provides a rich set of features for defining command line arguments and\nflags. It can also validate arguments. Try this:\n\n    $ toys greet --bye\n\nNotice that Toys automatically generated a usage summary for your tool. It also\nautomatically generates a full help screen, which you can view using the\n`--help` flag:\n\n    $ toys greet --help\n\nToys searches up the directory hierarchy for Toys files. So it will find this\n`.toys.rb` if you are located in this directory or any subdirectory. It will\nalso read multiple files if it finds them, so you can \"scope\" your tools more\nspecifically or generally by locating them in your directory hierarchy.\n\nIf you want to define \"global\" tools that apply anywhere, write a Toys file\neither in your home directory, or in the system configuration directory\n(usually `/etc`). Toys always searches these locations.\n\n### A more sophisticated example\n\nLet's take a look at another example that exercises some of the features you're\nlikely to see in real-world usage. Add the following to your `.toys.rb` file.\n(You don't need to replace the greet tool you just wrote; just add this new\ntool to the end of the file.)\n\n    tool \"new-repo\" do\n      desc \"Create a new git repo\"\n\n      optional_arg :name, desc: \"Name of the directory to create\"\n\n      include :exec, exit_on_nonzero_status: true\n      include :fileutils\n      include :terminal\n\n      def run\n        if name.nil?\n          response = ask \"Please enter a directory name: \"\n          set :name, response\n        end\n        if File.exist? name\n          puts \"Aborting because #{name} already exists\", :red, :bold\n          exit 1\n        end\n        logger.info \"Creating new repo in directory #{name}...\"\n        mkdir name\n        cd name do\n          create_repo\n        end\n        puts \"Created repo in #{name}\", :green, :bold\n      end\n\n      def create_repo\n        exec \"git init\"\n        File.write \".gitignore\", \u003c\u003c~CONTENT\n          tmp\n          .DS_Store\n        CONTENT\n        # You can add additional files here.\n        exec \"git add .\"\n        exec \"git commit -m 'Initial commit'\"\n      end\n    end\n\nNow you should have an additional tool called `new-repo` available. Type:\n\n    $ toys\n\nThe help screen lists both the `greet` tool we started with, and the new\n`new-repo` tool. This new tool creates a directory containing a newly created\ngit repo. (It assumes you have `git` available on your path.) Try running it:\n\n    $ toys new-repo foo\n\nThat should create a directory `foo`, initialize a git repository within it,\nand make a commit.\n\nNotice that this tool accepts a positional command line argument. Toys supports\nany combination of flags and required and optional arguments. This tool's\nargument is declared with a description string, which you can see if you view\nthe tool's help:\n\n    $ toys new-repo --help\n\nThe argument is marked as \"optional\" which means you can omit it. Notice that\nthe tool's code detects that it has been omitted and responds by prompting you\ninteractively for a directory name. You can also mark a positional argument as\n\"required\", which causes Toys to report a usage error if it is omitted.\n\nNext, notice this tool includes two methods, `create_repo` as well as `run`.\nThe \"entrypoint\" for a tool is always the `run` method, but each tool is\nactually a class under the hood, and you can add any helper methods you want.\nYou can even define and include modules if you want to share code across tools.\n\nFor our tool, notice that the three \"include\" lines are taking symbols rather\nthan modules. These symbols are the names of some of Toys's built-in helper\n*mixins*, which are configurable modules that enhance your tool. They may\nprovide methods your tool can call, or invoke other behavior. In our example:\n\n*   The `:exec` mixin provides a variety of methods for running external\n    commands. In this example, we use the `exec` method to run shell\n    commands, but you can also signal and control these commands, capture\n    and redirect streams, and so forth. Note that we pass the\n    `:exit_on_nonzero_status` option, which configures the `:exec` mixin to\n    abort the tool automatically if any of the external commands fails (similar\n    to `set -e` in bash). This is a common pattern when writing tools that\n    invoke external commands. (If you want more control, the `:exec` mixin also\n    provides ways to respond to result codes individually.)\n*   The `:fileutils` mixin provides the methods of the Ruby `FileUtils`\n    library, such as `mkdir` and `cd` used in this example. It's effectively\n    shorthand for `require \"fileutils\"; include ::FileUtils`.\n*   The `:terminal` mixin provides styled output, as you can see with the style\n    codes being passed to `puts`. It also provides some user interaction\n    commands such as `ask`, as well as spinners and other controls. You can see\n    operation of the `:terminal` mixin in the tool's output, which is styled\n    either green (for success) or red (on error) when running on a supported\n    tty.\n\nNow try running this:\n\n    $ toys new-repo bar --verbose\n\nYou'll notice some diagnostic log output. Toys provides a standard Ruby Logger\nfor each tool, and you can use it to emit diagnostic logs directly as\ndemonstrated in the example. Some other Toys features might also emit log\nentries: the `:exec` mixin, for example, by default logs every external command\nit runs (although this can be customized).\n\nBy default, only warnings and higher severity logs are displayed, but you can\nchange that by applying the `--verbose` or `--quiet` flags as we have done\nhere. These flags, like `--help`, are provided automatically to every tool.\n\n### A better Rake?\n\nLet's look at one more example. Traditionally, Ruby developers often use\nRakefiles to write scripts for tasks such as build, test, and deploy. And Toys\nis similar to Rake in how it uses directory-scoped files to define tools.\n\nBut Rake is really designed for dependency management, not for writing scripts.\nAs a result, some features, such as passing arguments to a task, are very\nclumsy with Rake.\n\nIf you have a project with a Rakefile, move into that directory and create a\nnew file called `.toys.rb` in that same directory (next to the Rakefile). Add\nthe following line to your `.toys.rb` file:\n\n    expand :rake\n\nThis syntax is called a \"template expansion.\" It's a way to generate tools\nprogrammatically. In this case, Toys provides the `:rake` template, which reads\nyour Rakefile and generates Toys tools corresponding to all your Rake tasks!\nNow if you run:\n\n    $ toys\n\nYou'll see that you now have tools associated with each of your Rake tasks. So\nif you have a `rake test` task, you can run it using `toys test`.\n\nNote that if you normally run Rake with Bundler (e.g. `bundle exec rake test`),\nyou may need to add Toys to your Gemfile and use Bundler to invoke Toys (i.e.\n`bundle exec toys test`). This is because Toys is just calling the Rake API to\nrun your task, and the Rake task might require the bundle. However, when Toys\nis not wrapping Rake, typical practice is actually *not* to use `bundle exec`.\nToys provides its own mechanisms to manage bundles or install gems for you.\n\nSo far, we've made Toys a front-end for your Rake tasks. This may be useful by\nitself. Toys lets you pass command line arguments \"normally\" to tools, whereas\nRake requires a weird square bracket syntax (which may also require escaping\ndepending on your shell.) Toys also provides more sophisticated online help\nthan Rake does.\n\nBut you also might find Toys a more natural way to *write* tasks, and indeed\nyou can often rewrite an entire Rakefile as a Toys file and get quite a bit of\nbenefit in readability and maintainability. For an example, see the\n[Toys file for the Toys gem itself](https://github.com/dazuma/toys/blob/main/toys/.toys/.toys.rb).\nIt contains Toys scripts that I use to develop, test, and release Toys itself.\nYes, Toys is self-hosted. You'll notice much of this Toys file consists of\ntemplate expansions. Toys provides templates for a lot of common build, test,\nand release tasks for Ruby projects.\n\nIf you're feeling adventurous, try translating some of your Rake tasks into\nnative Toys tools. You can do so in your existing `.toys.rb` file. Keep the\n`expand :rake` line at the *end* of the file, and locate your tools (whether\nsimple tools or template expansions) before it. That way, your Toys-native\ntools will take precedence, and `expand :rake` will proxy out to Rake only for\nthe remaining tasks that haven't been ported explicitly.\n\n### Learning more\n\nThis introduction should be enough to get you started. However, Toys is a deep\ntool with many more features, all explained in detail in the\n[User Guide](https://dazuma.github.io/toys/gems/toys/latest/file.guide.html).\n\nFor example, Toys lets you create tool namespaces and \"subtools\", and search\nfor tools by name and description. There are various ways to validate and\ninterpret command line arguments. You can create your own mixins and templates,\nand take advantage of a variety of third-party libraries such as Highline and\nTTY. Finally, if your `.toys.rb` files are growing too large or complicated,\nyou can replace them with `.toys` directories that contain tool definitions in\nseparate files. Such directories are versatile, letting you organize your tool\ndefinitions, along with shared code, normal Ruby classes, tests, and even data\nfiles for use by tools.\n\nYou can find detailed usage information, including the entire DSL, in the\n[class reference documentation](https://dazuma.github.io/toys/gems/toys/latest/Toys.html)\n\nUnlike most command line frameworks, Toys is *not primarily* designed to help\nyou build and ship a custom command line executable written in Ruby. However,\nyou *can* use it in that way with the \"toys-core\" API, available as a separate\ngem. You would effectively write your command line executable using the same\nToys DSL that you use to write `.toys.rb` files. For more info on using\ntoys-core, see\n[its documentation](https://dazuma.github.io/toys/gems/toys-core/latest).\n\n## Why Toys?\n\nI originally wrote Toys because I was accumulating dozens of *ad hoc* Ruby\nscripts I had written to automate various tasks in my workflow, everything from\nrefreshing credentials, to displaying git history in my favorite format, to\nrunning builds and tests of complex multi-component projects. It was becoming\ndifficult to remember which scripts did what, and what arguments each required,\nand I was constantly digging back into their source just to remember how to use\nthem. Furthermore, when writing new scripts, I was repeating the same\nOptionParser boilerplate and common functionality.\n\nToys was designed to address those problems by providing a framework for\nwriting and organizing your own command line scripts. You provide the actual\nfunctionality by writing Toys files, and Toys takes care of all the other\ndetails expected from a good command line tool. It provides a streamlined\ninterface for defining and handling command line flags and positional\narguments, and sensible ways to organize shared code. It automatically\ngenerates help text so you can see usage information at a glance, provides a\nsearch feature to help you find the script you need, and generates tab\ncompletion for your shell.\n\nToys can also be used to share scripts. For example, it can be used instead of\nRake to provide build and test scripts for a project. Unlike Rake tasks,\nscripts written for Toys can be invoked and passed arguments and flags using\nfamiliar Unix command line conventions. The Toys GitHub repo itself comes with\nToys scripts instead of Rakefiles.\n\n## System requirements\n\nToys requires Ruby 2.7 or later.\n\nMost parts of Toys work on JRuby. However, JRuby is not recommended because of\nJVM boot latency, lack of support for Kernel#fork, and other issues.\n\nMost parts of Toys work on TruffleRuby. However, TruffleRuby is not recommended\nbecause it has a few known bugs that affect Toys.\n\n## License\n\nCopyright 2019-2025 Daniel Azuma and the Toys contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdazuma%2Ftoys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdazuma%2Ftoys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdazuma%2Ftoys/lists"}