अपने कैरी-ऑन सामान को पकड़ें, क्योंकि आज हम पूर्वजों की श्रृंखला तक पूरी यात्रा करेंगे। हम एक विधि कॉल का पालन करेंगे और देखेंगे कि यह कैसे श्रृंखला में ऊपर जाता है और साथ ही यह पता लगाएंगे कि क्या होता है यदि विधि गायब है। और चूँकि हम आग से खेलना पसंद करते हैं, इसलिए हम यहीं नहीं रुकेंगे बल्कि आग से खेलना जारी रखेंगे। BasicObject#method_missing . को ओवरराइड करना . यदि आप ध्यान दें, तो हम इसका प्रयोग व्यावहारिक उदाहरण में भी कर सकते हैं। हालांकि कोई गारंटी नहीं है। चलो चलें!
पूर्वज श्रृंखला
आइए रूबी में पूर्वजों की जंजीरों के मूलभूत नियमों से शुरू करें:
- रूबी केवल एकल वंशानुक्रम का समर्थन करता है
- यह किसी ऑब्जेक्ट को मॉड्यूल के एक सेट को शामिल करने की भी अनुमति देता है
रूबी में, पूर्वजों की श्रृंखला किसी दिए गए वर्ग के लिए सभी विरासत में मिली कक्षाओं और मॉड्यूल के ट्रैवर्सल से बनी होती है।
आइए एक उदाहरण पर एक नज़र डालते हैं जो आपको दिखाता है कि रूबी में पूर्वजों की श्रृंखला को कैसे संभाला जाता है।
module Auth end
module Session end
module Iterable end
class Collection
prepend Iterable
end
class Users < Collection
prepend Session
include Auth
end
p Users.ancestors उत्पादन करता है:
[
Session, Users, Auth, # Users
Iterable, Collection, # Collection
Object, Kernel, BasicObject # Ruby Object Model
]
सबसे पहले, हम ancestors . को बुलाते हैं किसी दिए गए वर्ग की पूर्वज श्रृंखला तक पहुँचने के लिए वर्ग विधि।
हम देख सकते हैं कि Users.ancestors . को एक कॉल कक्षाओं और मॉड्यूल की एक सरणी देता है जिसमें क्रम में शामिल हैं:
Users. के प्रीपेड मॉड्यूल- द
Usersकक्षा Usersक्लास' में मॉड्यूल शामिल हैंCollectionक्लास' प्रीपेड मॉड्यूल — उपयोगकर्ताओं के प्रत्यक्ष अभिभावक के रूप मेंCollectionकक्षाCollectionक्लास' में मॉड्यूल शामिल हैं —कोई नहीं- द
Objectclass — किसी भी वर्ग की डिफ़ॉल्ट विरासत - द
Kernelमॉड्यूल —Object. में शामिल है और मुख्य विधियों को धारण करना - द
BasicObjectclass — रूबी में मूल वर्ग
इसलिए, किसी दिए गए ट्रैवर्स किए गए वर्ग या मॉड्यूल के लिए उपस्थिति का क्रम हमेशा इस प्रकार होता है:
- प्रीपेन्डेड मॉड्यूल
- कक्षा या मॉड्यूल
- इसमें शामिल मॉड्यूल
जब किसी वस्तु या वर्ग पर कोई विधि लागू की जाती है, तो मुख्य रूप से रूबी द्वारा पूर्वजों की श्रृंखला का पता लगाया जाता है।
विधि खोज पथ
जब कोई संदेश भेजा जाता है, तो रूबी संदेश प्राप्त करने वाले की पूर्वज श्रृंखला का पता लगाती है और जांचती है कि उनमें से कोई दिए गए संदेश का जवाब देता है या नहीं।
यदि पूर्वज श्रृंखला का दिया गया वर्ग या मॉड्यूल संदेश का जवाब देता है, तो इस संदेश से जुड़ी विधि निष्पादित की जाती है और पूर्वज श्रृंखला ट्रैवर्सल को रोक दिया जाता है।
class Collection < Array
end
Collection.ancestors # => [Collection, Array, Enumerable, Object, Kernel, BasicObject]
collection = Collection.new([:a, :b, :c])
collection.each_with_index # => :a
यहां, collection.each_with_index संदेश संख्यात्मक मॉड्यूल द्वारा प्राप्त किया जाता है। फिर Enumerable#each_with_index इस संदेश के लिए विधि कहा जाता है।
यहां, जब collection.each_with_index कहा जाता है, रूबी जाँचता है कि:
- संग्रह
each_with_index messageका जवाब देता है => नहीं - ऐरे
each_with_index messageका जवाब देता है => नहीं each_with_index messageका अनगिनत जवाब => हां
तो, यहाँ से, रूबी पूर्वजों की श्रृंखला ट्रैवर्सल को रोकता है और इस संदेश से जुड़ी विधि को कॉल करता है। हमारे मामले में, Enumerable#each_with_index विधि।
रूबी में, इस तंत्र को विधि लुकअप पथ . कहा जाता है ।
अब, क्या होगा यदि किसी दिए गए रिसीवर के पूर्वजों की श्रृंखला बनाने वाले वर्गों और मॉड्यूल में से कोई भी संदेश का जवाब नहीं देता है?
BasicObject#method_missing
अच्छा खेलने के साथ पर्याप्त! आइए चीजों को तोड़ें, डेवलपर शैली:अपवादों को फेंककर। हम एक Collection implement लागू करेंगे क्लास और इसके किसी एक इंस्टेंस पर अज्ञात विधि को कॉल करें।
class Collection
end
c = Collection.new
c.search('item1') # => NoMethodError: undefined method `search` for #<Collection:0x123456890>
यहाँ, Collection वर्ग search लागू नहीं करता है तरीका। तो एक NoMethodError उठाया है। लेकिन यह त्रुटि सुधार कहाँ से आता है?
त्रुटि BasicObject#method_missing . में उठाई गई है तरीका। इस विधि को तब कहा जाता है जब विधि लुकअप पथ अंत में दिए गए संदेश के अनुरूप कोई विधि नहीं मिल रही है।
ठीक है... लेकिन यह विधि केवल एक NoMethodError उत्पन्न करती है . इसलिए हमारे Collection . के संदर्भ में विधि को ओवरराइड करने में सक्षम होना बहुत अच्छा होगा कक्षा।
BasicObject#method_missing को ओवरराइड करना विधि
अंदाज़ा लगाओ? method_missing . को ओवरराइड करना पूरी तरह से ठीक है क्योंकि यह विधि विधि लुकअप पथ . के तंत्र के अधीन भी है . एक सामान्य विधि के साथ एकमात्र अंतर यह है कि हमें यकीन है कि यह विधि कम से कम एक बार विधि खोज पथ द्वारा मिल जाएगी। ।
दरअसल, BasicObject class — जो रूबी में किसी भी वर्ग का मूल वर्ग है — इस पद्धति के न्यूनतम संस्करण को परिभाषित करता है। क्लासिक रूबी मैजिक, क्या करना चाहिए?
तो चलिए इस विधि को हमारे Collection class में ओवरराइड करते हैं :
class Collection
def initialize
@collection = {}
end
def method_missing(method_id, *args)
if method_id[-1] == '='
key = method_id[0..-2]
@collection[key.to_sym] = args.first
else
@collection[method_id]
end
end
end
collection = Collection.new
collection.obj1 = 'value1'
collection.obj2 = 'value2'
collection.obj1 # => 'value1'
collection.obj2 # => 'value2'
यहां, Collection#method_missing @collection . के प्रतिनिधि के रूप में कार्य करता है उदाहरण चर। दरअसल, रूबी मोटे तौर पर ऑब्जेक्ट डेलिगेशन को संभालता है — c.f:delegate पुस्तकालय।
यदि अनुपलब्ध विधि एक सेटर विधि है (collection.obj1 = 'value1' ), फिर विधि का नाम (:obj1 ) कुंजी और तर्क के रूप में प्रयोग किया जाता है ('value1' ) @collection . के मान के रूप में हैश प्रविष्टि (@collection[:obj1] = 'value1' )।
एक HTML टैग जेनरेटर
अब जबकि हम जानते हैं कि method_missing . कैसे विधि पर्दे के पीछे काम करती है, आइए एक प्रतिलिपि प्रस्तुत करने योग्य उपयोग के मामले को लागू करें।
यहाँ, लक्ष्य निम्नलिखित DSL को परिभाषित करना है:
HTML.p 'hello world' # => <p>hello world</p>
HTML.div 'hello world' # => <div>hello world</div>
HTML.h1 'hello world' # => <h1>hello world</h1>
HTML.h2 'hello world' # => <h2>hello world</h2>
HTML.span 'hello world' # => <span>hello world</span>
HTML.p "hello #{HTML.b 'world'}" # => <p>hello <b>world</b></p>
ऐसा करने के लिए, हम HTML.method_missing . को लागू करने जा रहे हैं प्रत्येक HTML टैग के लिए एक विधि को परिभाषित करने से बचने के लिए विधि।
सबसे पहले, हम एक HTML . परिभाषित करते हैं मापांक। फिर हम एक method_missing . को परिभाषित करते हैं इस मॉड्यूल में वर्ग विधि:
module HTML
def HTML.method_missing(method_id, *args, &block)
"<#{method_id}>#{args.first}</#{method_id}>"
end
end
हमारी विधि अनुपलब्ध method_id . का उपयोग करके बस एक HTML टैग बनाएगी — :div HTML.div . पर कॉल करने के लिए , उदाहरण के लिए।
ध्यान दें कि क्लास मेथड भी मेथड लुकअप पाथ के अधीन हैं।
हम अपने HTML टैग जनरेटर को इस प्रकार बढ़ा सकते हैं:
- नेस्टेड टैग को संभालने के लिए ब्लॉक तर्क का उपयोग करना
- एकल टैग को संभालना —
<br/>उदाहरण के लिए
लेकिन ध्यान दें कि कोड की कुछ पंक्तियों के साथ, हम बड़ी मात्रा में HTML टैग जेनरेट करने में सक्षम हैं।
तो, संक्षेप में:
method_missing डीएसएल बनाने के लिए एक अच्छा प्रवेश बिंदु है जहां अधिकांश कमांड पहचाने गए पैटर्न का एक सेट साझा करेंगे।
निष्कर्ष
हम रूबी में पूर्वजों की श्रृंखला तक गए और BasicObject#method_missing में पहुंचे . BasicObject#method_missing रूबी हुक मेथड्स . का हिस्सा है . इसका उपयोग वस्तुओं के साथ उनके जीवनचक्र में कुछ सटीक क्षणों में बातचीत करने के लिए किया जाता है। किसी भी अन्य रूबी हुक विधियों की तरह , इस हुक विधि का उपयोग सावधानी से करना होगा। और ध्यान से, हमारा मतलब है कि इसे कभी भी रूबी ऑब्जेक्ट मॉडल . के व्यवहार को संशोधित नहीं करना चाहिए —सिवाय इसके कि जब आप इसके साथ खेल रहे हों या उस पर ब्लॉग पोस्ट लिख रहे हों;-)
वोइला!