12

Thor 可以method_option用来设置特定任务的选项。要为一个类中的所有任务设置选项,可以使用class_option. 但是,如果一个人想要一个类的一些任务,但不是全部,共享选项呢?

在下文中task1,和task2共享期权,但他们不共享所有期权,也不与 共享期权task3

require 'thor'

class Cli < Thor
  desc 'task1', 'Task 1'
  method_option :type, :type => :string, :required => true, :default => 'foo'
  def task1
  end

  desc 'task2', 'Task 2'
  method_option :type, :type => :string, :required => true, :default => 'foo'
  method_option :value, :type => :numeric
  def task2
  end

  desc 'task3', 'Task 3'
  method_option :verbose, :type => :boolean, :aliases => '-v'
  def task3
  end
end

Cli.start(ARGV)

声明method_option :type, :type => :string, :required => true, :default => 'foo'两者task1的问题task2是它违反了 DRY 原则。有没有一种惯用的方法来处理这个?

4

5 回答 5

14

method_optionthor.rb中定义,根据文档它采用以下参数:

  • name<Symbol>::参数的名称。
  • options<Hash>::如下面所描述的。

知道了这一点,您可以将参数存储method_option在一个数组中,并将该数组扩展为单独的参数,就像method_option调用的那样。

require 'thor'

class Cli < Thor
  shared_options = [:type, {:type => :string, :required => true, :default => 'foo'}]

  desc 'task1', 'Task 1'
  method_option *shared_options
  def task1
  end

  desc 'task2', 'Task 2'
  method_option *shared_options
  method_option :value, :type => :numeric
  def task2
  end

  desc 'task3', 'Task 3'
  method_option :verbose, :type => :boolean, :aliases => '-v'
  def task3
  end
end

Cli.start(ARGV)

我不知道这是否是惯用的,我认为它不是那么优雅。不过,这比违反 DRY 原则要好。

于 2013-01-16T14:23:23.883 回答
3

我只会使用这样的超类:

require 'thor'

class CliBase < Thor
  def self.shared_options

    method_option :verbose,
                  :aliases => '-v',
                  :type => :boolean,
                  :desc => 'Verbose',
                  :default => false,
                  :required => false

  end
end

...然后子类如下:

require 'cli_base'

class Cli < CliBase
  desc 'task1', 'Task 1'
  shared_options
  def task1
  end

  desc 'task2', 'Task 2'
  shared_options
  method_option :value, :type => :numeric
  def task2
  end

  desc 'task3', 'Task 3'
  method_option :colors, :type => :boolean, :aliases => '-c'
  def task3
  end
end

Cli.start(ARGV)
于 2013-04-19T22:02:57.390 回答
3

所以现在有一个很好的干方法可以做到这一点,但可能不属于惯用的要求,尽管我想为任何寻找更新答案的人提一下。

您可以首先使用 class_options 设置方法之间的大多数共享选项:

module MyModule
  class Hello < Thor
    class_option :name, :desc => "name", :required => true
    class_option :greet, :desc => "greeting to use", :required => true

    desc "Hello", "Saying hello"
    def say
      puts "#{options[:greet]}, #{options[:name]}!"
    end

    desc "Say", "Saying anything"
    remove_class_option :greet
    def hello
      puts "Hello, #{options[:name]}!"
    end

    def foo
      puts "Foo, #{options[:name]}!"
    end
  end
end

最好的部分是它适用于声明后的所有方法。将这些设置为 required 后,您可以看到第一个方法同时需要greetname,但sayfoo只需要名称。

于 2018-03-15T23:30:05.740 回答
2

我遇到了同样的问题,我使用了 NN 的回答。但是我发现了一些问题:

如果您想像示例中那样共享多个选项,则效果不佳。想象一下,您想:value在 task2 和 task3 之间共享。您可以创建另一个shared_options,也可以使用共享选项创建一个数组并使用 shared_option 名称访问它。

这可行,但它很冗长且难以阅读。我已经实现了一些小东西,以便能够共享选项。

Cli < Thor  
  class << self
      def add_shared_option(name, options = {})
        @shared_options = {} if @shared_options.nil?
        @shared_options[name] =  options
      end

      def shared_options(*option_names)
        option_names.each do |option_name|
          opt =  @shared_options[option_name]
          raise "Tried to access shared option '#{option_name}' but it was not previously defined" if opt.nil?
          option option_name, opt
        end
      end
    end
    #...commands 
end

这将创建一个以选项名称作为键的散列,并将“定义”(必需、默认等)作为值(这是一个散列)。这在之后很容易访问。

有了这个,您可以执行以下操作:

require 'thor'

class Cli < Thor

  add_shared_option :type,  :type => :string, :required => true, :default => 'foo'
  add_shared_option :value, :type => :numeric

  desc 'task1', 'Task 1'
  shared_options :type
  def task1
  end

  desc 'task2', 'Task 2'
  shared_options :type, :value
  def task2
  end

  desc 'task3', 'Task 3'
  shared_options :value
  def task3
  end
end

Cli.start(ARGV)

对我来说,它看起来更具可读性,如果命令的数量大于 3 或 4,这是一个很大的改进。

于 2014-07-18T16:20:56.613 回答
0

为了不一直输入“shared_options”,你也可以这样做:

require 'thor'

class Cli < Thor
  class << self
    private
    def shared_options!
      # list your shared options here
      method_option :opt1, type: :boolean
      method_option :opt2, type: :numeric
      # etc
    end

    # alias original desc so we can call it from inside new desc
    alias_method :orig_desc, :desc

    # redefine desc, calling original desc, and then applying shared_options!
    def desc(*args)
      orig_desc(*args)
      shared_options!
    end
  end

  desc 'task1', 'Task 1'

  def task1
  end

  desc 'task2', 'Task 2'

  def task2
  end

  desc 'task3', 'Task 3'

  def task3
  end
end

或者,如果您不想使用方法别名的杂技,您可以定义自己的方法“my_desc”并调用它而不是“desc”。

于 2016-04-05T18:59:39.350 回答