使用 Thor 创建嵌套子命令

问题描述 投票:0回答:3

我想创建一个 CLI 工具,其命令格式如下:

clitool jobs execute some-job --arg1 value --arg2 another_value

Thor 中是否可以有子命令的子命令?我还想保留我在类中为

clitool jobs execute
子命令定义的 class_options,以用于
execute
下的任何其他子命令。

ruby rubygems command-line-interface thor
3个回答
3
投票

我已经成功使用了子命令的子命令,尽管有一个小错误。我还没有尝试为子命令保留 class_options,所以我没有答案。

对于嵌套子命令,可以执行以下操作:

class Execute < Thor
  desc 'some_job', 'Execute something'
  option :arg1, type: :string, desc: 'First option'
  option :arg2, type: :string, desc: 'Second option'
  def some_job
    puts "Executing some_job:"
    puts "  --arg1 = #{options[:arg1]}"
    puts "  --arg2 = #{options[:arg2]}"
  end
end # class Execute

class Jobs < Thor
  # Other task definitions
  desc 'execute', 'Execute jobs'
  subcommand 'execute', Execute
end # class Jobs

class CliTool < Thor
  # Other task definitions
  desc 'jobs', 'Do stuff with jobs'
  subcommand 'jobs', Jobs
end

CliTool.start

这似乎符合你的要求:

$ clitool jobs execute some-job --arg1 value --arg2 another_value
Executing some_job:
  --arg1 = value
  --arg2 = another_value

$

似乎存在一个错误:子命令的子命令的帮助文本无法正常工作:

$ clitool help
Commands:
  clitool help [COMMAND] # Describe subcommands or one specific subcommand
  clitool jobs           # Do stuff with jobs

$ clitool jobs help
Commands:
  clitool jobs execute        # Execute jobs
  clitool jobs help [COMMAND] # Describe subcommands or one specific subcommand

$ clitool jobs help execute
Commands:
  clitool execute help [COMMAND] # Describe subcommands or one specific subcommand
  clitool execute some_job       # Execute something

$

最后的帮助文本应显示“clitool jobs执行some_job...”,但前缀

jobs
被省略。也许有一位大师可以告诉我如何纠正这个问题。


0
投票

我意识到这有点偏离主题,但这个主题出现在上面的答案和一些评论中。如果有人需要显示 Thor 嵌套子命令帮助并遇到提到的“错误”,您可以使用我创建的这个 gem 来处理它。我在我的几个 gem 中使用它:thor_nested_subcommand


0
投票

上面的解决方案对我来说不起作用,因为它没有修复深层嵌套子命令的列表,并且始终包含子命令子命令。最重要的是,它打印了深度嵌套的子命令,没有相应的父命令,但保留了基本命令。这真的让我很困惑,更不用说它会让使用我的 cli 的人感到多么困惑。在任何地方自动添加帮助命令对我来说没有意义,因为通过执行父命令无论如何都会显示帮助,所以我也删除了它。仅仅为了控制台命令列表而安装新的 gem 对我来说是“不”的。

我猴子修补了thor版本

1.3.0

# /tool/cli.rb

module Tool
  Helpers::Thor.patch

  class Cli < Thor
    desc "setup [COMMAND]", "setup systems, services, and projects"
    subcommand "setup", Tool::Commands::Setup
  end
end
# /tool/helpers/thor.rb

module Tool
  module Helpers
    module Thor
      def self.patch
        ::Thor.instance_eval do
          def printable_commands(all = true, subcommand = false)
            current_namespace = namespace
            base_namespace = "tool:cli"
            immediate_parent_command = current_namespace.split(":").last

            (all ? all_commands : commands).map do |_, command|
              next if command.hidden? || command.name == "help" # removes help

              is_direct_subcommand = if current_namespace == base_namespace
                command.ancestor_name.nil? || command.ancestor_name.empty?
              else
                command.ancestor_name == immediate_parent_command
              end

              next unless is_direct_subcommand

              item = []
              item << banner(command, false, subcommand)
              item << (command.description ? "# #{command.description.gsub(/\s+/m, " ")}" : "")
              item
            end.compact
          end

          def help(shell, subcommand = false)
            current_namespace = namespace
            immediate_parent_command = current_namespace.split(":").last

            list = printable_commands(true, subcommand)

            ::Thor::Util.thor_classes_in(self).each do |klass|
              if klass.namespace.split(":").last == immediate_parent_command
                list += klass.printable_commands(false, subcommand)
              end
            end

            sort_commands!(list)
            shell.say (defined?(@package_name) && @package_name) ? "#{@package_name} commands:" : "Commands:"
            shell.print_table(list, indent: 2, truncate: true)
            shell.say
            class_options_help(shell)
          end
        end
        ::Thor.instance_eval do
          protected

          def banner(command, namespace = nil, subcommand = false)
            command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
              formatted_usage.to_s
            end.join("\n")
          end
        end
      end
    end
  end
end
© www.soinside.com 2019 - 2024. All rights reserved.