रूबी ऑन रेल्स में सेटर विधि को ओवरराइड करने का सही तरीका क्या है?


184

मैं रूबी 3.2.2 पर रूबी का उपयोग कर रहा हूं और मैं यह जानना चाहूंगा कि क्या मेरी क्लास की विशेषता के लिए एक सेटर विधि को ओवरराइड करने के लिए निम्नलिखित "उचित" / "सही" / "सुनिश्चित" तरीका है।

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self[:attribute_name] = value
end

उपरोक्त कोड उम्मीद के मुताबिक काम करता है। हालांकि, मैं यह जानना चाहूंगा कि क्या, उपरोक्त कोड का उपयोग करके, भविष्य में मुझे समस्याएँ होंगी या, कम से कम, "रूब पर" "मुझे क्या उम्मीदें" / "हो सकती हैं" । यदि वह सेटर विधि को ओवरराइड करने का सही तरीका नहीं है, तो सही तरीका क्या है?


नोट : यदि मैं कोड का उपयोग करता हूं

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self.attribute_name = value
end

मुझे निम्नलिखित त्रुटि मिलती है:

SystemStackError (stack level too deep):
  actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70

4
मुझे लागू "" उचित "/" सही "/" सुनिश्चित "" शब्दावली से प्यार है। जब आप इसे 3 तरीके देते हैं तो यह वास्तव में सुनिश्चित करता है कि कोई गलत व्याख्या नहीं है। बहुत बढ़िया!
जय

5
@ जय - "सुंदरता इतालवीता"; -)
18

2
बस स्पष्ट होने के लिए, "स्टैक स्तर बहुत गहरा है" इस तथ्य का उल्लेख कर रहा है कि इसकी पुनरावर्ती कॉल ... इसकी कॉलिंग स्वयं।
निप्पिसॉरस

जवाबों:


294

================================================== ========================= अपडेट: 19 जुलाई, 2017

अब रेल डाक्यूमेंट भी superइस तरह उपयोग करने का सुझाव दे रहा है :

class Model < ActiveRecord::Base

  def attribute_name=(value)
    # custom actions
    ###
    super(value)
  end

end

================================================== =========================

मूल उत्तर

यदि आप मॉडल के माध्यम से पहुंचते समय किसी तालिका के स्तंभों के लिए सेटर विधियों को ओवरराइड करना चाहते हैं, तो यह ऐसा करने का तरीका है।

class Model < ActiveRecord::Base
  attr_accessible :attribute_name

  def attribute_name=(value)
    # custom actions
    ###
    write_attribute(:attribute_name, value)
    # this is same as self[:attribute_name] = value
  end

end

रेल दस्तावेज़ में डिफ़ॉल्ट पहुँच को ओवरराइड करना देखें ।

तो, आपका पहला तरीका मॉडल ऑन रूबी ऑन रेल्स में कॉलम बसने को ओवरराइड करने का सही तरीका है। ये एक्सेसर्स पहले से ही मॉडल के गुण के रूप में टेबल के कॉलम तक पहुंचने के लिए रेल द्वारा प्रदान किए जाते हैं। इसे हम ActiveRecord ORM मैपिंग कहते हैं।

यह भी ध्यान रखें कि attr_accessibleमॉडल के शीर्ष पर एक्सेसर्स का कोई लेना देना नहीं है। इसकी पूरी तरह से अलग कार्यशीलता है ( इस प्रश्न को देखें )

लेकिन शुद्ध रूबी में, यदि आपने एक वर्ग के लिए एक्सेसर्स को परिभाषित किया है और सेटर को ओवरराइड करना चाहते हैं, तो आपको इस तरह के उदाहरण चर का उपयोग करना होगा:

class Person
  attr_accessor :name
end

class NewPerson < Person
  def name=(value)
    # do something
    @name = value
  end
end

यह समझने में आसानी होगी कि आप क्या attr_accessorकरते हैं। कोड attr_accessor :nameइन दो विधियों (गेट्टर और सेटर) के बराबर है

def name # getter
  @name
end

def name=(value) #  setter
  @name = value
end

इसके अलावा आपकी दूसरी विधि विफल हो जाती है क्योंकि यह एक अनंत लूप का कारण होगा क्योंकि आप उसी विधि के attribute_name=अंदर उसी विधि को बुला रहे हैं ।


9
रेल 4 के लिए बस छोड़ दें attr_accessibleक्योंकि यह अब नहीं है, और इसे काम करना चाहिए
zigomir

11
क्यों नहीं बुलाते super?
नाथन लिलिएनथाल

1
मुझे इस बात का आभास था कि चूंकि एक्सेसर्स और लेखकों को गतिशील रूप से बनाया गया है, superइसलिए काम नहीं हो सकता है। लेकिन, ऐसा लगता है कि यह मामला नहीं है। मैंने अभी इसकी जाँच की और यह मेरे लिए काम करता है। इसके अलावा, यह सवाल वही पूछते हैं
rubyprince

4
के साथ एक विशाल गोत्र है write_attribute। बातचीत छोड़ दी जाएगी। ध्यान रखें कि write_attributeखजूर के साथ टाइमजोन रूपांतरण को छोड़ देंगे, जो लगभग हमेशा अवांछित होगा।
टिम स्कॉट

2
सुपर के रूप में अच्छी तरह से काम करेगा लेकिन वहाँ कुछ कारण है कि आप हमें यह नहीं करना चाहते हो सकता है। उदाहरण के लिए मोंगॉयड मणि में एक बग होता है जहां आप सरणी को धक्का नहीं दे सकते हैं यदि आप गेट्टर विधि को सुपर करते हैं। यह बग की वजह से स्मृति में सरणी का प्रबंधन तरीका है। इसके अलावा @ नाम भी सेट किए गए मान को वापस करेगा, फिर उस विधि को कॉल करें जो आपकी ओवरराइटिंग है। हालाँकि उपरोक्त समाधान में दोनों ठीक काम करेंगे।
newdark-it

44

superकीवर्ड का उपयोग करें :

def attribute_name=(value)
  super(value.some_custom_encode)
end

इसके विपरीत, पाठक को ओवरराइड करने के लिए:

def attribute_name
  super.some_custom_decode
end

1
स्वीकार किए गए IMO से बेहतर जवाब है क्योंकि यह विधि कॉल को उसी नाम तक सीमित रखता है। यह विशेषता_नाम =
एंड्रयू स्कवार्ट्ज

इस बदलाव की वजह से रेल्स राइडर 4.2 में ओवरड्राइडिंग का तरीका खतरनाक हो गया है: github.com/rails/rails/commit/… इससे पहले के फॉर्म में मदद करने वाले फील्ड के अनपस्ट मान को कॉल करेंगे और आपके कस्टम गेट्टर को कॉल नहीं करेंगे। अब वे आपके तरीके को कहते हैं, और इसलिए आप अपने रूपों में भ्रामक परिणाम उत्पन्न करेंगे, जिसके आधार पर आप मूल्य को ओवरराइड कर रहे हैं।
ब्रेंडन मुईर

16

रेल में ४

मान लें कि आपकी तालिका में आयु विशेषता है

def age=(dob)   
    now = Time.now.utc.to_date
    age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
    super(age) #must add this otherwise you need to add this thing and place the value which you want to save. 
  end

नोट: रेल में नए कामर्स 4 के लिए आपको मॉडल में attr_accessible निर्दिष्ट करने की आवश्यकता नहीं है । इसके बजाय आपको परमिट विधि का उपयोग करके नियंत्रक स्तर पर अपनी विशेषताओं को सफेद-सूची में रखना होगा ।


3

मैंने पाया है कि (कम से कम ActiveRecord संबंध संग्रह के लिए) निम्न पैटर्न काम करता है:

has_many :specialties

def specialty_ids=(values)
  super values.uniq.first(3)
end

(यह सरणी में पहले 3 गैर-डुप्लिकेट प्रविष्टियों को पास करता है।)


0

attr_writerसेटर को अधिलेखित करने का उपयोग करना

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.