The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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.