class Thor

Namespace

Namespace

Namespace

Namespace

Namespace

Namespace

Namespace

Namespace

Namespace

Namespace

Definitions

Constants

AmbiguousTaskError
DynamicTask

A dynamic command that handles method missing scenarios.

HELP_MAPPINGS

Shortcuts for help.

HiddenTask

A command that is hidden in help messages but still invocable.

ROOT

Absolute, expanded path to the gem's root directory.

@return [Pathname]

TEMPLATE_EXTNAME
THOR_RESERVED_WORDS

Thor methods that should not be overwritten by the user.

THOR_VERSION

The version of Thor that Atli is up to date with.

Right now, it's the version of Thor that was forked, but if I'm able to merge Thor updates in and end up doing so, I intend to update this to reflect it.

See {file:doc/files/notes/versioning.md} for details.

@return [String]

Task
UndefinedTaskError

Raised when a command was not found.

VERSION

Gem version string.

See {file:doc/files/notes/versioning.md} for details.

@return [String]

Public Class Methods

check_unknown_options!(options = {}) click to toggle source

Extend check unknown options to accept a hash of conditions.

Parameters

options<Hash>: A hash containing :only and/or :except keys

# File lib/thor.rb, line 452
def self.check_unknown_options!(options = {})
  @check_unknown_options ||= {}
  options.each do |key, value|
    if value
      @check_unknown_options[key] = Array(value)
    else
      @check_unknown_options.delete(key)
    end
  end
  @check_unknown_options
end
command_help(shell, command_name, subcommand = false) click to toggle source

Prints help information for the given command.

@param [Thor::Shell] shell

@param [String] command_name

@param [Boolean] subcommand

*Alti* *addition* - passed from {#help} when that command is being
invoked as a subcommand.

The values is passed through to {.banner} and eventually
{Command#formatted_usage} so that it can properly display the usage
message

    basename subcmd cmd ARGS...

versus what it did when I found it:

    basename cmd ARGS...

which, of course, doesn't work if +cmd+ is inside +subcmd+.

@return [nil]

# File lib/thor.rb, line 221
def self.command_help(shell, command_name, subcommand = false)
  meth = normalize_command_name(command_name)
  command = all_commands[meth]
  handle_no_command_error(meth) unless command

  shell.say "Usage:"
  shell.say "  #{banner(command, nil, subcommand)}"
  shell.say
  
  class_options_help \
    shell,
    command.options.values.group_by { |option| option.group }
  
  if command.long_description
    shell.say "Description:"
    shell.print_wrapped(command.long_description, :indent => 2)
  else
    shell.say command.description
  end
  
  unless command.examples.empty?
    shell.say "\n"
    shell.say "Examples:"
    shell.say "\n"
    
    command.examples.each_with_index do |example, index|
      lines = example.lines
      
      bullet = "#{ index + 1}.".ljust 4

      shell.say "#{ bullet }#{ lines[0] }"
      
      lines[1..-1].each do |line|
        shell.say "    #{ line }"
      end
    end
    
    shell.say "\n"
  end
  
  nil
end
default_command(meth = nil) click to toggle source

Sets the default command when thor is executed without an explicit command to be called.

Parameters

meth<Symbol>

name of the default command

# File lib/thor.rb, line 30
def self.default_command(meth = nil)
  if meth
    @default_command = meth == :none ? "help" : meth.to_s
  else
    @default_command ||= from_superclass(:default_command, "help")
  end
end
desc(usage, description, options = {}) click to toggle source

Defines the usage and the description of the next command.

Parameters

usage<String> description<String> options<String>

# File lib/thor.rb, line 66
def self.desc(usage, description, options = {})
  if options[:for]
    command = find_and_refresh_command(options[:for])
    command.usage = usage             if usage
    command.description = description if description
  else
    @usage = usage
    @desc = description
    @hide = options[:hide] || false
  end
end
disable_required_check!(*command_names) click to toggle source

Disable the check for required options for the given commands. This is useful if you have a command that does not need the required options to work, like help.

Parameters

Symbol …

A list of commands that should be affected.

# File lib/thor.rb, line 541
def self.disable_required_check!(*command_names)
  disable_required_check.merge(command_names)
end
example(example) click to toggle source

Add an example to the next defined command.

@param [String] example

The example text.

@return [nil]

# File lib/thor/example.rb, line 17
def self.example example
  @examples ||= []
  @examples << example
  nil
end
help(shell, subcommand = false) click to toggle source

Prints help information for this class.

@param [Thor::Shell] shell @return (see Thor::Base::ClassMethods#class_options_help)

# File lib/thor.rb, line 272
def self.help(shell, subcommand = false)
  list = printable_commands(true, subcommand)
  Thor::Util.thor_classes_in(self).each do |klass|
    list += klass.printable_commands(false)
  end
  list.sort! { |a, b| a[0] <=> b[0] }

  if defined?(@package_name) && @package_name
    shell.say "#{@package_name} commands:"
  else
    shell.say "Commands:"
  end

  shell.print_table(list, :indent => 2, :truncate => true)
  shell.say
  class_options_help(shell)
end
long_desc(long_description, options = {}) click to toggle source

Defines the long description of the next command.

Parameters

long description<String>

# File lib/thor.rb, line 84
def self.long_desc(long_description, options = {})
  if options[:for]
    command = find_and_refresh_command(options[:for])
    command.long_description = long_description if long_description
  else
    @long_desc = long_description
  end
end
map(mappings = nil) click to toggle source

Maps an input to a command. If you define:

map "-T" => "list"

Running:

thor -T

Will invoke the list command.

@example Map a single alias to a command

map 'ls' => :list

@example Map multiple aliases to a command

map ['ls', 'show'] => :list

@note

@param [nil | Hash<#to_s | Array<#to_s>, to_s>?] mappings

When `nil`, all mappings for the class are returned.

When a {Hash} is provided, sets the `mappings` before returning
all mappings.

@return [HashWithIndifferentAccess<String, Symbol>]

Mapping of command aliases to command method names.
# File lib/thor.rb, line 123
def self.map mappings = nil
  @map ||= from_superclass :map, HashWithIndifferentAccess.new

  if mappings
    mappings.each do |key, value|
      if key.respond_to? :each
        key.each { |subkey| @map[ subkey.to_s ] = value.to_s }
      else
        @map[ key.to_s ] = value.to_s
      end
    end
  end

  @map
end
method_option(name, **options) click to toggle source

Adds an option to the set of method options. If :for is given as option, it allows you to change the options from a previous defined command.

def previous_command
  # magic
end

method_option :foo => :bar, :for => :previous_command

def next_command
  # magic
end

Parameters

name<Symbol>

The name of the argument.

options<Hash>

Described below.

Options

:desc - Description for the argument. :required - If the argument is required or not. :default - Default value for this argument. It cannot be required and

have default values.

:aliases - Aliases for this option. :type - The type of the argument, can be :string, :hash, :array,

:numeric or :boolean.

:banner - String to show on usage notes. :hide - If you want to hide this option from the help.

# File lib/thor.rb, line 184
def self.method_option name, **options
  scope = if options[:for]
    find_and_refresh_command(options[:for]).options
  else
    method_options
  end

  build_option(name, options, scope)
end
method_options(options = nil) click to toggle source

Declares the options for the next command to be declared.

Parameters

Hash[Symbol => Object]

The hash key is the name of the option and the value

is the type of the option. Can be :string, :array, :hash, :boolean, :numeric or :required (string). If you give a value, the type of the value is used.

# File lib/thor.rb, line 147
def self.method_options(options = nil)
  @method_options ||= HashWithIndifferentAccess.new
  build_options(options, @method_options) if options
  @method_options
end
package_name(name, _ = {}) click to toggle source

Allows for custom “Command” package naming.

Parameters

name<String> options<Hash>

# File lib/thor.rb, line 19
def self.package_name(name, _ = {})
  @package_name = name.nil? || name == "" ? nil : name
end
printable_commands(all = true, subcommand = false) click to toggle source

Returns commands ready to be printed.

@param [Boolean] all

When `true`,

@return [Array<(String, String)>]

Array of pairs with:

-   `[0]` - The "usage" format.
-   `[1]` - The command description.
# File lib/thor.rb, line 302
def self.printable_commands all = true, subcommand = false
  (all ? all_commands : commands).map do |_, command|
    next if command.hidden?
    item = []
    item << banner(command, false, subcommand)
    item << ( command.description ?
              "# #{command.description.gsub(/\s+/m, ' ')}" : "" )
    item
  end.compact
end
register(klass, subcommand_name, usage, description, options = {}) click to toggle source

Registers another Thor subclass as a command.

Parameters

klass<Class>

Thor subclass to register

command<String>

Subcommand name to use

usage<String>

Short usage for the subcommand

description<String>

Description for the subcommand

# File lib/thor.rb, line 48
def self.register(klass, subcommand_name, usage, description, options = {})
  if klass <= Thor::Group
    desc usage, description, options
    define_method(subcommand_name) { |*args| invoke(klass, args) }
  else
    desc usage, description, options
    subcommand subcommand_name, klass
  end
end
running_from_source?() click to toggle source

Are we running from the source code (vesus from a Gem install)?

Looks for the `//dev` directory, which is not included in the package.

@return [Boolean]

# File lib/thor/version.rb, line 39
def self.running_from_source?
  ( ROOT + 'dev' ).directory?
end
stop_on_unknown_option!(*command_names) click to toggle source

Stop parsing of options as soon as an unknown option or a regular argument is encountered. All remaining arguments are passed to the command. This is useful if you have a command that can receive arbitrary additional options, and where those additional options should not be handled by Thor.

Example

To better understand how this is useful, let's consider a command that calls an external command. A user may want to pass arbitrary options and arguments to that command. The command itself also accepts some options, which should be handled by Thor.

class_option "verbose",  :type => :boolean
stop_on_unknown_option! :exec
check_unknown_options!  :except => :exec

desc "exec", "Run a shell command"
def exec(*args)
  puts "diagnostic output" if options[:verbose]
  Kernel.exec(*args)
end

Here exec can be called with --verbose to get diagnostic output, e.g.:

$ thor exec --verbose echo foo
diagnostic output
foo

But if --verbose is given after echo, it is passed to echo instead:

$ thor exec echo --verbose foo
--verbose foo

Parameters

Symbol …

A list of commands that should be affected.

# File lib/thor.rb, line 525
def self.stop_on_unknown_option!(*command_names)
  stop_on_unknown_option.merge(command_names)
end
subcommand(ui_name, subcommand_class, desc: nil) click to toggle source

Declare a subcommand by providing an UI name and subcommand class.

@example

class MySub < Thor
  desc 'blah', "Blah!"
  def blah
    # ...
  end
end

class Main < Thor
  subcommand 'my-sub', MySub, desc: "Do sub stuff"
end

@param [String | Symbol] ui_name

The name as you would like it to appear in the user interface 
(help, etc.).

{String#underscore} is called on this argument to create the "method 
name", which is used as the name of the method that will handle 
subcommand invokation, and as the "internal name" that is added to
{#subcommands} and used as it's key in {#subcommand_classes}.

The subcommand should be callable from the CLI using both names.

@param [Thor::Base] subcommand_class

The subcommand class.

Note that I have not tried using {Thor::Group} as subcommand classes,
and the documentation and online search results around it are typically
vague and uncertain.

@param [String?] desc:

Optional description of the subcommand for help messages.

If this option is provided, a {Thor.desc} call will be issued before 
the subcommand hanlder method is dynamically added.

If you don't provide this option, you probably want to make your
own call to {Thor.desc} before calling `.subcommand` like:

    desc 'my-sub SUBCOMMAND...', "Do subcommand stuff."
    subcommand 'my-sub', MySub

The `desc:` keyword args lets you consolidate this into one call:

    subcommand 'my-sub', MySub, desc: "Do subcommand stuff."

@return [nil]

This seemed to just return "whatever" (`void` as we say), but I threw
`nil` in there to be clear.
# File lib/thor.rb, line 395
def self.subcommand ui_name, subcommand_class, desc: nil
  ui_name = ui_name.to_s
  method_name = ui_name.underscore
  subcommands << method_name
  subcommand_class.subcommand_help ui_name
  subcommand_classes[method_name] = subcommand_class

  if desc
    self.desc "#{ ui_name } SUBCOMMAND...", desc
  end

  define_method method_name do |*args|
    args, opts = Thor::Arguments.split(args)
    invoke_args = [
      args,
      opts,
      {:invoked_via_subcommand => true, :class_options => options}
    ]
    invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
    invoke subcommand_class, *invoke_args
  end

  # Sigh... this used to just be `subcommand_class.commands...`, but that
  # fails when:
  #
  # 1.  A Thor class is created with commands defined, then
  # 2.  That Thor class is subclassed, then
  # 3.  The *subclass* as added as a {.subcommand}.
  #
  # This is because then the commands that need {Command#ancestor_name}
  # set are not part of {.commands}, only {.all_commands}.
  #
  # At the moment, this *seems* like an architectural problem, since
  # the commands being mutated via {Thor::Command#ancestor_name=} do
  # not *belong* to `self` - they are simply part of superclass, which
  # could logically be re-used across through additional sub-classes
  # by multiple {Thor} *unless* this is not party of the paradigm...
  # but the paradigm fundametals and constraints are never really laid
  # out anywhere.
  #
  # Anyways, switching to {subcommand_classes.all_commands...} hacks
  # the problem away for the moment.
  #
  subcommand_class.all_commands.each do |_meth, command|
    command.ancestor_name ||= ui_name
  end

  nil
end
subcommand_classes() click to toggle source

Map of subcommand names to Thor classes for this Thor class only.

@note

`.subcommands` is not necessarily equal to `.subcommand_classes.keys`
- it won't be when there are subcommands inherited from super classes.

@note

I'm not really sure how this relates to {Thor::Group}... and I'm not
going to take the time to find out now.

@return [Hash<String, Class<Thor::Base>]

# File lib/thor.rb, line 338
def self.subcommand_classes
  @subcommand_classes ||= {}
end
subcommands() click to toggle source

List of subcommand names, including those inherited from super classes.

@return [Array<String>]

# File lib/thor.rb, line 320
def self.subcommands
  @subcommands ||= from_superclass(:subcommands, [])
end

Public Instance Methods

check_requirement!() click to toggle source

Raises an error if @non_assigned_required array is not empty.

# File lib/thor/parser/arguments.rb, line 197
def check_requirement!
  return if @non_assigned_required.empty?
  names = @non_assigned_required.map do |o|
    o.respond_to?(:switch_name) ? o.switch_name : o.human_name
  end.join("', '")
  class_name = self.class.name.split("::").last.downcase
  raise RequiredArgumentMissingError,
    "No value provided for required #{class_name} '#{names}'"
end
current_is_switch?() click to toggle source

Check if the current value in peek is a registered switch.

Two booleans are returned. The first is true if the current value starts with a hyphen; the second is true if it is a registered switch.

# File lib/thor/parser/options.rb, line 221
def current_is_switch?
  case peek
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
    [true, switch?($1)]
  when SHORT_SQ_RE
    [true, $1.split("").any? { |f| switch?("-#{f}") }]
  else
    [false, false]
  end
end
current_is_switch_formatted?() click to toggle source
# File lib/thor/parser/options.rb, line 233
def current_is_switch_formatted?
  case peek
  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
    true
  else
    false
  end
end
current_is_value?() click to toggle source
# File lib/thor/parser/arguments.rb, line 101
def current_is_value?
  peek && peek.to_s !~ /^-/
end
last?() click to toggle source
# File lib/thor/parser/arguments.rb, line 77
def last?
  @pile.empty?
end
normalize_switch(raw_switch_arg) click to toggle source

Check if the given argument is actually a shortcut.

Also normalizes '_' to '-'.

@param [String] raw_switch_arg

The raw switch arg that we received (essentially, what was passed
on the CLI).

@return [String]

Normalized, de-aliased switch string.
# File lib/thor/parser/options.rb, line 281
def normalize_switch raw_switch_arg
  (@shorts[raw_switch_arg] || raw_switch_arg).tr("_", "-")
end
parse_array(name) click to toggle source

Runs through the argument array getting all strings until no string is found or a switch is found.

["a", "b", "c"]

And returns it as an array:

["a", "b", "c"]
# File lib/thor/parser/arguments.rb, line 141
def parse_array(name)
  return shift if peek.is_a?(Array)
  array = []
  array << shift while current_is_value?
  array
end
parse_boolean(switch) click to toggle source

Parse boolean values which can be given as –foo=true, –foo or –no-foo.

# File lib/thor/parser/options.rb, line 294
def parse_boolean(switch)
  if current_is_value?
    if ["true", "TRUE", "t", "T", true].include?(peek)
      shift
      true
    elsif ["false", "FALSE", "f", "F", false].include?(peek)
      shift
      false
    else
      !no_or_skip?(switch)
    end
  else
    @switches.key?(switch) || !no_or_skip?(switch)
  end
end
parse_hash(name) click to toggle source

Runs through the argument array getting strings that contains “:” and mark it as a hash:

[ "name:string", "age:integer" ]

Becomes:

{ "name" => "string", "age" => "integer" }
# File lib/thor/parser/arguments.rb, line 115
def parse_hash(name)
  return shift if peek.is_a?(Hash)
  hash = {}

  while current_is_value? && peek.include?(":")
    key, value = shift.split(":", 2)
    if hash.include? key
      raise MalformattedArgumentError,
        "You can't specify '#{key}' more than once in option " \
        "'#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}"
    end
    hash[key] = value
  end
  hash
end
parse_numeric(name) click to toggle source

Check if the peek is numeric format and return a Float or Integer. Check if the peek is included in enum if enum is provided. Otherwise raises an error.

# File lib/thor/parser/arguments.rb, line 153
def parse_numeric(name)
  return shift if peek.is_a?(Numeric)

  unless peek =~ NUMERIC && $& == peek
    raise MalformattedArgumentError,
      "Expected numeric value for '#{name}'; got #{peek.inspect}"
  end

  value = $&.index(".") ? shift.to_f : shift.to_i
  if @switches.is_a?(Hash) && switch = @switches[name]
    if switch.enum && !switch.enum.include?(value)
      raise MalformattedArgumentError,
        "Expected '#{name}' to be one of #{switch.enum.join(', ')}; " \
        "got #{value}"
    end
  end
  value
end
parse_peek(switch, option) click to toggle source

Parse the value at the peek analyzing if it requires an input or not.

@param [String] switch

The normalized option switch, as returned from {#normalize_switch}.
# File lib/thor/parser/options.rb, line 316
def parse_peek switch, option
  if current_is_switch_formatted? || last?
    if option.boolean?
      # No problem for boolean types
    elsif no_or_skip?(switch)
      return nil # User set value to nil
    elsif option.string? && !option.required?
      # Return the default if there is one, else the human name
      return option.lazy_default || option.default || option.human_name
    elsif option.lazy_default
      return option.lazy_default
    else
      raise MalformattedArgumentError,
        "No value provided for option '#{switch}'"
    end
  end

  @non_assigned_required.delete(option)
  send(:"parse_#{option.type}", switch)
end
parse_string(name) click to toggle source

Parse string: for –string-arg, just return the current value in the pile for –no-string-arg, nil Check if the peek is included in enum if enum is provided. Otherwise raises an error.

# File lib/thor/parser/arguments.rb, line 178
def parse_string(name)
  if no_or_skip?(name)
    nil
  else
    value = shift
    if @switches.is_a?(Hash) && switch = @switches[name]
      if switch.enum && !switch.enum.include?(value)
        raise MalformattedArgumentError,
          "Expected '#{name}' to be one of #{switch.enum.join(', ')}; " \
          "got #{value}"
      end
    end
    value
  end
end
parsing_options?() click to toggle source
# File lib/thor/parser/options.rb, line 286
def parsing_options?
  peek
  @parsing_options
end
peek() click to toggle source
# File lib/thor/parser/arguments.rb, line 82
def peek
  @pile.first
end
shift() click to toggle source
# File lib/thor/parser/arguments.rb, line 87
def shift
  @pile.shift
end
switch?(arg) click to toggle source
# File lib/thor/parser/options.rb, line 243
def switch?(arg)
  switch_option(normalize_switch(arg))
end
switch_option(arg) click to toggle source

Get the option for a switch arg.

Handles parsing `–no-<option>` and `–skip-<option>` styles as well.

@param [String] arg

The switch part of the CLI arg, like `--blah`.

@return [Thor::Option]

If we have an option for the switch.

@return [nil]

If we don't have an option for the switch.
# File lib/thor/parser/options.rb, line 261
def switch_option(arg)
  if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition
    @switches[arg] || @switches["--#{match}"]
  else
    @switches[arg]
  end
end
unshift(arg) click to toggle source
# File lib/thor/parser/arguments.rb, line 92
def unshift(arg)
  if arg.is_a?(Array)
    @pile = arg + @pile
  else
    @pile.unshift(arg)
  end
end