RubyのDSLSでPROCを使用します
-
16-10-2019 - |
質問
ユーザーの利便性とよりクリーンなコードのために、このように使用できるクラスを書きたいと思います。
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通訳者は常にそれを想定していると思います infile
の infile = '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