यहाँ पूरी कहानी है, यह समझने के लिए आवश्यक मेटाप्रोग्रामिंग अवधारणाओं की व्याख्या करना कि मॉड्यूल समावेश रूबी में किस तरह काम करता है।
एक मॉड्यूल शामिल होने पर क्या होता है?
एक वर्ग में एक मॉड्यूल शामिल करना वर्ग के पूर्वजों के लिए मॉड्यूल जोड़ता है । आप किसी भी वर्ग या मॉड्यूल के पूर्वजों को इसकी ancestors
विधि कहकर देख सकते हैं :
module M
def foo; "foo"; end
end
class C
include M
def bar; "bar"; end
end
C.ancestors
#=> [C, M, Object, Kernel, BasicObject]
# ^ look, it's right here!
जब आप किसी उदाहरण पर एक विधि कहते हैं C
, तो रूबी प्रदान की गई नाम के साथ एक इंस्टेंस विधि खोजने के लिए इस पूर्वजों की सूची के प्रत्येक आइटम को देखेंगे । जब से हम शामिल M
में C
, M
अब के एक पूर्वज है C
, इसलिए जब हम फोन foo
का एक उदाहरण पर C
, रूबी में है कि विधि मिलेगा M
:
C.new.foo
#=> "foo"
ध्यान दें कि समावेशन किसी भी उदाहरण या वर्ग विधियों को क्लास में कॉपी नहीं करता है - यह केवल क्लास में एक "नोट" जोड़ता है जिसे इसे शामिल मॉड्यूल में इंस्टेंस विधियों के लिए भी देखना चाहिए।
हमारे मॉड्यूल में "वर्ग" विधियों के बारे में क्या?
क्योंकि शामिल करने से केवल आवृत्ति के तरीके बदल जाते हैं, जिसमें एक वर्ग में एक मॉड्यूल शामिल है, केवल उस वर्ग पर अपनी आवृत्ति के तरीकों को उपलब्ध करता है। मॉड्यूल में "वर्ग" विधियां और अन्य घोषणाएं स्वचालित रूप से कक्षा में कॉपी नहीं की जाती हैं:
module M
def instance_method
"foo"
end
def self.class_method
"bar"
end
end
class C
include M
end
M.class_method
#=> "bar"
C.new.instance_method
#=> "foo"
C.class_method
#=> NoMethodError: undefined method `class_method' for C:Class
रूबी वर्ग के तरीकों को कैसे लागू करती है?
रूबी में, कक्षाएं और मॉड्यूल सादे वस्तु हैं - वे वर्ग के उदाहरण हैं Class
और Module
। इसका मतलब है कि आप गतिशील रूप से नई कक्षाएं बना सकते हैं, उन्हें चर आदि के लिए असाइन कर सकते हैं।
klass = Class.new do
def foo
"foo"
end
end
#=> #<Class:0x2b613d0>
klass.new.foo
#=> "foo"
रूबी में भी, आपको वस्तुओं पर तथाकथित सिंगलटन विधियों को परिभाषित करने की संभावना है । इन विधियों को वस्तु के विशेष, छिपे हुए एकल वर्ग में नई आवृत्ति विधियों के रूप में जोड़ा जाता है :
obj = Object.new
# define singleton method
def obj.foo
"foo"
end
# here is our singleton method, on the singleton class of `obj`:
obj.singleton_class.instance_methods(false)
#=> [:foo]
लेकिन कक्षाओं और मॉड्यूल सिर्फ सादे वस्तुओं के रूप में अच्छी तरह से नहीं कर रहे हैं? वास्तव में वे हैं! क्या इसका मतलब यह है कि उनके पास एकल तरीके भी हो सकते हैं? हाँ यह करता है! और इस तरह से क्लास तरीके पैदा होते हैं:
class Abc
end
# define singleton method
def Abc.foo
"foo"
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
या, वर्ग विधि को परिभाषित करने का अधिक सामान्य तरीका self
वर्ग परिभाषा ब्लॉक के भीतर उपयोग करना है, जो वर्ग ऑब्जेक्ट को बनाए जाने के लिए संदर्भित करता है:
class Abc
def self.foo
"foo"
end
end
Abc.singleton_class.instance_methods(false)
#=> [:foo]
मैं एक मॉड्यूल में वर्ग विधियों को कैसे शामिल करूं?
जैसा कि हमने अभी स्थापित किया है, वर्ग विधियां वास्तव में वर्ग वस्तु के एकल वर्ग पर केवल उदाहरण के तरीके हैं। क्या इसका मतलब यह है कि हम क्लास के तरीकों का एक गुच्छा जोड़ने के लिए सिंगलटन क्लास में एक मॉड्यूल शामिल कर सकते हैं ? हाँ यह करता है!
module M
def new_instance_method; "hi"; end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
self.singleton_class.include M::ClassMethods
end
HostKlass.new_class_method
#=> "hello"
यह self.singleton_class.include M::ClassMethods
रेखा बहुत अच्छी नहीं लगती है, इसलिए रूबी ने जोड़ा Object#extend
, जो ऐसा ही करता है - यानी ऑब्जेक्ट के सिंगलटन वर्ग में एक मॉड्यूल शामिल है:
class HostKlass
include M
extend M::ClassMethods
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ there it is!
extend
मॉड्यूल में कॉल को स्थानांतरित करना
यह पिछला उदाहरण दो कारणों से अच्छी तरह से संरचित कोड नहीं है:
- अब हम कॉल करनी होगी दोनों
include
और extend
में HostClass
परिभाषा हमारे मॉड्यूल ठीक से शामिल पाने के लिए। यह बहुत ही बोझिल हो सकता है अगर आपको बहुत सारे समान मॉड्यूल शामिल करने हों।
HostClass
सीधे संदर्भ M::ClassMethods
है, जो एक है कार्यान्वयन विस्तार मॉड्यूल के M
कि HostClass
पता है या के बारे में परवाह करने की जरूरत नहीं होनी चाहिए।
तो इस बारे में कैसे: जब हम include
पहली पंक्ति पर कॉल करते हैं, तो हम किसी तरह मॉड्यूल को सूचित करते हैं कि इसे शामिल किया गया है, और इसे हमारी कक्षा की वस्तु भी दें, ताकि यह extend
स्वयं को कॉल कर सके। इस तरह, यह क्लास के तरीकों को जोड़ने के लिए मॉड्यूल का काम है अगर वह चाहता है।
यह वही है जो विशेष self.included
विधि के लिए है। जब भी मॉड्यूल को किसी अन्य वर्ग (या मॉड्यूल) में शामिल किया जाता है, रूबी स्वचालित रूप से इस विधि को कॉल करती है, और पहले तर्क के रूप में होस्ट क्लास ऑब्जेक्ट में पास होती है:
module M
def new_instance_method; "hi"; end
def self.included(base) # `base` is `HostClass` in our case
base.extend ClassMethods
end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
def self.existing_class_method; "cool"; end
end
HostKlass.singleton_class.included_modules
#=> [M::ClassMethods, Kernel]
# ^ still there!
बेशक, कक्षा के तरीकों को जोड़ना केवल एक चीज नहीं है जिसे हम अंदर कर सकते हैं self.included
। हमारे पास क्लास ऑब्जेक्ट है, इसलिए हम उस पर किसी अन्य (क्लास) विधि को कॉल कर सकते हैं:
def self.included(base) # `base` is `HostClass` in our case
base.existing_class_method
#=> "cool"
end