Perl doesn’t enforce access to modules’ namespaces. This would
usually be considered a bad thing, but sometimes it allows us to work
around problems in modules without changing their code. Here’s a perfect
example:
I’ve been writing a script to talk to an XML-RPC endpoint, using
Frontier::Client but for
one of the requests, the script throws the following error:
wanted a data type, got `ex:i8'
Turning on debugging showed the response type was indeed ex:i8, which
isn’t one of the types that Frontier::Client supports.
<?xml version="1.0" encoding="UTF-8"?> <methodResponse xmlns: <params> <param> <value> <ex:i8>161</ex:i8> </value> </param> </params> </methodResponse>
Searching through the code shows Frontier::Client is a wrapper around
Frontier::RPC2 and the error message happens at the following
section:
} elsif ($scalars{$tag}) { $expat->{'rpc_text'} = ""; push @{ $expat->{'rpc_state'} }, 'cdata'; } else { Frontier::RPC2::die($expat, "wanted a data type, got `$tag'n"); }
So we can see that it’s looking up the tag into a hash called
%scalars to see if the type is a scalar type, otherwise throws
the error we saw. Looking at the top, we can see this hash:
%scalars = ( 'base64' => 1, 'boolean' => 1, 'dateTime.iso8601' => 1, 'double' => 1, 'int' => 1, 'i4' => 1, 'string' => 1, );
So, if we could add ex:i8 to this scalar, we could fix the
problem. We could fix the module, but that would require every user of
the script to patch their copy of the module. The alternative is to
inject something into that hash across module boundaries, which we can
do by just refering to the hash by it’s complete name including the
package name. We can use:
$Frontier::RPC2::scalars{'ex:i8'} = 1;
Now when we run the script, everything works. It’s not nice and it’s
dependent on Frontier::RPC2 not changing. but it allows us to get on
with our script.
on said:
I believe this technique is known as monkey patching: