POE::Wheel(3pm)

πŸ’– POE::Wheel β€” the manual page, but make it cute πŸ’–

okay so bestie, you're looking at POE::Wheel, and this is the 3pm page for it, straight out of the User Contributed Perl Documentation. it's the base class, the mother of all wheels, and honestly she's iconic. let's get into it 🫢


✨ NAME ✨

POE::Wheel β€” event-driven mixins for POE::Session. yes literally that's the whole name section, she's mysterious like that πŸ’…


🌸 SYNOPSIS 🌸

so, plot twist: this base class has no synopsis. none. zero. it's giving "i'm the blueprint, not the building." you're supposed to go consult one of the subclasses instead, bestie. go look at those, they have the actual vibes πŸ’–


πŸ¦‹ DESCRIPTION πŸ¦‹

okay so a POE::Wheel object encapsulates a bundle of event handlers that do one specific task. and not only that β€” it also manages the event watchers that trigger those handlers. it's a whole package deal, no cap. πŸŽ€

Object lifetime is very important for POE wheels. like, huge. at creation time, most wheels will add anonymous event handlers to the currently active session. in other words: the session that created the wheel gets modified to handle new events. event watchers may also be initialized as needed to trigger the new handlers. and those watchers? also owned by the session that created the wheel. it's giving codependent but in a cute way πŸ’œ

Sessions must not expose their wheels to other sessions. do not do this. it will likely cause problems, because wheels are tightly integrated with the sessions that created them. example: calling put() on a POE::Wheel::ReadWrite instance may enable a write-okay watcher. the handler for that watcher is already defined in the wheel's owner. if you call put() outside that session, you enable the write-okay watcher in the wrong session, and the event will never be handled. πŸ’€ literally never. ghosted forever.

likewise, wheels must be destroyed from within their creator sessions. otherwise breakage occurs when the wheels' DESTROY methods try to unregister event handlers and watchers from the wrong sessions. so to simplify everything, it's recommended to store POE::Wheel instances in heaps of the sessions that created them. keep your wheels at home, bestie 🏠

for example β€” creating a POE::Wheel::FollowTail object will register an event handler that periodically polls a file for new information. it will also start the timer that triggers the periodic polling. two for one, slay ✨

use POE;
use POE::Wheel::FollowTail;

my @files_to_tail = qw( messages maillog security );

foreach my $filename (@files_to_tail) {
  POE::Session->create(
    inline_states => {
      _start => sub {
        push @{$_[HEAP]{messages}}, POE::Wheel::FollowTail->new(
          Filename   => "/var/log/$filename",
          InputEvent => "got_input",
        );
      },
      got_input => sub {
        print "$filename: $_[ARG0]\n";
      },
    }
  );
}

POE::Kernel->run();
exit;

as illustrated in the previous example, it is possible β€” and sometimes recommended β€” to create more than one POE::Wheel of a particular type in the same session. a session with multiple wheels may scale better than separate sessions with one wheel apiece. when in doubt, benchmark. periodt. πŸ“Š

and unlike components (or cheese πŸ§€), wheels do not stand alone. each wheel must be created by a session in order to register event watchers and handlers within that session. wheels are thusly tightly coupled to their creator sessions and cannot be passed to other sessions. sorry babes, no lending πŸ’”


🌈 FILTERS, AND DRIVERS 🌈

many wheels perform data transfer operations on filehandles β€” and yes, that includes sockets, pipes, and just about anything else that can store or transfer data, iykyk.

to avoid subclass hell πŸ”₯, POE::Wheel objects may be customized at creation time by including other objects from the POE::Filter and POE::Driver namespaces. mix and match, it's giving build-your-own-adventure πŸ’…

πŸŽ€ Filters

POE "filters" implement data parsers and serializers. for example, POE::Filter::Line parses streams into records separated by some string (the traditional network newline by default). the Line filter also adds record separators to data being output. so organized πŸ’–

POE::Filter::HTTPD is a more complex example. it implements a subset of the server-side of the HTTP protocol. input streams are parsed into HTTP requests and wrapped in HTTP::Request objects. server code sends HTTP::Response objects back to the client, which are serialized so they may be sent to a socket. look at her go πŸ˜­πŸ‘‘

most wheels use POE::Filter::Line by default.

🌸 Drivers

POE "drivers" implement strategies for sending data to a filehandle and receiving input from it. a single POE::Wheel class may interact with files, pipes, sockets, or devices by using the appropriate driver. flexible queen πŸ’œ

POE::Driver::SysRW is the only driver that comes with POE. sysread() and syswrite() can handle nearly every kind of stream interaction, so there hasn't been much call for another type of driver. she simply does it all, no notes ✨


πŸ‘‘ METHODS πŸ‘‘

POE::Wheel defines a common interface that most subclasses use. subclasses may implement other methods, especially to help perform their unique tasks. if something useful isn't documented here, see the subclass before implementing a feature. don't be out here reinventing the wheel (pun fully intended πŸ›ž)

πŸ’« Required Methods

these methods are required by all subclasses. no exceptions, bestie.

  • new LOTS_OF_STUFF β€” new() instantiates and initializes a new wheel object and returns it. the new wheel will continue to function for as long as it exists, although other methods may alter the way it functions. part of any wheel's construction is the registration of anonymous event handlers to perform wheel-specific tasks. event watchers are also started to trigger the handlers when relevant activity occurs. every wheel has a different purpose and requires different constructor parameters, so LOTS_OF_STUFF is documented in each particular subclass. go read those, i promise it's worth it 🫢
  • DESTROY β€” DESTROY is Ye Olde Perl Object Destructor. when the wheel's last strong reference is relinquished, DESTROY triggers the wheel's cleanup. the object removes itself from the session that created it: active event watchers are stopped, and anonymous event handlers are unregistered. she cleans up after herself, we love a tidy icon 🧹✨
  • event EVENT_TYPE, EVENT_NAME [, EVENT_TYPE, EVENT_NAME, ....] β€” event() changes the events that a wheel will emit. its parameters are one or more pairs of EVENT_TYPEs and the EVENT_NAMEs to emit when each type of event occurs. if an EVENT_NAME is undefined, then the wheel will stop emitting that type of event. or the wheel may throw an error if the event type is required. ⚠️

EVENT_TYPEs differ for each wheel and correspond to the constructor parameters that match /.*Event$/. for example, POE::Wheel::ReadWrite may emit up to five different kinds of event: InputEvent, ErrorEvent, FlushedEvent, HighEvent, LowEvent. the name of each emitted event may be changed at run time. five whole moods πŸ’…

this example changes the events to emit on new input and when output is flushed. it stops the wheel from emitting events when errors occur:

$wheel->event(
  InputEvent   => 'new_input_event',
  ErrorEvent   => undef,
  FlushedEvent => 'new_flushed_event',
);

πŸ¦‹ I/O Methods

wheels that perform input and output may implement some or all of these methods. the put() method is a common omission. wheels that don't perform output do not have put() methods. if she doesn't do output, she simply doesn't have the method, and that's valid πŸ’–

  • put RECORD [, RECORD [, ....]] β€” put() sends one or more RECORDs to the wheel for transmitting. each RECORD is serialized by the wheel's associated POE::Filter so that it will be ready to transmit. the serialized stream may be transmitted immediately by the wheel's POE::Driver object, or it may be buffered in the POE::Driver until it can be flushed to the output filehandle. either way, it's handled πŸ’…

most wheels use POE::Filter::Line and POE::Driver::SysRW by default, so it's not necessary to specify them in most cases. defaults ate, no cap.

⭐ Class Static Functions

these functions expose information that is common to all wheels. they are not methods, so they should not be called as methods. seriously, don't. πŸ™ˆ

my $new_wheel_id = POE::Wheel::allocate_wheel_id();
POE::Wheel::free_wheel_id($new_wheel_id);
  • allocate_wheel_id β€” this is not a class method. every wheel has a unique ID. allocate_wheel_id() returns the next available unique wheel ID. wheel constructors use it to set their IDs internally.
package POE::Wheel::Example;
use base qw(POE::Wheel);

sub new {
  # ... among other things ...
  $self->[MY_WHEEL_ID] = POE::Wheel::allocate_wheel_id();
  return $self;
}

wheel IDs are used to tell apart events from similarly typed wheels. for example, a multi-file tail utility may handle all file input with the same function. wheel IDs may be used to tell which wheel generated the InputEvent being handled. detective work, but make it cute πŸ”

wheel IDs are often used to store wheel-local state in a session's heap.

sub handle_error {
  my $wheel_id = $_[ARG3];
  print "Wheel $wheel_id caught an error.  Shutting it down.\n";
  delete $_[HEAP]{wheels}{$wheel_id};
}
it is vital for wheels to free their allocated IDs when they are destroyed. POE::Wheel class keeps track of allocated wheel IDs to avoid collisions, and they will remain in memory until freed. see free_wheel_id(). don't leave your IDs just sitting there, that's a memory leak bestie 😭
  • free_wheel_id WHEEL_ID β€” this is not a class method. free_wheel_id() deallocates a wheel's ID so that it stops consuming memory and may be reused later. this is often called from a wheel's destructor.
package POE::Wheel::Example;
use base qw(POE::Wheel);

sub DESTROY {
  my $self = shift;
  # ... among other things ...
  POE::Wheel::free_wheel_id($self->[MY_WHEEL_ID]);
}

wheel IDs may be reused, although it has never been reported. two active wheels will never share the same ID, however. never ever. promise 🀞

  • ID β€” this is usually implemented in the subclass! the ID() method returns a wheel's unique ID. it is commonly used to match events with the wheels which generated them. again: this method is not implemented in this class! if it's missing from the subclass, please go pester that module author β€” thanks! πŸ’– (be nice about it tho)

🌟 SEE ALSO 🌟

the SEE ALSO section in POE contains a table of contents covering the entire POE distribution. go check it out, it's a whole vibe map πŸ—ΊοΈ

  • POE::Driver β€” a base class for file access drivers that POE::Wheel may use.
  • POE::Filter β€” a base class for data parsers and marshalers that POE::Wheel may use.
  • POE::Wheel::Curses β€” non-blocking input for Curses.
  • POE::Wheel::FollowTail β€” non-blocking file and FIFO monitoring.
  • POE::Wheel::ListenAccept β€” non-blocking server for existing sockets.
  • POE::Wheel::ReadLine β€” non-blocking console input, with full readline support.
  • POE::Wheel::ReadWrite β€” non-blocking stream I/O.
  • POE::Wheel::Run β€” non-blocking process creation and management.
  • POE::Wheel::SocketFactory β€” non-blocking socket creation, supporting most protocols and modes.

πŸ’€ BUGS πŸ’€

it would be nice if wheels were more like proper Unix streams. that's it. that's the whole bug section. iconic, honestly. she said what she said 🌈


🫢 AUTHORS & COPYRIGHTS 🫢

please see POE for more information about authors, contributors, and POE;s licensing. (yes, that semicolon is theirs, i'm not touching it, it's part of the lore now πŸ’œ)


and that's a wrap on POE::Wheel(3pm) from the User Contributed Perl Documentation, brought to you by perl v5.34.0, dated 2022-03-25. go forth and build wheels, bestie β€” just keep them in their home session, okay? slay πŸ’…βœ¨πŸŽ€πŸ¦‹πŸ’–

πŸ“¦ where this page comes from