[poe-commits] SF.net SVN: poe: [2336] trunk/poe/lib/POE
Brought to you by:
rcaputo
From: <rc...@us...> - 2008-05-26 22:00:55
|
Revision: 2336 http://poe.svn.sourceforge.net/poe/?rev=2336&view=rev Author: rcaputo Date: 2008-05-26 15:01:01 -0700 (Mon, 26 May 2008) Log Message: ----------- Redocument. Modified Paths: -------------- trunk/poe/lib/POE/Filter/Block.pm trunk/poe/lib/POE/Filter/Grep.pm trunk/poe/lib/POE/Filter/Map.pm trunk/poe/lib/POE/Filter/Stackable.pm trunk/poe/lib/POE/Filter.pm Modified: trunk/poe/lib/POE/Filter/Block.pm =================================================================== --- trunk/poe/lib/POE/Filter/Block.pm 2008-05-26 18:39:15 UTC (rev 2335) +++ trunk/poe/lib/POE/Filter/Block.pm 2008-05-26 22:01:01 UTC (rev 2336) @@ -170,50 +170,66 @@ [ $self->[FRAMING_BUFFER] ]; } -############################################################################### 1; __END__ =head1 NAME -POE::Filter::Block - filter between streams and blocks +POE::Filter::Block - translate data between streams and blocks =head1 SYNOPSIS - $filter = POE::Filter::Block->new( BlockSize => 1024 ); - $filter = POE::Filter::Block->new( - LengthCodec => [ \&encoder, \&decoder ] - ); - $arrayref_of_blocks = - $filter->get($arrayref_of_raw_chunks_from_driver); - $arrayref_of_streamable_chunks_for_driver = - $filter->put($arrayref_of_blocks); - $arrayref_of_leftovers = - $filter->get_pending(); + #!perl + use warnings; + use strict; + use POE::Filter::Block; + + my $filter = POE::Filter::Block->new( BlockSize => 8 ); + + # Prints three lines: abcdefgh, ijklmnop, qrstuvwx. + # Bytes "y" and "z" remain in the buffer and await completion of the + # next 8-byte block. + + $filter->get_one_start([ "abcdefghijklmnopqrstuvwxyz" ]); + while (1) { + my $block = $filter->get_one(); + last unless @$block; + print $block->[0], "\n"; + } + + # Print one line: yz123456 + + $filter->get_one_start([ "123456" ]); + while (1) { + my $block = $filter->get_one(); + last unless @$block; + print $block->[0], "\n"; + } + =head1 DESCRIPTION -The Block filter translates data between serial streams and blocks. -It can handle two kinds of block: fixed-length and length-prepended. +POE::Filter::Block translates data between serial streams and blocks. +It can handle fixed-length and length-prepended blocks, and it may be +extended to handle other block types. Fixed-length blocks are used when Block's constructor is called with a BlockSize value. Otherwise the Block filter uses length-prepended blocks. -Users who specify block sizes less than one deserve to be soundly -spanked. +Users who specify block sizes less than one deserve what they get. -In variable-length mode, a LengthCodec parameter is valid. The -LengthCodec should be a list reference of two functions: The length -encoder, and the length decoder: +In variable-length mode, a LengthCodec parameter may be specified. +The LengthCodec value should be a reference to a list of two +functions: the length encoder, and the length decoder: LengthCodec => [ \&encoder, \&decoder ] The encoder takes a reference to a buffer and prepends the buffer's length to it. The default encoder prepends the ASCII representation -of the buffer's length. The length is separated from the buffer by an -ASCII NUL ("\0") character. +of the buffer's length and a chr(0) byte to separate the length from +the actual data: sub _default_encoder { my $stuff = shift; @@ -221,9 +237,9 @@ return; } -Sensibly enough, the corresponding decoder removes the prepended -length and separator, returning its numeric value. It returns nothing -if no length can be determined. +The corresponding decoder returns the block length after removing it +and the separator from the buffer. It returns nothing if no length +can be determined. sub _default_decoder { my $stuff = shift; @@ -239,11 +255,12 @@ =head1 PUBLIC FILTER METHODS -Please see POE::Filter. +POE::Filter::Block has no additional public methods. =head1 SEE ALSO -POE::Filter. +Please see L<POE::Filter> for documentation regarding the base +interface. The SEE ALSO section in L<POE> contains a table of contents covering the entire POE distribution. @@ -262,4 +279,3 @@ =cut # rocco // vim: ts=2 sw=2 expandtab -# TODO - Redocument. Modified: trunk/poe/lib/POE/Filter/Grep.pm =================================================================== --- trunk/poe/lib/POE/Filter/Grep.pm 2008-05-26 18:39:15 UTC (rev 2335) +++ trunk/poe/lib/POE/Filter/Grep.pm 2008-05-26 22:01:01 UTC (rev 2336) @@ -104,15 +104,13 @@ } } -############################################################################### - 1; __END__ =head1 NAME -POE::Filter::Grep - POE Data Grepping Filter +POE::Filter::Grep - select or remove items based on simple rules =head1 SYNOPSIS Modified: trunk/poe/lib/POE/Filter/Map.pm =================================================================== --- trunk/poe/lib/POE/Filter/Map.pm 2008-05-26 18:39:15 UTC (rev 2335) +++ trunk/poe/lib/POE/Filter/Map.pm 2008-05-26 22:01:01 UTC (rev 2336) @@ -21,7 +21,7 @@ my $type = shift; croak "$type must be given an even number of parameters" if @_ & 1; my %params = @_; - + croak "$type requires a Code or both Get and Put parameters" unless ( defined($params{Code}) or (defined($params{Get}) and defined($params{Put})) Modified: trunk/poe/lib/POE/Filter/Stackable.pm =================================================================== --- trunk/poe/lib/POE/Filter/Stackable.pm 2008-05-26 18:39:15 UTC (rev 2335) +++ trunk/poe/lib/POE/Filter/Stackable.pm 2008-05-26 22:01:01 UTC (rev 2336) @@ -121,7 +121,7 @@ #------------------------------------------------------------------------------ sub filter_types { - map { ((ref $_) =~ /::(\w+)$/)[0] } @{$_[0]->[FILTERS]}; + map { ref($_) } @{$_[0]->[FILTERS]}; } #------------------------------------------------------------------------------ @@ -180,101 +180,196 @@ $filter; } -############################################################################### - 1; __END__ =head1 NAME -POE::Filter::Stackable - POE Multiple Filter Abstraction +POE::Filter::Stackable - combine multiple POE::Filter objects =head1 SYNOPSIS - $filter = new POE::Filter::Stackable(Filters => [ $filter1, $filter2 ]); - $filter = new POE::Filter::Stackable; - $filter->push($filter1, $filter2); - $filter2 = $filter->pop; - $filter1 = $filter->shift; - $filter->unshift($filter1, $filter2); - $arrayref_for_driver = $filter->put($arrayref_of_data); - $arrayref_for_driver = $filter->put($single_data_element); - $arrayref_of_data = $filter->get($arrayref_of_raw_data); - $arrayref_of_leftovers = $filter->get_pending; - @filter_type_names = $filter->filter_types; - @filter_objects = $filter->filters; + #!perl + use POE qw( + Wheel::FollowTail + Filter::Line Filter::Grep Filter::Stackable + ); + + POE::Session->create( + inline_states => { + _start => sub { + my $parse_input_as_lines = POE::Filter::Line->new(); + + my $select_sudo_log_lines = POE::Filter::Grep->new( + Put => sub { 1 }, + Get => sub { + my $input = shift; + return $input =~ /sudo\[\d+\]/i; + }, + ); + + my $filter_stack = POE::Filter::Stackable->new( + Filters => [ + $parse_input_as_lines, # closest to handle + $select_sudo_log_lines, # closest to wheel + ] + ); + + $_[HEAP]{tailor} = POE::Wheel::FollowTail->new( + Filename => "/var/log/system.log", + InputEvent => "got_log_line", + Filter => $filter_stack, + ); + }, + got_log_line => sub { + print "Log: $_[ARG0]\n"; + } + } + ); + + POE::Kernel->run(); + exit; + =head1 DESCRIPTION -The Stackable filter allows the use of multiple filters within a -single wheel. Internally, filters are stored in an array, with array -index 0 being "near" to the wheel's handle and therefore being the -first filter passed through using "get" and the last filter passed -through in "put". All POE::Filter public methods are implemented as -though data were being passed through a single filter; other program -components do not need to know there are multiple filters. +POE::Filter::Stackable combines multiple filters together in such a +way that they appear to be a single filter. All the usual POE::Filter +methods work, but data is secretly passed through the stacked filters +before it is returned. POE::Wheel objects and stand-alone programs +need no modifications to work with a filter stack. +In the L</SYNOPSIS>, POE::Filter::Line and POE::Filter::Grep are +combined into one filter that only returns a particular kind of line. +This can be more efficient than filtering lines in application space, +as fewer events may need to be dispatched and handled. + +Internally, filters are stored in an array. + +Data added by get_one_start() will flow through the filter array in +increasing index order. Filter #0 will have first crack at it, +followed by filter #1 and so. The get_one() call will return an item +after it has passed through the last filter. + +put() passes data through the filters in descending index order. Data +will go through the filter with the highest index first, and put() +will return the results after data has passed through filter #0. + =head1 PUBLIC FILTER METHODS -=over 4 +=head2 new -=item new +By default, new() creates an empty filter stack that behaves like +POE::Filter::Stream. It may be given optional parameters to +initialize the stack with an array of filters. -The new() method creates the Stackable filter. It accepts an optional -parameter "Filters" that specifies an arrayref of initial filters. If -no filters are given, Stackable will pass data through unchanged; this -is true if there are no filters present at any time. + my $sudo_lines = POE::Filter::Stackable->new( + Filters => [ + POE::Filter::Line->new(), + POE::Filter::Grep->new( + Put => sub { 1 }, # put all items + Get => sub { shift() =~ /sudo\[\d+\]/i }, + ), + ] + ); -=item pop -=item shift -=item push -=item unshift +=head2 pop -POE::Filter::Stackable::pop() -POE::Filter::Stackable::shift() -POE::Filter::Stackable::push($filter1, $filter2, ...) -POE::Filter::Stackable::unshift($filter1, $filter2...) +Behaves like Perl's built-in pop() for the filter stack. The +highest-indexed filter is removed from the stack and returned. Any +data remaining in the filter's input buffer is lost, but an +application may always call L<POE::Filter/get_pending> on the returned +filter. -These methods all function identically to the perl builtin functions -of the same name. push() and unshift() will return the new number of -filters inside the Stackable filter. + my $last_filter = $stackable->pop(); + my $last_buffer = $last_filter->get_pending(); -=item filter_types +=head2 shift -The filter_types() method returns a list of types for the filters -inside the Stackable filter, in order from near to far; for example, -qw(Block HTTPD). +Behaves like Perl's built-in shift() for the filter stack. The 0th +filter is removed from the stack and returned. Any data remaining in +the filter's input buffer is passed to the new head of the stack, or +it is lost if the stack becomes empty. An application may also call +L<POE::Filter/get_pending> on the returned filter to examine the +filter's input buffer. -=item filters + my $first_filter = $stackable->shift(); + my $first_buffer = $first_filter->get_pending(); -The filters() method returns a list of the objects inside the -Stackable filter, in order from near to far. +=head2 push FILTER[, FILTER] -=item * +push() adds one or more new FILTERs to the end of the stack. The +newly pushed FILTERs will process input last, and they will handle +output first. -See POE::Filter. + # Reverse data read through the stack. + # rot13 encode data sent through the stack. + $stackable->push( + POE::Filter::Map->( + Get => sub { return scalar reverse shift() }, + Put => sub { local $_ = shift(); tr[a-zA-Z][n-za-mN-ZA-M]; $_ }, + ) + ); -=back +=head2 unshift FILTER[, FILTER] +unshift() adds one or more new FILTERs to the beginning of the stack. +The newly unshifted FILTERs will process input first, and they will +handle output last. + +=head2 filters + +filters() returns a list of the filters inside the Stackable filter, +in the stack's native order. + +Calling C<<$filter_stack->filters()>> in the L</SYNOPSIS> would return +a list of two filter objects: + + POE::Filter::Line=ARRAY(0x8b5ee0) + POE::Filter::Grep=ARRAY(0x8b5f7c) + +=head2 filter_types + +filter_types() returns a list of class names for each filter in the +stack, in the stack's native order. + +Calling C<<$filter_stack->filter_types()>> in the L</SYNOPSIS> would +return a list of two class names: + + POE::FIlter::Line + POE::Filter::Grep + +It could easily be replaced by: + + my @filter_types = map { ref } $filter_stack->filters; + =head1 SEE ALSO -POE::Filter; POE::Filter::HTTPD; POE::Filter::Reference; -POE::Filter::Line; POE::Filter::Block; POE::Filter::Stream +L<POE::Filter> for more information about filters in general. +Specific filters, amongst which are: +L<POE::Filter::Block>, +L<POE::Filter::Grep>, +L<POE::Filter::HTTPD>, +L<POE::Filter::Line>, +L<POE::Filter::Map>, +L<POE::Filter::RecordBlock>, +L<POE::Filter::Reference>, +L<POE::Filter::Stream> + =head1 BUGS -Undoubtedly. None currently known. +None currently known. =head1 AUTHORS & COPYRIGHTS -The Stackable filter was contributed by Dieter Pearcey. Rocco Caputo -is sure to have had his hands in it. +The Stackable filter was contributed by Dieter Pearcey. Documentation +provided by Rocco Caputo. -Please see the POE manpage for more information about authors and +Please see the L<POE> manpage for more information about authors and contributors. =cut # rocco // vim: ts=2 sw=2 expandtab -# TODO - Redocument. Modified: trunk/poe/lib/POE/Filter.pm =================================================================== --- trunk/poe/lib/POE/Filter.pm 2008-05-26 18:39:15 UTC (rev 2335) +++ trunk/poe/lib/POE/Filter.pm 2008-05-26 22:01:01 UTC (rev 2336) @@ -44,7 +44,6 @@ return $nself; } -#------------------------------------------------------------------------------ 1; __END__ @@ -56,30 +55,42 @@ =head1 SYNOPSIS To use with POE::Wheel classes, pass a POE::Filter object to one of -the /.*Filter$/ constructor parameters. The following is not a fully -functional program: +the "...Filter" constructor parameters: -TODO - Test + #!perl - # Throw a "got_line" event for every line arriving on $socket. - $_[HEAP]{readwrite} = POE::Wheel::ReadWrite->new( - Handle => $socket, - Filter => POE::Filter::Line->new(), - InputEvent => "got_line", + use POE qw(Filter::Line Wheel::FollowTail); + + POE::Session->create( + inline_states => { + _start => sub { + $_[HEAP]{tailor} = POE::Wheel::FollowTail->new( + Filename => "/var/log/system.log", + InputEvent => "got_log_line", + Filter => POE::Filter::Line->new(), + ); + }, + got_log_line => sub { + print "Log: $_[ARG0]\n"; + } + } ); + POE::Kernel->run(); + exit; + Standalone use without POE: -TODO - Test - #!perl use warnings; use strict; + use POE::Filter::Line; my $filter = POE::Filter::Line->new( Literal => "\n" ); # Prints three lines: one, two three. + $filter->get_one_start(["one\ntwo\nthr", "ee\nfour"]); while (1) { my $line = $filter->get_one(); @@ -88,6 +99,7 @@ } # Prints two lines: four, five. + $filter->get_one_start(["\nfive\n"]); while (1) { my $line = $filter->get_one(); @@ -97,26 +109,23 @@ =head1 DESCRIPTION --><- AM HERE - POE::Filter objects plug into the wheels and define how the data will be serialized for writing and parsed after reading. POE::Wheel objects are responsible for moving data, and POE::Filter objects define how the data should look. -POE::Filter objects are simple by design. They do not use -higher-level POE features, so they are limited to serialization and -parsing. This may complicate the implementation of certain protocols -(such as HTTP 1.x), but it allows filters to be used in stand-alone -programs. +POE::Filter objects are simple by design. They do not use POE +internally, so they are limited to serialization and parsing. This +may complicate implementation of certain protocols (like HTTP 1.x), +but it allows filters to be used in stand-alone programs. Stand-alone use is very important. It allows application developers to create lightweight blocking libraries that may be used as simple clients for POE servers. POE::Component::IKC::ClientLite is a notable -example. This lightweight, blocking inter-kernel communication client -supports thin clients for gridded POE applications. The canonical use -case is to inject events into an IKC grid from CGI applications, which -require lightweight resource use. +example. This lightweight, blocking event-passing client supports +thin clients for gridded POE applications. The canonical use case is +to inject events into an IKC application or grid from CGI interfaces, +which require lightweight resource use. POE filters and drivers pass data in array references. This is slightly awkward, but it minimizes the amount of data that must be @@ -151,24 +160,26 @@ stream chunks. The chunks are added to the filter's internal buffer for parsing by get_one(). -The SYNOPSIS shows get_one_start() in use. +The L</SYNOPSIS> shows get_one_start() in use. =head2 get_one -get_one() parses zero or one complete record from the filter's -internal buffer. The data is returned as an ARRAYREF suitable for -passing to another filter or a POE::Wheel object. +get_one() parses zero or one complete item from the filter's internal +buffer. The data is returned as an ARRAYREF suitable for passing to +another filter or a POE::Wheel object. -get_one() is the lazy form of get(). It only parses only one record -at a time from the filter's buffer. +get_one() is the lazy form of get(). It only parses only one item at +a time from the filter's buffer. This is vital for applications that +may switch filters in mid-stream, as it ensures that the right filter +is in use at any given time. -The SYNOPSIS shows get_one() in use. +The L</SYNOPSIS> shows get_one() in use. =head2 get ARRAYREF get() is the greedy form of get_one(). It accpets an array reference containing unprocessed stream chunks, and it adds that data to the -filter's internal buffer. It then parses as many full records as +filter's internal buffer. It then parses as many full items as possible from the buffer and returns them in another array reference. Any unprocessed data remains in the filter's buffer for the next call. @@ -177,15 +188,23 @@ Here's the get() form of the SYNOPSIS stand-alone example: + #!perl + + use warnings; + use strict; + use POE::Filter::Line; + my $filter = POE::Filter::Line->new( Literal => "\n" ); # Prints three lines: one, two three. + my $lines = $filter->get(["one\ntwo\nthr", "ee\nfour"]); foreach my $line (@$lines) { print "$line\n"; } # Prints two lines: four, five. + $lines = $filter->get(["\nfive\n"]); foreach my $line (@$lines) { print "$line\n"; @@ -194,26 +213,26 @@ get() should not be used with wheels that support filter switching. Its greedy nature means that it often parses streams well in advance of a wheel's events. By the time an application changes the wheel's -filter, too much data may have been interpreted already. +filter, it's too late: The old fiter has already parsed the rest of +the received data. Consider a stream of letters, numbers, and periods. The periods signal when to switch filters from one that parses letters to one that parses numbers. -In our hypothetical application, letters must be parsed one at a time, -but numbers may be parsed in a chunk. We'll use a hypothetical -POE::Filter::Character to parse letters and POE::Filter::Line to parse -numbers. +In our hypothetical application, letters must be handled one at a +time, but numbers may be handled in chunks. We'll use +POE::Filter::Block with a BlockSize of 1 to parse letters, and +POE::FIlter::Line with a Literal terminator of "." to handle numbers. Here's the sample stream: abcdefg.1234567.hijklmnop.890.q -We'll start with a ReadWrite wheel configured to parse input by -character: +We'll start with a ReadWrite wheel configured to parse characters. $_[HEAP]{wheel} = POE::Wheel::ReadWrite->new( - Filter => POE::Filter::Characters->new(), + Filter => POE::Filter::Block->new( BlockSize => 1 ), Handle => $socket, InputEvent => "got_letter", ); @@ -238,7 +257,7 @@ If the greedy get() were used, the entire input stream would have been parsed as characters in advance of the first handle_letter() call. The set_filter() call would have been moot, since there would be no -unparsed input data remaining. +data left to be parsed. The "got_number" handler receives contiguous runs of digits as period-terminated lines. The greedy get() would cause a similar @@ -247,7 +266,9 @@ sub handle_numbers { my $numbers = $_[ARG0]; print "Got number(s): $numbers\n"; - $_[HEAP]->{wheel}->set_filter( POE::Filter::Character->new() ); + $_[HEAP]->{wheel}->set_filter( + POE::Filter::Block->new( BlockSize => 1 ) + ); $_[HEAP]->{wheel}->event( InputEvent => "got_letter" ); } @@ -255,38 +276,60 @@ =head2 put ARRAYREF -put() serializes records into a form that may be written to a file or -sent across a socket. It accepts a reference to a list of records, -and it returns a reference to a list of marshalled stream chunks. The -number of output chunks is not necessarily related to the number of -input records. +put() serializes items into a stream of octets that may be written to +a file or sent across a socket. It accepts a reference to a list of +items, and it returns a reference to a list of marshalled stream +chunks. The number of output chunks is not necessarily related to the +number of input items. -The list reference it returns may be passed directly to a driver. +In stand-alone use, put()'s output may be sent directly: - $driver->put( $filter->put( \@records ) ); - -Or put() may be used to serialize data for other calls. - my $line_filter = POE::Filter::Line->new(); my $lines = $line_filter->put(\@list_of_things); foreach my $line (@$lines) { print $line; } +The list reference it returns may be passed directly to a driver or +filter. Drivers and filters deliberately share the same put() +interface so that things like this are possible: + + $driver->put( + $transfer_encoding_filter->put( + $content_encoding_filter->put( + \@items + ) + ) + ); + + 1 while $driver->flush(\*STDOUT); + =head2 get_pending -get_pending() returns any data in a filter's input buffer. The -filter's input buffer is not cleared, however. get_pending() returns -a list reference if there's any data, or undef if the filter was -empty. +get_pending() returns any data remaining in a filter's input buffer. +The filter's input buffer is not cleared, however. get_pending() +returns a list reference if there's any data, or undef if the filter +was empty. POE::Wheel objects use get_pending() during filter switching. Unprocessed data is fetched from the old filter with get_pending() and injected into the new filter with get_one_start(). -Filters don't have output buffers, so there's no corresponding "put" -buffer accessor. + use POE::Filter::Line; + use POE::Filter::Stream; + my $line_filter = POE::Filter::Line->new(); + $line_filter->get_one_start([ "not a complete line" ]); + + my $stream_filter = POE::Filter::Stream->new(); + my $line_buffer = $line_filter->get_pending(); + $stream_filter->get_one_start($line_buffer) if $line_buffer; + + print "Stream: $_\n" foreach (@{ $stream_filter->get_one }); + +Full items are serialized whole, so there is no corresponding "put" +buffer or accessor. + =head1 SEE ALSO The SEE ALSO section in L<POE> contains a table of contents covering @@ -309,8 +352,6 @@ In theory, filters should be interchangeable. In practice, stream and block protocols tend to be incompatible. -TODO - The examples are untested. - =head1 AUTHORS & COPYRIGHTS Please see L<POE> for more information about authors and contributors. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |