class Toys::ToolDefinition

A ToolDefinition describes a single command that can be invoked using Toys. It has a name, a series of one or more words that you use to identify the tool on the command line. It also has a set of formal flags and command line arguments supported, and a block that gets run when the tool is executed.

Attributes

built_middleware[R]

The stack of built middleware specs for this tool.

@return [Array<Toys::Middleware>]

completion[R]

The completion strategy for this tool.

When reading, this may return an instance of one of the subclasses of {Toys::Completion::Base}, or a Proc that duck-types it. Generally, this defaults to a {Toys::ToolDefinition::DefaultCompletion}, providing a standard algorithm that finds appropriate completions from flags, positional arguments, and subtools.

When setting, you may pass any of the following:

*  `nil` or `:default` which sets the value to a default instance.
*  A Hash of options to pass to the
   {Toys::ToolDefinition::DefaultCompletion} constructor.
*  Any other form recognized by {Toys::Completion.create}.

@return [Toys::Completion::Base,Proc]

custom_context_directory[R]

The custom context directory set for this tool.

@return [String] The directory path @return [nil] if no custom context directory is set.

default_data[R]

The default context data set by arguments.

@return [Hash]

delegate_target[R]

The full name of the delegate target, if any.

@return [Array<String>] if this tool delegates @return [nil] if this tool does not delegate

desc[R]

The short description string.

When reading, this is always returned as a {Toys::WrappableString}.

When setting, the description may be provided as any of the following:

*  A {Toys::WrappableString}.
*  A normal String, which will be transformed into a
   {Toys::WrappableString} using spaces as word delimiters.
*  An Array of String, which will be transformed into a
   {Toys::WrappableString} where each array element represents an
   individual word for wrapping.

@return [Toys::WrappableString]

flag_groups[R]

A list of all defined flag groups, in order.

@return [Array<Toys::FlagGroup>]

flags[R]

A list of all defined flags.

@return [Array<Toys::Flag>]

full_name[R]

The name of the tool as an array of strings. This array may not be modified.

@return [Array<String>]

interrupt_handler[R]

The interrupt handler.

@return [Proc] The interrupt handler proc @return [Symbol] The name of a method to call @return [nil] if there is no interrupt handler

long_desc[R]

The long description strings.

When reading, this is returned as an Array of {Toys::WrappableString} representing the lines in the description.

When setting, the description must be provided as an Array where *each element* may be any of the following:

*  A {Toys::WrappableString} representing one line.
*  A normal String representing a line. This will be transformed into a
   {Toys::WrappableString} using spaces as word delimiters.
*  An Array of String representing a line. This will be transformed into
   a {Toys::WrappableString} where each array element represents an
   individual word for wrapping.

@return [Array<Toys::WrappableString>]

optional_args[R]

A list of all defined optional positional arguments.

@return [Array<Toys::PositionalArg>]

priority[R]

The priority of this tool definition.

@return [Integer]

remaining_arg[R]

The remaining arguments specification.

@return [Toys::PositionalArg] The argument definition @return [nil] if remaining arguments are not supported by this tool.

required_args[R]

A list of all defined required positional arguments.

@return [Array<Toys::PositionalArg>]

settings[R]

Settings for this tool

@return [Toys::Tool::Settings]

source_info[R]

Info on the source of this tool.

@return [Toys::SourceInfo] The source info @return [nil] if the source is not defined.

source_root[R]

The root source info defining this tool, or nil if there is no source.

@return [Toys::SourceInfo,nil]

subtool_middleware_stack[R]

The stack of middleware specs used for subtools.

This array may be modified in place.

@return [Array<Toys::Middleware::Spec>]

tool_class[R]

The tool class.

@return [Class]

usage_error_handler[R]

The usage error handler.

@return [Proc] The usage error handler proc @return [Symbol] The name of a method to call @return [nil] if there is no usage error handler

used_flags[R]

A list of flags that have been used in the flag definitions.

@return [Array<String>]

Public Class Methods

new(parent, full_name, priority, source_root, middleware_stack, middleware_lookup, tool_class = nil) click to toggle source

Create a new tool. Should be created only from the DSL via the Loader. @private

# File lib/toys/tool_definition.rb, line 19
def initialize(parent, full_name, priority, source_root, middleware_stack, middleware_lookup,
               tool_class = nil)
  @parent = parent
  @settings = Settings.new(parent: parent&.settings)
  @full_name = full_name.dup.freeze
  @priority = priority
  @source_root = source_root
  @built_middleware = middleware_stack.build(middleware_lookup)
  @subtool_middleware_stack = middleware_stack.dup

  @acceptors = {}
  @mixins = {}
  @templates = {}
  @completions = {}

  @precreated_class = tool_class

  reset_definition
end

Public Instance Methods

add_acceptor(name, acceptor = nil, type_desc: nil, &block) click to toggle source

Add a named acceptor to the tool. This acceptor may be refereneced by name when adding a flag or an arg. See {Toys::Acceptor.create} for detailed information on how to specify an acceptor.

@param name [String] The name of the acceptor. @param acceptor [Toys::Acceptor::Base,Object] The acceptor to add. You

can provide either an acceptor object, or a spec understood by
{Toys::Acceptor.create}.

@param type_desc [String] Type description string, shown in help.

Defaults to the acceptor name.

@param block [Proc] Optional block used to create an acceptor. See

{Toys::Acceptor.create}.

@return [self]

# File lib/toys/tool_definition.rb, line 568
def add_acceptor(name, acceptor = nil, type_desc: nil, &block)
  name = name.to_s
  if @acceptors.key?(name)
    raise ToolDefinitionError,
          "An acceptor named #{name.inspect} has already been defined in tool" \
          " #{display_name.inspect}."
  end
  @acceptors[name] = Toys::Acceptor.create(acceptor, type_desc: type_desc, &block)
  self
end
add_completion(name, completion = nil, **options, &block) click to toggle source

Add a named completion proc to this tool. The completion may be referenced by name when adding a flag or an arg. See {Toys::Completion.create} for detailed information on how to specify a completion.

@param name [String] The name of the completion. @param completion [Proc,Toys::Completion::Base,Object] The completion to

add. You can provide either a completion object, or a spec understood
by {Toys::Completion.create}.

@param options [Hash] Additional options to pass to the completion. @param block [Proc] Optional block used to create a completion. See

{Toys::Completion.create}.

@return [self]

# File lib/toys/tool_definition.rb, line 615
def add_completion(name, completion = nil, **options, &block)
  name = name.to_s
  if @completions.key?(name)
    raise ToolDefinitionError,
          "A completion named #{name.inspect} has already been defined in tool" \
          " #{display_name.inspect}."
  end
  @completions[name] = Toys::Completion.create(completion, **options, &block)
  self
end
add_flag(key, flags = [], accept: nil, default: nil, handler: nil, complete_flags: nil, complete_values: nil, report_collisions: true, group: nil, desc: nil, long_desc: nil, display_name: nil) click to toggle source

Add a flag to the current tool. Each flag must specify a key which the script may use to obtain the flag value from the context. You may then provide the flags themselves in `OptionParser` form.

@param key [String,Symbol] The key to use to retrieve the value from

the execution context.

@param flags [Array<String>] The flags in OptionParser format. If empty,

a flag will be inferred from the key.

@param accept [Object] An acceptor that validates and/or converts the

value. You may provide either the name of an acceptor you have
defined, or one of the default acceptors provided by OptionParser.
Optional. If not specified, accepts any value as a string.

@param default [Object] The default value. This is the value that will

be set in the context if this flag is not provided on the command
line. Defaults to `nil`.

@param handler [Proc,nil,:set,:push] An optional handler for

setting/updating the value. A handler is a proc taking two
arguments, the given value and the previous value, returning the
new value that should be set. You may also specify a predefined
named handler. The `:set` handler (the default) replaces the
previous value (effectively `-> (val, _prev) { val }`). The
`:push` handler expects the previous value to be an array and
pushes the given value onto it; it should be combined with setting
`default: []` and is intended for "multi-valued" flags.

@param complete_flags [Object] A specifier for shell tab completion

for flag names associated with this flag. By default, a
{Toys::Flag::DefaultCompletion} is used, which provides the flag's
names as completion candidates. To customize completion, set this to
a hash of options to pass to the constructor for
{Toys::Flag::DefaultCompletion}, or pass any other spec recognized
by {Toys::Completion.create}.

@param complete_values [Object] A specifier for shell tab completion

for flag values associated with this flag. Pass any spec
recognized by {Toys::Completion.create}.

@param report_collisions [Boolean] Raise an exception if a flag is

requested that is already in use or marked as disabled. Default is
true.

@param group [Toys::FlagGroup,String,Symbol,nil] Group for

this flag. You may provide a group name, a FlagGroup object, or
`nil` which denotes the default group.

@param desc [String,Array<String>,Toys::WrappableString] Short

description for the flag. See {Toys::ToolDefinition#desc} for a
description of allowed formats. Defaults to the empty string.

@param long_desc [Array<String,Array<String>,Toys::WrappableString>]

Long description for the flag. See {Toys::ToolDefinition#long_desc}
for a description of allowed formats. Defaults to the empty array.

@param display_name [String] A display name for this flag, used in help

text and error messages.

@return [self]

# File lib/toys/tool_definition.rb, line 793
def add_flag(key, flags = [],
             accept: nil, default: nil, handler: nil, complete_flags: nil,
             complete_values: nil, report_collisions: true, group: nil, desc: nil,
             long_desc: nil, display_name: nil)
  unless group.is_a?(FlagGroup::Base)
    group_name = group
    group = @flag_group_names[group_name]
    raise ToolDefinitionError, "No such flag group: #{group_name.inspect}" if group.nil?
  end
  check_definition_state(is_arg: true)
  accept = resolve_acceptor_name(accept)
  complete_flags = resolve_completion_name(complete_flags)
  complete_values = resolve_completion_name(complete_values)
  flag_def = Flag.new(key, flags, @used_flags, report_collisions, accept, handler, default,
                      complete_flags, complete_values, desc, long_desc, display_name, group)
  if flag_def.active?
    @flags << flag_def
    group << flag_def
  end
  @default_data[key] = default
  self
end
add_flag_group(type: :optional, desc: nil, long_desc: nil, name: nil, report_collisions: true, prepend: false) click to toggle source

Add a flag group to the group list.

The type should be one of the following symbols:

*  `:optional` All flags in the group are optional
*  `:required` All flags in the group are required
*  `:exactly_one` Exactly one flag in the group must be provided
*  `:at_least_one` At least one flag in the group must be provided
*  `:at_most_one` At most one flag in the group must be provided

@param type [Symbol] The type of group. Default is `:optional`. @param desc [String,Array<String>,Toys::WrappableString] Short

description for the group. See {Toys::ToolDefinition#desc} for a
description of allowed formats. Defaults to `"Flags"`.

@param long_desc [Array<String,Array<String>,Toys::WrappableString>]

Long description for the flag group. See
{Toys::ToolDefinition#long_desc} for a description of allowed
formats. Defaults to the empty array.

@param name [String,Symbol,nil] The name of the group, or nil for no

name.

@param report_collisions [Boolean] If `true`, raise an exception if a

the given name is already taken. If `false`, ignore. Default is
`true`.

@param prepend [Boolean] If `true`, prepend rather than append the

group to the list. Default is `false`.

@return [self]

# File lib/toys/tool_definition.rb, line 726
def add_flag_group(type: :optional, desc: nil, long_desc: nil,
                   name: nil, report_collisions: true, prepend: false)
  if !name.nil? && @flag_group_names.key?(name)
    return self unless report_collisions
    raise ToolDefinitionError, "Flag group #{name} already exists"
  end
  group = FlagGroup.create(type: type, name: name, desc: desc, long_desc: long_desc)
  @flag_group_names[name] = group unless name.nil?
  if prepend
    @flag_groups.unshift(group)
  else
    @flag_groups.push(group)
  end
  self
end
add_initializer(proc, *args, **kwargs) click to toggle source

Add an initializer.

@param proc [Proc] The initializer block @param args [Object…] Arguments to pass to the initializer @param kwargs [keywords] Keyword arguments to pass to the initializer @return [self]

# File lib/toys/tool_definition.rb, line 992
def add_initializer(proc, *args, **kwargs)
  check_definition_state
  @initializers << [proc, args, kwargs]
  self
end
add_mixin(name, mixin_module = nil, &block) click to toggle source

Add a named mixin module to this tool. You may provide a mixin module or a block that configures one.

@param name [String] The name of the mixin. @param mixin_module [Module] The mixin module. @param block [Proc] Define the mixin module here if a `mixin_module` is

not provided directly.

@return [self]

# File lib/toys/tool_definition.rb, line 589
def add_mixin(name, mixin_module = nil, &block)
  name = name.to_s
  if @mixins.key?(name)
    raise ToolDefinitionError,
          "A mixin named #{name.inspect} has already been defined in tool" \
          " #{display_name.inspect}."
  end
  @mixins[name] = mixin_module || Mixin.create(&block)
  self
end
add_optional_arg(key, default: nil, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) click to toggle source

Add an optional positional argument to the current tool. You must specify a key which the script may use to obtain the argument value from the context. If an optional argument is not given on the command line, the value is set to the given default.

@param key [String,Symbol] The key to use to retrieve the value from

the execution context.

@param default [Object] The default value. This is the value that will

be set in the context if this argument is not provided on the command
line. Defaults to `nil`.

@param accept [Object] An acceptor that validates and/or converts the

value. You may provide either the name of an acceptor you have
defined, or one of the default acceptors provided by OptionParser.
Optional. If not specified, accepts any value as a string.

@param complete [Object] A specifier for shell tab completion. See

{Toys::Completion.create} for recognized formats.

@param display_name [String] A name to use for display (in help text and

error reports). Defaults to the key in upper case.

@param desc [String,Array<String>,Toys::WrappableString] Short

description for the arg. See {Toys::ToolDefinition#desc} for a
description of allowed formats. Defaults to the empty string.

@param long_desc [Array<String,Array<String>,Toys::WrappableString>]

Long description for the arg. See {Toys::ToolDefinition#long_desc}
for a description of allowed formats. Defaults to the empty array.

@return [self]

# File lib/toys/tool_definition.rb, line 896
def add_optional_arg(key, default: nil, accept: nil, complete: nil,
                     display_name: nil, desc: nil, long_desc: nil)
  check_definition_state(is_arg: true)
  accept = resolve_acceptor_name(accept)
  complete = resolve_completion_name(complete)
  arg_def = PositionalArg.new(key, :optional, accept, default, complete,
                              desc, long_desc, display_name)
  @optional_args << arg_def
  @default_data[key] = default
  self
end
add_required_arg(key, accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) click to toggle source

Add a required positional argument to the current tool. You must specify a key which the script may use to obtain the argument value from the context.

@param key [String,Symbol] The key to use to retrieve the value from

the execution context.

@param accept [Object] An acceptor that validates and/or converts the

value. You may provide either the name of an acceptor you have
defined, or one of the default acceptors provided by OptionParser.
Optional. If not specified, accepts any value as a string.

@param complete [Object] A specifier for shell tab completion. See

{Toys::Completion.create} for recognized formats.

@param display_name [String] A name to use for display (in help text and

error reports). Defaults to the key in upper case.

@param desc [String,Array<String>,Toys::WrappableString] Short

description for the arg. See {Toys::ToolDefinition#desc} for a
description of allowed formats. Defaults to the empty string.

@param long_desc [Array<String,Array<String>,Toys::WrappableString>]

Long description for the arg. See {Toys::ToolDefinition#long_desc}
for a description of allowed formats. Defaults to the empty array.

@return [self]

# File lib/toys/tool_definition.rb, line 858
def add_required_arg(key, accept: nil, complete: nil, display_name: nil,
                     desc: nil, long_desc: nil)
  check_definition_state(is_arg: true)
  accept = resolve_acceptor_name(accept)
  complete = resolve_completion_name(complete)
  arg_def = PositionalArg.new(key, :required, accept, nil, complete,
                              desc, long_desc, display_name)
  @required_args << arg_def
  self
end
add_template(name, template_class = nil, &block) click to toggle source

Add a named template class to this tool. You may provide a template class or a block that configures one.

@param name [String] The name of the template. @param template_class [Class] The template class. @param block [Proc] Define the template class here if a `template_class`

is not provided directly.

@return [self]

# File lib/toys/tool_definition.rb, line 636
def add_template(name, template_class = nil, &block)
  name = name.to_s
  if @templates.key?(name)
    raise ToolDefinitionError,
          "A template named #{name.inspect} has already been defined in tool" \
          " #{display_name.inspect}."
  end
  @templates[name] = template_class || Template.create(&block)
  self
end
append_long_desc(long_desc) click to toggle source

Append long description strings.

You must pass an array of lines in the long description. See {#long_desc} for details on how each line may be represented.

@param long_desc [Array<Toys::WrappableString,String,Array<String>>] @return [self]

# File lib/toys/tool_definition.rb, line 547
def append_long_desc(long_desc)
  check_definition_state
  @long_desc.concat(WrappableString.make_array(long_desc))
  self
end
argument_parsing_disabled?() click to toggle source

Returns true if this tool has disabled argument parsing. @return [Boolean]

# File lib/toys/tool_definition.rb, line 378
def argument_parsing_disabled?
  @disable_argument_parsing
end
check_definition_state(is_arg: false, is_method: false) click to toggle source

Check that the tool can still be defined. Should be called internally or from the DSL only. @private

# File lib/toys/tool_definition.rb, line 1130
def check_definition_state(is_arg: false, is_method: false)
  if @definition_finished
    raise ToolDefinitionError,
          "Defintion of tool #{display_name.inspect} is already finished"
  end
  if is_arg && argument_parsing_disabled?
    raise ToolDefinitionError,
          "Tool #{display_name.inspect} has disabled argument parsing"
  end
  if (is_arg || is_method) && delegate_target
    raise ToolDefinitionError,
          "Tool #{display_name.inspect} is already delegating to another tool"
  end
  self
end
completion=(spec) click to toggle source

Set the completion strategy for this ToolDefinition.

See {#completion} for details.

@param spec [Object]

# File lib/toys/tool_definition.rb, line 1017
def completion=(spec)
  spec = resolve_completion_name(spec)
  spec =
    case spec
    when nil, :default
      DefaultCompletion
    when ::Hash
      spec[:""].nil? ? spec.merge({"": DefaultCompletion}) : spec
    else
      spec
    end
  @completion = Completion.create(spec, **{})
end
context_directory() click to toggle source

Return the effective context directory. If there is a custom context directory, uses that. Otherwise, looks for a custom context directory up the tool ancestor chain. If none is found, uses the default context directory from the source info. It is possible for there to be no context directory at all, in which case, returns nil.

@return [String] The effective context directory path. @return [nil] if there is no effective context directory.

# File lib/toys/tool_definition.rb, line 1042
def context_directory
  lookup_custom_context_directory || source_info&.context_directory
end
custom_context_directory=(dir) click to toggle source

Set the custom context directory.

See {#custom_context_directory} for details.

@param dir [String]

# File lib/toys/tool_definition.rb, line 1005
def custom_context_directory=(dir)
  check_definition_state
  @custom_context_directory = dir
end
definition_finished?() click to toggle source

Returns true if this tool's definition has been finished and is locked. @return [Boolean]

# File lib/toys/tool_definition.rb, line 370
def definition_finished?
  @definition_finished
end
delegate_to(target) click to toggle source

Causes this tool to delegate to another tool.

@param target [Array<String>] The full path to the delegate tool. @return [self]

# File lib/toys/tool_definition.rb, line 1052
def delegate_to(target)
  if @delegate_target
    raise ToolDefinitionError,
          "Cannot delegate tool #{display_name.inspect} because" \
          " it already delegates to \"#{@delegate_target.join(' ')}\"."
  end
  if includes_arguments?
    raise ToolDefinitionError,
          "Cannot delegate tool #{display_name.inspect} because" \
          " arguments have already been defined."
  end
  if runnable?
    raise ToolDefinitionError,
          "Cannot delegate tool #{display_name.inspect} because" \
          " the run method has already been defined."
  end
  disable_argument_parsing
  self.run_handler = make_delegation_run_handler(target)
  self.completion = DefaultCompletion.new(delegation_target: target)
  @delegate_target = target
  self
end
desc=(desc) click to toggle source

Set the short description string.

See {#desc} for details.

@param desc [Toys::WrappableString,String,Array<String>]

# File lib/toys/tool_definition.rb, line 521
def desc=(desc)
  check_definition_state
  @desc = WrappableString.make(desc)
end
disable_argument_parsing() click to toggle source

Disable argument parsing for this tool.

@return [self]

# File lib/toys/tool_definition.rb, line 652
def disable_argument_parsing
  check_definition_state
  if includes_arguments?
    raise ToolDefinitionError,
          "Cannot disable argument parsing for tool #{display_name.inspect}" \
          " because arguments have already been defined."
  end
  @disable_argument_parsing = true
  self
end
disable_flag(*flags) click to toggle source

Mark one or more flags as disabled, preventing their use by any subsequent flag definition. This may be used to prevent middleware from defining a particular flag.

@param flags [String…] The flags to disable @return [self]

# File lib/toys/tool_definition.rb, line 824
def disable_flag(*flags)
  check_definition_state(is_arg: true)
  flags = flags.uniq
  intersection = @used_flags & flags
  unless intersection.empty?
    raise ToolDefinitionError, "Cannot disable flags already used: #{intersection.inspect}"
  end
  @used_flags.concat(flags)
  self
end
display_name() click to toggle source

A displayable name of this tool, generally the full name delimited by spaces.

@return [String]

# File lib/toys/tool_definition.rb, line 294
def display_name
  full_name.join(" ")
end
enforce_flags_before_args(state = true) click to toggle source

Enforce that flags must come before args for this tool. You may disable enforcement by passoing `false` for the state.

@param state [Boolean] @return [self]

# File lib/toys/tool_definition.rb, line 670
def enforce_flags_before_args(state = true)
  check_definition_state
  if argument_parsing_disabled?
    raise ToolDefinitionError,
          "Cannot enforce flags before args for tool #{display_name.inspect}" \
          " because parsing is disabled."
  end
  @enforce_flags_before_args = state
  self
end
exact_flag_match_required?() click to toggle source

Returns true if this tool requires exact flag matches. @return [Boolean]

# File lib/toys/tool_definition.rb, line 394
def exact_flag_match_required?
  @require_exact_flag_match
end
finish_definition(loader) click to toggle source

Complete definition and run middleware configs. Should be called from the Loader only. @private

# File lib/toys/tool_definition.rb, line 1098
def finish_definition(loader)
  unless @definition_finished
    ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
      config_proc = proc { nil }
      @built_middleware.reverse_each do |middleware|
        config_proc = make_config_proc(middleware, loader, config_proc)
      end
      config_proc.call
    end
    flag_groups.each do |flag_group|
      flag_group.flags.sort_by!(&:sort_str)
    end
    @definition_finished = true
  end
  self
end
flags_before_args_enforced?() click to toggle source

Returns true if this tool enforces flags before args. @return [Boolean]

# File lib/toys/tool_definition.rb, line 386
def flags_before_args_enforced?
  @enforce_flags_before_args
end
handles_interrupts?() click to toggle source

Returns true if this tool handles interrupts. @return [Boolean]

# File lib/toys/tool_definition.rb, line 318
def handles_interrupts?
  !interrupt_handler.nil?
end
handles_usage_errors?() click to toggle source

Returns true if this tool handles usage errors. @return [Boolean]

# File lib/toys/tool_definition.rb, line 326
def handles_usage_errors?
  !usage_error_handler.nil?
end
include_mixin(mod, *args, **kwargs) click to toggle source

Include the given mixin in the tool class.

The mixin must be given as a module. You can use {#lookup_mixin} or {Loader#resolve_standard_mixin} to resolve named mixins.

@param mod [Module] The mixin module @return [self]

# File lib/toys/tool_definition.rb, line 479
def include_mixin(mod, *args, **kwargs)
  check_definition_state
  if tool_class.included_modules.include?(mod)
    raise ToolDefinitionError, "Mixin already included: #{mod.name}"
  end
  @includes_modules = true
  if tool_class.respond_to?(:super_include)
    tool_class.super_include(mod)
  else
    tool_class.include(mod)
  end
  if mod.respond_to?(:initializer)
    callback = mod.initializer
    add_initializer(callback, *args, **kwargs) if callback
  end
  if mod.respond_to?(:inclusion)
    callback = mod.inclusion
    tool_class.class_exec(*args, **kwargs, &callback) if callback
  end
  self
end
includes_arguments?() click to toggle source

Returns true if at least one flag or positional argument is defined for this tool. @return [Boolean]

# File lib/toys/tool_definition.rb, line 351
def includes_arguments?
  !default_data.empty? || !flags.empty? ||
    !required_args.empty? || !optional_args.empty? ||
    !remaining_arg.nil? || flags_before_args_enforced?
end
includes_definition?() click to toggle source

Returns true if this tool has any definition information. @return [Boolean]

# File lib/toys/tool_definition.rb, line 361
def includes_definition?
  includes_arguments? || runnable? || argument_parsing_disabled? ||
    includes_modules? || includes_description?
end
includes_description?() click to toggle source

Returns true if there is a specific description set for this tool. @return [Boolean]

# File lib/toys/tool_definition.rb, line 342
def includes_description?
  !long_desc.empty? || !desc.empty?
end
includes_modules?() click to toggle source

Returns true if this tool has at least one included module. @return [Boolean]

# File lib/toys/tool_definition.rb, line 334
def includes_modules?
  @includes_modules
end
interrupt_handler=(handler) click to toggle source

Set the interrupt handler.

@param handler [Proc,Symbol] The interrupt handler

# File lib/toys/tool_definition.rb, line 963
def interrupt_handler=(handler)
  check_definition_state(is_method: true)
  if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
    raise ToolDefinitionError, "Interrupt handler must be a proc or symbol"
  end
  @interrupt_handler = handler
end
lock_source(source) click to toggle source

Sets the path to the file that defines this tool. A tool may be defined from at most one path. If a different path is already set, it is left unchanged.

@param source [Toys::SourceInfo] Source info @return [self]

# File lib/toys/tool_definition.rb, line 509
def lock_source(source)
  @source_info ||= source
  self
end
long_desc=(long_desc) click to toggle source

Set the long description strings.

See {#long_desc} for details.

@param long_desc [Array<Toys::WrappableString,String,Array<String>>]

# File lib/toys/tool_definition.rb, line 533
def long_desc=(long_desc)
  check_definition_state
  @long_desc = WrappableString.make_array(long_desc)
end
lookup_acceptor(name) click to toggle source

Get the named acceptor from this tool or its ancestors.

@param name [String] The acceptor name. @return [Toys::Acceptor::Base] The acceptor. @return [nil] if no acceptor of the given name is found.

# File lib/toys/tool_definition.rb, line 433
def lookup_acceptor(name)
  @acceptors.fetch(name.to_s) { |k| @parent ? @parent.lookup_acceptor(k) : nil }
end
lookup_completion(name) click to toggle source

Get the named completion from this tool or its ancestors.

@param name [String] The completion name @return [Toys::Completion::Base,Proc] The completion proc. @return [nil] if no completion of the given name is found.

# File lib/toys/tool_definition.rb, line 466
def lookup_completion(name)
  @completions.fetch(name.to_s) { |k| @parent ? @parent.lookup_completion(k) : nil }
end
lookup_custom_context_directory() click to toggle source

Lookup the custom context directory in this tool and its ancestors. @private

# File lib/toys/tool_definition.rb, line 1079
def lookup_custom_context_directory
  custom_context_directory || @parent&.lookup_custom_context_directory
end
lookup_mixin(name) click to toggle source

Get the named mixin from this tool or its ancestors.

@param name [String] The mixin name. @return [Module] The mixin module. @return [nil] if no mixin of the given name is found.

# File lib/toys/tool_definition.rb, line 455
def lookup_mixin(name)
  @mixins.fetch(name.to_s) { |k| @parent ? @parent.lookup_mixin(k) : nil }
end
lookup_template(name) click to toggle source

Get the named template from this tool or its ancestors.

@param name [String] The template name. @return [Class,nil] The template class. @return [nil] if no template of the given name is found.

# File lib/toys/tool_definition.rb, line 444
def lookup_template(name)
  @templates.fetch(name.to_s) { |k| @parent ? @parent.lookup_template(k) : nil }
end
mark_includes_modules() click to toggle source

Mark this tool as having at least one module included. @private

# File lib/toys/tool_definition.rb, line 1087
def mark_includes_modules
  check_definition_state
  @includes_modules = true
  self
end
positional_args() click to toggle source

All arg definitions in order: required, optional, remaining.

@return [Array<Toys::PositionalArg>]

# File lib/toys/tool_definition.rb, line 403
def positional_args
  result = required_args + optional_args
  result << remaining_arg if remaining_arg
  result
end
require_exact_flag_match(state = true) click to toggle source

Require that flags must match exactly. (If false, flags can match an unambiguous substring.)

@param state [Boolean] @return [self]

# File lib/toys/tool_definition.rb, line 688
def require_exact_flag_match(state = true)
  check_definition_state
  if argument_parsing_disabled?
    raise ToolDefinitionError,
          "Cannot require exact flag match for tool" \
          " #{display_name.inspect} because parsing is disabled."
  end
  @require_exact_flag_match = state
  self
end
reset_definition() click to toggle source

Reset the definition of this tool, deleting all definition data but leaving named acceptors, mixins, and templates intact. Should be called only from the DSL. @private

# File lib/toys/tool_definition.rb, line 45
def reset_definition
  @tool_class = @precreated_class || create_class

  @source_info = nil
  @definition_finished = false

  @desc = WrappableString.new("")
  @long_desc = []

  @default_data = {}
  @used_flags = []
  @initializers = []

  default_flag_group = FlagGroup::Base.new(nil, nil, nil)
  @flag_groups = [default_flag_group]
  @flag_group_names = {nil => default_flag_group}

  @flags = []
  @required_args = []
  @optional_args = []
  @remaining_arg = nil

  @disable_argument_parsing = false
  @enforce_flags_before_args = false
  @require_exact_flag_match = false
  @includes_modules = false
  @custom_context_directory = nil

  @interrupt_handler = nil
  @usage_error_handler = nil
  @delegate_target = nil

  @completion = DefaultCompletion.new
end
resolve_flag(str) click to toggle source

Resolve the given flag given the flag string. Returns an object that describes the resolution result, including whether the resolution matched a unique flag, the specific flag syntax that was matched, and additional information.

@param str [String] Flag string @return [Toys::Flag::Resolution]

# File lib/toys/tool_definition.rb, line 418
def resolve_flag(str)
  result = Flag::Resolution.new(str)
  flags.each do |flag_def|
    result.merge!(flag_def.resolve(str))
  end
  result
end
root?() click to toggle source

Returns true if this tool is a root tool. @return [Boolean]

# File lib/toys/tool_definition.rb, line 302
def root?
  full_name.empty?
end
run_handler=(proc) click to toggle source

Set the run handler block

@param proc [Proc] The runnable block

# File lib/toys/tool_definition.rb, line 951
def run_handler=(proc)
  check_definition_state(is_method: true)
  tool_class.class_eval do
    define_method(:run, &proc)
  end
end
run_initializers(context) click to toggle source

Run all initializers against a context. Called from the Runner. @private

# File lib/toys/tool_definition.rb, line 1119
def run_initializers(context)
  @initializers.each do |func, args, kwargs|
    context.instance_exec(*args, **kwargs, &func)
  end
end
runnable?() click to toggle source

Returns true if this tool is marked as runnable. @return [Boolean]

# File lib/toys/tool_definition.rb, line 310
def runnable?
  tool_class.public_instance_methods(false).include?(:run)
end
set_remaining_args(key, default: [], accept: nil, complete: nil, display_name: nil, desc: nil, long_desc: nil) click to toggle source

Specify what should be done with unmatched positional arguments. You must specify a key which the script may use to obtain the remaining args from the context.

@param key [String,Symbol] The key to use to retrieve the value from

the execution context.

@param default [Object] The default value. This is the value that will

be set in the context if no unmatched arguments are provided on the
command line. Defaults to the empty array `[]`.

@param accept [Object] An acceptor that validates and/or converts the

value. You may provide either the name of an acceptor you have
defined, or one of the default acceptors provided by OptionParser.
Optional. If not specified, accepts any value as a string.

@param complete [Object] A specifier for shell tab completion. See

{Toys::Completion.create} for recognized formats.

@param display_name [String] A name to use for display (in help text and

error reports). Defaults to the key in upper case.

@param desc [String,Array<String>,Toys::WrappableString] Short

description for the arg. See {Toys::ToolDefinition#desc} for a
description of allowed formats. Defaults to the empty string.

@param long_desc [Array<String,Array<String>,Toys::WrappableString>]

Long description for the arg. See {Toys::ToolDefinition#long_desc}
for a description of allowed formats. Defaults to the empty array.

@return [self]

# File lib/toys/tool_definition.rb, line 934
def set_remaining_args(key, default: [], accept: nil, complete: nil,
                       display_name: nil, desc: nil, long_desc: nil)
  check_definition_state(is_arg: true)
  accept = resolve_acceptor_name(accept)
  complete = resolve_completion_name(complete)
  arg_def = PositionalArg.new(key, :remaining, accept, default, complete,
                              desc, long_desc, display_name)
  @remaining_arg = arg_def
  @default_data[key] = default
  self
end
simple_name() click to toggle source

The local name of this tool, i.e. the last element of the full name.

@return [String]

# File lib/toys/tool_definition.rb, line 284
def simple_name
  full_name.last
end
usage_error_handler=(handler) click to toggle source

Set the usage error handler.

@param handler [Proc,Symbol] The usage error handler

# File lib/toys/tool_definition.rb, line 976
def usage_error_handler=(handler)
  check_definition_state(is_method: true)
  if !handler.is_a?(::Proc) && !handler.is_a?(::Symbol) && !handler.nil?
    raise ToolDefinitionError, "Usage error handler must be a proc or symbol"
  end
  @usage_error_handler = handler
end

Private Instance Methods

create_class() click to toggle source
# File lib/toys/tool_definition.rb, line 1347
def create_class
  ::Class.new(@parent&.settings&.propagate_helper_methods ? @parent.tool_class : ::Toys::Context)
end
make_config_proc(middleware, loader, next_config) click to toggle source
# File lib/toys/tool_definition.rb, line 1351
def make_config_proc(middleware, loader, next_config)
  if middleware.respond_to?(:config)
    proc { middleware.config(self, loader, &next_config) }
  else
    next_config
  end
end
make_delegation_run_handler(target) click to toggle source
# File lib/toys/tool_definition.rb, line 1359
def make_delegation_run_handler(target)
  lambda do
    path = [target.join(" ").inspect]
    walk_context = self
    until walk_context.nil?
      name = walk_context[::Toys::Context::Key::TOOL_NAME]
      path << name.join(" ").inspect
      if name == target
        raise "Delegation loop: #{path.join(' <- ')}"
      end
      walk_context = walk_context[::Toys::Context::Key::DELEGATED_FROM]
    end
    cli = self[::Toys::Context::Key::CLI]
    cli.loader.load_for_prefix(target)
    unless cli.loader.tool_defined?(target)
      raise "Delegate target not found: \"#{target.join(' ')}\""
    end
    exit(cli.run(target + self[::Toys::Context::Key::ARGS], delegated_from: self))
  end
end
resolve_acceptor_name(name) click to toggle source
# File lib/toys/tool_definition.rb, line 1380
def resolve_acceptor_name(name)
  return name unless name.is_a?(::String)
  accept = lookup_acceptor(name)
  raise ToolDefinitionError, "Unknown acceptor: #{name.inspect}" if accept.nil?
  accept
end
resolve_completion_name(name) click to toggle source
# File lib/toys/tool_definition.rb, line 1387
def resolve_completion_name(name)
  return name unless name.is_a?(::String)
  completion = lookup_completion(name)
  raise ToolDefinitionError, "Unknown completion: #{name.inspect}" if completion.nil?
  completion
end