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

VERSION

10       version 1.28
11

SYNOPSIS

13        # A simple POE Component that sends ping events to registered sessions
14        # and plugins every second.
15
16        {
17            package SimplePoCo;
18
19            use strict;
20            use warnings;
21            use base qw(POE::Component::Pluggable);
22            use POE;
23            use POE::Component::Pluggable::Constants qw(:ALL);
24
25            sub spawn {
26                my ($package, %opts) = @_;
27                my $self = bless \%opts, $package;
28
29                $self->_pluggable_init(
30                    prefix => 'simplepoco_',
31                    types  => [qw(EXAMPLE)],
32                    debug  => 1,
33                );
34
35                POE::Session->create(
36                    object_states => [
37                        $self => { shutdown => '_shutdown' },
38                        $self => [qw(_send_ping _start register unregister __send_event)],
39                    ],
40                );
41
42                return $self;
43            }
44
45            sub shutdown {
46                my ($self) = @_;
47                $poe_kernel->post($self->{session_id}, 'shutdown');
48            }
49
50            sub _pluggable_event {
51                my ($self) = @_;
52                $poe_kernel->post($self->{session_id}, '__send_event', @_);
53            }
54
55            sub _start {
56                my ($kernel, $self) = @_[KERNEL, OBJECT];
57                $self->{session_id} = $_[SESSION]->ID();
58
59                if ($self->{alias}) {
60                    $kernel->alias_set($self->{alias});
61                }
62                else {
63                    $kernel->refcount_increment($self->{session_id}, __PACKAGE__);
64                }
65
66                $kernel->delay(_send_ping => $self->{time} || 300);
67                return;
68            }
69
70            sub _shutdown {
71                 my ($kernel, $self) = @_[KERNEL, OBJECT];
72
73                 $self->_pluggable_destroy();
74                 $kernel->alarm_remove_all();
75                 $kernel->alias_remove($_) for $kernel->alias_list();
76                 $kernel->refcount_decrement($self->{session_id}, __PACKAGE__) if !$self->{alias};
77                 $kernel->refcount_decrement($_, __PACKAGE__) for keys %{ $self->{sessions} };
78
79                 return;
80            }
81
82            sub register {
83                my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
84                my $sender_id = $sender->ID();
85                $self->{sessions}->{$sender_id}++;
86
87                if ($self->{sessions}->{$sender_id} == 1) {
88                    $kernel->refcount_increment($sender_id, __PACKAGE__);
89                    $kernel->yield(__send_event => 'simplepoco_registered', $sender_id);
90                }
91
92                return;
93            }
94
95            sub unregister {
96                my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
97                my $sender_id = $sender->ID();
98                my $record = delete $self->{sessions}->{$sender_id};
99
100                if ($record) {
101                    $kernel->refcount_decrement($sender_id, __PACKAGE__);
102                    $kernel->yield(__send_event => 'simplepoco_unregistered', $sender_id);
103                }
104
105                return;
106            }
107
108            sub __send_event {
109                my ($kernel, $self, $event, @args) = @_[KERNEL, OBJECT, ARG0..$#_];
110
111                return 1 if $self->_pluggable_process(EXAMPLE => $event, \(@args)) == PLUGIN_EAT_ALL;
112                $kernel->post($_, $event, @args) for keys %{ $self->{sessions} };
113            }
114
115            sub _send_ping {
116                my ($kernel, $self) = @_[KERNEL, OBJECT];
117
118                $kernel->yield(__send_event => 'simplepoco_ping', 'Wake up sleepy');
119                $kernel->delay(_send_ping => $self->{time} || 1);
120                return;
121            }
122        }
123
124        {
125            package SimplePoCo::Plugin;
126            use strict;
127            use warnings;
128            use POE::Component::Pluggable::Constants qw(:ALL);
129
130            sub new {
131                my $package = shift;
132                return bless { @_ }, $package;
133            }
134
135            sub plugin_register {
136                my ($self, $pluggable) = splice @_, 0, 2;
137                print "Plugin added\n";
138                $pluggable->plugin_register($self, 'EXAMPLE', 'all');
139                return 1;
140            }
141
142            sub plugin_unregister {
143                print "Plugin removed\n";
144                return 1;
145            }
146
147            sub EXAMPLE_ping {
148                my ($self, $pluggable) = splice @_, 0, 2;
149                my $text = ${ $_[0] };
150                print "Plugin got '$text'\n";
151                return PLUGIN_EAT_NONE;
152            }
153        }
154
155        use strict;
156        use warnings;
157        use POE;
158
159        my $pluggable = SimplePoCo->spawn(
160            alias => 'pluggable',
161            time  => 1,
162        );
163
164        POE::Session->create(
165            package_states => [
166                main => [qw(_start simplepoco_registered simplepoco_ping)],
167            ],
168        );
169
170        $poe_kernel->run();
171
172        sub _start {
173            my $kernel = $_[KERNEL];
174            $kernel->post(pluggable => 'register');
175            return;
176        }
177
178        sub simplepoco_registered {
179            print "Main program registered for events\n";
180            my $plugin = SimplePoCo::Plugin->new();
181            $pluggable->plugin_add('TestPlugin', $plugin);
182            return;
183        }
184
185        sub simplepoco_ping {
186            my ($heap, $text) = @_[HEAP, ARG0];
187            print "Main program got '$text'\n";
188            $heap->{got_ping}++;
189            $pluggable->shutdown() if $heap->{got_ping} == 3;
190            return;
191        }
192

DESCRIPTION

194       POE::Component::Pluggable is a base class for creating plugin enabled
195       POE Components. It is a generic port of POE::Component::IRC's plugin
196       system.
197
198       If your component dispatches events to registered POE sessions, then
199       POE::Component::Pluggable may be a good fit for you.
200
201       Basic use would involve subclassing POE::Component::Pluggable, then
202       overriding _pluggable_event() and inserting _pluggable_process()
203       wherever you dispatch events from.
204
205       Users of your component can then load plugins using the plugin methods
206       provided to handle events generated by the component.
207
208       You may also use plugin style handlers within your component as
209       _pluggable_process() will attempt to process any events with local
210       method calls first. The return value of these handlers has the same
211       significance as the return value of 'normal' plugin handlers.
212

PRIVATE METHODS

214       Subclassing POE::Component::Pluggable gives your object the following
215       'private' methods:
216
217   "_pluggable_init"
218       This should be called on your object after initialisation, but before
219       you want to start processing plugins. It accepts a number of
220       argument/value pairs:
221
222        'types', an arrayref of the types of events that your poco will support,
223                 OR a hashref with the event types as keys and their abbrevations
224                 (used as plugin event method prefixes) as values. This argument is
225                 mandatory.
226
227        'prefix', the prefix for your events (default: 'pluggable_');
228        'reg_prefix', the prefix for the register()/unregister() plugin methods
229                      (default: 'plugin_');
230        'debug', a boolean, if true, will cause a warning to be printed every time a
231                 plugin call fails.
232
233       Notes: 'prefix' should probably end with a '_'. The types specify the
234       prefixes for plugin handlers. You can specify as many different types
235       as you require.
236
237   "_pluggable_destroy"
238       This should be called from any shutdown handler that your poco has. The
239       method unloads any loaded plugins.
240
241   "_pluggable_process"
242       This should be called before events are dispatched to interested
243       sessions.  This gives pluggable a chance to discard events if requested
244       to by a plugin.
245
246       The first argument is a type, as specified to _pluggable_init().
247
248        sub _dispatch {
249            # stuff
250
251            return 1 if $self->_pluggable_process($type, $event, \(@args)) == PLUGIN_EAT_ALL;
252
253            # dispatch event to interested sessions.
254        }
255
256       This example demonstrates event arguments being passed as scalar refs
257       to the plugin system. This enables plugins to mangle the arguments if
258       necessary.
259
260   "_pluggable_event"
261       This method should be overridden in your class so that pipeline can
262       dispatch events through your event dispatcher. Pipeline sends a
263       prefixed 'plugin_add' and 'plugin_del' event whenever plugins are added
264       or removed, respectively.  A prefixed 'plugin_error' event will be sent
265       if a plugin a) raises an exception, b) fails to return a true value
266       from its register/unregister methods, or c) fails to return a valid EAT
267       constant from a handler.
268
269        sub _pluggable_event {
270            my $self = shift;
271            $poe_kernel->post($self->{session_id}, '__send_event', @_);
272        }
273
274       There is an example of this in the SYNOPSIS.
275

PUBLIC METHODS

277       Subclassing POE::Component::Pluggable gives your object the following
278       public methods:
279
280   "pipeline"
281       Returns the POE::Component::Pluggable::Pipeline object.
282
283   "plugin_add"
284       Accepts two arguments:
285
286        The alias for the plugin
287        The actual plugin object
288
289       The alias is there for the user to refer to it, as it is possible to
290       have multiple plugins of the same kind active in one
291       POE::Component::Pluggable object.
292
293       This method goes through the pipeline's push() method, which will call
294       "$plugin-"plugin_register($pluggable)>.
295
296       Returns the number of plugins now in the pipeline if plugin was
297       initialized, "undef"/an empty list if not.
298
299   "plugin_del"
300       Accepts one argument:
301
302        The alias for the plugin or the plugin object itself
303
304       This method goes through the pipeline's remove() method, which will
305       call "$plugin-"plugin_unregister($pluggable)>.
306
307       Returns the plugin object if the plugin was removed, "undef"/an empty
308       list if not.
309
310   "plugin_get"
311       Accepts one argument:
312
313        The alias for the plugin
314
315       This method goes through the pipeline's get() method.
316
317       Returns the plugin object if it was found, "undef"/an empty list if
318       not.
319
320   "plugin_list"
321       Takes no arguments.
322
323       Returns a hashref of plugin objects, keyed on alias, or an empty list
324       if there are no plugins loaded.
325
326   "plugin_order"
327       Takes no arguments.
328
329       Returns an arrayref of plugin objects, in the order which they are
330       encountered in the pipeline.
331
332   "plugin_register"
333       Accepts the following arguments:
334
335        The plugin object
336        The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
337        The event name[s] to watch
338
339       The event names can be as many as possible, or an arrayref. They
340       correspond to the prefixed events and naturally, arbitrary events too.
341
342       You do not need to supply events with the prefix in front of them, just
343       the names.
344
345       It is possible to register for all events by specifying 'all' as an
346       event.
347
348       Returns 1 if everything checked out fine, "undef"/an empty list if
349       something is seriously wrong.
350
351   "plugin_unregister"
352       Accepts the following arguments:
353
354        The plugin object
355        The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
356        The event name[s] to unwatch
357
358       The event names can be as many as possible, or an arrayref. They
359       correspond to the prefixed events and naturally, arbitrary events too.
360
361       You do not need to supply events with the prefix in front of them, just
362       the names.
363
364       It is possible to register for all events by specifying 'all' as an
365       event.
366
367       Returns 1 if all the event name[s] was unregistered, undef if some was
368       not found.
369

PLUGINS

371       The basic anatomy of a pluggable plugin is:
372
373        # Import the constants, of course you could provide your own
374        # constants as long as they map correctly.
375        use POE::Component::Pluggable::Constants qw( :ALL );
376
377        # Our constructor
378        sub new {
379            ...
380        }
381
382        # Required entry point for pluggable plugins
383        sub plugin_register {
384            my($self, $pluggable) = @_;
385
386            # Register events we are interested in
387            $pluggable->plugin_register($self, 'SERVER', qw(something whatever));
388
389            # Return success
390            return 1;
391        }
392
393        # Required exit point for pluggable
394        sub plugin_unregister {
395            my($self, $pluggable) = @_;
396
397            # Pluggable will automatically unregister events for the plugin
398
399            # Do some cleanup...
400
401            # Return success
402            return 1;
403        }
404
405        sub _default {
406            my($self, $pluggable, $event) = splice @_, 0, 3;
407
408            print "Default called for $event\n";
409
410            # Return an exit code
411            return PLUGIN_EAT_NONE;
412        }
413
414       As shown in the example above, a plugin's "_default" subroutine (if
415       present) is called if the plugin receives an event for which it has no
416       handler.
417
418       The special exit code CONSTANTS are documented in
419       POE::Component::Pluggable::Constants.  You could provide your own as
420       long as the values match up, though.
421

TODO

423       Better documentation >:]
424

KUDOS

426       APOCAL for writing the original POE::Component::IRC plugin system.
427
428       japhy for writing POE::Component::IRC::Pipeline which improved on it.
429
430       All the happy chappies who have contributed to POE::Component::IRC over
431       the years (yes, it has been years) refining and tweaking the plugin
432       system.
433
434       The initial idea was heavily borrowed from X-Chat, BIG thanks go out to
435       the genius that came up with the EAT_* system :)
436

SEE ALSO

438       POE::Component::IRC
439
440       POE::Component::Pluggable::Pipeline
441
442       Both POE::Component::Client::NNTP and POE::Component::Server::NNTP use
443       this module as a base, examination of their source may yield further
444       understanding.
445

AUTHORS

447       •   Chris Williams <chris@bingosnet.co.uk>
448
449       •   Apocalypse <perl@0ne.us>
450
451       •   Hinrik Örn Sigurðsson
452
453       •   Jeff Pinyan
454
456       This software is copyright (c) 2017 by Chris Williams.
457
458       This is free software; you can redistribute it and/or modify it under
459       the same terms as the Perl 5 programming language system itself.
460
461
462
463perl v5.38.0                      2023-07-21      POE::Component::Pluggable(3)
Impressum