2016-08-02 14 views
0

を呼び出す:私はそれらを呼び出すコントローラで動的な方法私は2つのメソッドを持つクラスを持っている。例えば、引数

class Example < ActiveRecord::Base 
    def method_one(value) 

    end 
    def method_two 

    end 
end 

と方法:

def example 
    ex = Example.find(params[:id]) 
    ex.send(params[:method], params[:value]) if ex.respond_to?(params[:method]) 
    end 

しかし、私はmethod_twoを呼び出すしようとすると、問題が来ます

ArgumentError (wrong number of arguments (1 for 0)) 

それは起こるparams[:value]リターン理由。 最も簡単な解決策は以下のとおりです。

def example 
    ex = Example.find(params[:id]) 
    if ex.respond_to?(params[:method]) 
     if params[:value].present? 
     ex.send(params[:method], params[:value]) 
     else 
     ex.send(params[:method]) 
     end 
    end 
    end 

それがnullかどう引数を渡さないために任意のより良い回避策があるかどうか、私は疑問に思います。

答えて

2

ので、私はあなたの前にparams[:method]をフィルタリングお勧めします。

allowed_methods = { 
    method_one: ->(ex){ex.method_one(params[:value])} 
    method_two: ->(ex){ex.method_two} 
} 
allowed_methods[params[:method]]&.call(ex) 

私は引数としたい任意の特殊なケースを処理するメソッドを呼び出すラムダにメソッド名をマッピングするハッシュを定義しました。

params[:method]allowed_methodsハッシュにある場合は、ラムダをキーとして取得します。

&.構文はルビー2.3の新しい安全航行演算子であり、そして - 短いために - レシーバがnilでない場合(すなわちallowed_methods[params[:method]]の結果)は、以下の方法を実行 あなたはルビー> = 2.3を使用していない場合あなたがparams[:method]の値をフィルタリングしていない場合、ユーザーは単に例えば:destroyを渡すことができます

allowed_methods[params[:method]].try(:call, ex) 

である、あなたのエントリを削除するには:あなたは、この場合も同様の挙動を持っており、代わりにtryを使用することができます確かにあなたが望むものではありません。

また、ex.send ...を呼び出して、オブジェクトのカプセル化をバイパスします。これは通常は避けてください。パブリックインターフェイスのみを使用するには、public_sendを使用することをお勧めします。


あなたのコードの大きなセキュリティ上の欠陥のもう一つのポイント:

evalは(実際にKernelから継承)Objectに定義されたプライベートな方法であるので、あなたは、任意のオブジェクトに対して、このようにそれを使用することができます。

object = Object.new 
object.send(:eval, '1+1') #=> 2 

今、あなたのコードで、ユーザーはparams[:value]params[:method]の値と任意のRubyコードとしてevalを置き、彼は実際にwhateveを行うことができます想像彼はあなたのアプリケーションの中で欲しい。

+0

私はセキュリティについて考えていましたが、私は 'eval'の場合は分かりませんでした。 'update'、' destroy'などのメソッドを渡すことは、私にはあまり大したことではありませんでした。なぜなら、それはユーザーがやりたいものの1つだったからです。 私には1つの質問があります。なぜあなたはallowed_methodsでハッシュをフリーズしませんか? – Gregy

+0

私は私の答えで最初に書いたものと似たようなものを使うことをお勧めします。より安全で、メソッドごとにパラメータの数を扱います。 – Geoffroy

+0

'allowed_methods'は間違いなく良い考えですが、実装はover-over-designの完全な例です。 '%i | method_one method_two |'で十分です。同じ量のパラメータを持ち歩くことを使うトリックは、非常に悪い考えです.SRPの原則が破られ、基本的にこのコードがサポートされなくなります。 – mudasobwa

1

あなたは何をしているかを理解していれば、簡単に回避策があります。

def method_one *args 
end 
def method_two * 
end 

と::

def method_two _ = nil 
end 

または

def method_two * 
end 

は、それが同様に他の方法でラウンドを動作します

ex.public_send(params[:method], *[params[:value]]) \ 
    if ex.respond_to?(params[:method]) 

追記:あなたが明示的にprivateメソッドを呼び出している場合を除きsendオーバーpublic_sendを好みます。


は、メソッドのシグネチャを変更することなく、のparamsをスプラッティング使用して:あなたは本当に危険なことやろうとしている何

ex.public_send(*[params[:method], params[:value]].compact) 
+0

私は方法を変更しないでください。あなたは両方の追加について何を説明できますか? – Gregy

+1

上記のすべてのスニペットは、最初のものを除いて[splatted params](https://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/)を使用してください。別の明示的な署名で異なるメソッドを呼び出す場合は、チェックを行うか、厄介な 'ex.public_send(* [params [:method]、params [:value]]。後者は 'nil'パラームをその場で削除します。 – mudasobwa

関連する問題