2009-08-25 12 views
5

यह पहले से मौजूद सार्वजनिक एपीआई के लिए है जिसे मैं तोड़ नहीं सकता, लेकिन मैं विस्तार करना चाहता हूं।बतख-टाइपिंग तारों, प्रतीकों और सरणी का सुरुचिपूर्ण तरीका?

वर्तमान में विधि एक स्ट्रिंग या एक प्रतीक या कुछ और कि भावना जब send

को पहले पैरामीटर के रूप में पारित मैं तार, प्रतीकों, वगैरह की एक सूची भेजने की क्षमता जोड़ना चाहते हैं बनाता है लेता है । मैं सिर्फ is_a? Array का उपयोग कर सकता हूं, लेकिन सूचियां भेजने के अन्य तरीके हैं, और यह बहुत रूबी-आश नहीं है।

मैं सूची में map पर कॉल कर रहा हूं, इसलिए पहला झुकाव respond_to? :map का उपयोग करना है। लेकिन एक स्ट्रिंग भी :map का जवाब देती है, जिससे यह काम नहीं करेगा।

+3

कुछ भी नहीं है अपने प्रश्न के साथ क्या करना है, लेकिन मुझे आशा है कि आप कुछ चेकों कर रहे हैं इससे पहले कि आप भेजने विधि के लिए उपयोगकर्ता इनपुट पारित! ओह। –

+0

अच्छा बिंदु। हमारे मामले में, दस्तावेज़ स्पष्ट रूप से बताते हैं कि पैरामीटर एक विधि का नाम है। यह एक गहरे पर्याप्त एपीआई कि यह पर्याप्त होना चाहिए है .... –

+0

यह दस्तावेज़ीकरण की बात नहीं है - यह एक सुरक्षा छेद है। अगर वे बैकटिक में गुजरते हैं, तो भेजें दूसरा पैरामीटर शेल में चलाया जाता है। क्या आप वास्तव में यही चाहते हैं? आप केस स्टेटमेंट में इनपुट की जांच करने और फिर भेजने के लिए अपने स्वयं के निर्माण के प्रतीक भेजना बेहतर कर रहे हैं। –

उत्तर

6

कैसे उन सब को Arrays के रूप में इलाज के बारे में? व्यवहार आप String रों के लिए चाहते हैं एक Array ही नहीं होने के कारण उसे रूप में ही है String:

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

[*arg] चाल से काम करता है क्योंकि सूचक ऑपरेटर (*) में ही एक भी तत्व बदल जाता है या एक इनलाइन सूची में एक Array इसके तत्वों का।

बाद

यह मूलतः सिर्फ एक वाक्य रचना मीठा संस्करण या Arnaud's answer है, हालांकि वहाँ सूक्ष्म अंतर कर रहे हैं अगर आप एक Array अन्य Array रों युक्त गुजरती हैं।

बाद में अभी भी

एक अतिरिक्त foo की वापसी मान साथ क्या करने वाले अंतर है। आप foo(bar, :baz) फोन हैं, तो आप [baz] वापस पाने के लिए आश्चर्य हो सकता है। इस समस्या के समाधान के लिए, आप एक Kestrel जोड़ सकते हैं:

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

जो हमेशा arg वापस आ जाएगी के रूप में पारित कर दिया। या आप returning(obj) कर सकते हैं ताकि आप चेन कॉल foo पर कर सकें। यह आप पर निर्भर करता है कि आप किस प्रकार के रिटर्न-वैल्यू व्यवहार चाहते हैं।

+0

मेरी मूल पोस्ट की तुलना में बहुत अधिक सुंदर। मैं * ऑपरेटर के लिए कभी भी उपयोग नहीं कर सकता। – Arnaud

+0

[* तर्क] शानदार! – Dmitry

0

क्या आप पैरामीटर.class.name पर आधारित व्यवहार स्विच कर सकते हैं? यह बदसूरत है, लेकिन अगर मैं सही ढंग से समझता हूं, तो आपके पास एक ही विधि है जिसे आप कई प्रकार से गुजरेंगे - आपको किसी भी तरह से अंतर करना होगा।

वैकल्पिक रूप से, केवल एक विधि जोड़ें जो सरणी प्रकार पैरामीटर को संभालती है। यह थोड़ा अलग व्यवहार है इसलिए एक अतिरिक्त विधि समझ में आ सकती है।

+0

आपकी दूसरी टिप्पणी एक बेहतर समाधान है, लेकिन मैं स्टैक ओवरफ्लो गुरुओं से अधिक की उम्मीद कर रहा हूं। पहले एक 'is_a रूप में एक ही समस्या है? ऐरे': कोई उपयोगकर्ता एक स्ट्रिंग भेज सकता है जो 'स्ट्रिंग' या ऐसी सूची नहीं है जो 'ऐरे' नहीं है। –

+0

मुझे यकीन नहीं है कि गैर-'स्ट्रिंग' तार कितने आम हैं।गैर-'ऐरे' सूचियां काफी आम हैं - कई वस्तुएं 'संख्यात्मक' हैं। दुर्भाग्यवश, तो तार हैं .... –

0

इन्हें भेजने से पहले अपनी वस्तुओं को क्रमबद्ध करने के लिए Marshal का उपयोग करें।

+0

मैं उलझन में हूँ। 'send' वह विधि है जो रुबी में नाम से फ़ंक्शन को कॉल करती है। मार्शल कैसे मदद करेगा? –

1

Array के बाद से और String दोनों Enumerables, वहाँ कम से कम में नहीं जिस तरह से चर्चा की जा रही है कहने के लिए एक सुंदर तरीका नहीं है "एक बात एक Enumberable है कि नहीं, बल्कि एक स्ट्रिंग," कर रहे हैं।

मुझे क्या होता Enumerable के लिए बतख-प्रकार (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 से कुछ क्लीनर मांगे? Array'। 'Is_a का उपयोग करना? स्ट्रिंग' (या आपके द्वारा दिखाए गए संस्करण) मूल एपीआई को किसी ऐसे व्यक्ति के लिए तोड़ता है जो 'प्रेषण' के लिए स्ट्रिंग की तरह कुछ क्वैक का उपयोग कर रहा है। वास्तव में, अधिकतर उपयोगकर्ता शायद इसके बजाय 'प्रतीक' का उपयोग करते हैं। मैं 'x.is_a? (स्ट्रिंग) || का उपयोग कर सकता था x.is_a? (प्रतीक) ', लेकिन यह बहुत रूबी-आश नहीं है, और मेरे कुछ उपयोगकर्ताओं के लिए एपीआई तोड़ सकता है। –

0

ऐसा न करने पर बंदरगाह करना चाहते हैं, बस भेजने से पहले उचित स्ट्रिंग में सूची को मालिश करें। आप 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

शायद सवाल काफी स्पष्ट नहीं था, लेकिन एक रात की नींद मुझे इस सवाल का जवाब देने के लिए दो स्वच्छ तरीके से पता चला है।

1: to_symString और Symbol पर उपलब्ध है और स्ट्रिंग की तरह किसी भी चीज़ पर उपलब्ध होना चाहिए।

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 पसंद है। मुझे गंभीर संदेह है कि पुरानी एपीआई के किसी भी उपयोगकर्ता को इस विधि द्वारा टाइपएरर को उठाए जाने की उम्मीद है ...

+1

पहला समाधान समझ में आता है, क्योंकि वास्तव में एक प्रतीक की अपेक्षा करता है। लेकिन मुझे उम्मीद है कि आप दूसरे समाधान के बारे में गंभीर नहीं हैं। अपवाद नियंत्रण प्रवाह नहीं हैं। –

+0

@ सराह: यादृच्छिक, लेकिन मुझे लगता है कि आपकी सभी टिप्पणियों में से लगभग 40% और एसओ पर प्रतिक्रियाओं में वाक्य शामिल है "अपवाद नियंत्रण प्रवाह नहीं हैं।" – Telemachus

+0

@Telemachus - कम से कम हाल ही में। :) –

1

मान लें कि आपका समारोह समारोह

नाम पर है चलो मैं साथ

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

आप केवल

समारोह के साथ

सरणियों के लिए अपने कार्य func_array को लागू करने के साथ खत्म मापदंडों से एक सरणी होगा (" हैलो वर्ल्ड ") आप एक .flatten मिल जाएगा! => [ "हैलो दुनिया"] समारोह के साथ ([ "हैलो", "दुनिया"]) आप a.flatten मिल जाएगा! => [ "हैलो", "दुनिया"]

2

एक महत्वपूर्ण विस्तार है कि जवाब के सभी में अनदेखा किया गया: तार, नहीं जवाब :map करने के लिए क्या इतना सरल जवाब मूल प्रश्न में है: बस respond_to? :map का उपयोग करें।

संबंधित मुद्दे