NAME
Ref::Store::Walkthrough - Walkthrough on how to rewrite a module using Ref::Store
This document is to supplement the synopsis in the main module docuemntation.
We will try to dissect and pseudo-refactor the code in POE::Component::Client::HTTP (refered to as poco-http) to demonstrate the usefulness of this module.
We will assume that there is a gloabl object, <$Table> which may presumably be stored on the heap
We have a bunch of key types, so let's register them.
my @KEY_TYPES;
BEGIN {
@KEY_TYPES = map 'KT_'.$_, (
"EXT_REQ", #HTTP::Request object
"POE_REQ", #POE::Component::Client::HTTP::Request object
"POE_REQID", #ID of the POE request
"POE_WID", #POE::Wheel ID, needed for events.
);
foreach my $kt (@KEY_TYPES) {
no strict 'refs';
*{$kt} = sub () { $kt }
}
}
#....
#Assume a table has been created by now
$Table->register_kt(@_) foreach (@KEY_TYPES);
The poco-http API takes a request object and optionally accepts a tag, by which the user can easily identify the response received. The prime internal identifier used by POE is an internal Request object (POE::Component::Client::HTTP::Request
), identified by its refaddr:
my $request = $heap->{factory}->create_request(
$http_request, $response_event, $tag, $progress_event,
$proxy_override, $sender
);
$heap->{request}->{$request->ID} = $request;
$heap->{ext_request_to_int_id}->{$http_request} = $request->ID;
Instead of the last two lines, we do:
$Table->store_kt($request->ID, KT_POE_REQID, $request, StongValue => 1);
#Because this is our primary reference.
$Table->store_kt($http_request, $request);
Later on, in the same function, we have this code:
if ($@) {
delete $heap->{request}->{$request->ID};
delete $heap->{ext_request_to_int_id}->{$http_request};
# we can reach here for things like host being invalid.
$request->error(400, $@);
}
Which can be refactored to:
$Table->purge($request);
Which will clean up everything associated with $request.
At this point, poco-http has submitted a request to its connection manager (POE::Component::Client::KeepAlive), and is now awaiting a response. Here is the code which handles it, with ommisions not pertinent to the description of the Ref::Store module.
sub _poco_weeble_connect_done {
my ($heap, $response) = @_[HEAP, ARG0];
my $connection = $response->{'connection'};
my $request_id = $response->{'context'};
if (defined $connection) {
DEBUG and warn "CON: request $request_id connected ok...";
#my $request = $heap->{request}->{$request_id};
Nothing revolutionary here, replace with:
my $request = $Table->fetch_kt(KT_POE_REQID, $request_id);
unless (defined $request) {
DEBUG and warn "CON: ignoring connection for canceled request";
return;
}
my $block_size = $heap->{factory}->block_size;
# get wheel from the connection
my $new_wheel = $connection->start(
Driver => POE::Driver::SysRW->new(BlockSize => $block_size),
InputFilter => POE::Filter::HTTPHead->new(),
OutputFilter => POE::Filter::Stream->new(),
InputEvent => 'got_socket_input',
FlushedEvent => 'got_socket_flush',
ErrorEvent => 'got_socket_error',
);
DEBUG and warn "CON: request $request_id uses wheel ", $new_wheel->ID;
# Add the new wheel ID to the lookup table.
#$heap->{wheel_to_request}->{ $new_wheel->ID() } = $request_id;
And instead of this construct, we use:
$Table->store_a($new_wheel->ID(), KT_POE_WID, $request);
We skip a bunch of SSL initialization code, since it does not seem to use any type of lookup
else {
DEBUG and warn(
"CON: Error connecting for request $request_id --- ", $_[SENDER]->ID
);
my ($operation, $errnum, $errstr) = (
$response->{function},
$response->{error_num} || '??',
$response->{error_str}
);
DEBUG and warn(
"CON: request $request_id encountered $operation error " .
"$errnum: $errstr"
);
DEBUG and warn "I/O: removing request $request_id";
#my $request = delete $heap->{request}->{$request_id};
#$request->remove_timeout();
#delete $heap->{ext_request_to_int_id}->{$request->[REQ_HTTP_REQUEST]};
Is replaced with:
$Table->purge($request);
$request->remove_timeout();
Here is the timeout function:
sub _poco_weeble_timeout {
my ($kernel, $heap, $request_id) = @_[KERNEL, HEAP, ARG0];
#my $request = delete $heap->{request}->{$request_id};
Instead, we delete ALL lookup data associated with the key by doing this:
my $request = $Table->purgeby_kt($request_id, KT_POE_REQID);
...
We don't need this line
delete $heap->{ext_request_to_int_id}->{$request->[REQ_HTTP_REQUEST]};
...
Nor do we need this
delete $heap->{wheel_to_request}->{$wheel_id};
...
etc. etc. The rest of the POE code is more or less the same.
Look here for some other code which could use an even better helping of this module.