Search code examples
rubycommand-line-interfacethor

Thor CLI: Setting a custom order of commands in help output


The thor gem seems to always order the defined commands alphabetically when printing its help output. Example:

#!/usr/bin/env ruby

require "thor"

class MyCLI < Thor
  desc "z", "this should go first"
  def z; end

  desc "a", "this should go second"
  def a; end
end

MyCLI.start(ARGV)

Saving this script as thor-test and calling it without arguments gives this output:

Commands:
  thor-test a               # this should go second
  thor-test help [COMMAND]  # Describe available commands or one specific command
  thor-test z               # this should go first

Question: How can I tell Thor to order the entries differently?


Solution

  • Seems as if Thor doesn't offer a configuration option for this. So I'll settle on some monkey-patching for now. aristotll's answer pointed me to the right place in Thor's source code.

    But instead of hacking the <=> method, I decided to change the implementation of the help method. This seems still cleaner to me and has the advantage that I can further influence the behavior of the help output:

    #!/usr/bin/env ruby
    
    require "thor"
    
    class MyCLI < Thor
      class << self
        def help(shell, subcommand = false)
          list = printable_commands(true, subcommand)
          Thor::Util.thor_classes_in(self).each do |klass|
            list += klass.printable_commands(false)
          end
    
          # Remove this line to disable alphabetical sorting
          # list.sort! { |a, b| a[0] <=> b[0] }
    
          # Add this line to remove the help-command itself from the output
          list.reject! {|l| l[0].split[1] == 'help'}
    
          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)
    
          # Add this line if you want to print custom text at the end of your help output.
          # (similar to how Rails does it)
          shell.say 'All commands can be run with -h (or --help) for more information.'
        end
      end
    
      desc "z", "this should go first"
      def z; end
    
      desc "a", "this should go second"
      def a; end
    end
    
    MyCLI.start(ARGV)