Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Ruby

रूबी के #dup और #clone . में गोता लगाना

आज की पोस्ट में, हम रूबी के #dup . पर गौर करेंगे और #clone . हम एक वास्तविक जीवन के उदाहरण से शुरू करेंगे जिसने इस रुचि को ट्रिगर किया। उसके बाद, हम #dup . सीखने के लक्ष्य के साथ और गहराई में जाएंगे रूबी में लागू किया गया है और इसकी तुलना #clone . से कैसे की जाती है . इसके बाद हम अपना #dup . लागू करके इसे समाप्त कर देंगे तरीका। चलो चलें!

मैंने Dup का उपयोग कैसे शुरू किया

जब मैंने एक ऐसी कंपनी में काम किया, जो एनजीओ के लिए चंदा इकट्ठा करने के लिए अभियान स्थापित करने में विशेषज्ञता रखती थी, तो मुझे नियमित रूप से अभियानों की नकल करनी पड़ती थी और नए अभियान बनाने पड़ते थे। उदाहरण के लिए, 2018 अभियान समाप्त होने के बाद, 2019 के लिए एक नए अभियान की आवश्यकता थी।

एक अभियान में आमतौर पर ढेर सारे कॉन्फ़िगरेशन विकल्प होते थे, जिन्हें दोबारा स्थापित करने का मेरा मन नहीं करता था। इसमें काफी समय लगेगा और त्रुटि-प्रवण था। इसलिए मैंने DB रिकॉर्ड को कॉपी करके शुरू किया और वहां से चला गया।

पहले कुछ अभियानों के लिए, मैंने वास्तव में इसे हाथ से कॉपी किया था। यह कुछ इस तरह दिख रहा था:

current_campaign = Campaign.find(1)
new_campaign = current_campaign
new_campaign.id = nil
new_campaign.created_at = nil
new_campaign.updated_at = nil
new_campaign.title = "Campaign 2019"
new_campaign.save!

यह काम करता है, लेकिन इसके लिए बहुत अधिक टाइपिंग की आवश्यकता होती है, इसका उल्लेख नहीं करना त्रुटि-प्रवण है। मैं created_at . सेट करना भूल गया हूँ करने के लिए nil अतीत में कुछ बार।

चूंकि यह थोड़ा दर्द जैसा महसूस हुआ, मैं सोच भी नहीं सकता था कि यह इसके बारे में जाने का सबसे अच्छा तरीका था। और यह पता चला, एक बेहतर तरीका है!

new_campaign = Campaign.find(1).dup
new_campaign.title = "Campaign 2019"
new_campaign.save!

यह आईडी और टाइमस्टैम्प को nil . पर सेट कर देगा , ठीक यही हम हासिल करना चाहते हैं।

इस तरह मैं पहली बार #dup . का उपयोग करने लगा . अब, आइए और गहराई से देखें कि कैसे #dup वास्तव में काम करता है।

क्या चल रहा है अंडर द हुड?

#dup . का डिफ़ॉल्ट रूबी कार्यान्वयन विधि आपको अपने ऑब्जेक्ट में एक विशेष इनिशियलाइज़र जोड़ने की अनुमति देती है जिसे केवल तब कहा जाता है जब किसी ऑब्जेक्ट को #dup के माध्यम से इनिशियलाइज़ किया जाता है। तरीका। ये तरीके हैं:

  • initialize_copy
  • initialize_dup

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

यह सीधे रूबी स्रोत कोड से लिया जाता है:

VALUE
rb_obj_dup(VALUE obj)
{
    VALUE dup;
 
    if (special_object_p(obj)) {
            return obj;
    }
    dup = rb_obj_alloc(rb_obj_class(obj));
    init_copy(dup, obj);
    rb_funcall(dup, id_init_dup, 1, obj);
 
    return dup;
}

हमारे लिए, दिलचस्प हिस्सा लाइन 11 पर है जहां रूबी प्रारंभकर्ता विधि #intialize_dup को कॉल करती है ।

<ब्लॉकक्वॉट>

rb_funcall एक फ़ंक्शन है जिसका उपयोग रूबी सी कोड में बहुत अधिक किया जाता है। इसका उपयोग किसी वस्तु पर विधियों को कॉल करने के लिए किया जाता है। इस मामले में यह id_init_dup . को कॉल करेगा dup पर वस्तु। 1 बताता है कि कितने तर्क हैं, इस मामले में केवल एक:obj

आइए थोड़ा और गहराई में जाएं और उस कार्यान्वयन को देखें:

VALUE
rb_obj_init_dup_clone(VALUE obj, VALUE orig)
{
    rb_funcall(obj, id_init_copy, 1, orig);
    return obj;
}

जैसा कि आप इस उदाहरण में देख सकते हैं, id_init_copy को कॉल करने के अलावा वास्तव में कुछ भी नहीं हो रहा है . अब जबकि हम खरगोश के छेद के नीचे हैं, आइए उस विधि को भी देखें:

VALUE
rb_obj_init_copy(VALUE obj, VALUE orig)
{
    if (obj == orig) return obj;
    rb_check_frozen(obj);
    rb_check_trusted(obj);
    if (TYPE(obj) != TYPE(orig) || rb_obj_class(obj) != rb_obj_class(orig)) {
    rb_raise(rb_eTypeError, "initialize_copy should take same class object");
    }
    return obj;
}

अधिक कोड होने के बावजूद, आंतरिक रूप से आवश्यक कुछ जांचों को छोड़कर कुछ विशेष नहीं हो रहा है (लेकिन यह एक और समय के लिए एक अच्छा विषय हो सकता है)।

तो कार्यान्वयन में क्या होता है कि रूबी आपको एक समापन बिंदु देता है और आपको अपने दिलचस्प व्यवहार को लागू करने के लिए आवश्यक उपकरण प्रदान करता है।

रेल का डुप कार्यान्वयन

रेल ने कई स्थानों पर ठीक यही किया है, लेकिन अभी के लिए, हम केवल इस बात में रुचि रखते हैं कि कैसे id और टाइमस्टैम्प फ़ील्ड साफ़ हो जाते हैं।

ActiveRecord के लिए कोर मॉड्यूल में आईडी साफ़ हो जाती है। यह ध्यान में रखता है कि आपकी प्राथमिक कुंजी क्या है, इसलिए यदि आपने इसे बदल दिया है, तब भी यह इसे रीसेट कर देगी।

# activerecord/lib/active_record/core.rb
def initialize_dup(other) # :nodoc:
  @attributes = @attributes.deep_dup
  @attributes.reset(self.class.primary_key)
 
  _run_initialize_callbacks
 
  @new_record               = true
  @destroyed                = false
  @_start_transaction_state = {}
  @transaction_state        = nil
 
  super
end

टाइमस्टैम्प को टाइमस्टैम्प मॉड्यूल में साफ़ किया जाता है। यह रेल को उन सभी टाइमस्टैम्प को साफ़ करने के लिए कहता है जो रेल बनाने और अपडेट करने के लिए उपयोग कर सकते हैं (created_at , created_on , updated_at और updated_on )।

# activerecord/lib/active_record/timestamp.rb
def initialize_dup(other) # :nodoc:
  super
  clear_timestamp_attributes
end

यहां एक दिलचस्प तथ्य यह है कि रेल ने जानबूझकर #initialize_dup . को ओवरराइड करना चुना है #initialize_copy . के बजाय विधि तरीका। ऐसा क्यों करेगा? आइए जांच करते हैं।

ऑब्जेक्ट#इनिशियलाइज़_कॉपी समझाया गया

उपरोक्त कोड स्निपेट में, हमने देखा कि रूबी कैसे #initialize_dup . को कॉल करती है जब आप .dup . का उपयोग करते हैं एक विधि पर। लेकिन एक #initialize_copy . भी है तरीका। इसका उपयोग कहां किया जाता है, इसे बेहतर ढंग से समझाने के लिए, आइए एक उदाहरण देखें:

class Animal
  attr_accessor :name
 
  def initialize_copy(*args)
    puts "#initialize_copy is called"
    super
  end
 
  def initialize_dup(*args)
    puts "#initialize_dup is called"
    super
  end
end
 
animal = Animal.new
animal.dup
 
# => #initialize_dup is called
# => #initialize_copy is called

अब हम देख सकते हैं कि कॉलिंग ऑर्डर क्या है। रूबी सबसे पहले #initialize_dup पर कॉल करती है और फिर #initialize_copy . पर कॉल करें . अगर हम कॉल को super . पर रखते #initialize_dup . से बाहर विधि, हमने कभी भी initialize_copy . नहीं कहा होगा , इसलिए इसे अंदर रखना महत्वपूर्ण है।

क्या कुछ कॉपी करने के अन्य तरीके हैं?

अब जब हमने इस कार्यान्वयन को देख लिया है, तो आप सोच रहे होंगे कि दो #initialize_* होने का क्या उपयोग है? तरीके। उत्तर है:वस्तुओं को कॉपी करने का एक और तरीका है, जिसे #clone . कहा जाता है . आप आमतौर पर #clone . का उपयोग करते हैं यदि आप किसी वस्तु को उसकी आंतरिक स्थिति सहित कॉपी करना चाहते हैं।

रेल अपने #dup . के साथ इसका उपयोग कर रहा है ActiveRecord पर विधि। यह #dup . का उपयोग करता है आपको किसी रिकॉर्ड को उसकी "आंतरिक" स्थिति (आईडी और टाइमस्टैम्प) के बिना डुप्लिकेट करने की अनुमति देने के लिए, और #clone छोड़ देता है लागू करने के लिए रूबी तक।

इस अतिरिक्त विधि के होने से #clone . का उपयोग करते समय एक विशिष्ट प्रारंभकर्ता की भी आवश्यकता होती है तरीका। इसके लिए आप #initialize_clone . को ओवरराइड कर सकते हैं . यह विधि #initialize_dup . के समान जीवनचक्र का उपयोग करती है और #initialize_copy . की ओर कॉल करेगा ।

यह जानकर, इनिशियलाइज़र विधियों का नामकरण थोड़ा अधिक समझ में आता है। हम उपयोग कर सकते हैं #initialize_(dup|clone) आप #dup . का उपयोग करते हैं या नहीं, इसके आधार पर विशिष्ट कार्यान्वयन के लिए या #clone . यदि हमारे पास व्यापक व्यवहार है जो दोनों के लिए उपयोग किया जाता है, तो आप इसे #initialize_copy के अंदर रख सकते हैं ।

जानवर का क्लोन बनाना

(सिर्फ एक उदाहरण, इस ब्लॉग पोस्ट के लिए किसी जानवर को चोट नहीं पहुंची)

अब आइए एक उदाहरण देखें कि यह व्यवहार में कैसे काम करता है।

class Animal
  attr_accessor :name, :dna, :age
 
  def initialize
    self.dna = generate_dna
  end
 
  def initialize_copy(original_animal)
    self.age = 0
    super
  end
 
  def initialize_dup(original_animal)
    self.dna = generate_dna
    self.name = "A new name"
    super
  end
 
  def initialize_clone(original_animal)
    self.name = "#{original_animal.name} 2"
    super
  end
 
  def generate_dna
    SecureRandom.hex
  end
end
 
bello = Animal.new
bello.name = "Bello"
bello.age = 10
 
bello_clone = bello.clone
bello_dup = bello.dup
 
bello_clone.name # => "Bello 2"
bello_clone.age # => 0
 
bello_dup.name # => "A new name"
bello_dup.age # => 0

आइए देखें कि वास्तव में यहां क्या हो रहा है। हमारे पास Animal . नामक एक वर्ग है , और इस पर निर्भर करते हुए कि हम जानवर की नकल कैसे करते हैं, उसका व्यवहार अलग होना चाहिए:

  • जब हम जानवर का क्लोन बनाते हैं, तो डीएनए वही रहता है, और उसका नाम मूल नाम होगा जिसमें 2 संलग्न होंगे।
  • जब हम जानवर की नकल करते हैं, तो हम मूल जानवर के आधार पर एक नया जानवर बनाते हैं। इसे अपना डीएनए और एक नया नाम मिलता है।
  • सभी मामलों में जानवर एक बच्चे के रूप में शुरू होता है।

ऐसा करने के लिए हमने तीन अलग-अलग प्रारंभकर्ता लागू किए। #initialize_(dup|clone) विधि हमेशा #initialize_copy तक कॉल करेगी , इस प्रकार यह सुनिश्चित करता है कि आयु 0 पर सेट है।

क्लोन और अन्य जानवरों को गोल करना

खुजली की व्याख्या करके शुरू करते हुए हमें खुद को खरोंचने की जरूरत है, हमने डेटाबेस रिकॉर्ड की प्रतिलिपि बनाने पर ध्यान दिया। हम अभियान उदाहरण में हाथ से कॉपी करने से #dup . पर गए और #clone . फिर हमने इसे व्यावहारिक से आकर्षक में ले लिया और देखा कि रूबी में इसे कैसे लागू किया जाता है। हमने #clone . के साथ भी खेला आईएनजी और #dup जानवरों में। हमें उम्मीद है कि आपने हमारे गहरे गोता लगाने का उतना ही आनंद लिया जितना हमने इसे लिखा था।


  1. रुबोकॉप के साथ लाइनिंग और ऑटो-फॉर्मेटिंग रूबी कोड

    लाइनिंग प्रोग्रामेटिक और शैलीगत त्रुटियों के लिए स्रोत कोड की स्वचालित जाँच है। यह जाँच एक स्थिर कोड विश्लेषण उपकरण द्वारा की जाती है जिसे लिंटर कहा जाता है। एक कोड फ़ॉर्मेटर, हालांकि, स्रोत कोड को स्वरूपित करने से संबंधित एक उपकरण है, ताकि यह नियमों के पूर्व-कॉन्फ़िगर किए गए सेट का सख्ती से पालन कर

  1. लॉगर और लॉगरेज के साथ रूबी में लॉगिंग

    रूबी में लॉग के साथ कार्य करना लॉगिंग उन प्राथमिक कार्यों में से एक है जिसे एक एप्लिकेशन आमतौर पर संबोधित करता है। लॉग का उपयोग तब किया जाता है जब आपको आवश्यकता होती है, उदाहरण के लिए, देखें कि आपके ऐप्स के अंदर क्या हो रहा है, उन पर नज़र रखें, या कुछ विशिष्ट डेटा के लिए मीट्रिक एकत्र करें। एक न

  1. रूबी में 4 सरल संस्मरण पैटर्न (और एक रत्न)

    संस्मरण एक ऐसी तकनीक है जिसका उपयोग आप अपने एक्सेसर विधियों को गति देने के लिए कर सकते हैं। यह उन तरीकों के परिणामों को कैश करता है जो समय लेने वाले काम करते हैं, ऐसे काम जिन्हें केवल एक बार करने की आवश्यकता होती है। रेल में, आप मेमोइज़ेशन को इतनी बार उपयोग करते हुए देखते हैं कि इसमें एक मॉड्यूल भी