Class: Toys::CLI

Inherits:
Object
  • Object
show all
Defined in:
core-docs/toys/cli.rb

Overview

A Toys-based CLI.

This is the entry point for command line execution, and the stable public interface to the framework. A CLI owns the configuration: it gathers all the settings in one place, constructs the Loader that finds and loads tool definitions, and constructs the Runner that runs them. It also provides #child, which clones the configuration so a tool can be run under modified settings.

Running a tool is delegated to the Runner; #run and #load_tool are thin wrappers around it that supply the CLI's configuration.

This is the class to instantiate to create a Toys-based command line executable. For example:

#!/usr/bin/env ruby
require "toys-core"
cli = Toys::CLI.new
cli.add_source do
  def run
    puts "Hello, world!"
  end
end
exit(cli.run(*ARGV))

The currently running CLI is also available at runtime, as Toys::Context#cli. Use it when a tool needs the CLI configuration itself, most often to build a modified copy with #child. For example:

# My .toys.rb
tool "bar" do
  def run
    # Run "some-tool" with the tools from the "my-tools" gem also
    # available.
    child = cli.child(copy_sources: true) do |c|
      c.add_source(Toys::SourceSpec.gem("my-tools"), high_priority: true)
    end
    child.run("some-tool")
  end
end

A tool that simply wants to invoke another tool should instead use the runner, as described in Runner.

Defined in the toys-core gem

Direct Known Subclasses

StandardCLI

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(executable_name: nil, middleware_stack: nil, extra_delimiters: "", toplevel_tool_dir_name: nil, toplevel_tool_file_name: nil, mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, logger_factory: nil, logger: nil, base_level: nil, error_handler: nil, completion: nil, source_list: nil, git_cache: nil, gems_util: nil) ⇒ CLI

Create a CLI.

Most configuration parameters (besides tool definitions and tool lookup paths) are set as options passed to the constructor. These options fall roughly into four categories:

  • Options affecting output behavior:
    • logger: A global logger for all tools to use
    • logger_factory: A proc that returns a logger to use
    • base_level: The default log level
    • error_handler: Callback for handling exceptions
    • executable_name: The name of the executable
  • Options affecting tool specification
    • extra_delimiters: Tool name delimiters besides space
    • completion: Tab completion handler
  • Options affecting tool definition
    • middleware_stack: The middleware applied to all tools
    • mixin_lookup: Where to find well-known mixins
    • middleware_lookup: Where to find well-known middleware
    • template_lookup: Where to find well-known templates
  • Options affecting tool sources
    • toplevel_tool_dir_name: Directory name containing tool files
    • toplevel_tool_file_name: File name for tools
    • source_list: Initial sources to populate
    • git_cache: How to resolve git sources
    • gems_util: How to resolve gem sources

Parameters:

  • logger (Logger) (defaults to: nil) —

    A global logger to use for all tools. This can be set if the CLI will call at most one tool at a time. However, it will behave incorrectly if the CLI might run multiple tools concurrently with different verbosity settings (since the logger cannot have multiple level settings simultaneously). In that case, do not set a global logger, but use the logger_factory parameter instead.

  • logger_factory (Proc) (defaults to: nil) —

    A proc that takes a ToolDefinition as an argument, and returns a Logger to use when running that tool. Optional. If not provided (and no global logger is set), default_logger_factory is called to get a basic default.

  • base_level (Integer) (defaults to: nil) —

    The logger level that should correspond to zero verbosity. Optional. If not provided, defaults to the level the logger has before a run adjusts it (which is often Logger::WARN). See the same argument to Runner#initialize for how this interacts with nested runs.

  • error_handler (Proc, nil) (defaults to: nil) —

    A proc that is called when an unhandled exception is detected. See the error_handler argument to Runner#initialize for the handler's contract. Because a CLI always wraps errors, a handler installed here sees only a Toys::ContextualError or a bare SignalException. Optional. If not provided, default_error_handler is called to get a basic default handler that reraises the exception.

  • executable_name (String) (defaults to: nil) —

    The executable name displayed in help text. Optional. Defaults to the ruby program name.

  • extra_delimiters (String) (defaults to: "") —

    A string containing characters that can function as delimiters in a tool name. Defaults to empty. Allowed characters are period, colon, and slash.

  • completion (Toys::Completion::Base) (defaults to: nil) —

    A specifier for shell tab completion for the CLI as a whole. Optional. If not provided, default_completion is called to get a default completion that delegates to the tool.

  • middleware_stack (Array<Toys::Middleware::Spec>) (defaults to: nil) —

    An array of middleware that will be used by default for all tools. Optional. If not provided, uses a default set of middleware defined in default_middleware_stack. To include no middleware, pass the empty array explicitly.

  • mixin_lookup (Toys::ModuleLookup) (defaults to: nil) —

    A lookup for well-known mixin modules (i.e. with symbol names). Optional. If not provided, defaults to the set of standard mixins provided by toys-core, as defined by default_mixin_lookup. If you explicitly want no standard mixins, pass an empty instance of ModuleLookup.

  • middleware_lookup (Toys::ModuleLookup) (defaults to: nil) —

    A lookup for well-known middleware classes. Optional. If not provided, defaults to the set of standard middleware classes provided by toys-core, as defined by default_middleware_lookup. If you explicitly want no standard middleware, pass an empty instance of ModuleLookup.

  • template_lookup (Toys::ModuleLookup) (defaults to: nil) —

    A lookup for well-known template classes. Optional. If not provided, defaults to the set of standard template classes provided by toys core, as defined by default_template_lookup. If you explicitly want no standard templates, pass an empty instance of ModuleLookup.

  • toplevel_tool_dir_name (String) (defaults to: nil) —

    Tools are loaded from directories of this name that appear in a search path. Optional. If not provided, search paths do not load tool directories. The standard toys executable sets this to ".toys".

  • toplevel_tool_file_name (String) (defaults to: nil) —

    Tools are loaded from files of this name that appear in a search path. Optional. If not provided, search paths do not load tool files. The standard toys executable sets this to ".toys.rb". Note: This setting does not affect the name of "index" toys files, which is fixed at ".toys.rb".

  • source_list (Toys::SourceList) (defaults to: nil) —

    An optional list of sources to prepopulate into the CLI.

  • git_cache (Toys::Utils::GitCache, nil) (defaults to: nil) —

    A custom GitCache instance to use when resolving git sources. Optional. If nil or not specified, uses a process-wide default GitCache.

  • gems_util (Toys::Utils::Gems, nil) (defaults to: nil) —

    A custom Gems utility instance to use when resolving gem sources. Optional. If nil or not specified, uses a process-wide default Gems utility.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'core-docs/toys/cli.rb', line 156

def initialize(executable_name: nil,
               middleware_stack: nil,
               extra_delimiters: "",
               toplevel_tool_dir_name: nil,
               toplevel_tool_file_name: nil,
               mixin_lookup: nil,
               middleware_lookup: nil,
               template_lookup: nil,
               logger_factory: nil,
               logger: nil,
               base_level: nil,
               error_handler: nil,
               completion: nil,
               source_list: nil,
               git_cache: nil,
               gems_util: nil)
  # Source available in the toys-core gem
end

Instance Attribute Details

#base_level ⇒ Integer? (readonly)

The initial logger level in this CLI, used as the level for verbosity 0. May be nil, indicating it will use the initial logger setting.

Returns:

  • (Integer, nil)


264
265
266
# File 'core-docs/toys/cli.rb', line 264

def base_level
  @base_level
end

#completion ⇒ Toys::Completion::Base, Proc (readonly)

The overall completion strategy for this CLI.

Returns:



270
271
272
# File 'core-docs/toys/cli.rb', line 270

def completion
  @completion
end

#executable_name ⇒ String (readonly)

The effective executable name used for usage text in this CLI.

Returns:

  • (String)


232
233
234
# File 'core-docs/toys/cli.rb', line 232

def executable_name
  @executable_name
end

#extra_delimiters ⇒ String (readonly)

The string of tool name delimiter characters (besides space).

Returns:

  • (String)


238
239
240
# File 'core-docs/toys/cli.rb', line 238

def extra_delimiters
  @extra_delimiters
end

#logger ⇒ Logger? (readonly)

The global logger, if any.

Returns:

  • (Logger, nil)


251
252
253
# File 'core-docs/toys/cli.rb', line 251

def logger
  @logger
end

#logger_factory ⇒ Proc (readonly)

The logger factory.

Returns:

  • (Proc)


257
258
259
# File 'core-docs/toys/cli.rb', line 257

def logger_factory
  @logger_factory
end

#tool_name_splitter ⇒ Toys::ToolNameSplitter (readonly)

The splitter that interprets delimiters in tool names, reflecting this CLI's #extra_delimiters.



245
246
247
# File 'core-docs/toys/cli.rb', line 245

def tool_name_splitter
  @tool_name_splitter
end

Class Method Details

.default_completion ⇒ Object

Returns a default Completion that simply uses the tool's completion.



612
613
614
# File 'core-docs/toys/cli.rb', line 612

def default_completion
  # Source available in the toys-core gem
end

.default_error_handler ⇒ Proc

Returns a bare-bones error handler that simply reraises the error it is given. A Toys::ContextualError is reraised as itself, so that a rescue block has access to the context information. An unhandled SignalException (or a subclass such as Interrupt) is also reraised as itself, so that the Ruby VM has a chance to handle it normally.

Returns:

  • (Proc)


595
596
597
# File 'core-docs/toys/cli.rb', line 595

def default_error_handler
  # Source available in the toys-core gem
end

.default_logger_factory ⇒ Proc

Returns a default logger factory that generates simple loggers that write to the current stderr.

Returns:

  • (Proc)


605
606
607
# File 'core-docs/toys/cli.rb', line 605

def default_logger_factory
  # Source available in the toys-core gem
end

.default_middleware_lookup ⇒ Toys::ModuleLookup

Returns a default ModuleLookup for middleware that points at the StandardMiddleware module.

Returns:



573
574
575
# File 'core-docs/toys/cli.rb', line 573

def default_middleware_lookup
  # Source available in the toys-core gem
end

.default_middleware_stack ⇒ Array<Toys::Middleware::Spec>

Returns a default set of middleware that may be used as a starting point for a typical CLI. This set includes the following in order:

Returns:



553
554
555
# File 'core-docs/toys/cli.rb', line 553

def default_middleware_stack
  # Source available in the toys-core gem
end

.default_mixin_lookup ⇒ Toys::ModuleLookup

Returns a default ModuleLookup for mixins that points at the StandardMixins module.

Returns:



563
564
565
# File 'core-docs/toys/cli.rb', line 563

def default_mixin_lookup
  # Source available in the toys-core gem
end

.default_template_lookup ⇒ Toys::ModuleLookup

Returns a default empty ModuleLookup for templates.

Returns:



582
583
584
# File 'core-docs/toys/cli.rb', line 582

def default_template_lookup
  # Source available in the toys-core gem
end

Instance Method Details

#add_config_block(high_priority: false, source_name: nil, context_directory: nil, &block) ⇒ self

Deprecated.

Prefer #add_source.

Add a block to the source list.

This is a deprecated legacy method that has been superseded by #add_source. Instead of:

cli.add_config_block do
  ...
end

You should now:

cli.add_source do
  ...
end

Or, if you need to configure the source name or context directory:

source = Toys::SourceSpec.block(context_directory: "/var/project") do
  ...
end
cli.add_source(source)

Parameters:

  • high_priority (boolean) (defaults to: false) —

    Add the source at the head of the priority list rather than the tail.

  • source_name (String) (defaults to: nil) —

    The source name that will be shown in documentation for tools defined in this block. If omitted, a default unique string will be generated.

  • block (Proc) —

    The source block, executed in the context of the tool DSL DSL::Tool.

  • context_directory (String, nil) (defaults to: nil) —

    The context directory for tools loaded from this block. You can pass a directory path as a string, or nil to denote no context. Defaults to nil.

Returns:

  • (self)

Raises:



531
532
533
534
535
536
# File 'core-docs/toys/cli.rb', line 531

def add_config_block(high_priority: false,
                     source_name: nil,
                     context_directory: nil,
                     &block)
  # Source available in the toys-core gem
end

#add_config_path(path, high_priority: false, source_name: nil, context_directory: :parent) ⇒ self

Deprecated.

Prefer #add_source.

Add a specific tool file or directory to the source list.

This is a deprecated legacy method that has been superseded by #add_source. However, note that while add_config_path sets a particular context directory by default, #add_source does not. So the equivalent of:

cli.add_config_path("/path/to/tools")

is technically:

source = Toys::SourceSpec.path("/path/to/tools",
                               context_directory: "/path/to")
cli.add_source(source)

Parameters:

  • path (String) —

    A path to add. May reference a single tool file or a tool directory.

  • high_priority (boolean) (defaults to: false) —

    Add the source at the head of the priority list rather than the tail.

  • source_name (String) (defaults to: nil) —

    A custom name for the root source. Optional.

  • context_directory (String, nil, :path, :parent) (defaults to: :parent) —

    The context directory for tools loaded from this path. You can pass a directory path as a string, :path to denote the given path, :parent to denote the given path's parent directory, or nil to denote no context. Defaults to :parent.

Returns:

  • (self)

Raises:



484
485
486
487
488
489
# File 'core-docs/toys/cli.rb', line 484

def add_config_path(path,
                    high_priority: false,
                    source_name: nil,
                    context_directory: :parent)
  # Source available in the toys-core gem
end

#add_search_path(search_path, high_priority: false, context_directory: :path) ⇒ self

Checks the given search directory. If it contains a tool file and/or tool directory (identified by the toplevel_tool_file_name and toplevel_tool_dir_name constructor arguments), those are added to the source list. If the given search directory path does not exist or does not contain either the file or directory, nothing is added.

The main Toys executable uses this method to load tools from directories in the TOYS_PATH.

Parameters:

  • search_path (String, Pathname) —

    A directory path to search for the well-known source file and directory. Must be a String or a Pathname. Paths should generally be absolute. Relative paths will be converted to absolute, using the current working directory at call time.

  • high_priority (boolean) (defaults to: false) —

    Add the sources at the head of the priority list rather than the tail.

  • context_directory (String, Pathname, nil, :path, :parent) (defaults to: :path) —

    The context directory for tools loaded from sources found using this method. You can pass a directory path as a String or Pathname, :path to denote the given search_path, :parent to denote the given search_path's parent directory, or nil to denote no context. Defaults to :path. If a path is provided, it should generally be an absolute path; any relative path will be expanded relative to the current working directory at call time.

Returns:

  • (self)

Raises:



340
341
342
343
344
# File 'core-docs/toys/cli.rb', line 340

def add_search_path(search_path,
                    high_priority: false,
                    context_directory: :path)
  # Source available in the toys-core gem
end

#add_search_path_hierarchy(start: nil, terminate: [], high_priority: false, context_directory: :path) ⇒ self

Walk up the directory hierarchy from the given start location, searching for toplevel tool files and directories, and add any found. Starts at the given directory and works up through parent directories until it reaches the file system root or it encounters one of the "terminate" directories.

The main Toys executable uses this method to load tools from the current directory and its ancestors.

Parameters:

  • start (String, Pathname, nil) (defaults to: nil) —

    The first directory path to search. If not given, defaults to the current working directory. If provided, must be a String or a Pathname. Paths should generally be absolute. Relative paths will be converted to absolute, using the current working directory at call time.

  • terminate (Array<String,Pathname>) (defaults to: []) —

    Optional list of directories that should terminate the search. If the walk up the directory tree encounters one of these directories, the search is halted without checking the terminating directory. Terminating directories should generally be absolute paths. Relative paths will be converted to absolute, using the current working directory at call time.

  • high_priority (boolean) (defaults to: false) —

    Add the sources at the head of the priority list rather than the tail.

  • context_directory (String, Pathname, nil, :path, :parent) (defaults to: :path) —

    The context directory for tools loaded from sources found using this method. You can pass a directory path as a String or Pathname, :path to denote the current path during the directory walk, :parent to denote the current walk directory's parent directory, or nil to denote no context. Defaults to :path, which is the behavior of the Toys executable when it loads tools from the current directory and its ancestors. If a context directory path is provided, it should generally be an absolute path; any relative path will be expanded relative to the current working directory at call time.

Returns:

  • (self)

Raises:



384
385
386
387
388
389
# File 'core-docs/toys/cli.rb', line 384

def add_search_path_hierarchy(start: nil,
                              terminate: [],
                              high_priority: false,
                              context_directory: :path)
  # Source available in the toys-core gem
end

#add_source(spec = nil, high_priority: false, &block) ⇒ self

Add a source to the source list, described by the given source spec.

This is generally used to load a static or "built-in" set of tools, either for a standalone command line executable based on Toys, or to provide a "default" set of tools for a dynamic executable. For example, the main Toys executable uses this to load the builtin tools from its "builtins" directory.

The source can be specified in one of three ways:

  • A source spec built using one of the SourceSpec module methods. If you need to configure the context directory or name of the source, you must use a full SourceSpec object.
  • A string (or other object convertible to a path, such as a Pathname) interpreted as a file system path, which will be passed to SourceSpec.path to get the source spec.
  • A block, which will be passed to SourceSpec.block to get the source spec. (Do not include an argument if passing a block.)

The spec is not resolved here. The loader resolves it, at most once, the first time it looks up a tool, so a source that cannot be read, fetched, or activated fails then rather than now.

Parameters:

  • spec (Toys::SourceSpec::Base, String) (defaults to: nil) —

    The source spec to add.

  • high_priority (boolean) (defaults to: false) —

    Add the source at the head of the priority list rather than the tail.

Returns:

  • (self)

Raises:

  • (ArgumentError) —

    if no source is given, or if the given source is neither a source spec nor a legal path.

  • (Toys::SourceListFinalizedError) —

    if the source list has already been finalized.



306
307
308
# File 'core-docs/toys/cli.rb', line 306

def add_source(spec = nil, high_priority: false, &block)
  # Source available in the toys-core gem
end

#child(copy_sources: false, **opts) {|cli| ... } ⇒ Toys::CLI

Make a clone of this CLI with the same settings.

By default, the new CLI has no tool sources, which is sometimes useful for calling another tool that has to be loaded from a different source configuration. Alternately, you can pass copy_sources: true to start with the same sources as the original (to which you can add additional sources before starting to load tools). Sources are copied before the block (if any) is called, so any sources the block adds at high priority will take priority over the originals.

Parameters:

  • copy_sources (boolean) (defaults to: false) —

    If true, the new CLI is populated with the same sources as the original. Default is false, resulting in a copy with no sources initially.

  • opts (keywords) —

    Any configuration arguments that should be modified from the original. See #initialize for a list of recognized keywords.

Yield Parameters:

  • cli (Toys::CLI) —

    If you pass a block, the new CLI is yielded to it so you can add paths and make other modifications.

Returns:



196
197
198
# File 'core-docs/toys/cli.rb', line 196

def child(copy_sources: false, **opts)
  # Source available in the toys-core gem
end

#finalize_sources! ⇒ self

Finalize the source list. Any subsequent attempt to add a source will raise SourceListFinalizedError.

Returns:

  • (self)


447
448
449
# File 'core-docs/toys/cli.rb', line 447

def finalize_sources!
  # Source available in the toys-core gem
end

#load_tool(*args, verbosity: 0) {|context| ... } ⇒ Object

Prepare a tool to be run, but just execute the given block rather than performing a full run of the tool. This is intended for testing tools.

Unlike #run, this neither wraps errors nor passes them to the error handler. An error such as a failure to parse arguments or to load the requested tool is raised out of this method as-is, so the block does not execute and this method does not return.

Note that calling this finalizes this CLI's source list if not already finalized. Any subsequent attempt to add a source raises SourceListFinalizedError.

Parameters:

  • args (String...) —

    Command line arguments specifying which tool to run and what arguments to pass to it. You may pass either a single array of strings, or a series of string arguments.

  • verbosity (Integer) (defaults to: 0) —

    Initial verbosity. Default is 0.

Yield Parameters:

Returns:

  • (Object) —

    The value returned from the block.



437
438
439
# File 'core-docs/toys/cli.rb', line 437

def load_tool(*args, verbosity: 0)
  # Source available in the toys-core gem
end

#loader ⇒ Toys::Loader

The current loader for this CLI.

Note that calling this finalizes this CLI's source list if not already finalized. Any subsequent attempt to add a source raises SourceListFinalizedError.

Returns:



209
210
211
# File 'core-docs/toys/cli.rb', line 209

def loader
  # Source available in the toys-core gem
end

#run(*args, verbosity: 0) ⇒ Integer

Run the CLI with the given command line arguments. Handles exceptions using the error handler.

Any error that is not handled by the tool itself is passed to this CLI's error handler, and this method returns the exit code that the handler produces. Ordinary errors arrive as a Toys::ContextualError wrapper, but a signal that no tool intercepted arrives as the SignalException itself, unwrapped. See the error_handler argument to #initialize.

Note that calling this finalizes this CLI's source list if not already finalized. Any subsequent attempt to add a source raises SourceListFinalizedError.

Parameters:

  • args (String...) —

    Command line arguments specifying which tool to run and what arguments to pass to it. You may pass either a single array of strings, or a series of string arguments.

  • verbosity (Integer) (defaults to: 0) —

    Initial verbosity. Default is 0.

Returns:

  • (Integer) —

    The resulting process status code (i.e. 0 for success).



412
413
414
# File 'core-docs/toys/cli.rb', line 412

def run(*args, verbosity: 0)
  # Source available in the toys-core gem
end

#runner ⇒ Toys::Runner

The runner this CLI uses to run tools, configured with this CLI's settings. Use it directly when you need more control over a single run than #run provides, such as turning off error handling.

Note that calling this finalizes this CLI's source list if not already finalized. Any subsequent attempt to add a source raises SourceListFinalizedError.

Returns:



224
225
226
# File 'core-docs/toys/cli.rb', line 224

def runner
  # Source available in the toys-core gem
end