File Coverage

blib/lib/Sprocket/Util/Observable.pm
Criterion Covered Total %
statement 47 57 82.4
branch 12 20 60.0
condition 1 3 33.3
subroutine 12 13 92.3
pod 5 6 83.3
total 77 99 77.7


line stmt bran cond sub pod time code
1             package Sprocket::Util::Observable;
2              
3 8     8   4647 use Sprocket::Event;
  8         25  
  8         56  
4 8     8   246 use Sprocket::Common;
  8         13  
  8         79  
5              
6 8     8   68 use Scalar::Util qw( reftype );
  8         16  
  8         504  
7              
8 8     8   40 use strict;
  8         12  
  8         243  
9 8     8   37 use warnings;
  8         11  
  8         6724  
10              
11             our $observable = {};
12             our $hooks = {};
13              
14             sub CALLBACK() { 0 }
15             sub UUID() { 1 }
16              
17             sub new {
18 8     8 0 20 my $class = shift;
19            
20 8   33     70 my $self = bless( { @_ }, ref $class || $class );
21              
22             # auto add a cleanup
23             ( $self->isa( 'Sprocket' ) ? $self : Sprocket->new() )->attach_hook(
24             'sprocket.shutdown',
25             sub {
26 5     5   47 $self->clear_hooks();
27             }
28 8 50       160 );
29            
30 8         49 return $self;
31             }
32              
33             sub register_hook {
34 22     22 1 55 my $self = shift;
35 22         87 my $hook_names =_array_ref( shift );
36              
37             $observable->{ $_ }++
38 22         285 foreach ( @$hook_names );
39            
40 22         33437 return;
41             }
42              
43             sub attach_hook {
44 9     9 1 25 my ( $self, $callback ) = @_[ 0, 2 ];
45 9         34 my $hook_names =_array_ref( $_[ 1 ] );
46              
47 9 50       66 unless ( reftype( $callback ) eq 'CODE' ) {
48             # XXX log
49 0         0 warn "invalid callback: $callback in Sprocket attach_hook, IGNORED!";
50 0         0 return;
51             }
52              
53 9         25 $hook_names = _array_ref( $hook_names );
54              
55 9         42 my $uuid = new_uuid();
56              
57 9 100       746795 $hooks->{ "$self" } = {} unless ( exists( $hooks->{ $self } ) );
58            
59 9         45 foreach ( @$hook_names ) {
60 15 100       83 if ( my $list = $hooks->{ $self }->{ $_ } ) {
61 1         5 push( @$list, [ $callback, $uuid ] );
62             } else {
63 14         100 $hooks->{$self}->{ $_ } = [ [ $callback, $uuid ] ];
64             }
65             }
66            
67 9         41 return $uuid;
68             }
69              
70             sub remove_hook {
71 0     0 1 0 my ( $self, $uuid ) = @_;
72              
73 0 0       0 return undef unless ( $hooks->{ $self } );
74              
75 0         0 foreach ( keys %{$hooks->{ $self }} ) {
  0         0  
76 0 0       0 next unless ( $hooks->{ $self }->{ $_ }->[ UUID ] eq $uuid );
77 0         0 my $h = delete $hooks->{ $self }->{ $_ };
78 0         0 return $h->[ CALLBACK ];
79             }
80              
81 0         0 return undef;
82             }
83              
84             sub broadcast {
85 92     92 1 170 my ( $self, $hook_name, $data ) = @_;
86            
87 92 50       340 warn "unscheduled broadcast on event hook: $hook_name"
88             unless( $observable->{ $hook_name } );
89            
90 92 50       657 $data = Sprocket::Event->new( $data )
91             unless ( UNIVERSAL::isa( $data, 'Sprocket::Event' ) );
92            
93 92         264 $data->hook( $hook_name );
94              
95 92 100       830 if ( $hooks->{ $self }->{ $hook_name } ) {
96 12         73 $_->[ CALLBACK ]->( $data )
97 12         18 foreach ( @{ $hooks->{ $self }->{ $hook_name } } );
98             }
99            
100 92         4335 return $data;
101             }
102              
103             sub clear_hooks {
104 10     10 1 15 my $self = shift;
105 10         29 delete $hooks->{ $self };
106 10         28 return;
107             }
108              
109             sub _array_ref {
110 40 100   40   226 return ( ref( $_[0] ) eq 'ARRAY' ) ? $_[0] : [ $_[0] ];
111             }
112              
113             1;
114              
115             __END__