2015-05-28 3 views
8

का उपयोग करती है, मैं रूबी के विशेष $& (returns last regex match) का उपयोग करने वाली विधि को उपनाम करने की कोशिश कर रहा हूं। मैं मैन्युअल रूप से कर सकते हैं और यह काम करता है:

original = String.instance_method(:sub) 
String.send(:define_method, :sub) do |*args, &block| 
    puts "called" 
    original.bind(self).call(*args, &block) 
end 
"foo".sub(/f/) { $&.upcase } 
    called 
    # => "Foo" 

लेकिन अगर मैं एक विधि है कि मेरे लिए यह करता है लिखने की कोशिश है, यह विफल रहता है:

def programatic_alias(klass, method_name) 
    original = klass.instance_method(method_name) 
    klass.send(:define_method, method_name) do |*args, &block| 
    puts "called" 
    original.bind(self).call(*args, &block) 
    end 
end 

programatic_alias(String, :sub) 
"foo".sub(/f/) { $&.upcase } 
    called 
    NoMethodError: undefined method `upcase' for nil:NilClass 
    called 
    called 
    called 
    from (irb):19:in `block in irb_binding' 

यह वैश्विक राज्य की तरह लग रहा से प्रभावित किया जा रहा है programatic_alias विधि का दायरा, लेकिन मुझे यकीन नहीं है कि यह क्या हो रहा है। प्रश्न यह हैं: मैं String#sub प्रोग्रामेटिक रूप से उपनाम कैसे कर सकता हूं ताकि यह रूबी के विशेष वैश्विक चर के साथ काम कर सके?

+0

माणिक के किन संस्करणों रहे हैं आप उपयोग कर रहे हैं? 2.2.1 के साथ मेरे लिए आपका उदाहरण काम कर रहा है। –

+0

मैं रूबी 2.2.2 का उपयोग कर रहा हूं। आपको यह सुनिश्चित करने की ज़रूरत है कि आप विभिन्न स्क्रिप्ट/आईआरबी सत्रों में उदाहरण चलाएं अन्यथा आप पहले परिभाषित विधि उपनाम का उपयोग कर रहे हैं। मैंने अपनी स्थानीय मशीन पर 2.2.1 पर कोशिश की, समस्या को पुन: पेश करने में सक्षम था। – Schneems

+0

आप '$ और' वैश्विक चर का उपयोग क्यों करेंगे? – hakcho

उत्तर

4

जहाँ तक मुझे पता है, आप यह नहीं कर सकते हैं। docs कहते हैं

ये वैश्विक चर थ्रेड-स्थानीय और विधि-स्थानीय चर हैं।

आप गहरे लाल रंग का स्रोत में खुदाई, तो $& कॉल last_match_getter जो rb_backref_get, जो vm_svar_get जो (कुछ और आंतरिक तरीकों से अधिक लंघन) वर्तमान नियंत्रण फ्रेम हो जाता है कहता है और वहाँ से डेटा पढ़ता से अपने डेटा हो जाता है तक पहुँचने। इस डेटा में से कोई भी रूबी एपीआई के संपर्क में नहीं है - इस डेटा को एक फ्रेम से उस तक प्रसारित करने का कोई तरीका नहीं है जिसे आप एक्सेस करना चाहते हैं।

आपके दूसरे उदाहरण में मूल विधि को कॉल आपके programatic_alias के अंदर हो रहा है विधि, और $& उस दायरे में सेट किया जा रहा है। इसी कारण से

'foo'.try(:sub, /f/) {$&.upcase} 

या तो काम नहीं करेगा।

आपका पहला उदाहरण आधा काम करता है क्योंकि वह जगह जहां sub कहा जाता है और वह स्थान जहां $& संदर्भित किया गया है (ब्लॉक के अंदर) एक ही विधि के दायरे में हैं (इस मामले में रूबी शीर्ष स्तर)। यह करने के लिए परिवर्तित करें:

original = String.instance_method(:sub) 
String.send(:define_method, :sub) do |*args, &block| 
    puts "called" 
    original.bind(self).call(*args, &block) 
end 

def x 
    "foo".sub(/f/) { $&.upcase } 
end 

x() 

और $& अब अपने ब्लॉक में परिभाषित किया गया है (यदि आप अपवाद x द्वारा फेंका पकड़ने आप देख सकते हैं $& शीर्ष स्तर पर किया जा रहा है कि)

+0

स्पष्ट रूप से यह एक बहुत पुराना (ज्ञात) मुद्दा है। यहां 200 9 से https://www.ruby-forum.com/topic/198458#1174493 से बात करने वाला धागा है। हालांकि आपकी व्याख्या के रूप में लगभग विस्तृत नहीं है, फिर से धन्यवाद! – Schneems

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