2010-07-13 9 views
11

मैं एक हुक विधि बनाना चाहता हूं जिसे कक्षा के किसी भी समारोह को हर बार बुलाया जाता है। मैं method_added की कोशिश की है, लेकिन यह वर्ग परिभाषा के समय में केवल एक बार निष्पादित करता है,"method_called" को परिभाषित करना .. मैं एक हुक विधि कैसे बना सकता हूं जिसे हर बार कक्षा के किसी भी कार्य को बुलाया जाता है?

class Base 

    def self.method_added(name) 
    p "#{name.to_s.capitalize} Method's been called!!" 
    end 
    def a 
    p "a called." 
    end 
    def b 
    p "b called." 
    end 
end 
t1 = Base.new 
t1.a 
t1.b 
t1.a 
t1.b 

Output: 

"A Method's been called!!" 
"B Method's been called!!" 
"a called." 
"b called." 
"a called." 
"b called." 

लेकिन मेरी आवश्यकता एक वर्ग कार्यक्रम में कहीं भी कहा जाता हो जाता है कि के किसी भी समारोह से चलाता है कि "method_called", हुक विधि है ।

Expected Output: 
"A Method's been called!!" 
"a called." 
"B Method's been called!!" 
"b called." 
"A Method's been called!!" 
"a called." 
"B Method's been called!!" 
"b called." 

यदि कोई परिभाषित मौजूदा हुक विधि है जो कि वही काम करती है, तो कृपया इसके बारे में बताएं।

अग्रिम धन्यवाद ..

+0

हां मैंने निश्चित रूप से उन पर ध्यान दिया और आपके उत्तर के लिए धन्यवाद। –

+0

माइकज का अर्थ है कि आपको अपने पिछले प्रश्नों के माध्यम से वापस जाना चाहिए और किसी भी उत्तर पर चेकबॉक्स पर क्लिक करना है जो आपके प्रश्न को पर्याप्त रूप से संबोधित करता है। – sarnold

उत्तर

17

Kernel#set_trace_func पर एक नजर डालें। यह आपको एक ऐसी प्रक्रिया निर्दिष्ट करने देता है जिसे किसी ईवेंट (जैसे एक विधि कॉल) होता है।

class Base 
    def a 
    puts "in method a" 
    end 

    def b 
    puts "in method b" 
    end 
end 

set_trace_func proc { |event, file, line, id, binding, classname| 
    # only interested in events of type 'call' (Ruby method calls) 
    # see the docs for set_trace_func for other supported event types 
    puts "#{classname} #{id} called" if event == 'call' 
} 

b = Base.new 
b.a 
b.b 

आउटपुट: यहाँ एक उदाहरण है

Base a called 
in method a 
Base b called 
in method b 
18

method_added है जब एक नई विधि वर्ग में जोड़ दिया गया कोड को चलाने के लिए; जब कोई विधि बुलाया जाता है तो यह रिपोर्ट नहीं करता है।

#!/usr/bin/ruby 

class Base 
    def self.method_added(name) 
    if /hook/.match(name.to_s) or method_defined?("#{name}_without_hook") 
     return 
    end 
    hook = "def #{name}_hook\n p 'Method #{name} has been called'\n #{name}_without_hook\nend" 
    self.class_eval(hook) 

    a1 = "alias #{name}_without_hook #{name}" 
    self.class_eval(a1) 

    a2 = "alias #{name} #{name}_hook" 
    self.class_eval(a2) 
    end 
    def a 
    p "a called." 
    end 
    def b 
    p "b called." 
    end 
end 
t1 = Base.new 
t1.a 
t1.b 
t1.a 
t1.b 

और उत्पादन:

$ ./meta.rb 
"Method a has been called" 
"a called." 
"Method b has been called" 
"b called." 
"Method a has been called" 
"a called." 
"Method b has been called" 
"b called." 
+1

मुझे यह पसंद है। यह वैश्विक 'set_trace_func' से कम आक्रामक प्रतीत होता है क्योंकि यह हैकरी केवल इस वर्ग के लिए बाध्य है। –

+0

बहुत बढ़िया हैक :) –

+0

बहुत चालाक! यह बहुत बुरा है कि ऐसी कोई भाषा सुविधा नहीं है जो हेवीवेट के बिना इसे आसान बनाता है। – bchurchill

1

मैं हाल ही में लिखा था (। आप की खोज की के रूप में)

आप mikej के जवाब का पालन नहीं करना चाहते हैं, तो यहाँ एक वर्ग है जिसे आपके विवरणों को लागू करता है ऐसा कुछ जो उपयोगी हो सकता है, हालांकि कुछ प्रावधान हैं (नीचे देखें)। यहाँ वर्ग आप करने के लिए अपने हुक जोड़ना चाहते है:

class Original 
    def regular_old_method msg 
    puts msg 
    end 

private 

    def always_called method_called 
    puts "'#{method_called.to_s.capitalize}' method's been called!" 
    end 
end 

और यहाँ कि हुक को जोड़ने के लिए कोड है:

class << Original 
    def new(*args) 
    inner = self.allocate 
    outer_name = self.name + 'Wrapper' 
    outer_class = Class.new do 
     def initialize inner_object 
     @inner = inner_object 
     end 
     def method_missing method_called, *args 
     @inner.send method_called, *args 
     @inner.send :always_called, method_called 
     end 
    end 
    outer_class_constant = Object.const_set(outer_name, outer_class) 
    inner.send :initialize, *args 
    outer_class_constant.new inner 
    end 
end 

यदि आप इसे इस तरह कहते हैं ...

o = Original.new 
o.regular_old_method "nothing unusual about this bit" 

आपको निम्न आउटपुट मिलता है:

इस बिट के बारे में कुछ भी असामान्य नहीं है

'Regular_old_method' विधि को कॉल किया गया है!

यह दृष्टिकोण एक बुरा विचार है, तो आपके कोड वर्ग नामों की जाँच हो सकता है, क्योंकि भले ही आप वर्ग के ऑब्जेक्ट के लिए कहा है 'मूल', क्या आप वापस मिल वर्ग की एक वस्तु था 'OriginalWrapper'।

प्लस मुझे लगता है कि 'नई' विधि के साथ गड़बड़ करने के लिए अन्य दोष हो सकते हैं, लेकिन रुबी मेटाप्रोग्रामिंग का मेरा ज्ञान अब तक फैला नहीं है।

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

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