如何让两个Thor任务共享选项?

时间:2013-01-15 20:28:54

标签: ruby thor

使用Thor,可以使用method_option为特定任务设置选项。要为类中的所有任务设置选项,可以使用class_option。但是,如果一个人想要一个班级的某些任务,而不是全部,那么分享选项呢?

在以下task1task2分享选项中,但它们不会共享所有选项,并且它们与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的陈述问题在于它违反了the DRY principle。是否有一种惯用的处理方式?

5 个答案:

答案 0 :(得分: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)

{{1}}

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

答案 1 :(得分: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)

答案 2 :(得分:2)

我遇到了同样的问题而且我使用的是N.N.回答。 但我发现了一些问题:

如果您想在示例中共享多个选项,则效果不佳。想象一下,您想在task2和task3之间共享:value。您可以创建另一个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,那么这是一个很大的进步。

答案 3 :(得分:2)

所以现在有一个很好的干燥方法,但可能不会落入惯用的要求,尽管我想为那些寻找更新答案的人提及它。 / p>

您可以先使用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

关于此的最好的部分是,它与声明后的所有方法有关。设置为必需,您可以看到第一种方法需要 greet name ,但 foo 只需要名称。

答案 4 :(得分: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”。