【问题标题】:How to make two thor tasks share options?如何让两个雷神任务共享选项?
【发布时间】:2013-01-15 20:28:53
【问题描述】:

使用 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)

同时为task1task2 声明method_option :type, :type =&gt; :string, :required =&gt; true, :default =&gt; 'foo' 的问题在于它违反了the DRY principle。有没有一种惯用的方法来处理这个问题?

【问题讨论】:

    标签: ruby thor


    【解决方案1】:

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

    • name&lt;Symbol&gt;:: 参数的名称。
    • options&lt;Hash&gt;:: 如下所述。

    知道这一点后,您可以将参数存储到 method_option 的数组中,并在调用 method_option 时将 expand that array into separate parameters 存储起来。

    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 原则要好。

    【讨论】:

    • 好主意,但我们可以更进一步,也许定义 shared_options 类方法委托给 method_option + 合并公共哈希?
    【解决方案2】:

    我会使用这样的超类:

    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)
    

    【讨论】:

    • 这里真的需要子类化吗?为什么不在 CLI 类中定义 shared_options?
    • 我认为有很多方法可以回答OP。我碰巧不止一次地鄙视编写代码,因此使用我的解决方案,编写基类,将其放入 gem 中,然后在编写另一个 Thor 脚本时使用它。毕竟,详细的 -v 是我将在单个实例之外再次使用的东西。但可以肯定的是,你可以随心所欲。
    【解决方案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 只需要名称。

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,我使用了 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,这是一个很大的改进。

      【讨论】:

        【解决方案5】:

        为了不一直输入“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”。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多