কম্পিউটার

রুবির #dup এবং #clone-এ ডুব দেওয়া

আজকের পোস্টে, আমরা রুবির #dup দেখব এবং #clone . আমরা একটি বাস্তব-জীবনের উদাহরণ দিয়ে শুরু করব যা এই আগ্রহকে ট্রিগার করেছে। এর পরে, কিভাবে #dup শেখার লক্ষ্য নিয়ে আমরা আরও গভীরে ডুব দেব রুবিতে প্রয়োগ করা হয় এবং এটি কিভাবে #clone এর সাথে তুলনা করে . তারপর আমরা আমাদের নিজস্ব #dup প্রয়োগ করে বন্ধ করব পদ্ধতি চলুন!

আমি কিভাবে Dup ব্যবহার শুরু করেছি

যখন আমি একটি কোম্পানিতে কাজ করতাম যেটি অনুদান সংগ্রহের জন্য এনজিও-এর জন্য প্রচারাভিযান স্থাপনে বিশেষীকৃত ছিল, তখন আমাকে নিয়মিত প্রচারাভিযান অনুলিপি করতে হতো এবং নতুন তৈরি করতে হতো। উদাহরণস্বরূপ, 2018 সালের প্রচারাভিযান শেষ হওয়ার পর, 2019-এর জন্য একটি নতুনের প্রয়োজন ছিল।

একটি প্রচারাভিযানে সাধারণত অনেকগুলি কনফিগারেশন বিকল্প থাকে, যা আমি আবার সেট আপ করার মত অনুভব করিনি। এটি বেশ কিছু সময় নেবে এবং ত্রুটি-প্রবণ ছিল। তাই আমি ডিবি রেকর্ড কপি করে শুরু করলাম এবং সেখান থেকে গেলাম।

প্রথম কয়েকটি প্রচারণার জন্য, আমি আসলে এটি হাতে কপি করেছি। এটি দেখতে এরকম কিছু ছিল:

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_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 ব্যবহার করেন যদি আপনি একটি বস্তুর অভ্যন্তরীণ অবস্থা সহ অনুলিপি করতে চান।

Rails তার #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 এর সাথেও খেলেছি ing এবং #dup প্রাণী। আমরা আশা করি আপনি আমাদের গভীর ডাইভ উপভোগ করেছেন যতটা আমরা এটি লিখেছি।


  1. RuboCop সহ রুবি কোড লিন্টিং এবং স্বয়ংক্রিয় ফর্ম্যাটিং

  2. Logger এবং Lograge সঙ্গে রুবি লগ ইন করুন

  3. রুবি স্ট্যাক ট্রেস পড়া এবং বোঝা

  4. রুবিতে 4টি সহজ মেমোাইজেশন প্যাটার্ন (এবং একটি রত্ন)