रेल एक बड़ा ढांचा है, और यह हर साल बड़ा होता जाता है। यह कुछ उपयोगी सुविधाओं के लिए किसी का ध्यान नहीं जाने के लिए आसान बनाता है। इस श्रृंखला में, हम विशिष्ट कार्यों के लिए रेल में निर्मित कुछ कम-ज्ञात कार्यक्षमता पर एक नज़र डालेंगे।
इस श्रृंखला के पहले लेख में, हम इस बात पर एक नज़र डालने जा रहे हैं कि Rails.env.test?
को कैसे कॉल किया जाता है? वास्तव में अल्पज्ञात StringInquirer
. का उपयोग करके अंडर-द-हुड काम करता है ActiveSupport से कक्षा। एक कदम और आगे बढ़ते हुए, हम StringInquirer
. के माध्यम से भी चलेंगे स्रोत कोड यह देखने के लिए कि यह कैसे काम करता है, जो (स्पॉइलर अलर्ट) विशेष method_missing
के माध्यम से रूबी के मेटाप्रोग्रामिंग का एक सरल उदाहरण है। विधि।
Rails.env हेल्पर
आप शायद पहले ही कोड देख चुके हैं जो रेल पर्यावरण की जांच करता है:
if Rails.env.test?
# return hard-coded value...
else
# contact external API...
end
हालांकि, test?
वास्तव में करते हैं, और यह विधि कहाँ से आती है?
अगर हम Rails.env
. चेक करते हैं कंसोल में, यह एक स्ट्रिंग की तरह काम करता है:
=> Rails.env
"development"
यहाँ स्ट्रिंग वह है जो RAILS*ENV पर्यावरण चर पर सेट है; हालांकि, यह _just* एक स्ट्रिंग नहीं है। कंसोल में कक्षा पढ़ते समय, हम देखते हैं:
=> Rails.env.class
ActiveSupport::StringInquirer
Rails.env
वास्तव में एक StringInquirer है।
StringInquirer
StringInquirer के लिए कोड संक्षिप्त और अच्छी तरह से प्रलेखित दोनों है। हालांकि, यह कैसे काम करता है, यह स्पष्ट नहीं हो सकता है जब तक कि आप रूबी की मेटाप्रोग्रामिंग क्षमताओं से परिचित न हों। हम यहां जो कुछ हो रहा है, उसके बारे में जानेंगे।
class StringInquirer < String
...
end
सबसे पहले, हम देखते हैं कि StringInquirer String का एक उपवर्ग है। यही कारण है कि Rails.env
जब हम इसे कॉल करते हैं तो एक स्ट्रिंग की तरह कार्य करता है। स्ट्रिंग से इनहेरिट करके, हम स्वचालित रूप से सभी स्ट्रिंग जैसी कार्यक्षमता प्राप्त करते हैं, इसलिए हम इसे स्ट्रिंग की तरह व्यवहार कर सकते हैं। Rails.env.upcase
काम करता है, जैसा कि ActiveRecordModel.find_by(string_column: Rails.env)
करता है ।
जबकि Rails.env
StringInquirer का एक आसान, अंतर्निहित उदाहरण है, हम अपना स्वयं का बनाने के लिए भी स्वतंत्र हैं:
def type
result = "old"
result = "new" if @new
ActiveSupport::StringInquirer.new(result)
end
फिर, हमें दिए गए मान पर प्रश्न-चिह्न विधियाँ मिलती हैं:
=> @new = false
=> type.old?
true
=> type.new?
false
=> type.testvalue?
false
=> @new = true
=> type.new?
true
=> type.old?
false
method_missing
method_missing
असली गुप्त चटनी यहाँ है। रूबी में, जब भी हम किसी वस्तु पर कोई विधि कहते हैं, रूबी विधि के लिए वस्तु के सभी पूर्वजों (यानी, आधार वर्ग या शामिल मॉड्यूल) को देखकर शुरू होती है। यदि कोई नहीं मिलता है, तो रूबी method_missing
. को कॉल करेगी , किसी भी तर्क के साथ, जिस विधि की तलाश कर रहा है, उसके नाम से गुजर रहा है।
डिफ़ॉल्ट रूप से, यह विधि केवल एक अपवाद उठाती है। हम सभी संभवत:NoMethodError: undefined method 'test' for nil:NilClass
से परिचित हैं। त्रुटि संदेश का प्रकार। हम अपना खुद का method_missing
. लागू कर सकते हैं यह कोई अपवाद नहीं उठाता है, जो वास्तव में StringInquirer
. है करता है:
def method_missing(method_name, *arguments)
if method_name.end_with?("?")
self == method_name[0..-2]
else
super
end
end
किसी भी के लिए विधि का नाम जो ?
. में समाप्त होता है , हम self
. के मान की जांच करते हैं (यानी, स्ट्रिंग) ?
. के बिना विधि नाम के विरुद्ध . दूसरा तरीका रखो, अगर हम StringInquirer.new("test").long_test_method_name?
कहते हैं , लौटाया गया मान "test" == "long_test_method_name"
. है ।
यदि विधि का नाम नहीं है एक प्रश्न चिह्न के साथ समाप्त, हम मूल method_missing
. पर वापस आते हैं (वह जो अपवाद उठाएगा)।
response_to_missing?
फ़ाइल में एक और तरीका है:respond_to_missing?
. हम कह सकते हैं कि यह method_missing
. का एक सहयोगी तरीका है . हालांकि method_missing
हमें कार्यक्षमता देता है, हमें रूबी को यह बताने का एक तरीका भी चाहिए कि हम इन प्रश्न-चिह्न-समाप्ति विधियों को स्वीकार करते हैं।
def respond_to_missing?(method_name, include_private = false)
method_name.end_with?("?") || super
end
यदि हम respond_to?
. को कॉल करते हैं तो यह चलन में आता है इस वस्तु पर। इसके बिना, अगर हम StringInquirer.new("test").respond_to?(:test?)
कहते हैं , परिणाम false
. होगा क्योंकि हमारे पास test?
. नामक कोई स्पष्ट विधि नहीं है . यह स्पष्ट रूप से भ्रामक है क्योंकि अगर मैं Rails.env.respond_to?(:test?)
को कॉल करता हूं, तो मुझे उम्मीद है कि यह सच हो जाएगा। ।
respond_to_missing?
वह है जो हमें रूबी को "हां, मैं उस विधि को संभाल सकता हूं" कहने की अनुमति देता है। यदि विधि का नाम प्रश्नवाचक चिह्न में समाप्त नहीं होता है, तो हम सुपरक्लास पद्धति पर वापस आ जाते हैं।
व्यावहारिक उपयोग के मामले
अब जबकि हम जानते हैं कि कैसे StringInquirer काम करता है, आइए उन उदाहरणों पर एक नज़र डालें जहाँ यह उपयोगी हो सकता है:
1. पर्यावरण चर
पर्यावरण चर दो मानदंडों को पूरा करते हैं जो उन्हें StringInquirer के लिए बढ़िया विकल्प बनाते हैं। सबसे पहले, उनके पास अक्सर संभावित मानों का एक सीमित, ज्ञात सेट होता है (जैसे enum
), और दूसरा, हमारे पास अक्सर सशर्त तर्क होते हैं जो उनके मूल्य पर निर्भर करते हैं।
मान लें कि हमारा ऐप भुगतान एपीआई से जुड़ा हुआ है, और हमारे पास पर्यावरण चर में संग्रहीत प्रमाण-पत्र हैं। हमारी उत्पादन प्रणाली में, यह स्पष्ट रूप से वास्तविक एपीआई है, लेकिन हमारे मंचन या विकास संस्करण में, हम उनके सैंडबॉक्स एपीआई का उपयोग करना चाहते हैं:
# ENV["PAYMENT_API_MODE"] = sandbox/production
class PaymentGateway
def api_mode
# We use ENV.fetch because we want to raise if the value is missing
@api_mode ||= ENV.fetch("PAYMENT_API_MODE").inquiry
end
def api_url
# Pro-tip: We *only* use production if MODE == 'production', and default
# to sandbox if the value is anything else, this prevents us using production
# values if the value is mistyped or incorrect
if api_mode.production?
PRODUCTION_URL
else
SANDBOX_URL
end
end
end
ध्यान दें कि उपरोक्त में, हम ActiveSupport के String#inquiry
. का उपयोग कर रहे हैं विधि, जो आसानी से एक स्ट्रिंग को हमारे लिए StringInquirer में बदल देती है।
2. एपीआई प्रतिक्रियाएं
ऊपर से हमारे भुगतान एपीआई उदाहरण को जारी रखते हुए, एपीआई हमें एक प्रतिक्रिया भेजेगा जिसमें कुछ सफलता/विफलता स्थिति शामिल है। फिर से, यह दो मानदंडों को पूरा करता है जो इसे StringInquirer के लिए एक उम्मीदवार बनाते हैं:संभावित मूल्यों का एक सीमित सेट और सशर्त तर्क जो इन मूल्यों का परीक्षण करेगा।
class PaymentGateway
def create_charge
response = JSON.parse(api_call(...))
result = response["result"].inquiry
if result.success?
...
else
...
end
# result still acts like a string
Rails.logger.info("Payment result was: #{result}")
end
end
निष्कर्ष
StringInquirer आपकी पिछली जेब में रखने के लिए एक दिलचस्प उपकरण है, लेकिन व्यक्तिगत रूप से, मैं इसके लिए बहुत बार नहीं पहुंचूंगा। इसके कुछ उपयोग हैं, लेकिन अधिकांश समय, किसी वस्तु पर एक स्पष्ट विधि आपको वही परिणाम देती है। स्पष्ट रूप से नामित विधि के कुछ लाभ भी हैं; यदि मान को कभी भी बदलने की आवश्यकता है, तो आपको केवल एक ही स्थान को अपडेट करना होगा, और यदि कोई डेवलपर विधि का पता लगाने की कोशिश कर रहा है तो यह कोडबेस को खोजना आसान बनाता है।
हालांकि यह StringInquirer पर केंद्रित है, इस लेख का उद्देश्य method_missing
का उपयोग करके रूबी की कुछ मेटाप्रोग्रामिंग क्षमताओं के लिए एक सौम्य परिचय देना है। . मैं कहूंगा कि आप शायद method_missing
. का उपयोग नहीं करना चाहते हैं आपके आवेदन में। हालांकि, यह अक्सर फ्रेमवर्क में उपयोग किया जाता है, जैसे कि रेल या रत्नों द्वारा प्रदान की जाने वाली डोमेन-विशिष्ट-भाषाएं, इसलिए जब आप समस्याओं का सामना करते हैं तो "सॉसेज कैसे बनाया जाता है" यह जानना बहुत उपयोगी हो सकता है।