Method: Thor.subcommand

Defined in:
lib/thor.rb

.subcommand(ui_name, subcommand_class, desc: nil) ⇒ nil

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

Examples:

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

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

Parameters:

  • ui_name (String | Symbol)

    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.

  • subcommand_class (Thor::Base)

    The subcommand class.

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

  • desc: (String?) (defaults to: nil)

    Optional description of the subcommand for help messages.

    If this option is provided, a 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 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."
    

Returns:

  • (nil)

    This seemed to just return “whatever” (‘void` as we say), but I threw `nil` in there to be clear.



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# 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