質問

ユーザーの利便性とよりクリーンなコードのために、このように使用できるクラスを書きたいと思います。

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が呼び出されると、変数はTheoraクラスのコンテキストで評価されません。通常、Method_Missingを使用して、すべてのパラメーターをクラスTheoraのクラス変数に入れたいと思いますが、エントリの正しい方法は見つかりません。

誰かが私を正しい方向に向けることができますか?

役に立ちましたか?

解決

それはあなたがそれを書いた方法ではできません、afaik。 Procの本体には独自の範囲があり、その範囲内で作成された変数はその外側に見えません。

慣用的なアプローチは、構成オブジェクトを作成してブロックに渡すことです。これは、そのオブジェクトのメソッドまたは属性を使用して行われる作業を説明します。次に、作業を行うときにそれらの設定が読まれます。これは、採用されたアプローチです create_table たとえば、ActiverCordの移行で。

だからあなたはこのようなことをすることができます:

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

私はバインディングが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