1POE::Component::PluggabUlsee(r3)Contributed Perl DocumenPtOaEt:i:oCnomponent::Pluggable(3)
2
3
4

NAME

6       POE::Component::Pluggable - A base class for creating plugin-enabled
7       POE Components.
8

SYNOPSIS

10        # A simple POE Component that sends ping events to registered sessions
11        # every 30 seconds. A rather convoluted example to be honest.
12
13        {
14            package SimplePoCo;
15
16            use strict;
17            use base qw(POE::Component::Pluggable);
18            use POE;
19            use POE::Component::Pluggable::Constants qw(:ALL);
20
21            sub spawn {
22                my ($package, %opts) = @_;
23                $opts{lc $_} = delete $opts{$_} for keys %opts;
24                my $self = bless \%opts, $package;
25
26                $self->_pluggable_init(prefix => 'simplepoco_');
27                POE::Session->create(
28                    object_states => [
29                        $self => { shutdown => '_shutdown' },
30                        $self => [qw(_send_ping _start register unregister __send_event)],
31                    ],
32                    heap => $self,
33                );
34
35                return $self;
36            }
37
38            sub shutdown {
39                my ($self) = @_;
40                $poe_kernel->post($self->{session_id}, 'shutdown');
41            }
42
43            sub _pluggable_event {
44                my ($self) = @_;
45                $poe_kernel->post($self->{session_id}, '__send_event', @_);
46            }
47
48            sub _start {
49                my ($kernel,$self) = @_[KERNEL,OBJECT];
50                $self->{session_id} = $_[SESSION]->ID();
51
52                if ($self->{alias}) {
53                    $kernel->alias_set($self->{alias});
54                }
55                else {
56                    $kernel->refcount_increment($self->{session_id}, __PACKAGE__);
57                }
58
59                $kernel->delay(_send_ping => $self->{time} || 300);
60                return;
61            }
62
63            sub _shutdown {
64                 my ($kernel, $self) = @_[KERNEL, OBJECT];
65                 $self->_pluggable_destroy();
66                 $kernel->alarm_remove_all();
67                 $self->alias_remove($_) for $kernel->alias_list();
68                 $kernel->refcount_decrement($self->{session_id}, __PACKAGE__) if !$self->{alias};
69                 $kernel->refcount_decrement($_, __PACKAGE__) for keys %{ $self->{sessions} };
70
71                 return;
72            }
73
74            sub register {
75                my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
76                my $sender_id = $sender->ID();
77                $self->{sessions}->{$sender_id}++;
78
79                if ($self->{sessions}->{$sender_id} == 1) {
80                    $kernel->refcount_increment($sender_id, __PACKAGE__);
81                    $kernel->yield(__send_event => $self->{_pluggable_prefix} . 'registered', $sender_id);
82                }
83
84                return;
85            }
86
87            sub unregister {
88                my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
89                my $sender_id = $sender->ID();
90                my $record = delete $self->{sessions}->{$sender_id};
91
92                if ($record) {
93                    $kernel->refcount_decrement($sender_id, __PACKAGE__);
94                    $kernel->yield(__send_event => $self->{_pluggable_prefix} . 'unregistered', $sender_id);
95                }
96
97                return;
98            }
99
100            sub __send_event {
101                my ($kernel, $self, $event, @args) = @_[KERNEL, OBJECT, ARG0..$#_];
102
103                return 1 if $self->_pluggable_process(PING => $event, \(@args)) == PLUGIN_EAT_ALL;
104
105                $kernel->post($_, $event, @args) for keys %{ $self->{sessions} };
106            }
107
108            sub _send_ping {
109                my ($kernel, $self) = @_[KERNEL, OBJECT];
110                my $event = $self->{_pluggable_prefix} . 'ping';
111                my @args = ('Wake up sleepy');
112                $kernel->yield(__send_event => $event, @args);
113                $kernel->delay(_send_ping => $self->{time} || 300);
114
115                return;
116            }
117        }
118
119            use POE;
120
121            my $pluggable = SimplePoCo->spawn(
122                alias => 'pluggable',
123                time => 30,
124            );
125
126            POE::Session->create(
127                package_states => [
128                    main => [qw(_start simplepoco_registered simplepoco_ping)],
129                ],
130            );
131
132            $poe_kernel->run();
133
134            sub _start {
135                my ($kernel, $heap) = @_[KERNEL, HEAP];
136                $kernel->post(pluggable => 'register');
137                return;
138            }
139
140            sub simplepoco_registered {
141                print "Yay, we registered\n";
142                return;
143            }
144
145            sub simplepoco_ping {
146                my ($sender, $text) = @_[SENDER, ARG0];
147                print "Got '$text' from ", $sender->ID, "\n";
148                return;
149            }
150

DESCRIPTION

152       POE::Component::Pluggable is a base class for creating plugin enabled
153       POE Components. It is a generic port of POE::Component::IRC's plugin
154       system.
155
156       If your component dispatches events to registered POE sessions, then
157       POE::Component::Pluggable may be a good fit for you.
158
159       Basic use would involve subclassing POE::Component::Pluggable, then
160       overriding "_pluggable_event()" and inserting "_pluggable_process()"
161       wherever you dispatch events from.
162
163       Users of your component can then load plugins using the plugin methods
164       provided to handle events generated by the component.
165
166       You may also use plugin style handlers within your component as
167       "_pluggable_process()" will attempt to process any events with local
168       method calls first. The return value of these handlers has the same
169       significance as the return value of 'normal' plugin handlers.
170

PRIVATE METHODS

172       Subclassing POE::Component::Pluggable gives your object the following
173       'private' methods:
174
175   "_pluggable_init"
176       This should be called on your object after initialisation, but before
177       you want to start processing plugins. It accepts a number of
178       argument/value pairs:
179
180        'prefix', the prefix for your events (default: 'pluggable_');
181        'reg_prefix', the prefix for the register()/unregister() plugin methods
182                      (default: 'plugin_');
183        'types', an arrayref of the types of events that your poco will support,
184                 OR a hashref with the event types as keys and their abbrevations
185                 (used as plugin event method prefixes) as values;
186
187       Notes: 'prefix' should probably end with a '_'. The types specify the
188       prefixes for plugin handlers. You can specify as many different types
189       as you require.
190
191   "_pluggable_destroy"
192       This should be called from any shutdown handler that your poco has. The
193       method unloads any loaded plugins.
194
195   "_pluggable_process"
196       This should be called before events are dispatched to interested
197       sessions.  This gives pluggable a chance to discard events if requested
198       to by a plugin.
199
200       The first argument is a type, as specified to "_pluggable_init()".
201
202        sub _dispatch {
203            # stuff
204
205            return 1 if $self->_pluggable_process($type, $event, \(@args)) == PLUGIN_EAT_ALL;
206
207            # dispatch event to interested sessions.
208        }
209
210       This example demonstrates event arguments being passed as scalar refs
211       to the plugin system. This enables plugins to mangle the arguments if
212       necessary.
213
214   "_pluggable_event"
215       This method should be overridden in your class so that pipeline can
216       dispatch events through your event dispatcher. Pipeline sends a
217       prefixed 'plugin_add' and 'plugin_del' event whenever plugins are added
218       or removed, respectively.
219
220        sub _pluggable_event {
221            my $self = shift;
222            $poe_kernel->post($self->{session_id}, '__send_event', @_);
223        }
224
225       There is an example of this in the SYNOPSIS.
226

PUBLIC METHODS

228       Subclassing POE::Component::Pluggable gives your object the following
229       public methods:
230
231   "pipeline"
232       Returns the POE::Component::Pluggable::Pipeline object.
233
234   "plugin_add"
235       Accepts two arguments:
236
237        The alias for the plugin
238        The actual plugin object
239
240       The alias is there for the user to refer to it, as it is possible to
241       have multiple plugins of the same kind active in one
242       POE::Component::Pluggable object.
243
244       This method goes through the pipeline's "push()" method, which will
245       call "$plugin-"plugin_register($pluggable)>.
246
247       Returns the number of plugins now in the pipeline if plugin was
248       initialized, "undef"/an empty list if not.
249
250   "plugin_del"
251       Accepts one argument:
252
253        The alias for the plugin or the plugin object itself
254
255       This method goes through the pipeline's "remove()" method, which will
256       call "$plugin-"plugin_unregister($pluggable)>.
257
258       Returns the plugin object if the plugin was removed, "undef"/an empty
259       list if not.
260
261   "plugin_get"
262       Accepts one argument:
263
264        The alias for the plugin
265
266       This method goes through the pipeline's "get()" method.
267
268       Returns the plugin object if it was found, "undef"/an empty list if
269       not.
270
271   "plugin_list"
272       Takes no arguments.
273
274       Returns a hashref of plugin objects, keyed on alias, or an empty list
275       if there are no plugins loaded.
276
277   "plugin_order"
278       Takes no arguments.
279
280       Returns an arrayref of plugin objects, in the order which they are
281       encountered in the pipeline.
282
283   "plugin_register"
284       Accepts the following arguments:
285
286        The plugin object
287        The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
288        The event name[s] to watch
289
290       The event names can be as many as possible, or an arrayref. They
291       correspond to the prefixed events and naturally, arbitrary events too.
292
293       You do not need to supply events with the prefix in front of them, just
294       the names.
295
296       It is possible to register for all events by specifying 'all' as an
297       event.
298
299       Returns 1 if everything checked out fine, "undef"/an empty list if
300       something is seriously wrong.
301
302   "plugin_unregister"
303       Accepts the following arguments:
304
305        The plugin object
306        The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
307        The event name[s] to unwatch
308
309       The event names can be as many as possible, or an arrayref. They
310       correspond to the prefixed events and naturally, arbitrary events too.
311
312       You do not need to supply events with the prefix in front of them, just
313       the names.
314
315       It is possible to register for all events by specifying 'all' as an
316       event.
317
318       Returns 1 if all the event name[s] was unregistered, undef if some was
319       not found.
320

PLUGINS

322       The basic anatomy of a pluggable plugin is:
323
324        # Import the constants, of course you could provide your own
325        # constants as long as they map correctly.
326        use POE::Component::Pluggable::Constants qw( :ALL );
327
328        # Our constructor
329        sub new {
330            ...
331        }
332
333        # Required entry point for pluggable plugins
334        sub plugin_register {
335            my($self, $pluggable) = @_;
336
337            # Register events we are interested in
338            $pluggable->plugin_register($self, 'SERVER', qw(something whatever));
339
340            # Return success
341            return 1;
342        }
343
344        # Required exit point for pluggable
345        sub plugin_unregister {
346            my($self, $pluggable) = @_;
347
348            # Pluggable will automatically unregister events for the plugin
349
350            # Do some cleanup...
351
352            # Return success
353            return 1;
354        }
355
356        sub _default {
357            my($self, $pluggable, $event) = splice @_, 0, 3;
358
359            print "Default called for $event\n";
360
361            # Return an exit code
362            return PLUGIN_EAT_NONE;
363        }
364
365       As shown in the example above, a plugin's "_default" subroutine (if
366       present) is called if the plugin receives an event for which it has no
367       handler.
368
369       The special exit code CONSTANTS are documented in
370       POE::Component::Pluggable::Constants.  You could provide your own as
371       long as the values match up, though.
372

TODO

374       Better documentation >:]
375

AUTHOR

377       Chris 'BinGOs' Williams <chris@bingosnet.co.uk>
378

LICENSE

380       Copyright "(c)" Chris Williams, Apocalypse, Hinrik A~Xrn SigurA~Xsson
381       and Jeff Pinyan
382
383       This module may be used, modified, and distributed under the same terms
384       as Perl itself. Please see the license that came with your Perl
385       distribution for details.
386

KUDOS

388       APOCAL for writing the original POE::Component::IRC plugin system.
389
390       japhy for writing POE::Component::IRC::Pipeline which improved on it.
391
392       All the happy chappies who have contributed to POE::Component::IRC over
393       the years (yes, it has been years) refining and tweaking the plugin
394       system.
395
396       The initial idea was heavily borrowed from X-Chat, BIG thanks go out to
397       the genius that came up with the EAT_* system :)
398

SEE ALSO

400       POE::Component::IRC
401
402       POE::Component::Pluggable::Pipeline
403
404       Both POE::Component::Client::NNTP and POE::Component::Server::NNTP use
405       this module as a base, examination of their source may yield further
406       understanding.
407
408
409
410perl v5.12.0                      2009-08-18      POE::Component::Pluggable(3)
Impressum