2009-08-25 7 views
5

これは私が壊れない既存の公開API用ですが、私は拡張したいと思います。ダックタイピング文字列、記号、配列のエレガントな方法は?

現在の方法は、文字列または私はエトセトラ、記号、文字列のリストを送信する機能を追加したいsend

への最初のパラメータとして渡されたときに、理にかなっている記号や何かを取ります。私はちょうどis_a? Arrayを使うことができますが、リストを送信する他の方法があります。それはあまりルビー・イッシュではありません。

私はリストにmapを呼び出す予定なので、最初の傾きはrespond_to? :mapです。しかし、文字列も:mapに対応しているため、動作しません。

+3

あなたの質問には関係ありませんが、sendメソッドにユーザー入力を渡す前にいくつかのチェックをしてください! Yikes。 –

+0

良い点。私たちの場合、ドキュメントには、パラメータがメソッド名であることが明確に記載されています。これは十分な深い十分なAPIです... –

+0

これはドキュメントの問題ではありません。セキュリティホールです。彼らがバックティックを通過すると、sendの2番目のパラメータはシェルで実行されます。それは本当にあなたが望むものですか?あなたは、ケースステートメントの入力を調べて、あなた自身の建設のシンボルを送信する方がはるかに優れています。 –

答えて

6

これらのすべてをArraysとして扱うのはどうでしょうか?スプラット演算子(*が)自体に単一の要素をオンまたはインラインリストにArrayので

def foo(obj, arg) 
    [*arg].each { |method| obj.send(method) } 
end 

[*arg]トリック作品:あなたがString秒間たい行動はStringがあることだけ含むArrayの場合と同じですその要素の。あなたが他のArray Sを含むArrayを渡すと微妙な違いがありますが

その後

これは、基本的には構文的に甘くバージョンまたはArnaud's answerです。

その後まだ

fooの戻り値で行うことを有する追加違いがあります。 foo(bar, :baz)に電話すると、[baz]が返ってくる可能性があります。これを解決するには、Kestrelを追加することができます:渡されたとして、常にargを返します

def foo(obj, arg) 
    returning(arg) do |args| 
    [*args].each { |method| obj.send(method) } 
    end 
end 

。またはreturning(obj)を呼び出して、fooにコールを連鎖させることもできます。どのような戻り値の振る舞いが必要なのかはあなた次第です。

+0

私の元のポストよりずっと美しい。 私は*演算子に慣れることができません。 – Arnaud

+0

[* arg]素晴らしい! – Dmitry

0

parameter.class.nameに基づいて動作を切り替えることはできますか?醜いですが、正しく理解すれば、複数の型を渡すという単一の方法があります。何とか差別化する必要があります。

または、配列型パラメータを処理するメソッドを追加するだけです。わずかに異なる動作であるため、余分な方法が意味をなさないかもしれません。

+0

あなたの2番目のコメントはよりよい解決策ですが、私はもっともっとstackoverflowの得意先から期待しています。 最初のものは 'is_aと同じ問題がありますか?配列 ':ユーザは' String'でない文字列や 'Array'ではないリストを送ることができます。 –

+0

しかし、 'string'以外の文字列がどのように共通しているのかよくわかりません。'Array'以外のリストはかなり一般的です - 多くのオブジェクトは 'Enumerable'です。残念ながら、文字列もそうです。 –

0

Marshalを使用して、オブジェクトを送信する前にシリアル化します。

+0

私は混乱しています。 'send'は、Rubyで名前で関数を呼び出すメソッドです。マーシャルはどのように助けますか? –

1

ArrayStringは両方ともEnumerablesであるため、「少なくとも文字列ではない文字列である」と言っても洗練された方法はありません。私は何だろうと、

可算のためのアヒル型(responds_to? :[])があり、その後のようなので、caseステートメントを使用します。

def foo(obj, arg) 
    if arg.respond_to?(:[]) 
    case arg 
    when String then obj.send(arg) 
    else arg.each { |method_name| obj.send(method_name) } 
    end 
    end 
end 

かさえクリーナー:

def foo(obj, arg) 
    case arg 
    when String then obj.send(arg) 
    when Enumerable then arg.each { |method| obj.send(method) } 
    else nil 
    end 
end 
+0

拡張説明をありがとう、元の質問で私は 'is_aより清潔なものを求めた?配列 'である。 'is_aを使う? String'(またはあなたが示したようなバリアント)は、 'send'のためのStringのような文字列を使用している誰かの元のAPIを破壊します。実際、ほとんどのユーザはおそらく代わりに 'Symbol'を使用します。 'x.is_a?(String)|| x.is_a?(Symbol) 'と書いてありますが、これはあまりルビー・イッシュではありません。 –

0

をあなたがない場合monkeypatchしたい、送信する前に適切な文字列にリストをマッサージするだけです。あなたがmonkeypatchingや継承、同じメソッドシグネチャを維持したい気にしない場合:

class ToBePatched 
    alias_method :__old_takes_a_string, :takes_a_string 

    #since the old method wanted only a string, check for a string and call the old method 
    # otherwise do your business with the map on things that respond to a map. 
    def takes_a_string(string_or_mappable) 
     return __old_takes_a_string(string_or_mappable) if String === string_or_mappable 
     raise ArgumentError unless string_or_mappable.responds_to?(:map) 
     # do whatever you wish to do 
    end 
end 
0

おそらく、質問が十分に明確ではありませんでしたが、夜の睡眠は、私にこの質問に答えるには、2つのクリーンな方法を示しました。

1:to_symStringSymbolで使用可能で、文字列のように鳴く何にも利用可能であるべきです。

if arg.respond_to? :to_sym 
    obj.send(arg, ...) 
else 
    # do array stuff 
end 

2:配列を渡したときにスローTypeErrorを送信します。

begin 
    obj.send(arg, ...) 
rescue TypeError 
    # do array stuff 
end 

特に#2が好きです。古いAPIのユーザーの誰かが、このメソッドでTypeErrorを発生させることを期待しています...

+1

sendは実際にシンボルを期待しているので、最初の解決策が理にかなっています。しかし、第二の解決策について真剣に考えていないことを願っています。例外は制御フローではありません。 –

+0

@Sarah:ランダムですが、SOのあなたのコメントと回答のおよそ40%は「例外は制御フローではありません」という文章が含まれていると思います。 – Telemachus

+0

@Telemachus - 少なくとも最近。 :) –

1

私は「あなたは(だけ

funcを持つ配列のためにあなたの関数func_arrayの実装で終わる

def func(param) 
    a = Array.new 
    a << param 
    a.flatten! 
    func_array(a) 
end 

とパラメータから配列になるだろうのは、あなたの関数が

funcを命名されたとしましょうこんにちは世界 ")あなたはa.lattenを得るでしょう! => ["hello world"] func(["hello"、 "world"])あなたはa.flattenを取得します! => ["hello"、 "world"]

2

回答の全てで見落とされた重要な詳細:文字列は:mapない応答を行うので、最も簡単な答えは、元の質問である:ちょうどrespond_to? :mapを使用しています。

関連する問題