Computer >> Máy Tính >  >> Lập trình >> Ruby

Object Marshalling trong Ruby

Trong bài viết này, chúng ta sẽ đi sâu vào việc kiểm tra đối tượng. Chúng tôi sẽ giải thích nó là gì, hãy nhìn vào mô-đun Marshall, và sau đó đi qua một ví dụ. Sau đó, chúng tôi sẽ đi sâu hơn một bước và so sánh _dumpself._load các phương pháp. Đi thôi!

Object Marshalling là gì?

Khi bạn đang viết mã, bạn có thể muốn lưu một đối tượng và truyền nó sang một chương trình khác hoặc sử dụng lại nó trong lần thực thi chương trình tiếp theo của bạn. Ví dụ, đối tượng sắp xếp được sử dụng trong Sidekiq; khi một công việc Sidekiq được xếp hàng trong ứng dụng Ruby on Rails, thì một chuỗi tuần tự của công việc này - không khác gì một đối tượng - được chèn vào Redis. Sau đó, quy trình Sidekiq có thể giải mã JSON này và tạo lại công việc ban đầu từ JSON.

Trong lập trình máy tính, quá trình tuần tự hóa và giải mã hóa đối tượng này là những gì chúng ta thường gọi là đối tượng sắp xếp . Bây giờ, hãy xem xét những gì Ruby cung cấp ban đầu để xử lý Object Marshalling.

Mô-đun Thống chế

Vì Ruby là một ngôn ngữ lập trình hướng đối tượng hoàn toàn, nó cung cấp một cách để tuần tự hóa và lưu trữ các đối tượng bằng cách sử dụng Marshall mô-đun trong thư viện tiêu chuẩn của nó. Nó cho phép bạn tuần tự hóa một đối tượng thành một luồng byte có thể được lưu trữ và giải mã hóa trong một quy trình Ruby khác.

Vì vậy, hãy tuần tự hóa một chuỗi và xem xét kỹ hơn đối tượng được tuần tự hóa.

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

Sau đó, chúng tôi gọi Marshal.dump phương thức mô-đun để tuần tự hóa chuỗi của chúng tôi. Chúng tôi lưu trữ giá trị trả về — chứa chuỗi được tuần tự hóa của chúng tôi — trong serialized_string Biến đổi. Chuỗi này có thể được lưu trữ trong một tệp và tệp có thể được sử dụng lại để tạo lại đối tượng ban đầu trong một quy trình khác. Sau đó, chúng tôi gọi Marshal.load phương thức để tạo lại đối tượng ban đầu từ luồng byte.

Chúng ta có thể thấy rằng chuỗi mới được hoàn nguyên này có một object_id khác hơn hello_world chuỗi, có nghĩa là một đối tượng khác, nhưng chứa cùng một dữ liệu.

Tuyệt đấy! Nhưng thế nào là Marshal mô-đun có thể tái tạo lại chuỗi không? Và, điều gì sẽ xảy ra nếu tôi muốn có quyền kiểm soát các thuộc tính để tuần tự hóa và giải mã hóa?

Một ví dụ cụ thể về Object Marshalling

Để trả lời những câu hỏi này, hãy triển khai chiến lược sắp xếp trên một cấu trúc tùy chỉnh có tên là User .

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

Người dùng User struct xác định 3 thuộc tính:fullname , ageroles . Đối với ví dụ này, chúng tôi có một quy tắc kinh doanh trong đó chúng tôi chỉ tuần tự hóa khi nó phù hợp với các tiêu chí sau:

  • fullname chứa ít hơn 64 ký tự
  • roles mảng không chứa :admin vai trò

Để làm như vậy, chúng tôi có thể xác định một User#marshal_dump phương pháp để thực hiện chiến lược tuần tự hóa tùy chỉnh của chúng tôi. Phương thức này sẽ được gọi khi chúng ta gọi Marshal.dump phương thức với một phiên bản của User struct dưới dạng tham số. Hãy xác định phương pháp này:

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/"
 

Trong ví dụ trên, chúng ta có thể thấy rằng User#marshal_dump của chúng tôi phương thức được gọi khi chúng ta gọi Marshal.dump (người dùng). user_dump biến chứa chuỗi là chuỗi tuần tự hóa của User của chúng tôi ví dụ.

Bây giờ chúng ta đã có bãi chứa của mình, hãy giải mã hóa nó để khôi phục lại người dùng của chúng ta. Để làm như vậy, chúng tôi xác định một User#marshal_load phương pháp chịu trách nhiệm triển khai chiến lược deserialization của User đổ.

Vì vậy, hãy xác định phương pháp này.

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=[]>

Trong ví dụ trên, chúng ta có thể thấy rằng phương pháp User#marshal_load method của chúng tôi được gọi khi chúng ta gọi Marshal.load(user_dump) . original_user biến chứa một cấu trúc là sự tái tạo của phiên bản người dùng của chúng tôi.

Lưu ý rằng original_user.roles không tương tự với user.roles mảng kể từ trong quá trình tuần tự hóa, user.roles bao gồm :admin vai diễn. Vì vậy, user.roles chưa được tuần tự hóa thành user_dump biến.

Phương thức _dump và self._load

Khi Marshal.dumpMarshal.load được gọi, các phương thức này gọi marshal_dumpmarshal_load các phương thức trên đối tượng được truyền dưới dạng tham số của các phương thức này.

Nhưng, điều gì sẽ xảy ra nếu tôi nói với bạn rằng Marshal.dumpMarshal.load các phương thức cố gắng gọi hai phương thức khác có tên là _dumpself._load trên đối tượng được truyền dưới dạng tham số?

Phương pháp _dump

Sự khác biệt giữa marshal_dump_dump phương pháp là:

  • bạn cần xử lý chiến lược tuần tự hóa ở cấp thấp hơn khi sử dụng _dump - bạn cần trả về một chuỗi đại diện cho dữ liệu để tuần tự hóa
  • marshal_dump phương thức được ưu tiên hơn _dump nếu cả hai đều được xác định

Hãy xem ví dụ sau:

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"

Trong User#_dump , chúng tôi phải khởi tạo và trả về đối tượng tuần tự hóa - chuỗi đại diện cho quá trình tuần tự hóa của bạn.

Trong ví dụ sau, chúng tôi xác định User#marshal_dumpUser#_dump và trả về một chuỗi để xem phương thức nào được gọi là

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"

Chúng tôi có thể thấy rằng chỉ có User#marshal_dump được gọi ngay cả khi cả hai đều được xác định.

Phương thức self._load

Bây giờ, hãy xem marshal_load_load phương pháp.

Sự khác biệt giữa marshal_load_load phương pháp là:

  • Bạn cần xử lý chiến lược deserialization ở cấp độ thấp hơn khi sử dụng _load - Bạn chịu trách nhiệm khởi tạo đối tượng gốc.
  • marshal_load phương thức lấy một đối tượng deserialized làm đối số khi _self.load phương thức lấy chuỗi được tuần tự hóa làm đối số.
  • marshal_load phương thức là một phương thức phiên bản khi self._load là một phương thức lớp.

Hãy xem ví dụ sau:

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=[]>

Trong User._load phương pháp:

  • chúng tôi giải mã chuỗi được trả về bởi User#_dump phương pháp
  • chúng tôi khởi tạo một User mới bằng cách chuyển thông tin được giải thích trên không

Chúng tôi có thể thấy rằng chúng tôi chịu trách nhiệm phân bổ và khởi tạo đối tượng được sử dụng để tạo lại người dùng ban đầu của chúng tôi.

Vì vậy, Marshal.load được kết hợp với marshal_load đảm nhận việc khởi tạo đối tượng ban đầu được hoàn nguyên. Sau đó, nó gọi marshal_load phương thức với đối tượng được tuần tự hóa được truyền dưới dạng đối số trên đối tượng mới được khởi tạo.

Ngược lại, một cuộc gọi tới Marshal.load được kết hợp với _load cho phép self._load phương pháp lớp do:

phụ trách
  • giải mã dữ liệu được trả về bởi _dump phương pháp
  • khởi tạo đối tượng ban đầu đã hoàn nguyên

Kết luận

Tùy thuộc vào nhu cầu của mình, bạn có thể quyết định triển khai chiến lược tuần tự hóa / giải mã hóa cao hơn hoặc thấp hơn. Để làm như vậy, bạn có thể sử dụng mô-đun Marshal kết hợp với các phương pháp móc câu Marshal thích hợp.

Voilà!