Data::Transform - base class for protocol abstractions
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 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 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 copied on Perl's stack.
All Data::Transform classes must support the minimal interface, defined here. Specific filters may implement and document additional methods.
new() creates and initializes a new filter. Constructor parameters vary from one Data::Transform subclass to the next, so please consult the documentation for your desired filter.
get_one_start() accepts an array reference containing unprocessed stream chunks. The chunks are added to the filter's internal buffer for parsing by get_one().
get_one() parses zero or one complete item from the filter's internal 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.
get() is the greedy form of get_one(). It accepts an array reference containing unprocessed stream chunks, and it adds that data to the 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.
This should only be used if you don't care how long the processing takes. Unless responsiveness doesn't matter for your application, you should really be using get_one_start() and get_one().
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.
A flag method that always returns 1. This can be used in e.g. POE to check if the class supports Data::Transform::Meta, which all Data::Transform subclasses should, but POE::Filter classes don't. Doing it this way instead of checking if a filter is a Data::Transform subclass allows for yet another filters implementation that is meant to transparently replace this to be used by POE without changes to POE.
clone() creates and initializes a new filter based on the constructor parameters of the existing one. The new filter is a near-identical copy, except that its buffers are 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.
Full items are serialized whole, so there is no corresponding "put" buffer or accessor.
Data::Transform implements most of the public API above to help ensure uniform behaviour across all subclasses. This implementation expects your object to be an array ref. Data::Transform provides a default implementation for the following methods:
get() is implemented in terms of get_one_start() and get_one(). Since having to handle Data::Transform::Meta packets means that you have to keep a list of incoming packets, it is highly unlikely that you will ever need to override get_one_start(), since all it does is add to the list. It assumes the list is kept as an array ref in the first entry of your object's list.
get_one is in turn implemented in terms of the following two methods:
This is where you do all your filter's input work. There is no default implementation. It has a single method parameter which may contain a single chunk of raw data to process. get_one() will also call it without new data to see if not all raw data from the previous chunk had been processed.
Override this if you need to act on metadata packets that are embedded into the input stream. The default implementation just returns the packet. If you override this, make sure you return the packet as well, so that if your filter is being used in a filter stack, the filters below you get a chance to handle it as well.
put() is implemented in terms of the following methods. It's unlikely you want to override put() instead of these:
Gets called for each packet of regular data in the list passed to put().
Gets called for each packet of metadata in the list passed to put(). The default implementation just returns the packet. If you override this, make sure you end with returning it too, so that when your filter is used in a stack, the filters above you get a chance to handle it too.
The default implementation just returns the list of raw packets still in your queue. If your filter doesn't always return one cooked packet for each raw packet it receives, you will have to override this to also return the data it has stored while assembing complete cooked packets.
This is just a flag method signifying that Data::Transform, unlike POE::Filter supports handling metadata.
So, in the best case, you only have to implement new(), clone(), _handle_get_data() and _handle_put_data(). Likely you will want to override get_pending() as well, but usually nothing more is needed.
Data::Transform is released under the GPL version 2.0 or higher. See the file LICENCE for details.