一个独立的Ruby可执行文件中如何为Thor命令进行命名空间划分

16

在命令行上调用thor命令时,方法是按其模块/类结构进行命名空间处理的,例如:

class App < Thor
  desc 'hello', 'prints hello'
  def hello
    puts 'hello'
  end
end

将使用命令运行

thor app:hello

然而,如果您通过将其自我执行来实现

App.start

在底部,你可以运行类似下面的命令:

app hello

有没有一种方法来对这些命令进行命名空间处理?这样你就可以调用,例如:

app say:hello
app say:goodbye
2个回答

24

另一种方法是使用register:

class CLI < Thor
  register(SubTask, 'sub', 'sub <command>', 'Description.')
end

class SubTask < Thor
  desc "bar", "..."
  def bar()
    # ...
  end
end

CLI.start

现在假设你的可执行文件名为foo,你可以调用:

$ foo sub bar

在当前的Thor版本(0.15.0.rc2)中存在一个错误,导致帮助文本跳过子命令的名称空间:

$ foo sub
Tasks:
   foo help [COMMAND]  # Describe subcommands or one specific subcommand
   foo bar             #

您可以通过重写self.banner并明确设置命名空间来解决此问题。

class SubTask < Thor
  namespace :sub

  def bar ...

  def self.banner(task, namespace = true, subcommand = false)
    "#{basename} #{task.formatted_usage(self, true, subcommand)}"
  end
end

formatted_usage的第二个参数是banner原始实现的唯一差异。你也可以这样做,只需执行一次并使其他子命令Thor类从SubTask继承。现在你得到:

$ foo sub
Tasks:
   foo sub help [COMMAND]  # Describe subcommands or one specific subcommand
   foo sub bar             #

希望这能有所帮助。


1
天啊!你在我生命中的前8个小时在哪里?感谢你提供如此优雅的解决方案。 - elmt
那对我没用(thor 0.18.1),但在https://github.com/wycats/thor/issues/261#issuecomment-16880836中描述的类似解决方法确实有效。 - Robert J Berger

5
这是一种使用App作为默认命名空间的方法(虽然有点hacky):
#!/usr/bin/env ruby
require "rubygems"
require "thor"

class Say < Thor
  # ./app say:hello
  desc 'hello', 'prints hello'
  def hello
    puts 'hello'
  end
end

class App < Thor
  # ./app nothing
  desc 'nothing', 'does nothing'
  def nothing
    puts 'doing nothing'
  end
end

begin
  parts = ARGV[0].split(':')
  namespace = Kernel.const_get(parts[0].capitalize)
  parts.shift
  ARGV[0] = parts.join
  namespace.start
rescue
  App.start
end

或者,也不是最理想的情况:
define_method 'say:hello'

那么没有简单的方法来做到这一点吗?它就是不被支持的吗? - rubiii
我不能确定。我尝试找到一种干净的方法,但失败了。我认为命名空间方法只适用于使用thor命令而不是独立可执行文件。 - Ian Fleeton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接