如何将rubys OptionParser子类化(或与之共享代码),例如共享期权?

如何将rubys OptionParser子类化(或与之共享代码),例如共享期权?,ruby,command-line-interface,Ruby,Command Line Interface,我想为多个脚本共享某些选项,并且更喜欢使用“内置”optpass而不是其他cli或optionparsing框架 我很快地查看了MRIs optpasse.rb,不明白如何最好地对OptionParser子类(初始值设定项需要一个块) 最理想的情况是,我希望得到这样的代码 # exe/a_script require 'mygem' options = {whatever: 'default'} Mygem::OptionParser.new do |opts| opts.on('--wh

我想为多个脚本共享某些选项,并且更喜欢使用“内置”optpass而不是其他cli或optionparsing框架

我很快地查看了MRIs optpasse.rb,不明白如何最好地对OptionParser子类(初始值设定项需要一个块)

最理想的情况是,我希望得到这样的代码

# exe/a_script
require 'mygem'

options = {whatever: 'default'}
Mygem::OptionParser.new do |opts|
  opts.on('--whatever') do |w|
    options[:whatever] = w
  end
end.parse!
以及作为消费者的第二个脚本:

# exe/other_script
require 'mygem'


options = {and_another: 'default'}
Mygem::OptionParser.new do |opts|
  opts.on('--and_another') do |a|
    options[:and_another] = w
  end
end.parse!
并在通用自定义OptionParser中定义“默认选项”(例如“-v”表示详细,而“-h”表示帮助)

# lib/mygem/mygem_optionparser.rb
require 'optparse'
module Mygem
  class OptionParser < OptionParser
    # magic
    # define opts.on("-v") -> set options[:verbose],
    # define opts.on_tail("-h", "print help and exit") ...
  end
end
我想知道我还没有找到任何关于这个场景的教程或示例,我想这不是一个罕见的用例。是的,我绝对想坚持使用“optpasse”


更新我感到困惑,没有查看正确的optpase源代码,因此没有看到它会产生self(这让我有点吃惊:)。到目前为止答案很好。

您可以使用默认选项定义一个
DefaultOptPasser

# default_parser.rb
require 'optparse'
require 'ostruct'

class DefaultOptParser
    attr_accessor :options

    def initialize
        @options = OpenStruct.new

        @parser = OptionParser.new do |opts|
          opts.banner = "Usage: example.rb [options]"

          opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
            options.verbose = v
          end
        end
    end

    def parse
        @parser.parse!
        @options
    end
end

p DefaultOptParser.new.parse
# basic_parser.rb
require_relative "default_parser"

class BasicModeParser < DefaultOptParser
    def initialize
        super
        @parser.on("-b", "--basic-mode", "Basic mode operation") do |v|
            options.basic = v
        end
    end
end

p BasicModeParser.new.parse
当您运行上述代码时

> ruby default_parser.rb -v
#<OpenStruct verbose=true>
>ruby default\u parser.rb-v
#

接下来定义一个类,该类是上述类的子类,并添加额外的解析选项

# default_parser.rb
require 'optparse'
require 'ostruct'

class DefaultOptParser
    attr_accessor :options

    def initialize
        @options = OpenStruct.new

        @parser = OptionParser.new do |opts|
          opts.banner = "Usage: example.rb [options]"

          opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
            options.verbose = v
          end
        end
    end

    def parse
        @parser.parse!
        @options
    end
end

p DefaultOptParser.new.parse
# basic_parser.rb
require_relative "default_parser"

class BasicModeParser < DefaultOptParser
    def initialize
        super
        @parser.on("-b", "--basic-mode", "Basic mode operation") do |v|
            options.basic = v
        end
    end
end

p BasicModeParser.new.parse
#basic_parser.rb
需要\u相对“默认\u解析器”
类BasicModelParser
当您运行上述代码时

> ruby basic_parser.rb -v -b
#<OpenStruct verbose=true, basic=true>
>ruby basic_parser.rb-v-b
#


以上工作基于我目前对
OptionParser

的理解。您可以使用默认解析选项定义
DefaultOptParser

# default_parser.rb
require 'optparse'
require 'ostruct'

class DefaultOptParser
    attr_accessor :options

    def initialize
        @options = OpenStruct.new

        @parser = OptionParser.new do |opts|
          opts.banner = "Usage: example.rb [options]"

          opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
            options.verbose = v
          end
        end
    end

    def parse
        @parser.parse!
        @options
    end
end

p DefaultOptParser.new.parse
# basic_parser.rb
require_relative "default_parser"

class BasicModeParser < DefaultOptParser
    def initialize
        super
        @parser.on("-b", "--basic-mode", "Basic mode operation") do |v|
            options.basic = v
        end
    end
end

p BasicModeParser.new.parse
当您运行上述代码时

> ruby default_parser.rb -v
#<OpenStruct verbose=true>
>ruby default\u parser.rb-v
#

接下来定义一个类,该类是上述类的子类,并添加额外的解析选项

# default_parser.rb
require 'optparse'
require 'ostruct'

class DefaultOptParser
    attr_accessor :options

    def initialize
        @options = OpenStruct.new

        @parser = OptionParser.new do |opts|
          opts.banner = "Usage: example.rb [options]"

          opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
            options.verbose = v
          end
        end
    end

    def parse
        @parser.parse!
        @options
    end
end

p DefaultOptParser.new.parse
# basic_parser.rb
require_relative "default_parser"

class BasicModeParser < DefaultOptParser
    def initialize
        super
        @parser.on("-b", "--basic-mode", "Basic mode operation") do |v|
            options.basic = v
        end
    end
end

p BasicModeParser.new.parse
#basic_parser.rb
需要\u相对“默认\u解析器”
类BasicModelParser
当您运行上述代码时

> ruby basic_parser.rb -v -b
#<OpenStruct verbose=true, basic=true>
>ruby basic_parser.rb-v-b
#


以上工作基于我目前对OptionParser的理解,我还没有使用OptionParser,因此可能有更好的方法来实现这一点,但我还是要尝试一下

(出于我们的目的)最重要的一点是,它为给定的块产生
self
。要使子类工作相同,我们所要做的就是使其
initialize
方法也产生
self

require 'optparse'
require 'ostruct'

module MyGem
  class OptionParser < ::OptionParser
    attr_reader :options

    def initialize(*args)
      @options = OpenStruct.new

      super *args
      default_options!

      yield(self, options) if block_given?
    end

    private
    def default_options!
      on '--whatever=WHATEVER' do |w|
        options.whatever = w
      end
    end
  end
end
这将为用户提供如下结果:

$ruby script.rb--whatever=www--and other=aaa
#
作为
yield(self,options)
的替代方法,我们可以使用
yield self
,但是用户需要在块内执行例如
parser.options.whatever=…

另一种选择是将
和block
参数添加到
initialize
,然后执行
实例评估(&block)
,而不是
屈服
。这将在实例上下文中评估块,因此用户可以直接访问
选项
属性(以及所有其他实例方法等),例如:

parser = MyGem::OptionParser.new do
  on '--and-another=ANOTHER' do |a|
    options.another = a
  end
end

parser.parse!

但是,这也有缺点,用户必须知道块将在实例上下文中进行计算。就我个人而言,我更喜欢显式的
yield(self,options)

我没有使用OptionParser,所以可能有更好的方法来实现这一点,但我还是要尝试一下

(出于我们的目的)最重要的一点是,它为给定的块产生
self
。要使子类工作相同,我们所要做的就是使其
initialize
方法也产生
self

require 'optparse'
require 'ostruct'

module MyGem
  class OptionParser < ::OptionParser
    attr_reader :options

    def initialize(*args)
      @options = OpenStruct.new

      super *args
      default_options!

      yield(self, options) if block_given?
    end

    private
    def default_options!
      on '--whatever=WHATEVER' do |w|
        options.whatever = w
      end
    end
  end
end
这将为用户提供如下结果:

$ruby script.rb--whatever=www--and other=aaa
#
作为
yield(self,options)
的替代方法,我们可以使用
yield self
,但是用户需要在块内执行例如
parser.options.whatever=…

另一种选择是将
和block
参数添加到
initialize
,然后执行
实例评估(&block)
,而不是
屈服
。这将在实例上下文中评估块,因此用户可以直接访问
选项
属性(以及所有其他实例方法等),例如:

parser = MyGem::OptionParser.new do
  on '--and-another=ANOTHER' do |a|
    options.another = a
  end
end

parser.parse!

但是,这也有缺点,用户必须知道该块将在实例上下文中进行计算。就我个人而言,我更喜欢显式的
yield(self,options)

感谢您指向原始代码。我一定是完全搞砸了,看了其他一些optpass。yield(self,options)看起来也很整洁。谢谢你指向原始代码。我一定是把事情搞砸了,看看其他的optpass。收益率(self,options)看起来也很整洁。