2016-12-07 3 views
3

この「洗練された」ビジネスを理解しようとしています。洗練された洗練の使い方

私はコアクラスを洗練モジュールを作ってるんだ:

module StringPatch 
    refine String do 
    def foo 
     true 
    end 
    end 
end 

その後洗練

class PatchedClass 
end 

PatchedClass.send :using, StringPatch 

を使用するクラスを、私はこのエラーを取得:

どう
RuntimeError: Module#using is not permitted in methods 

私はこの仕事をすることができますか? 私は、特定のスコープ内でのみコアクラスを動的に修正しようとしています。クラスとインスタンスのスコープでパッチを利用できるようにします。

答えて

4

私が知る限り、usingがメインにあり、usingがクラスまたはモジュールにあるときの現在のクラス/モジュール定義の終わりまでは、スクリプトの最後まで洗練されています。

module StringPatch 
    refine String do 
    def foo 
     true 
    end 
    end 
end 

class PatchedClass 
    using StringPatch 
    puts "test".foo 
end 

class PatchedClass 
    puts "test".foo #=> undefined method `foo' for "test":String (NoMethodError) 
end 

これは、あなたが動的にクラスやモジュールにusingを呼び出すために管理する場合、その効果は、直接削除されることを意味します。あなたが方法でrefineを使用することはできませんが、洗練されたクラスのメソッドを定義することができ

:ご質問について

class PatchedClass 
    using StringPatch 
    def foo 
    "test".foo #=> true 
    end 
end 

class PatchedClass 
    def bar 
    "test".foo 
    end 
end 

patched = PatchedClass.new 
puts patched.foo #=> true 
puts patched.bar #=> undefined method `foo' for "test":String (NoMethodError) 

、このdiscussionは面白いかもしれません。改良を目的に制限されているように見えますが、なぜ私は知らない。

Because refinement activation should be as static as possible.

+0

どのようにインスタンスメソッドで洗練を使用することができますを使用することはできませんか? –

+0

動的スコープのパッチを得るための別の方法を知っていますか? –

+0

私はあなたの助けに感謝します。私はそのスレッドを見ましたが、本当にそれを理解できませんでした。それが私がここに投稿した理由です!私は6つの 'パッチクラス'を持っており、それらを一度に適用したいと思います。洗練されたものは使いにくいようです。私は何かのために周りを突き進まさなければならないでしょう。 –

2

精密化が厳密にスコープされ、それはスタンドとして動的に使用することはできません。私は、モジュールを構造化して、必要に応じて洗練されたものやサルのパッチとして使うことができます。このアプローチにはまだ限界がありますが、一般的に洗練されていますが、それでも便利です。単一インスタンスとして使用

module StringPatch  
    def foo 
     true 
    end 

    refine String do 
     include StringPatch 
    end 
end 
洗練として使用

class PatchedClass 
    using StringPatch 
end 

obj = PatchedClass.new 
obj.extend(StringPatch) 

をパッチあなたはもちろん、モンキーパッチとして全体のクラスを拡張することができますが、その後、あなたはクラスを汚染永遠に永遠に。

PatchedClass.prepend(StringPatch) 
+0

最初の3つのコードスニペットを実行してから 'obj.instance_exec {" ".foo}"を実行しましたが、NoMethodErrorを取得しました。私は、コアクラスのダイナミックでスコープのあるパッチをあきらめています。 –

+0

インスタンスごとに使うつもりならば、 'obj.extend(YourModuleWithMethods)'と 'obj.foo'を実行することができます。あなたは洗練された/追加があまりにも深くならないならば、デコレータパターンが十分であるかどうかを見ることができます。 – kawikak

+0

洗練されたもう少し練習した後、私は今あなたがこの答えでやっていることを理解することができます。具体的に 'obj.extend(StringPatch)'を使って、私は最初に 'obj.instance {" ".foo}'を試しましたが、今ではレキシカルスコープのためにうまくいかないことを認識しています。全体的に、ありがとう - この二重目的のパッチのパターン、私は[gemmy](http://github.com/maxpleaner/gemmy)のような私のコアエクステンションの宝石に組み込みました。 –

2

私は自分自身を追加する必要があると思います。

usingで余裕がほとんどないようです。メソッドで使用できないエラーは深刻です。私が見つけた最高の揺れ部屋は、変数を引数として渡して繰り返し呼び出すことです。

私は2つのパッチクラスを持っているので、定数をPatchesとすることができます。これは両方を含むリストです。次にクラス/モジュールにパッチをロードしたいので、反復的にusingを実行できます。

module StringPatch 
    refine String do 
    def patch; :string_patch; end 
    end 
end 

module HashPatch 
    refine Hash do 
    def patch; :hash_patch; end 
    end 
end 

Patches = [StringPatch, HashPatch] 
class C 
    Patches.each { |x| using x } 
    def self.test 
    [''.patch, {}.patch] 
    new.test 
    end 
    def test 
    [''.patch, {}.patch] 
    end 
end 

C.test 

usingインスタンスとクラススコープの両方にパッチが利用できるようにしません。

制限は、usingコールを抽象化する方法がないことです。

私はPatches.each &method(:using)

を言うか、メソッドにPatches.each { |x| using x }を移動し、

またはeval "Patches.each { |x| using x}"

関連する問題