Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

วัตถุ Marshalling ใน Ruby

ในบทความนี้ เราจะมาเจาะลึกเรื่องการจัดการวัตถุ เราจะอธิบายว่ามันคืออะไร ดูโมดูล Marshall แล้วดูตัวอย่าง จากนั้นเราจะเจาะลึกลงไปอีกขั้นและเปรียบเทียบ _dump และ self._load วิธีการ ไปกันเถอะ!

Object Marshalling คืออะไร

เมื่อคุณกำลังเขียนโค้ด คุณอาจต้องการบันทึกอ็อบเจ็กต์และส่งไปยังโปรแกรมอื่นหรือนำมาใช้ใหม่ในการรันโปรแกรมครั้งต่อไปของคุณ การจัดวางวัตถุใช้ใน Sidekiq ตัวอย่างเช่น เมื่องาน Sidekiq ถูกจัดคิวในแอปพลิเคชัน Ruby on Rails การทำให้เป็นอนุกรมของงานนี้ — ซึ่งไม่มีอะไรมากไปกว่าอ็อบเจกต์ — จะถูกแทรกใน Redis กระบวนการ Sidekiq จะสามารถดีซีเรียลไลซ์ JSON นี้และสร้างงานเดิมจาก JSON ขึ้นใหม่ได้

ในการเขียนโปรแกรมคอมพิวเตอร์ กระบวนการทำให้เป็นอนุกรมและดีซีเรียลไลซ์เซชันของอ็อบเจ็กต์คือสิ่งที่เราเรียกกันทั่วไปว่า object marshalling . ตอนนี้ มาดูกันว่า Ruby มีไว้เพื่อจัดการกับ Object Marshalling อย่างไร

โมดูลจอมพล

เนื่องจาก Ruby เป็นภาษาการเขียนโปรแกรมเชิงวัตถุอย่างสมบูรณ์ จึงมีวิธีการทำให้เป็นอนุกรมและจัดเก็บวัตถุโดยใช้ Marshall โมดูลในไลบรารีมาตรฐาน ช่วยให้คุณสามารถทำให้เป็นอนุกรมของวัตถุไปยังสตรีมไบต์ที่สามารถจัดเก็บและยกเลิกการทำให้เป็นอนุกรมในกระบวนการ Ruby อื่นได้

ดังนั้น เรามาทำให้เป็นอนุกรมของสตริงและดูวัตถุที่จัดลำดับให้ละเอียดยิ่งขึ้น

hello_world = 'hello world!'
 
serialized_string = Marshal.dump(hello_world) # => "\x04\bI\"\x11hello world!\x06:\x06ET"
serialized_string.class                       # => String
 
deserialized_hello_world = Marshal.load(serialized_string) # => "hello world!"
 
hello_world.object_id              # => 70204420126020
deserialized_hello_world.object_id # => 70204419825700

จากนั้นเราเรียก Marshal.dump วิธีการโมดูลเพื่อทำให้เป็นอนุกรมของสตริงของเรา เราเก็บค่าส่งคืนซึ่งมีสตริงที่ทำให้เป็นอนุกรมของเราใน serialized_string ตัวแปร. สตริงนี้สามารถเก็บไว้ในไฟล์และสามารถใช้ไฟล์ซ้ำเพื่อสร้างออบเจ็กต์เดิมในกระบวนการอื่นได้ จากนั้นเราเรียก Marshal.load วิธีการสร้างอ็อบเจ็กต์ดั้งเดิมจากไบต์สตรีม

เราจะเห็นว่าสตริงที่สร้างใหม่นี้มี object_id . ที่แตกต่างกัน กว่า hello_world string ซึ่งหมายความว่าเป็นวัตถุอื่น แต่มีข้อมูลเดียวกัน

สวยเย็น! แต่อย่างไร Marshal โมดูลสามารถสร้างสตริงใหม่ได้หรือไม่? แล้วถ้าฉันต้องการควบคุมแอตทริบิวต์ที่จะจัดลำดับและดีซีเรียลไลซ์ให้มีลักษณะเป็นอย่างไร

ตัวอย่างที่เป็นรูปธรรมของการมาร์แชลวัตถุ

ในการตอบคำถามเหล่านี้ เรามาปรับใช้กลยุทธ์การจัดวางในโครงสร้างแบบกำหนดเองที่ชื่อว่า User .

User = Struct.new(:fullname, :age, :roles)
 
user = User.new('Mehdi Farsi', 42, [:admin, :operator])

User struct กำหนดแอตทริบิวต์ 3 อย่าง:fullname , age และ roles . สำหรับตัวอย่างนี้ เรามีกฎทางธุรกิจที่เราจัดลำดับเมื่อตรงกับเกณฑ์ต่อไปนี้เท่านั้น:

  • The fullname มีอักขระน้อยกว่า 64 ตัว
  • บทบาท roles อาร์เรย์ไม่มี :admin บทบาท

ในการทำเช่นนั้น เราสามารถกำหนด User#marshal_dump เพื่อใช้กลยุทธ์การจัดลำดับแบบกำหนดเองของเรา เมธอดนี้จะถูกเรียกเมื่อเราเรียกใช้ Marshal.dump เมธอดที่มีอินสแตนซ์ User จัดโครงสร้างเป็นพารามิเตอร์ มานิยามวิธีนี้กัน:

User = Struct.new(:age, :fullname, :roles) do
  def marshal_dump
    {}.tap do |result|
      result[:age]      = age
      result[:fullname] = fullname if fullname.size <= 64
      result[:roles]    = roles unless roles.include? :admin
    end
  end
end
 
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
 
user_dump = Marshal.dump(user) # 'in User#marshal_dump'
user_dump                      # => "\x04\bU:\tUser{\a:\bageI\"\x10Mehdi Farsi\x06:\x06ET:\rfullnamei/"
 

ในตัวอย่างข้างต้น เราจะเห็นว่า User#marshal_dump . ของเรา เมธอดจะถูกเรียกเมื่อเราเรียกใช้ Marshal.dump(ผู้ใช้) user_dump ตัวแปรมีสตริงที่เป็นอนุกรมของ User . ของเรา ตัวอย่าง

ตอนนี้เรามีการถ่ายโอนข้อมูลแล้ว ให้ทำการดีซีเรียลไลซ์ข้อมูลเพื่อสร้างผู้ใช้ของเราใหม่ ในการทำเช่นนั้น เรากำหนด User#marshal_load เมธอดซึ่งมีหน้าที่ในการปรับใช้กลยุทธ์ดีซีเรียลไลเซชันของ User ทิ้ง

มานิยามวิธีนี้กัน

User = Struct.new(:age, :fullname, :roles) do
  def marshal_dump
    {}.tap do |result|
      result[:age]      = age
      result[:fullname] = fullname if fullname.size <= 64
      result[:roles]    = roles unless roles.include? :admin
    end
  end
 
  def marshal_load(serialized_user)
    self.age      = serialized_user[:age]
    self.fullname = serialized_user[:fullname]
    self.roles    = serialized_user[:roles] || []
  end
end
 
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
 
user_dump = Marshal.dump(user) # 'in User#marshal_dump'
user_dump                      # => "\x04\bU:\tUser{\a:\bagei/:\rfullnameI\"\x10Mehdi Farsi\x06:\x06ET"
 
original_user = Marshal.load(user_dump)  # 'in User#marshal_load'
original_user                            # => #<struct User age=42, fullname="Mehdi Farsi", roles=[]>

ในตัวอย่างข้างต้น เราจะเห็นว่า User#marshal_load method . ของเรา ถูกเรียกเมื่อเราเรียกใช้ Marshal.load(user_dump) . original_user ตัวแปรมีโครงสร้างที่สร้างอินสแตนซ์ผู้ใช้ของเราใหม่

โปรดทราบว่า original_user.roles ไม่เหมือนกับ user.roles อาร์เรย์ตั้งแต่ในระหว่างการทำให้เป็นอันดับ user.roles รวม :admin บทบาท. ดังนั้น user.roles ไม่ถูกทำให้เป็นอนุกรมใน user_dump ตัวแปร

วิธีการ _dump และ self._load

เมื่อ Marshal.dump และ Marshal.load ถูกเรียกใช้ เมธอดเหล่านี้เรียก marshal_dump และ marshal_load เมธอดบนอ็อบเจ็กต์ที่ส่งผ่านเป็นพารามิเตอร์ของเมธอดเหล่านี้

แต่ถ้าฉันบอกคุณว่า Marshal.dump และ Marshal.load เมธอดพยายามเรียกเมธอดอีกสองวิธีชื่อ _dump และ self._load บนวัตถุที่ส่งผ่านเป็นพารามิเตอร์?

วิธีการ _dump

ความแตกต่างระหว่าง marshal_dump และ _dump วิธีการคือ:

  • คุณต้องจัดการกลยุทธ์การทำให้เป็นอันดับที่ต่ำกว่าเมื่อใช้ _dump method — คุณต้องส่งคืนสตริงที่แสดงถึงข้อมูลเพื่อทำให้เป็นอนุกรม
  • the marshal_dump เมธอดมีความสำคัญเหนือ _dump ถ้าทั้งสองถูกกำหนดไว้

มาดูตัวอย่างต่อไปนี้กัน:

User = Struct.new(:age, :fullname, :roles) do
  def _dump level
    [age, fullname].join(':')
  end
end
 
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
 
Marshal.dump(user) # => "\x04\bIu:\tUser\x1342:Mehdi Farsi\x06:\x06EF"

ใน User#_dump เมธอด เราต้องสร้างอินสแตนซ์และส่งคืนอ็อบเจ็กต์การทำให้เป็นอนุกรม — สตริงที่แสดงถึงการทำให้เป็นอนุกรมของคุณ

ในตัวอย่างต่อไปนี้ เรากำหนด User#marshal_dump และ User#_dump เมธอดและส่งคืนสตริงเพื่อดูว่าเมธอดใดเรียกว่า

User = Struct.new(:age, :fullname, :roles) do
  def marshal_dump
    'in User#marshal_dump'
  end
 
  def _dump level
    'in User#_dump'
  end
end
 
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
 
user_dump = Marshal.dump(user) # "in User#marshal_dump"

เราจะเห็นว่าเฉพาะ User#marshal_dump ถูกเรียกแม้ว่าจะมีการกำหนดทั้งสองไว้ก็ตาม

วิธีการโหลดตัวเอง

ทีนี้มาดูที่ marshal_load และ _load วิธีการ

ความแตกต่างระหว่าง marshal_load และ _load วิธีการคือ:

  • คุณต้องจัดการกลยุทธ์ดีซีเรียลไลเซชันในระดับที่ต่ำกว่าเมื่อใช้ _load method — คุณมีหน้าที่สร้างอินสแตนซ์ของวัตถุดั้งเดิม
  • The marshal_load เมธอดรับวัตถุดีซีเรียลไลซ์เป็นอาร์กิวเมนต์เมื่อ _self.load เมธอดรับสตริงที่ต่อเนื่องกันเป็นอาร์กิวเมนต์
  • The marshal_load เมธอดเป็นเมธอดอินสแตนซ์เมื่อ self._load เป็นวิธีการเรียน

ลองมาดูตัวอย่างต่อไปนี้:

User = Struct.new(:age, :fullname, :roles) do
  def _dump level
    [age, fullname].join(':')
  end
 
  def self._load serialized_user
    user_info = serialized_user.split(':')
    new(*user_info, Array.new)
  end
end
 
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
 
user_dump = Marshal.dump(user)
user_dump # => "\x04\bIu:\tUser\x1342:Mehdi Farsi\x06:\x06EF"
 
original_user = Marshal.load(user_dump)
original_user # => #<struct User age="Mehdi Farsi", fullname=42, roles=[]>

ใน User._load วิธีการ:

  • เราทำการดีซีเรียลไลซ์สตริงที่ส่งคืนโดย User#_dump วิธีการ
  • เราสร้าง User ใหม่ โดยส่งข้อมูลดีซีเรียลไลซ์

เราเห็นได้ว่าเรามีหน้าที่ในการจัดสรรและสร้างอินสแตนซ์ของอ็อบเจ็กต์ที่ใช้สร้างผู้ใช้เดิมของเราใหม่

ดังนั้น Marshal.load ควบคู่ไปกับ marshal_load ดูแลการสร้างอินสแตนซ์ของวัตถุดั้งเดิมที่สร้างใหม่ จากนั้นจะเรียก marshal_load เมธอดที่มีวัตถุต่อเนื่องเป็นอาร์กิวเมนต์บนวัตถุที่สร้างอินสแตนซ์ใหม่

ตรงกันข้าม เรียก Marshal.load ควบคู่ไปกับ _load ให้ self._load วิธีการเรียนอยู่ในความดูแลของ:

  • ดีซีเรียลไลซ์ข้อมูลที่ส่งคืนโดย _dump วิธีการ
  • สร้างตัวอย่างวัตถุดั้งเดิมที่สร้างขึ้นใหม่

บทสรุป

ขึ้นอยู่กับความต้องการของคุณ คุณสามารถตัดสินใจใช้กลยุทธ์การทำให้เป็นอนุกรม/ดีซีเรียลไลซ์เซชั่นที่สูงขึ้นหรือต่ำลงได้ ในการดำเนินการดังกล่าว คุณสามารถใช้โมดูล Marshal ร่วมกับวิธี Marshal hook ที่เหมาะสมได้

ว้าว!