root/lang/perl/Class-Hookable/trunk/lib/Class/Hookable.pm @ 1745

Revision 1745, 11.0 kB (checked in by nyarla, 6 years ago)

lang/perl/Class-Hookable: fix POD.

Line 
1package Class::Hookable;
2
3use strict;
4use warnings;
5
6use Carp ();
7use Scalar::Util();
8
9our $VERSION = '0.01';
10
11sub new { bless {}, shift }
12
13sub hooks {
14    my ( $self ) =  @_;
15
16    if ( ref $self->{'Class::Hookable'} ne 'HASH' ) {
17        $self->{'Class::Hookable'} = {
18            hooks => {},
19        };
20    }
21
22    return $self->{'Class::Hookable'}->{'hooks'};
23}
24
25sub register_hook {
26    my ( $self, $plugin, @hooks ) = @_;
27
28    Carp::croak "Plugin object is not blessed object or class name"
29        if ( ref $plugin && ! Scalar::Util::blessed($plugin) );
30
31    while ( my ( $hook, $callback ) = splice @hooks, 0, 2 ) {
32        my $action = {
33            plugin      => $plugin,
34            callback    => $callback,
35        };
36
37        if ( $self->filter_plugin( $hook, $action ) ) {
38            $self->hooks->{$hook} = []
39                if ( ref $self->hooks->{$hook} ne 'ARRAY' );
40
41            push @{ $self->hooks->{$hook} }, $action;
42        }
43    }
44}
45
46sub filter_plugin { 1 }
47
48sub registered_hooks {
49    my ( $self, $object ) = @_;
50
51    if ( ref $object && ! Scalar::Util::blessed( $object ) ) {
52        Carp::croak "Argument is not blessed object or class name.";
53    }
54
55    my $is_class = ( ! ref $object ) ? 1 : 0 ;
56    my @hooks = ();
57
58    for my $hook ( keys %{ $self->hooks } ) {
59        for my $action ( @{ $self->hooks->{$hook} } ) {
60            my $plugin = $action->{'plugin'};
61            my $class  = ref $plugin || $plugin;
62            if ( $is_class ) {
63                push @hooks, $hook if ( $class eq $object );
64            }
65            else {
66                push @hooks, $hook if ( $plugin eq $object );
67            }
68        }
69    }
70
71    return @hooks;
72}
73
74sub registered_plugins {
75    my ( $self, $hook ) = @_;
76
77    Carp::croak "Hook name is not specified." if ( ! defined $hook );
78
79    my $list = $self->hooks->{$hook};
80       $list ||= [];
81
82    return @{ $list };
83}
84
85sub delete_plugin {
86    my ( $self, $object, @hooks ) = @_;
87
88    if ( ref $object && ! Scalar::Util::blessed($object) ) {
89        Carp::croak "Argument is not blessed object or class name.";
90    }
91
92    my $is_class = ( ! ref $object ) ? 1  : 0 ;
93    @hooks = keys %{ $self->hooks } if ( @hooks == 0 );
94
95    for my $hook ( $self->registered_hooks( $object ) ) {
96        next if ( ! grep { $hook eq $_ } @hooks );
97
98        my @actions = ();
99        for my $action ( $self->registered_plugins( $hook ) ) {
100            my $plugin  = $action->{'plugin'};
101            my $class   = ref $plugin || $plugin;
102            if ( $is_class ) {
103                push @actions, $action if ( $class ne $object );
104            }
105            else {
106                push @actions, $action if ( $plugin ne $object );
107            }
108        }
109
110        $self->hooks->{$hook} = \@actions;
111    }
112
113}
114
115sub delete_hook {
116    my ( $self, $hook, @plugins ) = @_;
117
118    Carp::croak "Hook is not specified." if ( ! defined $hook );
119
120    if ( @plugins == 0 ) {
121        $self->hooks->{$hook} = [];
122    }
123    else {
124        for my $plugin ( @plugins ) {
125            $self->delete_plugin( $plugin, $hook );
126        }
127    }
128}
129
130sub run_hook {
131    my ( $self, $hook, $args, $once, $callback ) = @_;
132
133    if ( defined $callback && ref $callback ne 'CODE' ) {
134        Carp::croak "callabck is not code reference.";
135    }
136   
137
138    my @results;
139
140    my $context = ( defined $self->context ) ? $self->context : $self ;
141
142    for my $action ( $self->registered_plugins( $hook ) ) {
143        if ( $self->dispatch_plugin( $hook, $args, $action ) ) {
144            my $plugin = $action->{'plugin'};
145            my $result = $action->{'callback'}->( $plugin, $context, $args );
146            $callback->( $result ) if ( $callback );
147            if ( $once ) {
148                return $result if ( defined $once );
149            }
150            else {
151                push @results, $result;
152            }
153        }
154    }
155
156    return if ( $once );
157    return @results;
158}
159
160sub run_hook_once {
161    my ( $self, $hook, $args, $callback ) = @_;
162    return $self->run_hook( $hook, $args, 1, $callback );
163}
164
165sub dispatch_plugin { 1 }
166
167sub context {
168    my $self = shift;
169
170    if ( ref $self->{'Class::Hookable'} ne 'HASH' ) {
171        $self->{'Class::Hookable'} = {
172            hooks => {},
173        };
174    }
175
176    if ( @_ ) {
177        my $context = shift;
178        $self->{'Class::Hookable'}->{'context'} = $context;
179    }
180    else {
181        return $self->{'Class::Hookable'}->{'context'};
182    }
183}
184
1851;
186__END__
187
188=head1 NAME
189
190Class::Hookable - Base class for hook mechanism
191
192=head1 SYNOPSIS
193
194  package MyApp::Plugins;
195  use base qw( Class::Hookable );
196 
197  my $hook = MyApp::Plugins->new;
198 
199  $hook->register_hook(
200      $plugin,
201      'hook.name' => $plugin->can('callback'),
202  );
203 
204  $hook->run_hook('hook.name', $args);
205
206=head1 DESCRIPTION
207
208Class::Hookable is the simple base class for the hook mechanism.
209This module supports only a hook mechanism.
210
211This module was made by making reference to the hook mechanism of L<Plagger>.
212I thank L<Tatsuhiko miyagawa|http://search.cpan.org/~miyagawa/> who made wonderful application.
213
214=head1 METHODS
215
216=head2 new
217
218  my $hook = Class::Hookalbe->new;
219
220This method is a constructor of Class::Hookable.
221Nothing but that is being done.
222
223=head2 register_hook
224
225  $hook->register_hook(
226      $plugin,
227      'hook.A' => $plugin->can('callbackA'),
228      'hook.B' => $plugin->can('callbackB'),
229  );
230
231This method registers a plugin object and callbacks which corresponds to hooks.
232
233The plugin object is specified as the first argument,
234and one after that is specified by the order of C<'hook' =E<gt> \&callabck>.
235
236=head2 filter_plugin
237
238  sub filter_plugin {
239      my ( $self, $hook, $action ) = @_;
240      my ( $plugin, $callback ) = @{ $action }{qw( plugin callback )};
241      # your filter code
242  }
243
244When registering a plugin, this method is filtered a plugin.
245Arguments are passed by the order of C<$hook> and C<$action>.
246
247=over 2
248
249=item C<$hook>
250
251The hook name specified as the run_hook method.
252
253=item C<$action>
254
255The hash reference including plugin and callback.
256
257=back
258
259When this method has returned ture, plugin and hook are registered,
260and when having returned false, it isn't registered.
261
262This method exists to rewrite when inheriting.
263
264=head2 registered_hooks
265
266  my @hooks = $hook->registered_hooks( $plugin );
267  my @hooks = $hook->registered_hooks( 'ClassName' );
268
269This method returns hooks with which a plugin is registered.
270An argument is plugin object or class name.
271
272=head2 registered_plugins
273
274  for my $action ( $hook->registered_plugins('hook.name') ) {
275      my ( $plugin, $callback ) = @{ $action }{qw( plugin callback )};
276      # some code
277  }
278
279This method returns plugin and callback registered with a hook.
280Return value is a list of hash reference including plugin and callback.
281
282=head2 delete_plugin
283
284  $hook->delete_plugin( $plugin );
285  $hook->delete_plugin( 'ClassName', 'hook.A', 'hook.B' );
286
287This method delete a registered plugin.
288
289When specifying only a plugin object (or class name) as an argument,
290a plugin is deleted from all hooks.
291
292And when specifying a plugin object (or class name) and hooks as arguments,
293a plugin is deleted from specified hooks.
294
295=head2 delete_hook
296
297  $hook->delete_hook( 'hook.name' );
298  $hook->delete_hook( 'hook.name', $pluginA, 'ClassName' );
299
300This method delete a registered hook.
301
302When specifying only a hook as an argument,
303all plugin registered with the hook are deleted.
304
305And when specifying a hook and plugin object (or class name) as arguments,
306specified plugins are deleted from a specified hook.
307
308=head2 run_hook
309
310  $hook->run_hook( $hook, $args, $once, $callback );
311  my @results = $hook->run_hook('hook.name', \%args, undef, \&callback);
312  my $result  = $hook->run_hook('hook.name', \%args, 1, \&callback);
313
314This method calls callback of the registered plugin to hook by the registered order.
315Arguments are specified by the order of C<$hook>, C<$args>, C<$once> and C<$callback>.
316
317B<Arguments to run_hook method>:
318
319=over 4
320
321=item C<$hook>
322
323Designation of the hook with which a plugin was registered.
324This argument is indispensable.
325
326=item C<$args>
327
328The argument which passes it to callback.
329This argument is optional.
330
331=item C<$once>
332
333When this argument becomes true, this method finishes calling callback
334when the first return value has been received.
335
336This argument is optional.
337
338=item C<$callback>
339
340  my $callback = sub {
341      my ( $result ) = @_;
342      # some code
343  }
344
345This argument specifies code reference.
346
347When having received a return value from callback of the registered,
348the callback specified by this argument is called.
349
350A return value of callback of registered plugin is passed to an argument of this callback.
351
352=back
353
354B<Argument to callback of the registered plugin>:
355
356  sub callback {
357      my ( $plugin, $context, $args ) = @_;
358      # some code
359  }
360
361The argument by which it is passed to callback is C<$plugin>, C<$context>, C<$args>.
362
363=over 3
364
365=item C<$plugin>
366
367The plugin object which passed a plugin and callback to the register_hook method when registering.
368
369=item C<$context>
370
371The context object.
372
373When C<$hook-E<gt>context> is specified, the specified object is passed,
374and when it isn't so, C<$hook>(Class::Hookable) object is passed.
375
376see also L<"context"> method.
377
378=item C<$args>
379
380C<$args> specified by the run_hook method.
381
382=back
383
384=head2 run_hook_once
385
386  my $result = $hook->run_hook_once( $hook, $args, $callback );
387
388This method is an alias of C<$hook-E<gt>run_hook( $hook, $args, 1, \&callback )>.
389
390=head2 dispatch_plugin
391
392  sub dispatch_plugin {
393      my ( $self, $hook, $args, $action ) = @_;
394      my ( $plugin, $callabck ) = @{ $action }{qw( plugin callback )};
395      # some code
396  }
397
398When calling a hook, this method does a dispatch of a plugin.
399Argument are passed by the order of C<$hook>, C<$args>, C<$action>.
400
401=over 3
402
403=item C<$hook>
404
405The hook name specified by the run_hook method.
406
407=item C<$args>
408
409The argument specified by the run_hook method.
410
411=item C<$action>
412
413The hash reference including the plugin and the callback.
414
415=back
416
417When this method has returned true, callback of a plugin is called,
418and when having returned false, callback isn't called.
419
420This method exists to rewrite when inheriting.
421
422=head2 context
423
424  my $context = $hook->context;
425  $hook->context( $context );
426
427This method is accessor of context object.
428
429When specifying object by this method,
430it's passed to callback of the plugin as context object.
431
432see also L<"run_hook"> method.
433
434=head2 hooks
435
436  my $hooks = $hook->hooks;
437
438This method is accessor to hash reference which keeps hooks.
439all method of Class::Hookable is accessing hooks through this method.
440
441=head1 AUTHOR
442
443Naoki Okamura (Nyarla) E<lt>thotep@nayrla.netE<gt>
444
445=head1 LICENSE
446
447This library is free software; you can redistribute it and/or modify
448it under the same terms as Perl itself.
449
450=cut
Note: See TracBrowser for help on using the browser.