Rubyでのオブジェクトマーシャリング
この記事では、オブジェクトのマーシャリングについて詳しく説明します。それが何であるかを説明し、マーシャルモジュールを見てから、例を見ていきます。次に、さらに深く進んで、 _dumpを比較します。 およびself._load メソッド。行きましょう!
オブジェクトマーシャリングとは何ですか?
コードを記述しているときは、オブジェクトを保存して別のプログラムに送信するか、次のプログラム実行で再利用することをお勧めします。たとえば、オブジェクトマーシャリングはSidekiqで使用されます。 SidekiqジョブがRubyonRailsアプリケーションにエンキューされると、このジョブのシリアル化(オブジェクトにすぎない)がRedisに挿入されます。 Sidekiqプロセスは、このJSONを逆シリアル化し、JSONから元のジョブを再構成できます。
コンピュータプログラミングでは、オブジェクトのシリアル化と逆シリアル化のこのプロセスは、一般にオブジェクトマーシャリングと呼ばれるものです。 。それでは、Rubyがオブジェクトマーシャリングを処理するためにネイティブに提供するものを見てみましょう。
マーシャルモジュール
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より 文字列。これは、別のオブジェクトですが、同じデータが含まれていることを意味します。
かなりクール!しかし、元帥はどうですか モジュールは文字列を再構築できますか?また、シリアル化および逆シリアル化する属性を制御したい場合はどうすればよいですか?
オブジェクトマーシャリングの具体例
これらの質問に答えるために、 Userという名前のカスタム構造体にマーシャリング戦略を実装しましょう。 。
User = Struct.new(:fullname, :age, :roles)
user = User.new('Mehdi Farsi', 42, [:admin, :operator])
ユーザー structは3つの属性を定義します: fullname 、 age 、および roles 。この例では、次の基準に一致する場合にのみシリアル化するビジネスルールがあります。
フルネームコード> 64文字未満が含まれていますロール配列に: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)を呼び出すと、メソッドが呼び出されます。 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メソッドであることがわかります。 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という名前の他の2つのメソッドを呼び出そうとします およびself._load パラメータとして渡されたオブジェクトに?
_dumpメソッド
marshal_dumpの違い および_dump メソッドは次のとおりです:
-
_dumpを使用する場合は、シリアル化戦略を下位レベルで処理する必要があります メソッド—シリアル化するデータを表す文字列を返す必要があります -
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のみが表示されます 両方とも定義されていても呼び出されます。
self._loadメソッド
それでは、 marshal_loadを見てみましょう。 および_load メソッド。
marshal_loadの違い および_load メソッドは次のとおりです:
-
_loadを使用する場合は、より低いレベルで逆シリアル化戦略を処理する必要があります メソッド—元のオブジェクトのインスタンス化を担当します。 -
marshal_load_self.loadの場合、メソッドは逆シリアル化されたオブジェクトを引数として取ります メソッドは、シリアル化された文字列を引数として取ります。 -
marshal_loadmethodは、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によって返された文字列を逆シリアル化します メソッド - 新しい
ユーザーをインスタンス化します デシリアライズされた情報を渡すことによって
元のユーザーの再構成に使用されたオブジェクトの割り当てとインスタンス化を担当していることがわかります。
したがって、 Marshal.load marshal_loadに結合 再構成された元のオブジェクトのインスタンス化を処理します。次に、 marshal_loadを呼び出します 新しくインスタンス化されたオブジェクトの引数として渡されたシリアル化されたオブジェクトを使用するメソッド。
それどころか、 Marshal.loadの呼び出し _loadに結合 self._load クラスメソッドが担当します:
-
_dumpによって返されたデータの逆シリアル化 メソッド - 再構成された元のオブジェクトのインスタンス化
結論
ニーズに応じて、より高いまたはより低いシリアル化/逆シリアル化戦略を実装することを決定できます。これを行うには、適切なマーシャルフックメソッドに結合されたマーシャルモジュールを使用できます。
Voilà!
-
Ruby2.6の9つの新機能
Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,
-
Ruby Freezeメソッド–オブジェクトの可変性を理解する
オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー