POE-Component-Syndicator-0.06000755001750001750 011577603105 16077 5ustar00hinrikhinrik000000000000README000644001750001750 3460311577603105 17065 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06NAME POE::Component::Syndicator - A POE component base class which implements the Observer pattern SYNOPSIS package POE::Component::IRC; use strict; use warnings; use POE; use base 'POE::Component::Syndicator'; # our constructor sub spawn { my ($package, %args) = @_; # process arguments... my $self = bless \%args, $package; # set up our plugin system and POE session $self->_syndicator_init( prefix => 'irc_', reg_prefix => 'PCI_', types => [SERVER => 'S', USER => 'U'], object_states => [qw( syndicator_started shutdown )], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; # connect to a server, etc... } # plugin handler for SERVER event 'hlagh' sub S_hlagh { # ... } sub shutdown { my ($kernel, $self) = @_[KERNEL, OBJECT]; # disconnect from a server, etc... # shut down the syndicator $self->_syndicator_destroy(); } DESCRIPTION POE::Component::Syndicator is a base class for POE components which need to handle a persistent resource (e.g. a connection to an IRC server) for one or more sessions in an extendable way. This module (as well as Object::Pluggable, which this module inherits from) was born out of POE::Component::IRC, the guts of which quickly spread to other POE components. Now they can all inherit from this module instead. The component provides an event queue, which can be managed with the methods documented below. It handles delivery of events to the object itself, all interested plugins, and all interested sessions. Component lifetime You start by calling "_syndicator_init", which will create a POE session with your object as its heap, and a few event handlers installed. The events described in "Local events" delimit the start and end of the session's lifetime. In between those, interested plugins and sessions will receive various events, usually starting with "syndicator_registered". In this phase, your subclass and plugins can call the methods and send the events documented below. When the component has been shut down, sessions (but not plugins) will receive a "syndicator_shutdown" event. After this, the component will become unusable. A note on events In this document, an *event* (unless explicitly referred to as a *POE event*) is defined as a message originating from POE::Component::Syndicator, delivered to plugins (and the subclass) via plugin methods and to registered sessions as POE events. Interested sessions are considered consumers only, so they always receive copies of event arguments, whereas interested plugins and subclasses receive scalar references to them. This allows them to alter, add, or remove event arguments before sessions (or even other plugins) receive them. For more information about plugins, see Object::Pluggable's documentation. A subclass does not have to register for plugin events. Two event types are supported: SERVER and USER, though their names can be overriden (see "_syndicator_init"). SERVER events These represent data received from the network or some other outside resource (usually a server, hence the default name). SERVER events are generated by the "send_event*" methods. These events are delivered to the subclass and plugins (method "S_foo") and interested sessions (event "syndicator_foo"). USER events These represent commands about to be sent to a server or some other resource. USER events are generated by "send_user_event". In addition, all POE events sent to this component's session (e.g. with "yield") which do not have a handler will generate corresponding USER events. USER events are considered more private, so they are only delivered to the subclass and plugins, not to sessions. PRIVATE METHODS The following methods should only be called by a subclass. "_syndicator_init" You should call this in your constructor. It initializes Object::Pluggable, creates the Syndicator's POE session, and calls the "syndicator_started" POE events. It takes the following arguments: 'prefix', a prefix for all your event names, when sent to interested sessions. If you don't supply this, Object::Pluggable's default ('pluggable') will be used. 'reg_prefix', the prefix for the "register()"/"unregister()" plugin methods If you don't supply this, Object::Pluggable's default ('plugin_') will be used. 'debug', a boolean, if true, will cause a warning to be printed every time a plugin event handler raises an exception. 'types', a 2-element arrayref of the types of events that your component will support, or a 4-element (2 pairs) arrayref where the event types are keys and their abbrevations (used as plugin event method prefixes) are values (see "A note on events" and Object::Pluggable for more information). The two event types are fundamentally different, so make sure you supply them in the right order. If you don't provide this argument, "[ SERVER => 'S', USER => 'U' ]" will be used. 'register_signal', the name of the register signal (see "SIGNALS"). Defaults to 'SYNDICATOR_REGISTER'. 'shutdown_signal', the name of the shutdown signal (see "SIGNALS"). Defaults to 'SYNDICATOR_SHUTDOWN'. 'object_states' an arrayref of additional object states to add to the POE session. Same as the 'object_states' argument to POE::Session's "create" method. You'll want to add a handler for at least the "syndicator_started" event. 'options', a hash of options for POE::Session's constructor. If you call "_syndicator_init" from inside another POE session, the component will automatically register that session as wanting all events. That session will first receive a "syndicator_registered" event. "_syndicator_destroy" Call this method when you want Syndicator to clean up (delete all plugins, etc) and make sure it won't keep the POE session alive after all remaining events have been processed. A "syndicator_shutdown" event (or similar, depending on the prefix you chose) will be generated. Any argument passed to "_syndicator_destroy" will be passed along with that event. Note: this method will clear all alarms for the POE session. PUBLIC METHODS "session_id" Returns the component's POE session id. "session_alias" Returns the component's POE session alias. "yield" This method provides an alternative, object-based means of posting events to the component. First argument is the event to post, following arguments are sent as arguments to the resultant post. "call" This method provides an alternative, object-based means of calling events to the component. First argument is the event to call, following arguments are sent as arguments to the resultant call. "send_event" Adds a new SERVER event onto the end of the queue. The event will be processed after other pending events, if any. First argument is an event name, the rest are the event arguments. $component->send_event('irc_public, 'foo!bar@baz.com', ['#mychan'], 'message'); "send_event_next" Adds a new SERVER event to the start of the queue. The event will be the next one to be processed. First argument is an event name, the rest are the event arguments. "send_event_now" Sends a new SERVER event immediately. Execution of the current POE event will be suspended (i.e. this call will block) until the new event has been processed by the component class and all plugins. First argument is an event name, the rest are the event arguments. "send_user_event" Sends a new USER event immediately. You should call this before every command you send to your remote server/resource. Only the subclass and plugins will see this event. Takes two arguments, an event name and an arrayref of arguments. Returns one of the "EAT" constants listed in Object::Pluggable::Constants. After this method returns, the arrayref's contents may have been modified by the subclass or plugins. $component->send_user_event('PRIVMSG', '#mychan', 'message'); "delay" This method provides a way of posting delayed events to the component. The first argument is an arrayref consisting of the delayed command to post and any command arguments. The second argument is the time in seconds that one wishes to delay the command being posted. my $alarm_id = $component->delay(['mode', $channel, '+o', $dude], 60); "delay_remove" This method removes a previously scheduled delayed event from the component. Takes one argument, the "alarm_id" that was returned by a "delay" method call. Returns an arrayref of arguments to the event that was originally requested to be delayed. my $arrayref = $component->delay_remove($alarm_id); EVENTS Local events The component will send the following POE events to its session. "syndicator_started" Called after the session has been started (like "_start" in POE::Kernel. This is where you should do your POE-related setup work such as adding new event handlers to the session. "syndicator_stopped" Called right before the session is about to die (like "_stop" in POE::Kernel). Input events Other POE sessions can send the following POE events to the Syndicator's session. "register" Takes any amount of arguments: a list of event names that your session wants to listen for, minus the prefix (specified in "_syndicator_init" in "syndicator_init"). $kernel->post('my syndicator', 'register', qw(join part quit kick)); Registering for the special event 'all' will cause it to send all events to your session. Calling it with no event names is equivalent to calling it with 'all' as an argumente. Registering will generate a "syndicator_registered" event that your session can trap. Registering with multiple component sessions can be tricky, especially if one wants to marry up sessions/objects, etc. Check the SIGNALS section for an alternative method of registering with multiple components. "unregister" Takes any amount of arguments: a list of event names which you *don't* want to receive. If you've previously done a "register" for a particular event which you no longer care about, this event will tell the component to stop sending them to you. (If you haven't, it just ignores you. No big deal.) Calling it with no event names is equivalent to calling it with 'all' as an argument. If you have registered for the special event 'all', attempting to unregister individual events will not work. This is a 'feature'. "shutdown" By default, POE::Component::Syndicator sessions never go away. You can send its session a "shutdown" event manually to make it delete itself. Terminating multiple Syndicators can be tricky. Check the "SIGNALS" section for a method of doing that. "_default" Any POE events sent to the Syndicator's session which do not have a handler will go to the Syndicator's "_default" handler, will generate "USER events" of the same name. If you install your own "_default" handler, make sure you do the same thing before you handle an event: use Object::Pluggable::Constants 'PLUGIN_EAT_ALL'; $poe_kernel->state('_default', $self, '__default'); sub __default { my ($self, $event, $args) = @_[OBJECT, ARG0, ARG1]; # do nothing if a plugin eats the event return if $self->send_user_event($event, [@$args]) == PLUGIN_EAT_ALL; # handle the event # ... } Note that the handler for the "_default" event must be named something other than '_default', because that name is reserved for the plugin-type default handler (see the Object::Pluggable docs). Output events The Syndicator will send the following events at various times. The 'syndicator_' prefix in these event names can be customized with a 'prefix' argument to "_syndicator_init" in "_syndicator_init". "syndicator_registered" Sent once to the requesting session on registration (see "register"). "ARG0" is a reference to the component's object. "syndicator_shutdown" Sent to all interested sessions when the component has been shut down. See "_syndicator_destroy". "syndicator_delay_set" Sent to the subclass, plugins, and all interested sessions on a successful addition of a delayed event using the "delay" method. "ARG0" will be the alarm_id which can be used later with "delay_remove". Subsequent parameters are the arguments that were passed to "delay". "syndicator_delay_removed" Sent to the subclass, plugins, and all interested sessions when a delayed event is successfully removed. "ARG0" will be the alarm_id that was removed. Subsequent parameters are the arguments that were passed to "delay". All other events All other events sent by the Syndicator are USER events (generated with "send_user_event") and SERVER events (generated with "send_event*") which will be delivered normally. Your subclass and plugins are responsible for generating them. SIGNALS The component will handle a number of custom signals that you may send using POE::Kernel's "signal" method. They allow any session to communicate with every instance of the component in certain ways without having references to their objects or knowing about their sessions. The names of these signals can be customized with "_syndicator_init". "SYNDICATOR_REGISTER" Registers for an event with the component. See "register". "SYNDICATOR_SHUTDOWN" Causes a 'shutdown' event to be sent to your session. Any arguments to the signal will be passed along to the event. That's where you should clean up and call "_syndicator_destroy". AUTHOR Hinrik Örn Sigurðsson, hinrik.sig@gmail.com, Chris "BinGOs" Williams chris@bingosnet.co.uk, Apocalypse apocal@cpan.org, and probably others. LICENSE AND COPYRIGHT Copyright 2011 Hinrik Örn Sigurðsson This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. Changes000644001750001750 150511577603105 17453 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06Revision history for POE-Component-Syndicator 0.06 Mon Jun 20 08:35:15 GMT 2011 - Support non-numeric session IDs - Make warnings fatal 0.05 Wed May 25 06:26:11 GMT 2011 - Fix incorrect return value from delay_remove() and the syndicator_delay_removed event 0.04 Fri May 20 12:11:17 GMT 2011 - Make it so that register/unregister without arguments are equivalent to passing 'all' as an argument. 0.03 Sat May 14 18:42:39 GMT 2011 - Croak if the user tries to supply handlers for the register/unregister events. 0.02 Sat May 14 18:34:26 GMT 2011 - Document what a subclass should do in a custom _default handler, and allow it to be specified in object_states when calling new() 0.01 Tue May 3 10:57:44 GMT 2011 - First release, excised from the guts of POE::Component::IRC. LICENSE000644001750001750 4374111577603105 17215 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06This software is copyright (c) 2011 by Hinrik Örn Sigurðsson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2011 by Hinrik Örn Sigurðsson. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2011 by Hinrik Örn Sigurðsson. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End dist.ini000644001750001750 110311577603105 17616 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06name = POE-Component-Syndicator author = Hinrik Örn Sigurðsson copyright_holder = Hinrik Örn Sigurðsson license = Perl_5 [@AVAR] dist = POE-Component-Syndicator authority = cpan:HINRIK bugtracker = rt use_CompileTests = 0 nextrelease_format = %-5v %{ccc MMM d HH:mm:ss V YYYY}d github_user = hinrik git_tag_message = CPAN release %v no_AutoPrereq = 1 [Prereqs / Runtime] Object::Pluggable = 1.29 POE = 1.311 [Prereqs / BuildRequires] Test::More = 0 xt000755001750001750 011577603105 16453 5ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06pod.t000644001750001750 20111577603105 17532 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xtuse Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); META.yml000644001750001750 155111577603105 17432 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06--- abstract: 'A POE component base class which implements the Observer pattern' author: - 'Hinrik Örn Sigurðsson ' build_requires: Test::More: 0 configure_requires: ExtUtils::MakeMaker: 6.30 dynamic_config: 0 generated_by: 'Dist::Zilla version 4.200006, CPAN::Meta::Converter version 2.110440' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: POE-Component-Syndicator no_index: directory: - t - xt - utils requires: Object::Pluggable: 1.29 POE: 1.311 resources: bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=POE-Component-Syndicator homepage: http://search.cpan.org/dist/POE-Component-Syndicator/ license: http://dev.perl.org/licenses/ repository: git://github.com/hinrik/poe-component-syndicator.git version: 0.06 x_authority: cpan:HINRIK MANIFEST000644001750001750 66411577603105 17276 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06Changes LICENSE MANIFEST MANIFEST.SKIP META.json META.yml Makefile.PL README dist.ini lib/POE/Component/Syndicator.pm t/01_compile.t t/02_basic.t t/03_parent_session.t t/04_signal.t t/05_multi_signal.t t/06_delays.t t/07_send_event_now.t t/08_send_event_next.t t/09_event_order.t t/10_default.t t/11_bare_register.t t/release-pod-syntax.t xt/perl_critic.t xt/perl_critic_t.t xt/perlcriticrc xt/perlcriticrc_t xt/pod.t xt/pod_coverage.t META.json000644001750001750 307411577603105 17604 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06{ "abstract" : "A POE component base class which implements the Observer pattern", "author" : [ "Hinrik \u00c3\u0096rn Sigur\u00c3\u00b0sson " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 4.200006, CPAN::Meta::Converter version 2.110440", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "POE-Component-Syndicator", "no_index" : { "directory" : [ "t", "xt", "utils" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : 0 } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.30" } }, "runtime" : { "requires" : { "Object::Pluggable" : "1.29", "POE" : "1.311" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-POE-Component-Syndicator@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=POE-Component-Syndicator" }, "homepage" : "http://search.cpan.org/dist/POE-Component-Syndicator/", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/hinrik/poe-component-syndicator.git", "web" : "http://github.com/hinrik/poe-component-syndicator" } }, "version" : "0.06", "x_authority" : "cpan:HINRIK" } Makefile.PL000644001750001750 213511577603105 20132 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06 use strict; use warnings; use ExtUtils::MakeMaker 6.30; my %WriteMakefileArgs = ( 'ABSTRACT' => 'A POE component base class which implements the Observer pattern', 'AUTHOR' => 'Hinrik Örn Sigurðsson ', 'BUILD_REQUIRES' => { 'Test::More' => '0' }, 'CONFIGURE_REQUIRES' => { 'ExtUtils::MakeMaker' => '6.30' }, 'DISTNAME' => 'POE-Component-Syndicator', 'EXE_FILES' => [], 'LICENSE' => 'perl', 'NAME' => 'POE::Component::Syndicator', 'PREREQ_PM' => { 'Object::Pluggable' => '1.29', 'POE' => '1.311' }, 'VERSION' => '0.06', 'test' => { 'TESTS' => 't/*.t' } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); t000755001750001750 011577603105 16263 5ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.0602_basic.t000644001750001750 355311577603105 20177 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/t#!/usr/bin/env perl use strict; use warnings FATAL => 'all'; use POE; use Test::More tests => '4'; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_bar my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register', qw(bar shutdown)); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender) = @_[KERNEL, SENDER]; pass('Interested session got server event my_bar'); $kernel->post($sender, 'shutdown'); } sub my_shutdown { pass('Interested session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } MANIFEST.SKIP000644001750001750 10511577603105 20031 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06^POE-Component-Syndicator- ^cover_db/ ^utils/developer/ ^README.pod$ 06_delays.t000644001750001750 333111577603105 20375 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 4; { package MyComponent; use strict; use warnings FATAL => 'all'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(shutdown)], ], ); return $self; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_registered my_delay_set my_delay_removed _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield(register => 'all'); } sub my_registered { my ($heap, $irc) = @_[HEAP, ARG0]; $heap->{alarm_id} = $irc->delay(['foo', 'bar'], 5); ok($heap->{alarm_id}, 'Set alarm'); } sub my_delay_set { my ($heap, $event, $alarm_id) = @_[HEAP, STATE, ARG0]; is($alarm_id, $heap->{alarm_id}, $_[STATE]); my $opts = $synd->delay_remove($alarm_id); ok($opts, 'Delay Removed'); } sub my_delay_removed { my ($kernel, $heap, $alarm_id) = @_[KERNEL, HEAP, ARG0]; is($alarm_id, $heap->{alarm_id}, $_[STATE] ); $kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 04_signal.t000644001750001750 266011577603105 20373 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 2; { package MyComponent; use strict; use warnings FATAL => 'all'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(shutdown)], ], ); return $self; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start _shutdown my_registered my_shutdown )], ], ); $poe_kernel->run(); sub _start { my ($kernel, $session) = @_[KERNEL, SESSION]; $kernel->delay('_shutdown', 60, 'Timed out'); $kernel->signal($kernel, 'SYNDICATOR_REGISTER', $session, 'all'); } sub my_registered { pass('my_registered'); $poe_kernel->signal($poe_kernel, 'SYNDICATOR_SHUTDOWN'); } sub my_shutdown { pass('my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 01_compile.t000644001750001750 15111577603105 20514 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use Test::More tests => 1; use_ok 'POE::Component::Syndicator'; 10_default.t000644001750001750 411611577603105 20535 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/t#!/usr/bin/env perl use strict; use warnings FATAL => 'all'; use POE; use Test::More tests => '6'; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], $self => { _default => '__default' }, ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } sub __default { my ($self, $event, $args) = @_[OBJECT, ARG0, ARG1]; pass('Got _default event'); $self->send_user_event($event, [@$args]); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_bar my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register', qw(bar shutdown)); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender) = @_[KERNEL, SENDER]; pass('Interested session got server event my_bar'); $kernel->post($sender, 'shutdown'); } sub my_shutdown { pass('Interested session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } perlcriticrc000644001750001750 117711577603105 21230 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xt# level 3 is a nice compromise severity = 3 verbose = 3 # these policies are either too anal, demand additional dependencies, # or inhibit backwards-compatability [-RegularExpressions::RequireExtendedFormatting] [-ValuesAndExpressions::ProhibitConstantPragma] [-Variables::ProhibitPackageVars] [-Variables::RequireLocalizedPunctuationVars] [-BuiltinFunctions::ProhibitStringyEval] [-Subroutines::ProhibitBuiltinHomonyms] [-Subroutines::RequireArgUnpacking] [-Subroutines::ProhibitUnusedPrivateSubroutines] [-ErrorHandling::RequireCarping] [-ErrorHandling::RequireCheckingReturnValueOfEval] [-Modules::ProhibitAutomaticExportation] perl_critic.t000644001750001750 100111577603105 21266 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xtuse strict; use warnings; use File::Spec; use Test::More; use English qw(-no_match_vars); eval { require Test::Perl::Critic; }; if ( $EVAL_ERROR ) { my $msg = 'Test::Perl::Critic required to criticise code'; plan( skip_all => $msg ); } elsif ($Perl::Critic::VERSION lt 1.108) { my $msg = 'Perl::Critic >= 1.108 required to criticise code'; plan( skip_all => $msg ); } my $rcfile = File::Spec->catfile( 'xt', 'perlcriticrc' ); Test::Perl::Critic->import( -profile => $rcfile ); all_critic_ok(); perlcriticrc_t000644001750001750 160611577603105 21550 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xt# level 3 is a nice compromise severity = 3 verbose = 3 # these policies are either too anal, demand additional dependencies, # or inhibit backwards-compatability [-RegularExpressions::RequireExtendedFormatting] [-ValuesAndExpressions::ProhibitConstantPragma] [-Variables::ProhibitPackageVars] [-Variables::RequireLocalizedPunctuationVars] [-BuiltinFunctions::ProhibitStringyEval] [-Subroutines::ProhibitBuiltinHomonyms] [-Subroutines::ProhibitUnusedPrivateSubroutines] [-Subroutines::RequireArgUnpacking] [-ErrorHandling::RequireCarping] [-ErrorHandling::RequireCheckingReturnValueOfEval] # more policies we don't care about in tests [-InputOutput::ProhibitExplicitStdin] [-InputOutput::RequireBriefOpen] [-Modules::RequireEndWithOne] [-Modules::RequireExplicitPackage] [-Modules::RequireFilenameMatchesPackage] [-Subroutines::RequireFinalReturn] [-ControlStructures::ProhibitCascadingIfElse] pod_coverage.t000644001750001750 35311577603105 21415 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xtuse Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@; my @modules = all_modules(); plan tests => scalar @modules; pod_coverage_ok($_) for @modules; perl_critic_t.t000644001750001750 101611577603105 21617 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/xtuse strict; use warnings; use File::Spec; use Test::More; use English qw(-no_match_vars); eval { require Test::Perl::Critic; }; if ( $EVAL_ERROR ) { my $msg = 'Test::Perl::Critic required to criticise code'; plan( skip_all => $msg ); } elsif ($Perl::Critic::VERSION lt 1.108) { my $msg = 'Perl::Critic >= 1.108 required to criticise code'; plan( skip_all => $msg ); } my $rcfile = File::Spec->catfile( 'xt', 'perlcriticrc_t' ); Test::Perl::Critic->import( -profile => $rcfile ); all_critic_ok(glob 't/0*'); 09_event_order.t000644001750001750 431511577603105 21436 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 5; my ($SESSION_GOT_BAR, $SESSION_GOT_BAZ); { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); $self->send_event('my_baz'); return PLUGIN_EAT_NONE; } sub S_bar { ok(!$SESSION_GOT_BAR, 'Subclass got server event bar before a registered session did'); return PLUGIN_EAT_NONE; } sub S_baz { ok($SESSION_GOT_BAR, 'Registered session got server event bar before subclass got server event baz'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_bar my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register', qw(bar baz shutdown)); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender, $heap) = @_[KERNEL, SENDER, HEAP]; pass('Interested session got server event my_bar'); $SESSION_GOT_BAR = 1; $kernel->post($sender, 'shutdown'); } sub my_shutdown { $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 05_multi_signal.t000644001750001750 303611577603105 21604 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 4; { package MyComponent; use strict; use warnings FATAL => 'all'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(shutdown)], ], ); return $self; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd1 = MyComponent->spawn(); my $synd2 = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start _shutdown my_registered my_shutdown )], ], ); $poe_kernel->run(); sub _start { my ($kernel, $session) = @_[KERNEL, SESSION]; $kernel->delay('_shutdown', 60, 'Timed out'); $kernel->signal($kernel, 'SYNDICATOR_REGISTER', $session, 'all'); } sub my_registered { pass('my_registered'); $_[HEAP]{registered}++; if ($_[HEAP]{registered} == 2) { $poe_kernel->signal($poe_kernel, 'SYNDICATOR_SHUTDOWN'); } } sub my_shutdown { pass('my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 11_bare_register.t000644001750001750 353111577603105 21727 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/t#!/usr/bin/env perl use strict; use warnings FATAL => 'all'; use POE; use Test::More tests => '4'; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_bar my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register'); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender) = @_[KERNEL, SENDER]; pass('Interested session got server event my_bar'); $kernel->post($sender, 'shutdown'); } sub my_shutdown { pass('Interested session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 07_send_event_now.t000644001750001750 411611577603105 22134 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 5; my $PROCESSED_U_FOO = 0; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); $self->send_event_now('my_baz'); $PROCESSED_U_FOO = 1; return PLUGIN_EAT_NONE; } sub S_baz { is($PROCESSED_U_FOO, 0, 'Subclass got server event my_baz before U_foo had been processed'); return PLUGIN_EAT_NONE; } sub S_bar { my ($self) = @_; pass('Subclass got server event my_bar'); $self->yield('shutdown'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register', qw(bar baz shutdown)); $synd->yield('foo'); } sub my_shutdown { pass('Interested session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 03_parent_session.t000644001750001750 346611577603105 22156 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/t#!/usr/bin/env perl use strict; use warnings FATAL => 'all'; use POE; use Test::More tests => '4'; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } POE::Session->create( package_states => [ main => [qw( _start my_bar my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); my $synd = MyComponent->spawn(); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender) = @_[KERNEL, SENDER]; pass('Parent session got server event my_bar'); $kernel->post($sender, 'shutdown'); } sub my_shutdown { pass('Parent session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } 08_send_event_next.t000644001750001750 421611577603105 22311 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/tuse strict; use warnings FATAL => 'all'; use POE; use Test::More tests => 5; { package MyComponent; use strict; use warnings FATAL => 'all'; use Object::Pluggable::Constants 'PLUGIN_EAT_NONE'; use POE; use Test::More; use base 'POE::Component::Syndicator'; sub spawn { my ($package, %args) = @_; my $self = bless \%args, $package; $self->_syndicator_init( debug => 1, prefix => 'my_', object_states => [ $self => [qw(syndicator_started shutdown)], ], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; pass('Subclass got syndicator_started event'); } sub U_foo { my ($self) = $_[OBJECT]; pass('Subclass got user event foo'); $self->send_event('my_bar'); $self->send_event_next('my_baz'); return PLUGIN_EAT_NONE; } sub shutdown { my ($self) = $_[OBJECT]; $self->_syndicator_destroy(); } } my $synd = MyComponent->spawn(); POE::Session->create( package_states => [ main => [qw( _start my_bar my_baz my_shutdown _shutdown )], ], ); $poe_kernel->run(); sub _start { $poe_kernel->delay('_shutdown', 60, 'Timed out'); $synd->yield('register', qw(bar baz shutdown)); $synd->yield('foo'); } sub my_bar { my ($kernel, $sender, $heap) = @_[KERNEL, SENDER, HEAP]; $heap->{got_bar} = 1; pass('Interested session got server event my_bar'); $kernel->post($sender, 'shutdown'); } sub my_baz { my ($kernel, $sender, $heap) = @_[KERNEL, SENDER, HEAP]; is($heap->{got_bar}, undef, 'Interested session got server event my_baz before my_bar'); $kernel->post($sender, 'shutdown'); } sub my_shutdown { pass('Interested session got server event my_shutdown'); $poe_kernel->yield('_shutdown'); } sub _shutdown { my ($kernel, $error) = @_[KERNEL, ARG0]; fail($error) if defined $error; $kernel->alarm_remove_all(); $kernel->signal($kernel, 'SYNDICATOR_SHUTDOWN'); } release-pod-syntax.t000644001750001750 45011577603105 22312 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/t#!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } use Test::More; eval "use Test::Pod 1.41"; plan skip_all => "Test::Pod 1.41 required for testing POD" if $@; all_pod_files_ok(); Component000755001750001750 011577603105 21153 5ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/lib/POESyndicator.pm000644001750001750 7250111577603105 24014 0ustar00hinrikhinrik000000000000POE-Component-Syndicator-0.06/lib/POE/Componentpackage POE::Component::Syndicator; BEGIN { $POE::Component::Syndicator::AUTHORITY = 'cpan:HINRIK'; } BEGIN { $POE::Component::Syndicator::VERSION = '0.06'; } use strict; use warnings FATAL => 'all'; use Carp qw(carp croak); use Object::Pluggable::Constants qw(:ALL); use POE; use base 'Object::Pluggable'; use constant REFCOUNT_TAG => 'POE::Component::Syndicator registered'; sub _pluggable_event { my ($self, $event, @args) = @_; $self->send_event($event, @args); return; } sub _syndicator_init { my ($self, %args) = @_; $args{prefix} = 'syndicator_' if !defined $args{prefix}; $self->{_syndicator}{prefix} = $args{prefix}; if (defined $args{types}) { croak("'types' argument must be an array") if ref $args{types} ne 'ARRAY'; if (@{ $args{types} } == 4) { $self->{_syndicator}{server_event} = $args{types}[0]; $self->{_syndicator}{user_event} = $args{types}[2]; $args{types} = { @{ $args{types} } }; } elsif (@{ $args{types} } == 2) { $self->{_syndicator}{server_event} = $args{types}[0]; $self->{_syndicator}{user_event} = $args{types}[1]; } else { croak('Only two event types are supported'); } } else { $args{types} = { SERVER => 'S', USER => 'U' }; $self->{_syndicator}{server_event} = 'SERVER'; $self->{_syndicator}{user_event} = 'USER'; } # set up the plugin system $self->_pluggable_init( prefix => delete $args{prefix}, reg_prefix => delete $args{reg_prefix}, debug => delete $args{debug}, types => delete $args{types}, ); if (ref $args{object_states} eq 'ARRAY') { my $start_stop = "Don't install handlers for _start or _stop. Use" . "_syndicator_started or _syndicator_stopped instead"; my $reg_unreg = "Don't install handlers for register or unregister." . " Those are handled by POE::Component::Syndicator"; for (my $i = 1; $i <= $#{ $args{object_states} }; $i += 2) { my $events = $args{object_states}[$i]; if (ref $events eq 'HASH') { if (defined $events->{_start} || defined $events->{_stop}) { croak($start_stop); } elsif (defined $events->{register} || defined $events->{unregister}) { croak($reg_unreg); } } elsif (ref $events eq 'ARRAY') { for my $event (@$events) { if ($event eq '_start' || $event eq '_stop') { croak($start_stop); } elsif ($event eq 'register' || $event eq 'unregister') { croak($reg_unreg); } } } } } # set up our POE session POE::Session->create( object_states => [ $self => { _start => '_syndicator_start', _default => '_syndicator_default', _stop => '_syndicator_stop', register => '_syndicator_register', unregister => '_syndicator_unregister', }, $self => [qw( _syndicator_shutdown _syndicator_send_pending_events _syndicator_delay _syndicator_delay_remove _syndicator_sig_register _syndicator_sig_shutdown _syndicator_sig_die )], ($args{object_states} ? @{ $args{object_states} } : ()), ], ($args{options} ? (options => delete $args{options}) : ()), args => [%args], heap => $self, ); return; } sub _syndicator_sig_die { my ($kernel, $self, $ex) = @_[KERNEL, OBJECT, ARG1]; chomp $ex->{error_str}; my $error = "Event $ex->{event} in session ".$ex->{dest_session}->ID ." raised exception:\n $ex->{error_str}"; warn $error, "\n"; $kernel->sig_handled(); return; } sub _syndicator_destroy { my ($self, @args) = @_; $self->call('_syndicator_shutdown', @args); return; } sub _syndicator_shutdown { my ($kernel, $self, @args) = @_[KERNEL, OBJECT, ARG0..$#_]; return if $self->{_shutting_down}; $kernel->alarm_remove_all(); $self->_pluggable_destroy(); $self->send_event($self->{_syndicator}{prefix} . 'shutdown', @args); $self->{_shutting_down} = 1; return; } sub _syndicator_start { my ($kernel, $sender, $session, $self, %args) = @_[KERNEL, SENDER, SESSION, OBJECT, ARG0..$#_]; $kernel->sig('DIE', '_syndicator_sig_die'); $self->{_syndicator}{session_id} = $session->ID(); # set an alias to keep our session alive if (defined $args{alias}) { $kernel->alias_set($args{alias}); $self->{_syndicator}{session_alias} = $args{alias}; } else { $kernel->alias_set("$self"); $self->{_syndicator}{session_alias} = "$self"; } $args{register_signal} = 'SYNDICATOR_REGISTER' if !defined $args{register_signal}; $kernel->sig($args{register_signal}, '_syndicator_sig_register'); $args{shutdown_signal} = 'SYNDICATOR_SHUTDOWN' if !defined $args{shutdown_signal}; $kernel->sig($args{shutdown_signal}, '_syndicator_sig_shutdown'); # if called from a parent session, register the parent for all events # and detach our session from the parent if ($sender != $kernel) { my $sender_id = $sender->ID; my $prefix = $self->{_syndicator}{prefix}; $self->{_syndicator}{events}{all}{$sender_id} = $sender_id; $self->{_syndicator}{sessions}{$sender_id}{ref} = $sender_id; $self->{_syndicator}{sessions}{$sender_id}{refcnt}++; $kernel->refcount_increment($sender_id, REFCOUNT_TAG); $kernel->post($sender, "${prefix}registered", $self); $kernel->detach_myself(); } $kernel->call($session, 'syndicator_started'); return; } sub _syndicator_default { my ($self, $event, $args) = @_[OBJECT, ARG0, ARG1]; return if $event =~ /^_/; return if $event =~ /^syndicator_(?:started|stopped)$/; $self->send_user_event($event, [@$args]); return; } sub _syndicator_stop { my ($kernel, $self) = @_[KERNEL, OBJECT]; $kernel->call($self->{_syndicator}{session_id}, 'syndicator_stopped'); return; } sub _syndicator_unregister_sessions { my ($self) = @_; for my $session_id ( keys %{ $self->{_syndicator}{sessions} } ) { my $refcnt = $self->{_syndicator}{sessions}{$session_id}{refcnt}; while ($refcnt-- > 0) { $poe_kernel->refcount_decrement($session_id, REFCOUNT_TAG); } delete $self->{_syndicator}{sessions}{$session_id}; } return; } sub yield { my ($self, @args) = @_; $poe_kernel->post($self->{_syndicator}{session_id}, @args); return; } sub _syndicator_sig_register { my ($kernel, $self, $session, $signal, $sender, @events) = @_[KERNEL, OBJECT, SESSION, ARG0..$#_]; if (!@events || !defined $sender) { warn "Signal $signal: not enough arguments\n"; return; } my $sender_id; if (my $ref = $kernel->alias_resolve($sender)) { $sender_id = $ref->ID(); } else { warn "Signal $signal: can't resolve sender $sender\n"; return; } $self->_syndicator_reg($sender_id, @events); return; } sub _syndicator_sig_shutdown { my ($kernel, $self, @args) = @_[KERNEL, OBJECT, ARG2..$#_]; $kernel->yield('shutdown', @args) if !$self->{_shutdown_event_sent}; $self->{_shutdown_event_sent} = 1; return; } sub _syndicator_register { my ($kernel, $self, $session, $sender, @events) = @_[KERNEL, OBJECT, SESSION, SENDER, ARG0 .. $#_]; @events = 'all' if !@events; my $sender_id = $sender->ID(); $self->_syndicator_reg($sender_id, @events); return; } sub _syndicator_reg { my ($self, $sender_id, @events) = @_; my $prefix = $self->{_syndicator}{prefix}; for my $event (@events) { $self->{_syndicator}{events}{$event}{$sender_id} = $sender_id; $self->{_syndicator}{sessions}{$sender_id}{ref} = $sender_id; if (!$self->{_syndicator}{sessions}{$sender_id}{refcnt} && $sender_id ne $self->{_syndicator}{session_id}) { $poe_kernel->refcount_increment($sender_id, REFCOUNT_TAG); } $self->{_syndicator}{sessions}{$sender_id}{refcnt}++; } # BINGOS: # Apocalypse is gonna hate me for this as 'registered' events will bypass # the plugin system, but I can't see how this event will be relevant # without some sort of reference, like what session has registered. I'm # not going to start hurling session references around at this point :) $poe_kernel->post($sender_id, "${prefix}registered", $self); return; } sub _syndicator_unregister { my ($kernel, $self, $session, $sender, @events) = @_[KERNEL, OBJECT, SESSION, SENDER, ARG0 .. $#_]; @events = 'all' if !@events; my $sender_id = $sender->ID(); my $prefix = $self->{_syndicator}{prefix}; for my $event (@events) { my $blah = delete $self->{_syndicator}{events}{$event}{$sender_id}; if (!defined $blah) { warn "Sender $sender_id hasn't registered for '$event' events"; next; } if (!keys %{ $self->{_syndicator}{events}{$event} }) { delete $self->{_syndicator}{events}{$event}; } if (--$self->{_syndicator}{sessions}{$sender_id}{refcnt} <= 0) { delete $self->{_syndicator}{sessions}{$sender_id}; if ($session != $sender) { $kernel->refcount_decrement($sender_id, REFCOUNT_TAG); } } } return; } sub delay { my ($self, $arrayref, $time) = @_; if (!defined $arrayref || ref $arrayref ne 'ARRAY' || !@$arrayref) { croak('First argument to delay() must be a populated ARRAYREF'); } croak('No time specified') if !defined $time; return $self->call('_syndicator_delay', [@$arrayref], $time); } sub _syndicator_delay { my ($kernel, $self, $arrayref, $time) = @_[KERNEL, OBJECT, ARG0, ARG1]; my $event = shift @$arrayref; my $alarm_id = $kernel->delay_set($event, $time, @$arrayref); if ($alarm_id) { my $prefix = $self->{_syndicator}{prefix}; $self->send_event("${prefix}delay_set", $alarm_id, $event, @$arrayref); } return $alarm_id; } sub delay_remove { my ($self, $alarm_id) = @_; croak('No alarm id specified') if !defined $alarm_id; return $self->call('_syndicator_delay_remove', $alarm_id); } sub _syndicator_delay_remove { my ($kernel, $self, $alarm_id) = @_[KERNEL, OBJECT, ARG0]; my @old_alarm_list = $kernel->alarm_remove($alarm_id); if (@old_alarm_list) { my $args = $old_alarm_list[-1]; my $prefix = $self->{_syndicator}{prefix}; $self->send_event("${prefix}delay_removed", $alarm_id, $args); return $args; } return; } sub call { my ($self, @args) = @_; return $poe_kernel->call($self->{_syndicator}{session_id}, @args); } sub session_id { my ($self) = @_; return $self->{_syndicator}{session_id}; } sub session_alias { my ($self) = @_; return $self->{_syndicator}{session_alias}; } sub send_user_event { my ($self, $event, $args) = @_; push @{ $self->{_syndicator}{pending_events} }, []; my $user_type = $self->{_syndicator}{user_event}; my $eat = $self->_pluggable_process($user_type, $event, $args); $self->call('_syndicator_send_pending_events'); return $eat; } sub send_event { my ($self, $event, @args) = @_; $self->yield('_syndicator_send_pending_events', $event, @args); return; } sub send_event_now { my ($self, $event, @args) = @_; $self->call('_syndicator_send_pending_events', $event, @args); return; } sub send_event_next { my ($self, $event, @args) = @_; if (!$self->{_syndicator}{pending_events} || !@{ $self->{_syndicator}{pending_events} }) { croak('send_event_next() can only be called from an event handler'); } else { $event =~ s/^\Q$self->{_syndicator}{prefix}//; push @{ $self->{_syndicator}{pending_events}[-1] }, [$event, \@args]; } return; } sub _syndicator_send_pending_events { my ($kernel, $session, $self, $new_event, @args) = @_[KERNEL, SESSION, OBJECT, ARG0, ARG1..$#_]; my $session_id = $session->ID(); my %sessions; my $prefix = $self->{_syndicator}{prefix}; # create new context if we were passed an event directly if (defined $new_event) { $new_event =~ s/^\Q$prefix//; my @our_events = [$new_event, \@args]; push @{ $self->{_syndicator}{pending_events} }, \@our_events; } while (my ($ev) = shift @{ $self->{_syndicator}{pending_events}[-1] }) { last if !defined $ev; my ($event, $args) = @$ev; my @ids = ( (exists $self->{_syndicator}{events}{all} ? values %{ $self->{_syndicator}{events}{all} } : ()), (exists $self->{_syndicator}{events}{$event} ? values %{ $self->{_syndicator}{events}{$event} } : ()), ); $sessions{$_} = $_ for @ids; # Make sure our session gets notified of any requested events before # any other bugger if (delete $sessions{$session_id}) { $kernel->call($session_id, "$prefix$event", @$args); } # then let the plugin system process this my $server_type = $self->{_syndicator}{server_event}; if ($self->_pluggable_process($server_type, $event, $args) != PLUGIN_EAT_ALL) { # and finally, let registered sessions process it for my $session (values %sessions) { # We have to use call() here to maintain consistency, for # example if a subclass maintains state which needs to make # sense at the time this event is delivered (e.g. # POE::Component::IRC::State). But this is not good if the # user decides to use $poe_kernel->run_while() (which # POE::Quickie and LWP::UserAgent::POE do). But then again # that's a risk for everyone using call(), and the user # should know not to use such modules in event handlers # which might get call()ed by foreign sessions. $kernel->call($session, "$prefix$event", @$args); } } # unregister all sessions if we're shutting down if ($event eq 'shutdown') { $self->_syndicator_unregister_sessions(); } } pop @{ $self->{_syndicator}{pending_events} }; return; } 1; =encoding utf8 =head1 NAME POE::Component::Syndicator - A POE component base class which implements the Observer pattern =head1 SYNOPSIS package POE::Component::IRC; use strict; use warnings; use POE; use base 'POE::Component::Syndicator'; # our constructor sub spawn { my ($package, %args) = @_; # process arguments... my $self = bless \%args, $package; # set up our plugin system and POE session $self->_syndicator_init( prefix => 'irc_', reg_prefix => 'PCI_', types => [SERVER => 'S', USER => 'U'], object_states => [qw( syndicator_started shutdown )], ); return $self; } sub syndicator_started { my ($kernel, $self) = @_[KERNEL, OBJECT]; # connect to a server, etc... } # plugin handler for SERVER event 'hlagh' sub S_hlagh { # ... } sub shutdown { my ($kernel, $self) = @_[KERNEL, OBJECT]; # disconnect from a server, etc... # shut down the syndicator $self->_syndicator_destroy(); } =head1 DESCRIPTION POE::Component::Syndicator is a base class for POE components which need to handle a persistent resource (e.g. a connection to an IRC server) for one or more sessions in an extendable way. This module (as well as L, which this module inherits from) was born out of L, the guts of which quickly spread to other POE components. Now they can all inherit from this module instead. The component provides an event queue, which can be managed with the methods documented below. It handles delivery of events to the object itself, all interested plugins, and all interested sessions. =head2 Component lifetime You start by calling L|/_syndicator_init>, which will create a POE session with your object as its heap, and a few event handlers installed. The events described in L delimit the start and end of the session's lifetime. In between those, interested plugins and sessions will receive various events, usually starting with L|/_syndicator_registered>. In this phase, your subclass and plugins can call the L and send the L documented below. When the component has been shut down, sessions (but not plugins) will receive a L|/_syndicator_shutdown> event. After this, the component will become unusable. =head2 A note on events In this document, an I (unless explicitly referred to as a I) is defined as a message originating from POE::Component::Syndicator, delivered to plugins (and the subclass) via plugin methods and to registered sessions as POE events. Interested sessions are considered consumers only, so they always receive copies of event arguments, whereas interested plugins and subclasses receive scalar references to them. This allows them to alter, add, or remove event arguments before sessions (or even other plugins) receive them. For more information about plugins, see L's documentation. A subclass does not have to register for plugin events. Two event types are supported: SERVER and USER, though their names can be overriden (see L|/_syndicator_init>). =head3 SERVER events These represent data received from the network or some other outside resource (usually a server, hence the default name). SERVER events are generated by the L|/send_event> methods. These events are delivered to the subclass and plugins (method C) and interested sessions (event C). =head3 USER events These represent commands about to be sent to a server or some other resource. USER events are generated by L|/send_user_event>. In addition, all POE events sent to this component's session (e.g. with L|/yield>) which do not have a handler will generate corresponding USER events. USER events are considered more private, so they are only delivered to the subclass and plugins, not to sessions. =head1 PRIVATE METHODS The following methods should only be called by a subclass. =head2 C<_syndicator_init> You should call this in your constructor. It initializes L, creates the Syndicator's POE session, and calls the L|/syndicator_started> POE events. It takes the following arguments: B<'prefix'>, a prefix for all your event names, when sent to interested sessions. If you don't supply this, L's default (B<'pluggable'>) will be used. B<'reg_prefix'>, the prefix for the C/C plugin methods If you don't supply this, L's default (B<'plugin_'>) will be used. B<'debug'>, a boolean, if true, will cause a warning to be printed every time a plugin event handler raises an exception. B<'types'>, a 2-element arrayref of the types of events that your component will support, or a 4-element (2 pairs) arrayref where the event types are keys and their abbrevations (used as plugin event method prefixes) are values (see L and L for more information). The two event types are fundamentally different, so make sure you supply them in the right order. If you don't provide this argument, C<< [ SERVER => 'S', USER => 'U' ] >> will be used. B<'register_signal'>, the name of the register signal (see L). Defaults to B<'SYNDICATOR_REGISTER'>. B<'shutdown_signal'>, the name of the shutdown signal (see L). Defaults to B<'SYNDICATOR_SHUTDOWN'>. B<'object_states'> an arrayref of additional object states to add to the POE session. Same as the 'object_states' argument to L's C method. You'll want to add a handler for at least the L|/syndicator_started> event. B<'options'>, a hash of options for L's constructor. If you call C<_syndicator_init> from inside another POE session, the component will automatically register that session as wanting all events. That session will first receive a L|/syndicator_registered> event. =head2 C<_syndicator_destroy> Call this method when you want Syndicator to clean up (delete all plugins, etc) and make sure it won't keep the POE session alive after all remaining events have been processed. A L|/syndicator_shutdown> event (or similar, depending on the prefix you chose) will be generated. Any argument passed to C<_syndicator_destroy> will be passed along with that event. B this method will clear all alarms for the POE session. =head1 PUBLIC METHODS =head2 C Returns the component's POE session id. =head2 C Returns the component's POE session alias. =head2 C This method provides an alternative, object-based means of posting events to the component. First argument is the event to post, following arguments are sent as arguments to the resultant post. =head2 C This method provides an alternative, object-based means of calling events to the component. First argument is the event to call, following arguments are sent as arguments to the resultant call. =head2 C Adds a new SERVER event onto the end of the queue. The event will be processed after other pending events, if any. First argument is an event name, the rest are the event arguments. $component->send_event('irc_public, 'foo!bar@baz.com', ['#mychan'], 'message'); =head2 C Adds a new SERVER event to the start of the queue. The event will be the next one to be processed. First argument is an event name, the rest are the event arguments. =head2 C Sends a new SERVER event immediately. Execution of the current POE event will be suspended (i.e. this call will block) until the new event has been processed by the component class and all plugins. First argument is an event name, the rest are the event arguments. =head2 C Sends a new USER event immediately. You should call this before every command you send to your remote server/resource. Only the subclass and plugins will see this event. Takes two arguments, an event name and an arrayref of arguments. Returns one of the C constants listed in L. After this method returns, the arrayref's contents may have been modified by the subclass or plugins. $component->send_user_event('PRIVMSG', '#mychan', 'message'); =head2 C This method provides a way of posting delayed events to the component. The first argument is an arrayref consisting of the delayed command to post and any command arguments. The second argument is the time in seconds that one wishes to delay the command being posted. my $alarm_id = $component->delay(['mode', $channel, '+o', $dude], 60); =head2 C This method removes a previously scheduled delayed event from the component. Takes one argument, the C that was returned by a L|/delay> method call. Returns an arrayref of arguments to the event that was originally requested to be delayed. my $arrayref = $component->delay_remove($alarm_id); =head1 EVENTS =head2 Local events The component will send the following POE events to its session. =head3 C Called after the session has been started (like C<_start> in L. This is where you should do your POE-related setup work such as adding new event handlers to the session. =head3 C Called right before the session is about to die (like C<_stop> in L). =head2 Input events Other POE sessions can send the following POE events to the Syndicator's session. =head3 C Takes any amount of arguments: a list of event names that your session wants to listen for, minus the prefix (specified in L/_syndicator_init>). $kernel->post('my syndicator', 'register', qw(join part quit kick)); Registering for the special event B<'all'> will cause it to send all events to your session. Calling it with no event names is equivalent to calling it with B<'all'> as an argumente. Registering will generate a L|/syndicator_registered> event that your session can trap. Registering with multiple component sessions can be tricky, especially if one wants to marry up sessions/objects, etc. Check the L section for an alternative method of registering with multiple components. =head3 C Takes any amount of arguments: a list of event names which you I want to receive. If you've previously done a L|/register> for a particular event which you no longer care about, this event will tell the component to stop sending them to you. (If you haven't, it just ignores you. No big deal.) Calling it with no event names is equivalent to calling it with B<'all'> as an argument. If you have registered for the special event B<'all'>, attempting to unregister individual events will not work. This is a 'feature'. =head3 C By default, POE::Component::Syndicator sessions never go away. You can send its session a C event manually to make it delete itself. Terminating multiple Syndicators can be tricky. Check the L section for a method of doing that. =head3 C<_default> Any POE events sent to the Syndicator's session which do not have a handler will go to the Syndicator's C<_default> handler, will generate L of the same name. If you install your own C<_default> handler, make sure you do the same thing before you handle an event: use Object::Pluggable::Constants 'PLUGIN_EAT_ALL'; $poe_kernel->state('_default', $self, '__default'); sub __default { my ($self, $event, $args) = @_[OBJECT, ARG0, ARG1]; # do nothing if a plugin eats the event return if $self->send_user_event($event, [@$args]) == PLUGIN_EAT_ALL; # handle the event # ... } Note that the handler for the C<_default> event must be named something other than '_default', because that name is reserved for the plugin-type default handler (see the L docs). =head2 Output events The Syndicator will send the following events at various times. The B<'syndicator_'> prefix in these event names can be customized with a B<'prefix'> argument to L/_syndicator_init>. =head3 C Sent once to the requesting session on registration (see L|/register>). C is a reference to the component's object. =head3 C Sent to all interested sessions when the component has been shut down. See L|/_syndicator_destroy>. =head3 C Sent to the subclass, plugins, and all interested sessions on a successful addition of a delayed event using the L|/delay> method. C will be the alarm_id which can be used later with L|/delay_remove>. Subsequent parameters are the arguments that were passed to L|/delay>. =head3 C Sent to the subclass, plugins, and all interested sessions when a delayed event is successfully removed. C will be the alarm_id that was removed. Subsequent parameters are the arguments that were passed to L|/delay>. =head3 All other events All other events sent by the Syndicator are USER events (generated with L|/send_user_event>) and SERVER events (generated with L|/send_event>) which will be delivered normally. Your subclass and plugins are responsible for generating them. =head1 SIGNALS The component will handle a number of custom signals that you may send using L's C method. They allow any session to communicate with every instance of the component in certain ways without having references to their objects or knowing about their sessions. The names of these signals can be customized with L|/_syndicator_init>. =head2 C Registers for an event with the component. See L|/register>. =head2 C Causes a 'shutdown' event to be sent to your session. Any arguments to the signal will be passed along to the event. That's where you should clean up and call L|/_syndicator_destroy>. =head1 AUTHOR Hinrik Ern SigurEsson, L, Chris C Williams L, Apocalypse L, and probably others. =head1 LICENSE AND COPYRIGHT Copyright 2011 Hinrik Ern SigurEsson This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut