रेल 4.2 की घोषणा में आगामी रेल 5 के बारे में कुछ दिलचस्प खबरें थीं:शायद इसके लिए रूबी 2.2 की आवश्यकता होगी। जो इसे रूबी 2 की सभी अच्छी चीजों का लाभ उठाने वाला पहला रेल संस्करण बना देगा।
पोस्ट में कचरा एकत्रित प्रतीकों और कीवर्ड तर्कों का उल्लेख किया गया है। लेकिन मेरे लिए, सबसे दिलचस्प रूबी 2 सुविधाओं में से एक है मॉड्यूल#प्रीपेन्ड।
alias_method_chain
का अच्छा (और बुरा)
जब मैंने पहली बार रेल सीखा, alias_method_chain
मेरे दिमाग को पूरी तरह से उड़ा दिया। इसने वास्तव में दिखाया कि रूबी कितनी लचीली हो सकती है।
कोड की केवल एक पंक्ति के साथ, आप किसी विधि के काम करने के तरीके को पूरी तरह से बदल सकते हैं। अब आपको अपने इच्छित कोड को जोड़ने के लिए पुस्तकालयों के आसपास हैक करने की आवश्यकता नहीं है, आप इसे बस फ्लाई पर जोड़ सकते हैं। alias_method_chain
जेम्स के लिए मेरे पहले पैच का नेतृत्व किया, जिसके कारण मेरा पहला पुल अनुरोध हुआ, जिसके कारण मेरा पहला ओपन सोर्स योगदान हुआ।
लेकिन मंकी पैचिंग की तरह, alias_method_chain
अत्यधिक उपयोग हो गया, और इसकी समस्याएं स्पष्ट होने लगीं:
- इससे उत्पन्न होने वाले विधि नाम भ्रमित करने वाले हैं, जिससे त्रुटियों को ढूंढना और डीबग करना कठिन हो जाता है। उदाहरण के लिए:
class Person
def greeting
"Hello"
end
end
module GreetingWithExcitement
def self.included(base)
base.class_eval do
alias_method_chain :greeting, :excitement
end
end
def greeting_with_excitement
"#{greeting_without_excitement}!!!!!"
end
end
Person.send(:include, GreetingWithExcitement)
अगर आपको Person#greeting
. में कोई त्रुटि हुई है , बैकट्रेस आपको बताएगा कि वास्तव में Person#greeting_without_excitement
में एक त्रुटि हुई है . लेकिन उस विधि को परिभाषित भी कहाँ किया जाता है? मैं इसे कहीं नहीं देखता। आप कैसे जानते हैं कि कौन सा greeting
वह विधि है जिसमें बग है? और विधि के नाम और भी अधिक भ्रमित करते हैं जितना अधिक आप श्रृंखलाबद्ध करते हैं।
-
यदि आप
alias_method_chain
को कॉल करते हैं एक ही कक्षा में समान पैरामीटर के साथ दो बार, आप स्टैक ओवरफ़्लो का कारण बन सकते हैं। (क्या आप देख सकते हैं क्यों?) यह सामान्य रूप से तब तक नहीं होता, जब तक आपकीrequire
कथन संगत हैं कि वे किन रास्तों का उपयोग करते हैं। लेकिन अगर आप अक्सर रेल कंसोल में कोड पेस्ट करते हैं तो यह बहुत कष्टप्रद होता है। -
और बाकी बातें येहुदा काट्ज के ब्लॉग पोस्ट द्वारा बताई गई हैं। इस पोस्ट ने बहुत से रेल डेवलपर्स को
alias_method_chain
. को छोड़ना शुरू करने के लिए मना लिया मॉड्यूल विरासत के पक्ष में।
तो, इसका उपयोग अभी भी क्यों किया जाता है?
आप अधिकांश alias_method_chain
. को बदल सकते हैं मॉड्यूल में उन विधियों को ओवरराइड करके, और उन मॉड्यूल को अपने बच्चे वर्गों में शामिल करके। लेकिन यह केवल तभी काम करता है जब आप अपने सुपरक्लास को ओवरराइड करना चाहते हैं, न कि अपनी कक्षा को। वह है:
class ParentClass
def log
puts "In parent"
end
end
class ChildClass < ParentClass
def log
puts "In child"
super
end
def log_with_extra_message
puts "In child, with extra message"
log_without_extra_message
end
alias_method_chain :log, :extra_message
end
अगर आप ChildClass.new.log
चलाते हैं , आप देखेंगे:
In child, with extra message
In child
In parent
यदि आपने alias_method_chain
. के बजाय मॉड्यूल का उपयोग करने का प्रयास किया है , आप आउटपुट प्राप्त कर सकते हैं:
In child
In child, with extra message
In parent
लेकिन आप नहीं कर सकते log
को बदले बिना मूल आउटपुट से मिलान करें ChildClass
में विधि . रूबी विरासत उस तरह से काम नहीं करती है। खैर, ऐसा नहीं हुआ।
रूबी 2.0 में क्या बदला?
रूबी 2.0 तक, नीचे कोड जोड़ने का कोई तरीका नहीं था एक वर्ग, केवल इसके ऊपर। लेकिन prepend
के साथ , आप एक मॉड्यूल से एक विधि के साथ कक्षा में एक विधि को ओवरराइड कर सकते हैं, और फिर भी super
के साथ कक्षा के कार्यान्वयन तक पहुंच सकते हैं . इसलिए, हमारे पिछले उदाहरण का उपयोग करके, हम इसके साथ मूल आउटपुट प्राप्त कर सकते हैं:
class ParentClass
def log
puts "In parent"
end
end
module ExtraMessageLogging
def log
puts "In child, with extra message"
super
end
end
class ChildClass < ParentClass
prepend ExtraMessageLogging
def log
puts "In child"
super
end
end
In child, with extra message
In child
In parent
बिल्कुल सही।
अगर prepend
अपने सिर को चारों ओर लपेटना अभी भी मुश्किल है, इसे ऐसा कुछ करने के बारे में सोचें:
class NewChildClass < ChildClass
include ExtraMessageLogging
end
ChildClass = NewChildClass
सिवाय इसके कि यह आपके वर्ग के नामों के साथ खिलवाड़ नहीं करेगा, और यह उन वस्तुओं को प्रभावित करता है जो पहले से मौजूद हैं।
(हां, आप रूबी में कक्षा के नाम पुन:असाइन कर सकते हैं। नहीं, यह शायद एक अच्छा विचार नहीं है।)
रेल के लिए इसका क्या अर्थ है?
तो, alias_method_chain
. का उपयोग करने का आखिरी बहाना रूबी 2.0 में चला गया है। हम alias_method_chain
. के कुछ शेष उदाहरणों में से एक ले सकते हैं रेल में:
require 'active_support/core_ext/module/aliasing'
class Range #:nodoc:
def each_with_time_with_zone(&block)
ensure_iteration_allowed
each_without_time_with_zone(&block)
end
alias_method_chain :each, :time_with_zone
def step_with_time_with_zone(n = 1, &block)
ensure_iteration_allowed
step_without_time_with_zone(n, &block)
end
alias_method_chain :step, :time_with_zone
private
def ensure_iteration_allowed
if first.is_a?(Time)
raise TypeError, "can't iterate from #{first.class}"
end
end
end
और इसके बजाय इसे एक मॉड्यूल से बदलें:
require 'active_support/core_ext/module/aliasing'
module RangeWithTimeWithZoneSupport #:nodoc:
def each(&block)
ensure_iteration_allowed
super(&block)
end
def step(n = 1, &block)
ensure_iteration_allowed
super(n, &block)
end
private
def ensure_iteration_allowed
if first.is_a?(Time)
raise TypeError, "can't iterate from #{first.class}"
end
end
end
Range.send(:prepend, RangeSupportingTimeWithZone)
यह अधिक साफ है, Range#each
उसका नाम बदला नहीं जाता, और ensure_iteration_allowed
मंकी-पैच इन नहीं है।
विरासत का उपयोग करें, पैच का नहीं
रूबी आपको एक टन लचीलापन देती है, और यही एक कारण है कि मुझे यह पसंद है। लेकिन इसमें एक शक्तिशाली ऑब्जेक्ट मॉडल भी है। इसलिए जब आप अपना खुद का कोड इंजेक्ट करना चाहते हैं, तो इसे हैक करने से पहले मॉड्यूल और इनहेरिटेंस पर भरोसा करने का प्रयास करें। आपके कोड को समझना और डिबग करना बहुत आसान होगा, और आप alias_method_chain
जैसी किसी चीज़ के कुछ कठिन-से-पहचाने दुष्प्रभावों से बचेंगे ।
alias_method_chain
रेल में मुझे पेश किए गए सबसे अच्छे तरीकों में से एक था। लेकिन इसके दिन गिने-चुने हैं। हमने इसे बढ़ा दिया है। और जब यह चला जाएगा तो मैं इसे याद नहीं करूंगा।