さまざまなDSLがRubyのライブラリとフレームワークで広く配布されています。 たとえば、Railsでは、DSLを使用して移行を作成します。
それでは、RubyがDSLを構築するために提供する機能を見てみましょう。
コンピューターの構成を記述する単純な形式が必要になります。
簡単な例:
CPU:2.2 GHz メモリー:1ギガバイト ディスク:250ギガバイト
ここで、Rubyの助けを借りて、このような記述に便利なDSLを作成します。
ステージ1。
この記述をRubyコードに変換します。たとえば、次のようにします(メモリをメガバイトで、周波数をメガヘルツで保存します)
comp = Computer.new comp.cpu = 2.2 * 1024 comp.ram = 2 * 1024 comp.disk = 1 * 1024
クラスコード基本:
クラスコンピュータ attr_accessor:cpu attr_accessor:ram attr_accessor:ディスク 終わり
Rubyでは、すべてのインスタンス変数(@で始まる変数など)はプライベートです。 オブジェクトメソッド内でのみアクセスできます。 属性を作成するには、この属性の値を設定および取得するための2つのメソッドを宣言する必要があります。
def cpu @cpu 終わり def cpu = val @cpu = val 終わり
または単に呼び出す:
attr_accessor :cpu
、これらのメソッドを生成します
ステージ2。
それで何? ここには新しいものは何もありません。属性を持つオブジェクトを使用するだけです。 コードを少し改善してみましょう。
あなたの目を引く最初のものは、ギガバイトを独立してメガバイトに変換するなどです。 直せ!
comp = Computer.new comp.cpu = 2.2.ghz comp.ram = 2.gb comp.disk = 1.gb
これを行うには、
ghz
メソッドと
gb
メソッドを
Numeric
クラスにミックスし
ghz
数値クラス def ghz 自己* 1000 終わり def gb 自己* 1024 終わり def mhz 自己 終わり def mb 自己 終わり 終わり
また、2つのメソッドmhzとmbを追加しました。cpu.ram= 512の代わりにcpu.ram = 512.mbです。
Rubyには、新しいメソッドを任意のクラスにミックスインする機能があります。 つまり クラスは作成された後でも拡張できます。 メソッドをクラスと混合すると、すべてのインスタンスで使用可能になります。
クラスString デフクール self +「かっこいい!」 終わり 終わり
cool self
メソッドでは、これはオブジェクト自体の値へのポインターであり、メソッドの戻り値はその最後の行の実行の結果であるため、
puts "my string".cool
「My string is cool!」と表示されます
メソッドghzなどは、Numericクラスに混合しました。これは、Rubyのすべての数値の親クラスであるためです。 整数と小数の両方について
ステージ3。
すでに良い。 しかし、「comp」を示す必要があることも恥ずかしいです。各パラメーターの前に。 小さなガールフレンドを作りましょう:
comp = Computer.new do CPU 2.2.ghz ラム2.gb ディスク1.gb 終わり
それははるかに良く見えますよね? しかし、問題は、それが機能するかどうかです。
それを理解しましょう。 有効なRubyコードのようです。 CPU、RAM、およびディスクは、コンピュータークラスのインスタンスで呼び出されないため、メソッドではなく機能になりました。
このようなもの:
def cpu val comp.cpu = val 終わり
しかし、この関数にcomp変数をどのように渡すのでしょうか?
cpu 2.ghz、comp? しかし、その後、すべての表現力が引き出されます。 さて、このオブジェクトのコンテキストでこれらのメソッドを実行できたら...
そして、やっぱりできる! Rubyは、instance_evalメソッドを使用してこの機会を提供します。
次に、Computerクラスの新しい実装を見てください
クラスコンピュータ #initializeメソッドはコンストラクターです def initialize&block#&blockは、コードブロックがメソッドに渡されることを意味します instance_eval&block#マジックのinstance_evalメソッドを呼び出してブロックを渡します 終わり #ここでは、属性の代わりにメソッドを宣言したため、cpu = 2.ghzの代わりにcpu 2.ghzと記述します def cpu val @cpu_clock = val 終わり def ram val @ram_size = val 終わり def disk val @disk_size = val 終わり #CPUメソッドなどがあるため、値を設定する必要はありません。 #したがって、attr_accessorの代わりにattrを呼び出しますが、値を設定するメソッドを生成しません #しかし、わずかな制限があります。 動的言語はメソッドをオーバーロードできません #(attr_accessorは実際にメソッドを作成します) #次に、属性に対して他の名前を選択する必要があります attr:cpu_clock attr:ram_size attr:disk_size 終わり
このメソッドは何をしますか? すべてが非常に簡単です。 すでに上で述べたように、このオブジェクトのコンテキストでブロック(またはコードのある行)を実行します。
また、CPUなどのメソッドはComputerクラスで宣言されているため、呼び出されます。 そして、このオブジェクトのためだけに。
ステージ4。
ここで、コンピューターにさまざまな特性が無制限にあると想像してください。 たとえば、BIOSサイズまたはバス解像度:
comp = Computer.new do BIOS 0.5.mb バス100 終わり
誰もが予測することはできませんが、そのような特性を追加できるようにしたいと思います。
そしてここで、Rubyの機能、つまりmethod_missingメソッドが再び役立ちます。
method_missingは、存在しないメソッドを呼び出そうとしたときに呼び出されるオブジェクトの特別なメソッドです。 例:
クラステスト def method_missing name、* args、&block #nameはメソッドの名前、* argsはその属性、&blockはコードブロック(存在する場合)です。 name.to_s + "called"を置きます 終わり 終わり t = Test.new t.some_method#「some_method called」を出力します t.asdf# "asdf called"
Computerクラスに戻ります。
クラスコンピュータ def初期化とブロック instance_eval&ブロック 終わり def method_missing name、* args、&block instance_variable_set( "@#{name}"。to_sym、args [0])#インスタンス変数を作成して値を割り当てる self.class.send(:define_method、name、proc {instance_variable_get( "@#{name}")})#この変数にアクセスするためのメソッドを作成 終わり 終わり
今、それはどのように機能しますか ブロックをクラスのコンストラクターに渡します。 このブロックは、このクラスのインスタンスのコンテキストで実行されます。 実行中に、存在しないメソッドが呼び出されます;代わりに、
method_missing
は、nameパラメーターがメソッド名に等しく、
args
引数の配列で実行されます。 次に、メソッドに一致する名前と、呼び出されたメソッドの最初の属性に等しい値を持つインスタンス変数を作成します。 この変数の値を取得するメソッドと同様に。
ところで、method_missingはActiveRecordのRailsで
Person.find_all_by_name
何千ものメソッドを作成するために使用されます
結果のコードはここにあります 。
さらなる改善
DSLをさらに便利にするために、他に考えられることはありますか?
DSLはプログラマーだけでなく、プログラミングに不慣れな人々のためにも設計できるため、プログラマーでも編集できないファイルに記述を転送することは論理的です。 そして、このファイルをロードして解釈します。
my_pc.conf:
cpu 1.8.mgh
ram 512.mb
disk 40.gb
これを行うのは非常に簡単です。上で書いたように、instance_evalメソッドにブロックの代わりにコードを含む行を渡すことができます。
第二に、ロシア語のファンの場合、次のように書くことができます。
comp = Computer.new do
2.2.ghz
2.gb
1.gb
end
これが機能するためには、インタプリタを呼び出すときに-Kuオプションを追加するだけです。 たとえば、次のように:
ruby -Ku test.rb
上記では、有効なRubyコードである単純なDSLを構築しました。 ただし、有効性は拒否できます。 たとえば、ポイントを取り除きます。
cpu 1.ghz
代わりに
cpu 1.ghz
記述します。 次に、少し前処理を行う必要があります。 たとえば、正規表現を使用して、これらのポイントを追加します。
そして今、これらの改善点を組み合わせると、最初に挙げた例を解釈できます。
CPU:2.2 GHz メモリー:1ギガバイト ディスク:250ギガバイト
自分で試してみてください:)
今少し自己宣伝:)
この手法を自分で考え出したとき、DSLを使用して有効なXHTMLを生成する簡単なライブラリを作成しました。
ポイントは、無効なものを書き込もうとすると、生成プロセスでエラーが発生することです。
gemパッケージとして設計したので、次のように配置できます。
Windowsの場合:
gem install rml
* nixシステムの場合:
sudo gem install rml
プロジェクトページ: http : //rubyforge.org/projects/rml/
例とドキュメントは、 http : //rml.rubyforge.org/にあります。
真実は英語にありますが、興味があれば、簡単なメモを書くことができます。