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_load
methodは、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になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー