File Coverage

blib/lib/Class/Interfaces.pm
Criterion Covered Total %
statement 51 51 100.0
branch 26 26 100.0
condition 1 2 50.0
subroutine 8 8 100.0
pod n/a
total 86 87 98.8


line stmt bran cond sub pod time code
1              
2             package Class::Interfaces;
3              
4 1     1   1279 use strict;
  1         3  
  1         46  
5 1     1   6 use warnings;
  1         1  
  1         411  
6              
7             our $VERSION = '0.04';
8              
9             sub import {
10 11     11   16444 my $class = shift;
11 11         138 my %interfaces = @_;
12 11         33 foreach my $interface (keys %interfaces) {
13             # build the interface
14 16         25 my (@methods, @subclasses);
15 16 100       70 if (ref($interfaces{$interface}) eq 'HASH') {
    100          
    100          
16 5         9 my $interface_spec = $interfaces{$interface};
17             # if we have an isa
18 5 100       7 if (exists ${$interface_spec}{isa}) {
  5         15  
19             # if is an array (multiple inheritance)
20 3 100       11 if (ref($interface_spec->{isa}) eq 'ARRAY') {
21 1         1 @subclasses = @{$interface_spec->{isa}};
  1         4  
22             }
23             else {
24             # if its another kind of ref, its an error
25 2 100       11 (!ref($interface_spec->{isa}))
26             || $class->_error_handler("Interface ($interface) isa list must be an array reference");
27             # otherwise its just a single item
28 1         4 @subclasses = $interface_spec->{isa};
29             }
30             }
31 4 100       6 if (exists ${$interface_spec}{methods}) {
  4         13  
32 3 100       18 (ref($interface_spec->{methods}) eq 'ARRAY')
33             || $class->_error_handler("Method list for Interface ($interface) must be an array reference");
34 2         3 @methods = @{$interface_spec->{methods}};
  2         6  
35             }
36             }
37             elsif (ref($interfaces{$interface}) eq 'ARRAY') {
38 9         11 @methods = @{$interfaces{$interface}};
  9         26  
39             }
40             elsif (!defined($interfaces{$interface})) {
41             # allow undefined here, this indicates an empty
42             # interface, sometimes called a marker interface
43             ;
44             }
45             else {
46 1         8 $class->_error_handler("Cannot use a " . $interfaces{$interface} . " to build an interface");
47             }
48             # now create the interfaces
49 13         43 my $package = $class->_build_interface_package($interface, @subclasses);
50 13     1   566 eval $package;
  1         1921  
51 13 100       52 $class->_error_handler("Could not create Interface ($interface) because", $@) if $@;
52 11         16 eval {
53 11         71 my $method_stub = $class->can('_method_stub');
54 1     1   23 no strict 'refs';
  1         2  
  1         394  
55             # without at least this VERSION declaration
56             # a Marker interface will not work with
57             # 'use base' basically it would complain
58             # that the package is empty.
59             # we only assign this if the VERSION is already
60             # empty too, so we don't step on any customizations
61             # done in subclasses.
62 11   50     15 ${"${interface}::"}{VERSION} ||= -1;
  11         61  
63             # now we create all our methods :)
64 11         23 foreach my $method (@methods) {
65 12 100       47 ($method !~ /^(BEGIN|INIT|CHECK|END|DESTORY|AUTOLOAD|import|bootstrap)$/)
66             || $class->_error_handler("Cannot create an interface using reserved perl methods");
67 11         11 *{"${interface}::${method}"} = $method_stub;
  11         76  
68             }
69             };
70 11 100       147 $class->_error_handler("Could not create sub methods for Interface ($interface) because", $@) if $@;
71             }
72             }
73              
74             sub _build_interface_package {
75 13     13   30 my ($class, $interface, @subclasses) = @_;
76 13         29 my $package = "package $interface;";
77 13 100       39 $package .= "\@${interface}::ISA = qw(" . (join " " => @subclasses) . ");" if @subclasses;
78 13         31 return $package;
79             }
80              
81             sub _error_handler {
82 6     6   12 my ($class, $message, $sub_exception) = @_;
83 6 100       28 die "$message : $sub_exception" if $sub_exception;
84 4         52 die "$message";
85             }
86              
87 3     3   1682 sub _method_stub { die "Method Not Implemented" }
88              
89             1;
90              
91             __END__