2012-06-01 8 views
9

メソッド引数リストにインスタンス変数名を指定するCoffeeScript機能と同様のルビー動作を実装する予定はありますか? Ruby:インスタンス変数をメソッドの引数として自動的に設定しますか?

class User 
    def initialize(@name, age) 
    # @name is set implicitly, but @age isn't. 
    # the local variable "age" will be set, just like it currently works. 
    end 
end 

と同じように、私はこの質問を認識してよ:in Ruby can I automatically populate instance variables somehow in the initialize method?が、(私自身を含む)すべてのソリューションは、ルビーのシンプル哲学を合わせていないようです。

そして、この現象を起こすことには何らかの欠点がありますか?

UPDATE

は、その理由の一つは、(自分を繰り返さない)Rubyコミュニティの理念DRYです。同じ名前のインスタンス変数に代入したいので、引数変数の名前を繰り返す必要があることがよくあります。

def initialize(name) 
    # not DRY 
    @name = name 
end 

私が考えることができる1つの欠点は、体がないと方法が何もしていないように見えるかもしれないということです。あなたがすばやくスキャンしている場合、これはノーオペレーションのように見えるかもしれません。しかし、私は時間が与えられたと思う、我々は適応することができます。

もう1つの欠点:身体の他のインスタンス変数を設定していて、すべての割り当てを最初に置くと、読み込みを試みると、そこに割り当てが発生していることを知るために、引数リストしかし、これは、定数やメソッド呼び出しを見て、その定義にジャンプしなければならないことよりも、これが難しいとは思わない。

# notice: instance var assignments are happening in 2 places! 
def initialize(@name) 
    @errors = [] 
end 

答えて

4

いくつかの熟考の後、私は実際にルビーメソッドから引数名を得ることが可能かどうか疑問に思った。もしそうなら、 "iv_"のような特別な引数プレフィックスを使用して、どの引数をインスタンス変数として設定するかを指定することができます。

そして可能です:How to get argument names using reflection

はい!だから私はおそらく私のためにこれを処理するモジュールを書くことができます。私はモジュールのヘルパーメソッドを呼び出すと、呼び出し側のローカルなので、引数の値が分からないので、私は立ち往生しています。ああ、しかし、ルビーはBindingオブジェクトを持っています。

ここでモジュール(ルビー1.9のみ)です:

module InstanceVarsFromArgsSlurper 
    # arg_prefix must be a valid local variable name, and I strongly suggest 
    # ending it with an underscore for readability of the slurped args. 
    def self.enable_for(mod, arg_prefix) 
    raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i 
    mod.send(:include, self) 
    mod.instance_variable_set(:@instance_vars_from_args_slurper_prefix, arg_prefix.to_s) 
    end 

    def slurp_args(binding) 
    defined_prefix = self.class.instance_variable_get(:@instance_vars_from_args_slurper_prefix) 
    method_name = caller[0][/`.*?'/][1..-2] 
    param_names = method(method_name).parameters.map{|p| p.last.to_s } 
    param_names.each do |pname| 
     # starts with and longer than prefix 
     if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1 
     ivar_name = pname[defined_prefix.size .. -1] 
     eval "@#{ivar_name} = #{pname}", binding 
     end 
    end 
    nil 
    end 
end 

そして、ここでは、使用です:

class User 
    InstanceVarsFromArgsSlurper.enable_for(self, 'iv_') 

    def initialize(iv_name, age) 
    slurp_args(binding) # this line does all the heavy lifting 
    p [:iv_name, iv_name] 
    p [:age, age] 
    p [:@name, @name] 
    p [:@age, @age] 
    end 
end 

user = User.new("Methuselah", 969) 
p user 

出力:

[:iv_name, "Methuselah"] 
[:age, 969] 
[:@name, "Methuselah"] 
[:@age, nil] 
#<User:0x00000101089448 @name="Methuselah"> 

それはあなたが空を持たせません。メソッド本体ですが、DRYです。各メソッドでslurp_argsを呼び出すのではなく、どのようなメソッドがalias_methodによって実装されているかを指定するだけで、さらに強化することができると確信しています。すべてのメソッドが定義された後でなければなりません。

おそらく、モジュールとヘルパーメソッド名が改善される可能性があることに注意してください。私はちょうど心に来た最初のものを使用しました。

+0

この回答ありがとうございます。それは間違いなくもっと価値のある仕事のようです。 :) – hrdwdmrbl

+0

@ジャッククックあなたは実際にセットアップを一度行うだけです。あなたのクラスでは、 'enable_for'と' slurp_args'を呼び出すだけです。 – Kelvin

0

私はあなた自身の質問に答えたと思いますが、それはルビーのシンプルさの哲学に合っていません。メソッド内でパラメータをどのように扱うかを複雑にし、変数を管理するロジックをメソッドのパラメータに移します。私は、コードの読みやすさを向上させるという議論を見ることができますが、それはあまり冗長ではないと私を襲ってしまいます。

の@ paramはと競合しなければならないいくつかのシナリオ:

def initialize(first, last, @scope, @opts = {}) 

def search(@query, condition) 

def ratchet(@*arg ) 

は、これらのシナリオのすべてが有効であるべきか?ちょうどinitialize@*argは私の心の中で特にうっ血だ。これらのルールと例外はすべてRuby言語をより複雑にします。自動インスタンス変数の利益のために、私はそれがそれに値するとは思わない。

+2

私はsplattedインスタンスargに '* @ arg'を想定します。それ以外のシナリオは、特に醜い、複雑な、またはそれに続くことは難しいと思われるものはありません。インスタンスのvarロジックについては、すでにargのリストに設定することはできますが、これは工夫されています: 'def run(a =(@ v = 1));終わり。はい、それは醜悪なものであり、argが渡されないとvarは設定されません。しかし、この種の割り当てはすでに許可されていることを示しています。私はそれがコードDRYerを維持するのに役立つと思います。私は私の質問に別の例を追加するつもりです。 – Kelvin

+0

おっと、私はDRYerというコードを作ったと言いましたが、私の質問ではその提案を意味しました。 – Kelvin

2

まあ、実際に...

class User 
    define_method(:initialize) { |@name| } 
end 

User.new(:name).instance_variable_get :@name 
# => :name 

1.8.7で動作しますが、ない1.9.3インチさて、ちょうどここでは私はこれについて学んだ...

+2

+1は勇敢な試みです。まだ試していないが、それが1.9.3でうまくいかない場合、それはもっと新しいことだ。 – Kelvin

関連する問題