4.3 Handling Unknown Objects with DRbUnknown
In the previous section, we learned that dRuby passes by value if an object can be Marshal.dump
ed. However, what happens if the receiver of the object doesn’t know about the class definition of the object? In this section, we’ll learn about handling unknown objects. We’ll use the same foo.rb
sample. Let’s start terminal 1 as a server and terminal 2 as a client and then pass the Foo
object from the client.
# [Terminal 1] |
|
% irb --prompt simple -r drb/drb |
|
>> front = {} |
|
>> DRb.start_service('druby://localhost:12345', front) |
# [Terminal 2] |
|
% irb --prompt simple -r drb/drb -r ./foo.rb |
|
>> DRb.start_service |
|
>> there = DRbObject.new_with_uri('druby://localhost:12345') |
|
>> foo = Foo.new('Foo1') |
|
>> there[:foo] = foo |
|
=> #<Foo:0x ..... "> |
irb at terminal 1 doesn’t know the class definition of Foo
. We just set an instance of Foo
on front[:foo]
. Let’s see what kind of object is passed to terminal 1.
# [Terminal 1] |
|
>> front[:foo] |
|
=> #<DRb::DRbUnknown:0x.... @buf="?004?00.....", @name="Foo"> |
Hmmm, front[:foo]
isn’t an instance of Foo
. It says it’s an instance of DRbUnknown
.
When an unknown object is loaded via Marshal.load
and an exception is raised, dRuby captures it and loads DRbUnknown
instead. DRbUnknown
knows two things. One is the string buffer that failed to be loaded, and the other is the name of the class or module.
You can use the following methods to find out information about each:
-
DRbUnknown#buf
-
Buffer of the serialized string that
Marshal.load
failed to load -
DRbUnknown#name
-
Name of the unknown class or module names
-
DRBUnknown#reload
-
Retries
Marshal.load
When dRuby receives an unknown class, it creates the DRbUnknown
object automatically. You can’t call the method against DRbUnknown
, but you can transfer DRbUnknown
.
Let’s pass the DRbUnknown
object from terminal 1 to terminal 2.
# [Terminal 2] |
|
>> bar = there[:foo] |
|
=> #<Foo:0x .... @name="Foo1"> |
|
>> foo.__id__ == bar.__id__ |
|
=> false |
Terminal 2 received a new Foo
instance, instead of DRbUnkown
. (We compare by looking at the value of __id__
.)
When dRuby does Marshal.load
to the DRbUnknown
object, it tries to Marshal.load
against the buffer, and it returns the real object instead of DRbUnknown
when successful.
Let’s transfer the object using the third terminal.
# [Terminal 3] |
|
% irb --prompt simple -r drb/drb |
|
>> DRb.start_service |
|
>> there = DRbObject.new_with_uri('druby://localhost:12345') |
|
>> unknown = there[:foo] |
|
=> #<DRb::DRbUnknown:0x.... @buf="?004?00.....", @name="Foo"> |
|
>> unknown.name |
|
=> "Foo" |
Terminal 3 does receive DRbUnknown
because it doesn’t know about the Foo
class definition. The result of unknown.name
is Foo
. Now require foo.rb
, and try it again.
# [Terminal 3] |
|
>> unknown.reload |
|
=> #<DRb::DRbUnknown:0x.... @buf="?004?00.....", @name="Foo"> |
|
>> require 'foo' |
|
>> unknown.reload |
|
=> #<Foo:0x .... @name="Foo1"> |
When you first did unknown.reload
, you received DRbUnknown
. When you tried it again after requiring the class, then it returned Foo
. dRuby tried reloading the object.
Let’s send the object again from terminal 1. This time, it should receive Foo
, instead of DRbUnknown
.
# [Terminal 3] |
|
>> foo = there[:foo] |
|
=> #<Foo:0x .... @name="Foo1"> |
Good! Since it now knows the definition of Foo
, it did receive Foo
.
By using DRbUnknown
, you can keep unknown objects, even though you can’t call their methods.
Why do we need such functionalities?
Consider a Queue
service. The Queue
is responsible for transferring objects from one process to another. If DRbUnknown
did not exist, then the proxying Queue
service would require the class definitions of every class that may go through the Queue
. DRbUnknown
becomes very handy in such cases.