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 mergeThor
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
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
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
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
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 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
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
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
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
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
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
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
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
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
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 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
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
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
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
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
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
# 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
# File lib/thor/parser/arguments.rb, line 101 def current_is_value? peek && peek.to_s !~ /^-/ end
# File lib/thor/parser/arguments.rb, line 77 def last? @pile.empty? end
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
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 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
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
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 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: 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
# File lib/thor/parser/options.rb, line 286 def parsing_options? peek @parsing_options end
# File lib/thor/parser/arguments.rb, line 82 def peek @pile.first end
# File lib/thor/parser/arguments.rb, line 87 def shift @pile.shift end
# File lib/thor/parser/options.rb, line 243 def switch?(arg) switch_option(normalize_switch(arg)) end
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
# 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