为了用户方便和更干净的代码,我想编写可以这样使用的类:

Encoder::Theora.encode do
  infile = "path/to/infile"
  outfile = "path/to/outfile"
  passes = 2
  # ... more params
end

现在的挑战是,在我的编码方法中可以使用该参数。

module Encoder
  class Theora
    def self.encode(&proc)
      proc.call
      # do some fancy encoding stuff here
      # using the parameters from the proc
    end
  end
end

这种方法不起作用。当调用PROC时,变量未在定理类的上下文中评估。通常,我想使用method_missing将每个参数都放入类Theora类的类变量中,但是我找不到条目的正确方法。

谁能将我指向正确的方向?

有帮助吗?

解决方案

阿法克(Afaik),它不能像你写的那样完成。 Proc的主体具有自己的范围,并且在该范围内创建的变量在其外部不可见。

惯用方法是创建一个配置对象并将其传递到块中,该对象描述了使用该对象的方法或属性进行的工作。然后,在完成工作时会读取这些设置。这是采取的方法 create_table 例如,在Activerecord迁移中。

因此,您可以做这样的事情:

module Encoder
  class Theora
    Config = Struct.new(:infile, :outfile, :passes)

    def self.encode(&proc)
      config = Config.new
      proc.call(config)
      # use the config settings here
      fp = File.open(config.infile)       # for example
      # ...
    end
  end
end

# then use the method like this:
Encoder::Theora.encode do |config|
  config.infile = "path/to/infile"
  config.outfile = "path/to/outfile"
  config.passes = 2
  # ...
end

其他提示

我不确定是否可以让DSL使用分配,我认为Ruby解释器会始终假设 infileinfile = 'path/to/something' 在这种情况下是局部变量(但是 self.infile = 'path/to/something' 可以使工作)。但是,如果您可以在没有该特定细节的情况下生活,则可以像这样实现DSL:

module Encoder
  class Theora
    def self.encode(&block)
      instance = new
      instance.instance_eval(&block)
      instance
    end

    def infile(path=nil)
      @infile = path if path
      @infile
    end
  end
end

并这样使用:

Encoder::Theora.encode do
  infile 'path/somewhere'
end

(类似地实施其他属性)。

在玩此操作时,我到达了以下内容,我不一定建议,并且不太适合所需的语法,但确实可以使用分配(某种)。因此,本着完整的精神细读:

module Encoder
  class Theora
    def self.encode(&proc)
      infile = nil
      outfile = nil
      yield binding
    end
  end
end

Encoder::Theora.encode do |b|
  b.eval <<-ruby
    infile = "path/to/infile"
    outfile = "path/to/outfile"
  ruby
end

我相信binding.eval仅在Ruby 1.9中起作用。另外,似乎需要在屈服之前声明本地变量,否则它不会起作用 - 有人知道为什么吗?

好的,首先,我必须说PMDBOI的答案非常优雅,几乎可以肯定是正确的。

尽管如此,以防万一您想要超级切割的DSL

Encoder::Theora.encode do
  infile "path/to/infile"
  outfile "path/to/outfile"
  passes 2
end

您可以做一些丑陋的事情:

require 'blockenspiel'
module Encoder
  class Theora
    # this replaces pmdboi's elegant Struct
    class Config
      include Blockenspiel::DSL
      def method_missing(method_id, *args, &blk)
        if args.length == 1
          instance_variable_set :"@#{method_id}", args[0]
        else
          instance_variable_get :"@#{method_id}"
        end
      end
    end

    def self.encode(&blk)
      config = Config.new
      Blockenspiel.invoke blk, config
      # now you can do things like
      puts config.infile
      puts config.outfile
      puts config.passes
    end
  end
end
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top