2015-08-26 4 views
11

मैं एक प्लगइन बना रहा हूं जो एक डेवलपर को क्लास परिभाषा (सामान्य कार्य_स पैटर्न के बाद) में एक सरल घोषणा के साथ कक्षा में विभिन्न विशेषताओं को जोड़ने की अनुमति देगा। जिस तरह से कोड specific_method_to_use पैरामीटर एक तरीके के रूप में मौजूद है, लेकिन आम तौर पर है:रूबी क्लास परिभाषा के अंत में कोड चलाने के लिए मैं एक हुक कैसे सेट कर सकता हूं?

उदाहरण के लिए, प्लगइन लेने वाली कोड

class YourClass 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

मेरे सवाल उठता है, क्योंकि मैं जाँच करें कि के लिए प्रदान किया गया मान त्रुटि के लिए चाहते हैं कैसा लग सकता है संगठित और लोड किया गया, विधि अभी तक मौजूद नहीं है।

मेरी प्लगइन में कोड अंतरिम रूप से इस तरह दिखता है:

module MyPlugin 
    extend ActiveSupport::Concern 

    module ClassMethods 
    def consumes_my_plugin(options = {}) 
     raise ArgumentError.new("#{options[:specific_method_to_use]} is not defined") if options[:specific_method_to_use].present? && !self.respond_to?(options[:specific_method_to_use]) 
    end 
    end 
end 

यह काम करेगा:

class YourClass 
    def your_method; true; end 

    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

लेकिन यह कैसे ज्यादातर लोगों कोड लिखने है, और यह नहीं होगा:

class YourClass 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 

    def your_method; true; end 
end 

मैं आपके क्लास लोड समय में कैसे असफल हो सकता हूं? मैं इसे त्रुटि में नहीं चाहता, न कि रनआउट समय पर NoMethodError के साथ। क्या मैं उस लाइन के निष्पादन को रोक सकता हूं जो पूरे वर्ग को लोड होने तक ArgumentError उठाता है, या इसे प्राप्त करने के लिए कुछ और चालाक करता है?

+1

आप बिंदु जहां 'specific_method_to_use' लागू होता पर चेक कर सकते हैं कक्षा लोड समय पर चेक करने के बजाय? –

+0

: विशिष्ट_method_to_use केवल रनटाइम पर आक्रमण किया जाता है, इसलिए हालांकि मैं निश्चित रूप से NoMethodError को पकड़ सकता हूं और इसके आसपास डेवलपर को रचनात्मक संदेश प्रदान कर सकता हूं, बग केवल रनटाइम पर पकड़ा जाएगा, लोड समय पर नहीं ... बाद वाला मुझे लगता है कि बेहतर है। – LikeMaBell

+1

चेक जो आप 'self.respond_to?' का उपयोग कर कर रहे हैं, उदाहरण विधि 'YourClass # your_method' की जांच नहीं करेगा; यह सिंगलटन/क्लास विधि 'YourClass.your_method' की तलाश करेगा। मेरे जवाब में मैंने इसे 'self.instance_methods.include' में संशोधित किया। इसे वापस 'respond_to' में बदलने के लिए स्वतंत्र महसूस करें?'अगर मैंने आपके इरादे को गलत समझा। –

उत्तर

5

आपकी कक्षा :end ईवेंट भेजते समय ट्रैक करने के लिए TracePoint का उपयोग करें। आपकी समस्या

को


विशिष्ट समाधान यहाँ कैसे आप अपने पूर्व मौजूदा मॉड्यूल में सीधे TracePoint फिट कर सकते हैं है।

require 'active_support/all' 

module MyPlugin 
    extend ActiveSupport::Concern 

    module ClassMethods 
    def consumes_my_plugin(**options) 
     m = options[:specific_method_to_use] 

     TracePoint.trace(:end) do |t| 
     break unless self == t.self 

     raise ArgumentError.new("#{m} is not defined") unless instance_methods.include?(m) 

     t.disable 
     end 
    end 
    end 
end 

नीचे दिए गए उदाहरणों का प्रदर्शन निर्दिष्ट किया गया है कि यह काम करता है:

# `def` before `consumes`: evaluates without errors 
class MethodBeforePlugin 
    include MyPlugin 
    def your_method; end 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

# `consumes` before `def`: evaluates without errors 
class PluginBeforeMethod 
    include MyPlugin 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
    def your_method; end 
end 

# `consumes` with no `def`: throws ArgumentError at load time 
class PluginWithoutMethod 
    include MyPlugin 
    consumes_my_plugin option1: :value1, specific_method_to_use: :your_method 
end 

जनरल समाधान

इस मॉड्यूल आप किसी भी कक्षा में एक self.finalize कॉलबैक बनाने देगी।

module Finalize 
    def self.extended(obj) 
    TracePoint.trace(:end) do |t| 
     if obj == t.self 
     obj.finalize 
     t.disable 
     end 
    end 
    end 
end 

अब आप अपने वर्ग का विस्तार करने और self.finalize है, जो जैसे ही वर्ग परिभाषा समाप्त होता चला जाएगा परिभाषित कर सकते हैं:

class Foo 
    puts "Top of class" 

    extend Finalize 

    def self.finalize 
    puts "Finalizing #{self}" 
    end 

    puts "Bottom of class" 
end 

puts "Outside class" 

# output: 
# Top of class 
# Bottom of class 
# Finalizing Foo 
# Outside class 
+0

यह वही उत्तर जैसा दिखता है जिसे मैं ढूंढ रहा था। धन्यवाद। मेरी माफी है कि उसने आपको पूर्ण बकाया नहीं दिया - मैं बक्षीस देने वाली अवधि के दौरान यात्रा कर रहा था, और ऐसा लगता है कि मुझे पूरी राशि का पुरस्कार नहीं दिया जाएगा। – LikeMaBell

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

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