Pipeline Power

james on 2002-08-15T09:41:45

This morning coming into work, I knew I needed to hack together a quick proxy server so that I could examine a web protocol stream going back and forth across the network. Whereas previously I'd have sat down and written the whole thing out by hand, this time I decided to use OpenFrame 3 to put together a quick solution.

I had to write just a few lines of code to make it all work -- and some of that code was out of politeness rather than need. The code I wrote was simply a pipeline segment that is added to the OpenFrame pipeline. It simply takes the HTTP::Request out of the store, forwards it to the right place, and then returns the response back to the pipeline:

package Segment::Forwarder;

use strict; use warnings::register;

use LWP::UserAgent; use Pipeline::Segment; use base qw ( Pipeline::Segment );

sub init { my $self = shift; my $lwp = LWP::UserAgent->new( env_proxy => 1 ); $self->lwp( $lwp ); }

sub lwp { my $self = shift; my $lwp = shift; if (defined( $lwp )) { $self->{lwp} = $lwp; return $self; } else { return $self->{lwp}; } }

sub dispatch { my $self = shift; my $pipe = shift;

my $httpr = $pipe->store->get( 'HTTP::Request' ); return $self->lwp->request( $httpr ); }

1;


accessors

Dom2 on 2002-08-15T17:12:43

Hmmm, I've been caught out by using accessors like that before. This is a more general code issue, and it's probably fine for your small code, but what happens if you want to store undef in your accessor?

This is the reason I am leaning more and more towards Class::Accessor and similiar. I note that you'd have to override set() to return $self that way, however.

-Dom

Re:accessors

james on 2002-08-15T17:31:25

> Hmmm, I've been caught out by using accessors like
> that before. This is a more general code issue,
> and it's probably fine for your small code, but
> what happens if you want to store undef in your
> accessor?

Generally I work very hard to make sure I never need to have an undef in any given state :-)

The object in question will always be in one of two states:
  1. Usable - there is a valid LWP::UserAgent object
  2. Unusable - there is no valid LWP::UserAgent object
Cases can be made for wanting to store undef however :-)

I'm pretty big on returning $self from accessors in 'set' mode. That way you can move parameter passing away from the constructor, which makes subclassing later on much easier. If you want set things up at construction time, you can chain your method calls, and still get everything nice and concise in the real estate department without sacrificing flexibility.

Or maybe I'm a SmallTalk junkie :-)

Re:accessors

wickline on 2002-08-15T23:11:02

> what happens if you want to store undef

I usually write something like
sub foo {
    my $self = shift;
    return   @_  ? $self->{foo}=$_[0]  : $self->{foo};
}
which gives you the ability to set something to undef but doesn't let you chain method calls. If you wanted to chain method calls, you could instead write it this way
sub foo {
    my $self = shift;
    if (@_) {
        $self->{foo} = shift;
        return $self;
    } else {
        return $self->{foo};
    }
}
-matt

Now I'm just being silly...

wickline on 2002-08-16T00:28:09

Put these two subroutines somewhere
sub non_chaining_accessor {
    my $name = (caller 1)[3];
    $name = substr $name, rindex($name,':')+1;
    $#_  ? $_[0]->{$name}=$_[1]  : $_[0]->{$name}
}
sub chaining_accessor {
    my $name = (caller 1)[3];
    $name = substr $name, rindex($name,':')+1;
    if ($#_) {
        $_[0]->{$name} = $_[1];
        return $_[0];
    } else {
        return $_[0]->{$name};
    }
}
and then when you want accessors of either type, just do this:
sub foo { &non_chaining_accessor }
sub bar {     &chaining_accessor }
sub baz { &non_chaining_accessor }
sub bop {     &chaining_accessor }
and your subs will be accessors for like-named hash keys in a hash-ref-based objet. They'll be slower though (extra subroutine call and three extra function calls per invocation). I suppose you could cache the sub name to hash key lookups to gain a bit of speed in future calls, if that's worth the bother: one function call and a hash lookup instead of three function calls.

Well, it looks like I'm on the way to re-inventing Class::Accessors now. I just looked at that module, and it seems to do the same sort of thing, but much nicer. It goes another step and lets you just specify the names of the accessors, and it creates the subs and stuffs them in your namespace for you. Since it knows the names of the subs when it creates them, it can avoid that repeated callerIt's also much more configurable.

At least it was a fun excersize :)

-matt