File Coverage

blib/lib/Blosxom/Plugin.pm
Criterion Covered Total %
statement 132 135 97.7
branch 40 46 86.9
condition 7 9 77.7
subroutine 25 25 100.0
pod 6 8 75.0
total 210 223 94.1


line stmt bran cond sub pod time code
1             package Blosxom::Plugin;
2 9     9   194921 use 5.008_009;
  9         35  
  9         346  
3 9     9   51 use strict;
  9         16  
  9         266  
4 9     9   43 use warnings;
  9         26  
  9         291  
5 9     9   120 use Carp qw/croak/;
  9         16  
  9         1457  
6              
7             our $VERSION = '0.02004';
8              
9             sub import {
10 2     2   38 my $class = shift;
11 2         9 my $component = scalar caller;
12 9     9   58 my $stash = do { no strict 'refs'; \%{"$component\::"} };
  9         17  
  9         5170  
  2         28  
  2         3  
  2         7  
13              
14 2         4 my %is_excluded;
15 2         4 while ( my ($method, $glob) = each %{$stash} ) {
  228         877  
16 226 100       202 next unless defined *{$glob}{CODE};
  226         722  
17 28         69 $is_excluded{$method}++;
18             }
19              
20 2         4 my ( @requires, @accessors );
21              
22             my @exports = (
23 1     1   14 requires => sub { shift; push @requires, @_ },
  1         4  
24             mk_accessors => sub {
25 1     1   10 my $pkg = shift;
26 1 50       7 my @args = ref $_[0] eq 'HASH' ? %{$_[0]} : map { $_ => undef } @_;
  1         6  
  0         0  
27 1         5 push @accessors, @args,
28             },
29             init => sub {
30 2     2   4 my ( $comp, $plugin ) = @_;
31              
32 2 100       4 if ( my @methods = grep { !$plugin->can($_) } @requires ) {
  4         37  
33 1         3 my $methods = join ', ', @methods;
34 1         26 croak "Can't apply '$comp' to '$plugin' - missing $methods";
35             }
36              
37 1         5 for ( my $i = 0; $i < @accessors; $i += 2 ) {
38 2         11 $plugin->add_attribute( @accessors[$i, $i+1] );
39             }
40              
41 1         2 while ( my ($method, $glob) = each %{$stash} ) {
  8         29  
42 7 100       6 if ( my $code = *{$glob}{CODE} ) {
  7         21  
43 5 100       16 next if $is_excluded{$method};
44 1         3 $plugin->add_method( $method => $code );
45             }
46             }
47              
48 1         4 return;
49             },
50 2         23 );
51              
52             # export mixin methods
53 9     9   56 no strict 'refs';
  9         15  
  9         1989  
54 2         14 while ( my ( $method, $code ) = splice @exports, 0, 2 ) {
55 6         10 *{ "$component\::$method" } = $code;
  6         23  
56 6         20 $is_excluded{ $method }++;
57             }
58              
59 2         99 return;
60             }
61              
62             my %attribute_of;
63              
64             sub mk_accessors {
65 2     2 1 48 my $package = shift;
66 2 50       12 my @accessors = ref $_[0] eq 'HASH' ? %{ $_[0] } : map { $_ => undef } @_;
  2         12  
  0         0  
67              
68 9     9   127 no strict 'refs';
  9         15  
  9         7834  
69 2         16 while ( my ($field, $default) = splice @accessors, 0, 2 ) {
70 6         26 *{"$package\::$field"} = $package->make_accessor($field, $default);
  6         50  
71             }
72              
73 2         6 return;
74             }
75              
76             sub make_accessor {
77 11     11 0 21 my $package = shift;
78 11         18 my $name = shift;
79 11         51 my $default = shift;
80 11   100     86 my $attribute = $attribute_of{$package} ||= {};
81              
82 11 100       43 if ( ref $default eq 'CODE' ) {
    100          
83             return sub {
84 4 100   4   24 return $attribute->{$name} = $_[1] if @_ == 2;
85 3 100       12 return $attribute->{$name} if exists $attribute->{$name};
86 2         8 return $attribute->{$name} = $package->$default;
87 4         39 };
88             }
89             elsif ( defined $default ) {
90             return sub {
91 7 100   7   3277 return $attribute->{$name} = $_[1] if @_ == 2;
92 5 50       17 return $attribute->{$name} if exists $attribute->{$name};
93 5         30 return $attribute->{$name} = $default;
94 6         36 };
95             }
96             else {
97             return sub {
98 2 100   2   2678 @_ > 1 ? $attribute->{$name} = $_[1] : $attribute->{$name};
99 1         5 };
100             }
101              
102 0         0 return;
103             }
104              
105 2 100   2 1 1205 sub end { %{ $attribute_of{$_[0]} } = () if exists $attribute_of{$_[0]} }
  1         6  
106              
107             sub dump {
108 4     4 1 1883 my $package = shift;
109 4         3120 require Data::Dumper;
110 4   50     8924 local $Data::Dumper::Maxdepth = shift || 1;
111 4         19 Data::Dumper::Dumper( $attribute_of{$package} );
112             }
113              
114 8     8 0 21 sub component_base_class { __PACKAGE__ }
115              
116             sub load_components {
117 9     9 1 324 my $package = shift;
118 9         29 my @args = @_;
119 9         66 my $prefix = $package->component_base_class;
120              
121 9         23 my ( $component, %is_loaded, %has_conflict, %code_of );
122              
123             local *add_component
124 9 50   3   56 = sub { push @args, @_ > 2 ? @_[1, 2] : $_[1] };
  3         39  
125              
126             local *add_method = sub {
127 16     16   67 my ( $pkg, $method, $code ) = @_;
128 16 100       619 unless ( defined &{"$package\::$method"} ) {
  16         118  
129 14   100     21 push @{ $has_conflict{$method} ||= [] }, $component;
  14         84  
130 14         103 $code_of{ $method } = $code;
131             }
132 9         58 };
133              
134 9         40 while ( @args ) {
135 17         79 $component = do {
136 17         32 my $class = shift @args;
137              
138 17 50 66     144 if ( $class !~ s/^\+// and $class !~ /^$prefix/ ) {
139 3         11 $class = "$prefix\::$class";
140             }
141              
142 17 100       75 if ( $is_loaded{$class}++ ) {
143 2 50       5 shift @args if ref $args[0] eq 'HASH';
144 2         6 next;
145             }
146              
147 15         59 ( my $file = $class ) =~ s{::}{/}g;
148 15         1870 require "$file.pm";
149              
150 15         43 $class;
151             };
152              
153 15 100       56 my $config = ref $args[0] eq 'HASH' ? shift @args : undef;
154              
155 15         127 $component->init( $package, $config );
156             }
157              
158 8 100       35 if ( %code_of ) {
159 9     9   61 no strict 'refs';
  9         17  
  9         2825  
160 6         38 while ( my ( $method, $components ) = each %has_conflict ) {
161 13 100       16 delete $has_conflict{ $method } if @{ $components } == 1;
  13         42  
162 13         22 *{ "$package\::$method" } = $code_of{ $method };
  13         277  
163             }
164             }
165              
166 8 100       32 if ( %has_conflict ) {
167 1         41 croak join "\n", map {
168 1         4 "Due to a method name conflict between components " .
169 1         17 "'" . join( ' and ', sort @{ $has_conflict{$_} } ) . "', " .
170             "the method '$_' must be implemented by '$package'";
171             } keys %has_conflict;
172             }
173              
174 7         59 return;
175             }
176              
177             sub add_attribute {
178 5     5 1 11 my ( $pkg, $name, $default ) = @_;
179 5         622 $pkg->add_method( $name => $pkg->make_accessor($name, $default) );
180             }
181              
182 1     1 1 827 sub has_method { defined &{"$_[0]::$_[1]"} }
  1         9  
183              
184             1;
185              
186             __END__