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

Revision 1961, 12.3 kB (checked in by nyarla, 6 years ago)

lang/perl/Class-Hookable: I rewrote the part of POD.

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