Rodish¶ ↑
Rodish parses an argv array using a routing tree approach. It is designed to make it easy to implement command line applications that support multiple levels of subcommands, with options at each level.
Installation¶ ↑
gem install rodish
Resources¶ ↑
- Documentation
- Source
- Report a Bug
- Ask a Question
Simple Example¶ ↑
Here’s a simple commented example with a single subcommand:
require "rodish" # This just creates a normal Ruby class. For each argv parse, Rodish will # create an instance of this class class CliExample # This allows instances of the class to be instantiated with or without # a default person. def initialize(default_person=nil) @default_person = default_person end # This installs the Rodish processor into CliExample. It extends the # CliExample class with the Rodish::Processor module. Rodish.processor(self) # Without arguments, the on method configures the root command for # the processor. on do # Provide description for root command desc "Example Hello World CLI" # This method call creates a hello subcommand of the current/root command. on "hello" do # Provide description for this subcommand desc "Says hello to person or world" # This adds a usage string and a -p options for the hello subcommand. # The block passed is used to set the options via the optparse library. options("cli-example hello [options]") do on("-p", "--person=name", "say hello to a specific person") end # If the given argv is for the hello subcommand, this block will be # executed in the context of the CliExample instance. run do |opts| "Hello #{opts[:person] || @default_person || 'World'}" end end end end # This requests Rodish to process the provided argv. Rodish will determine # the related command block to execute and execute it. The return value of # that block will be returned to the caller. CliExample.process(["hello"]) # => "Hello World" # Additional arguments passed to .process are passed to .new. In this # example, this sets the default person. CliExample.process(["hello"], "Adol") # => "Hello Adol" # This shows an example of passing an option to a subcommand, and using # the option value when returning a response. CliExample.process(["hello", "-p", "Feena"], "Adol") # => "Hello Feena"
Rodish DSL¶ ↑
Inside the on
block with no arguments, you are in the context of the root command. The following methods are available for configuring the processing of the command.
on
¶ ↑
The on
method adds a subcommand of the current command, and yields to the block to configure the subcommand. All of the methods described in the Rodish DSL section can be executed inside the on
block, and arbitrary levels of subcommands are supported.
desc
¶ ↑
Provides a description for the command. This description will show up in the help output for the command.
options
¶ ↑
The options
method sets up an options parser for the current command. The default options parser disallows any options. Options are parsed into a hash, which is yielded to commands (as in the above example).
This method requires a String argument for the usage for the current command. You can also provide a key
keyword argument, to put parsed options into a subhash of the main options hash, which can be useful when options are parsed at multiple levels.
A block must be provided, which is executed in the context of a Rodish::OptionParser instance. Rodish::OptionParser is a subclass of Ruby’s standard OptionParser (from optparse
), with a few additional methods.
banner
¶ ↑
Set the usage banner for the command, without allowing options for the command.
args
¶ ↑
The args
method sets the number of arguments accepted when running the command. The default for args
is 0
. You can provide either an Integer to accept a fixed number of arguments, or a Range to allow any number of arguments in that range.
run
¶ ↑
The run
method sets the block to run for the current command. If the command accepts a fixed number of arguments, those arguments are yielded as the first arguments to the command. If the command accepts a range of argument numbers, then the remaining argv array will be passed as the first argument.
The block will be passed two additional arguments, the options already parsed, and the current Rodish::Command object.
autoload_subcommand_dir
¶ ↑
The autoload_subcommand_dir
takes a directory, and will autoload subcommands from the given directory. Filenames ending in .rb
in this directory will be treated as subcommands, and requiring the file should add the appropriate subcommand.
This allows you to design complex command line programs where only the parts of the program needed to handle the given argv are loaded.
Object Model¶ ↑
Rodish has four main classes/modules:
- Rodish::Processor
-
This is the module that extends the class you load Rodish into. The class becomes a Rodish processor, and contains a reference to the root command for the processor.
- Rodish::Command
-
Handles processing the argv. Contains references to subcommands, similar to a tree.
- Rodish::DSL
-
Used for configuring the Rodish::Command instances.
- Rodish::OptionParser
-
A subclass of Ruby’s standard OptionParser, used for parsing options for commands.
Each Rodish processor has a separate subclass of Command, DSL, and OptionParser.
Plugins¶ ↑
After installing Rodish into a processor, the plugin method can be used to load plugins into the processor. Plugins can change how the processor and related command, DSL, and option parsers work. Each plugin is a module that can contain one or more of the following submodules:
-
ProcessorMethods
-
Extends the processor with additional methods
-
CommandMethods
-
Module that is included in the Command subclass for the processor.
-
DSLMethods
-
Module that is included in the DSL subclass for the processor.
-
OptionParserMethods
-
Module that is included in the OptionParser subclass for the processor.
Plugins can also implement singleton before_load
and/or after_load
methods, which are called before or after loading the plugin modules, respectively.
When loading plugins, you can pass the module instance directly:
class App Rodish.processor(self) plugin PluginModule end
However, the typical usage is passing a symbol for the plugin:
class App Rodish.processor(self) plugin :plugin_name end
If the plugin has not already been registered, Rodish will require the rodish/plugins/plugin_name
file, which should register the plugin. See plugins that ship with Rodish for an example of how to create a plugin.
The following plugins ship with Rodish:
- cache_help_output
-
Cache help output when freezing command.
- help_examples
-
Allow including example help output with command usage.
- help_option_values
-
Allow showing allowed option values for help options.
- help_order
-
Support overriding order of command help sections.
- invalid_args_message
-
Support overriding the message shown when an invalid number of arguments is provided.
- is
-
Adds
is
method as a shortcut for creating a subcommand withon
andrun
. - post_commands
-
Adds support for subcommands that appear after arguments (see below).
- run_is
-
Adds
run_is
method as a shortcut for creating a post subcommand withrun_on
andrun
. - skip_option_parsing
-
Allows for skipping option parsing, treating all elements of argv as arguments.
- usages
-
Get a hash with help output for all commands/subcommands for the processor.
- wrap_options_separator
-
Add wrap method to options parser, for wrapping long separator lines
Post Commands¶ ↑
The post_commands plugin adds support for commands that appear after arguments. It adds the following configuration methods:
run_on
¶ ↑
The run_on
method is similar to on
, except it creates a post subcommand instead of a normal subcommand. Post subcommands allow the run
block to parse part of the remaining argv array, and then call a subcommand with the modified (or a new) argv array. You dispatch to post subcommands inside the run
block by calling run
on the command argument:
on "hello" do args(2...) run do |argv, opts, command| @name = argv.shift command.run(self, opts, argv) end run_on "world" do run do "Hello #{@name.upcase} World!" end end end # process(%w[hello foo world]) # => "Hello FOO World!"
post_options
¶ ↑
The post_options
method sets an option parser that is used for post subcommands. This parses options from the argv array that passed to command.run
, before calling the related subcommand. Example:
on "hello" do args(2...) post_options("Usage: hello name [options] subcommand ...") do on("-c", "--cap", "capitalize instead of uppercase") end run do |argv, opts, command| @name = argv.shift command.run(self, opts, argv) end run_is "world" do |opts| name = opts[:cap] ? @name.capitalize : @name.upcase "Hello #{name} World!" end end # process(%w[hello foo world]) # => "Hello FOO World!" # process(%w[hello foo -c world]) # => "Hello Foo World!"
post_banner
¶ ↑
Set the usage banner for using post subcommands with the command, without allowing post options for the command.
autoload_post_subcommand_dir
¶ ↑
The autoload_post_subcommand_dir
operates the same as autoload_subcommand_dir
, but it handles post subcommands instead of normal subcommands.
Examples¶ ↑
The tests that ship with Rodish fully cover all of Rodish’s functionality.
If you would like to view a production example using Rodish, which uses most of Rodish’s features, please see UbiCli, which is part of Ubicloud:
-
Main class: github.com/ubicloud/ubicloud/blob/main/lib/ubi_cli.rb
-
Commands (separate command per file): github.com/ubicloud/ubicloud/tree/main/cli-commands
History¶ ↑
Rodish was extracted from Ubicloud (github.com/ubicloud/ubicloud), and is the argv processor used in Ubicloud’s command line interface.
Naming¶ ↑
The name Rodish was chosen because Rodish uses an API similar (-ish) to the Roda web framework (roda.jeremyevans.net), and the library is designed for use in applications executed from a shell (sh).
License¶ ↑
MIT
Author¶ ↑
Jeremy Evans <code@jeremyevans.net>