File Coverage

lib/Class/Interface.pm
Criterion Covered Total %
statement 107 149 71.8
branch 33 70 47.1
condition 12 20 60.0
subroutine 17 20 85.0
pod 4 7 57.1
total 173 266 65.0


line stmt bran cond sub pod time code
1             package Class::Interface;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Class::Interface - A class for implementing/extending interfaces/abstracts in Perl.
8              
9             =head1 SYNOPSIS
10              
11             =head2 Declaring an interface
12              
13             package Bouncable;
14              
15             use Class::Interface;
16             &interface; # this actually declares the interface
17              
18             sub bounce;
19             sub getBounceBack;
20              
21             1;
22              
23             This creates an interface (a contract between classes if you like)
24             that specifies that each class implementing the Bouncable
25             interface must have an implementation of the routines bounce
26             and getBounceBack.
27              
28             =head2 Declaring an implementing class
29              
30             package Ball;
31              
32             use Class::Interface;
33             &implements( 'Bouncable' );
34              
35             sub bounce {
36             my $self = shift;
37             print "The ball is bouncing @ ".$self->getBounceBack." strength"
38             }
39              
40             sub getBounceBack {
41             return 10;
42             }
43              
44             1;
45              
46             =head2 Declaring an abstract
47              
48             package AbstractInterestCalculator;
49              
50             use Class::Interface;
51             &abstract; # this actually declares this class to be abstract;
52              
53             use Class::AccessorMaker {
54             interest => 5.1,
55             maxInterestValue => 0,
56             }
57              
58             # a hook for doing calculations
59             sub calculate {
60             my ( $self, $value ) = @_;
61              
62             $self->prepare();
63             $value += $self->getInterestValue( $value );
64              
65             return $value;
66             }
67              
68             sub prepare; # prepare calculations
69             sub getInterstValue; # get the interest value
70              
71             1;
72              
73             =head2 Extending from an abstract class
74              
75             package LowInterestCalculator;
76              
77             use Class::Interface;
78             &extends( 'AbstractInterestCalculator' );
79              
80             sub prepare {
81             my ( $self ) = @_;
82             $self->interest(1.3);
83              
84             # we don't give interest if the value of the account is or
85             # exceeds $10.000
86             $self->maxInterestValue(10000)
87             }
88              
89             sub getInterstValue {
90             my ( $self, $value ) = @_
91              
92             if ( $self->maxInterestValue &&
93             $value >= $self->maxInterestValue ) {
94             return 0;
95             }
96              
97             $value *= $self->interest;
98              
99             return $value;
100             }
101              
102             =head1 DESCRIPTION
103              
104             Performs some underwater perl-magic to ensure interfaces are
105             interfaces and classes that implement the interface actually do so.
106              
107             =head1 INTERFACE RULES
108              
109             =over 4
110              
111             =item * An interface must use the Class::Interface module.
112              
113             =item * An interface must call the 'interface' method.
114              
115             =item * An interface must declare at least one routine
116              
117             =item * Routines may not have an implementation
118              
119             =back
120              
121             =head1 ABSTRACT RULES
122              
123             =over 4
124              
125             =item * An abstract must use the Class::Interface module.
126              
127             =item * An abstract must call the 'abstract' method.
128              
129             =item * An abstract must declare at least one abstract routine.
130              
131             =back
132              
133             =head1 ROUTINE RULES
134              
135             =over 4
136              
137             =item * Routines must be declared as one of:
138              
139             =over 4
140              
141             =item - sub routine;
142              
143             =item - sub routine {}
144              
145             =back
146              
147             B: When using curly braces in routine declarations they must stay
148             on the same line. The amount of whitespace between them and/or the
149             routine name is free of ruling.
150              
151             =back
152              
153             =head1 ANNOTATIONS
154              
155             It helps to think of these methods as Java style annotations. But
156             instead of calling them with @interface you use &interface.
157              
158             =cut
159              
160 5     5   32 use strict;
  5         11  
  5         206  
161 5     5   28 no strict 'refs';
  5         14  
  5         144  
162              
163 5     5   38 use base qw(Exporter);
  5         10  
  5         818  
164             @Class::Interface::EXPORT = qw(implements interface extends abstract);
165              
166 5     5   29 use Carp;
  5         14  
  5         6236  
167              
168             # some default class vars
169             $Class::Interface::VERSION = "1.01";
170              
171             # some class vars for changing behaviour
172             $Class::Interface::AUTO_CONSTRUCTOR = 0;
173             $Class::Interface::CONFESS = 0;
174              
175             # define a contract
176             sub error(*);
177              
178             =pod
179              
180             =head2 &interface()
181              
182             Turns the calling class into an interface.
183              
184             =cut
185             sub interface() {
186 10     10 1 1426 my $caller = caller();
187              
188 10 50 33     66 return if !$caller || $caller eq "main";
189              
190             # interfaces should be usable.
191 4     4   26 eval "use $caller";
  4         7  
  4         71  
  10         344  
192 10 50       91 error $@ if $@;
193              
194 10         27 my @subs = inspectInterface($caller);
195              
196 10 50       74 error "Interface $caller does not provide any methods" if $#subs < 0;
197              
198             # first prevent usage of interfaces (but allow it from me).
199 10         107 *{ $caller . "::import" } = sub {
200 19     19   60 my $caller = caller();
201              
202 19 50 66     2415 if ( $caller ne "Class::Interface" and $caller ne "main" ) {
203 3         6 error "$caller is an interface. It can't be used";
204             }
205 10         1068 };
206              
207             # tell any interface users this is an interface and return the
208             # expected routines.
209 10         68 *{ $caller . "::__get_interface_methods__" } = sub {
210 19     19   1380 return @subs;
211 10         45 };
212             }
213              
214             =pod
215              
216             =head2 &abstract()
217              
218             Turns the calling class into an abstract.
219              
220             =cut
221             sub abstract() {
222 6     6 1 23 my $caller = caller();
223              
224 3 50 33     27 return if !$caller || $caller eq "main";
225              
226             # interfaces should be usable.
227 3     8   165 eval "use $caller";
  8         2166  
  8         20  
  8         86  
228 3 50       15 error $@ if $@;
229              
230 3         10 my @subs = inspectInterface( $caller, 1 );
231              
232             # abstract classes must have abstract methods
233 3 50       13 error "Abstract interface $caller does not provide any methods" if $#subs < 0;
234              
235             # tell any abstract users this is an abstract and return the
236             # expected routines.
237 3         23 *{ $caller . "::__get_abstract_methods__" } = sub {
238 3     6   931 return @subs;
239 3         12 };
240              
241             # overwrite the abstract routines and make them die on invocation
242 3         9 foreach my $sub (@subs) {
243 3         28 *{ $caller . "::" . $sub } = sub {
244 0     0   0 die("You are trying to invoke the abstract method $sub from $caller");
245 3         12 };
246             }
247             }
248              
249             =pod
250              
251             =head2 &implements()
252              
253             Loads the given interfaces and checks the calling class for presence
254             of the wanted routines.
255              
256             If all goes well pushes the name of the interface to the ISA array of
257             the class.
258              
259             =cut
260             sub implements(@) {
261 9     9 1 21 my $caller = caller;
262              
263 9         14 my %missing;
264 9         21 foreach my $implements (@_) {
265 15         761 eval "use $implements;";
266 15 50       44 error
267             "$caller tries to implement non existing interface $implements -- $@"
268             if $@;
269              
270 15 50       22 unless ( defined ( &{ $implements . "::__get_interface_methods__" } ) ) {
  15         576  
271 0         0 error "$caller tries to implement non-interface $implements"
272             }
273              
274             # find the subs from the interface
275 15         21 foreach my $sub ( &{ $implements . "::__get_interface_methods__" } ) {
  15         51  
276 42 50       242 unless ( UNIVERSAL::can( $caller, $sub ) ) {
277 0 0       0 $missing{$implements} = [] unless exists $missing{$implements};
278 0         0 push @{ $missing{$implements} }, $sub;
  0         0  
279             }
280             }
281             }
282              
283 9 50       42 if ( keys %missing ) {
284 0         0 my $dieMessage = "";
285 0         0 foreach my $interface ( keys %missing ) {
286 0         0 foreach my $sub ( @{ $missing{$interface} } ) {
  0         0  
287 0 0       0 $dieMessage .= ",\n" if $dieMessage;
288 0         0 $dieMessage .= "$caller fails to implement $sub from $interface";
289             }
290             }
291              
292 0         0 error $dieMessage;
293             }
294              
295             # make sure the import is not found through inheritance.
296 9 50       12 unless ( defined &{ $caller . "::import" } ) {
  9         56  
297 9     9   40 *{ $caller . "::import" } = sub {
  9         579  
298              
299             # don't cascade up to the interface.
300             }
301 9         32 }
302              
303 9         23 makeMagicConstructor($caller);
304              
305 9         11 push @{ $caller . "::ISA" }, @_;
  9         109  
306             }
307              
308             =pod
309              
310             =head2 &extends()
311              
312             Loads the given abstract class and checks the calling class for presence
313             of the abstract routines.
314              
315             If all goes well pushes the name of the abstract class to the ISA
316             array of the class.
317              
318             =cut
319             sub extends(*) {
320 2     2 1 6 my $caller = caller();
321              
322 2         4 my %missing;
323 2         7 foreach my $extends (@_) {
324 2         100 eval "use $extends;";
325 2 50       23 error
326             "$caller tries to implement non existing abstract class $extends -- $@"
327             if $@;
328              
329 2 50       3 unless ( defined ( &{ $extends . "::__get_abstract_methods__" } ) ) {
  2         40  
330 0         0 error "$caller tries to implement non-abstract $extends"
331             }
332              
333              
334             # find the subs from the interface
335 2         3 foreach my $sub ( &{ $extends . "::__get_abstract_methods__" } ) {
  2         8  
336 2 50       16 unless ( UNIVERSAL::can( $caller, $sub ) ) {
337 0 0       0 $missing{$extends} = [] unless exists $missing{$extends};
338 0         0 push @{ $missing{$extends} }, $sub;
  0         0  
339             }
340             }
341             }
342              
343 2 50       9 if ( keys %missing ) {
344 0         0 my $dieMessage = "";
345 0         0 foreach my $abstract ( keys %missing ) {
346 0         0 foreach my $sub ( @{ $missing{$abstract} } ) {
  0         0  
347 0 0       0 $dieMessage .= ",\n" if $dieMessage;
348 0         0 $dieMessage .=
349             "$caller fails to implement $sub from abstract class $abstract";
350             }
351             }
352              
353 0         0 error $dieMessage;
354             }
355              
356 2         4 makeMagicConstructor($caller);
357              
358 2         2 push @{ $caller . "::ISA" }, @_;
  2         23  
359             }
360              
361             # private methods
362             #
363              
364             # perform interface inspections
365             sub inspectInterface {
366 10     10 0 20 my $interface = shift;
367 10   100     49 my $asAbstract = shift || 0;
368              
369 5     5   41 no warnings 'uninitialized';
  5         9  
  5         4240  
370              
371 10         44 ( my $keyName = $interface ) =~ s/\:\:/\//g;
372 10         18 $keyName .= ".pm";
373              
374 10         22 my $file = $INC{$keyName};
375 10         414 open( local *IN, "<$file" );
376              
377 10         27 my @subs = ();
378 10         15 my $usesInterfaces;
379             my $callsInterface;
380 10         264 while ( chomp( my $line = ) ) {
381             # leave if the source file says so.
382 166 50       444 last if $line eq "__END__";
383              
384 166 100       386 $usesInterfaces = 1 if $line =~ /^use Class::Interface/i;
385 166 100       341 $callsInterface = 1 if $line =~ /^\&?interface\(?\)?/;
386 166 100 100     544 $callsInterface = 1 if ( $asAbstract && $line =~ /^\&?abstract\(?\)?/ );
387              
388 166 100       1046 if ( $line =~ /^sub/ ) {
389             # strip of any comments
390 25 100       142 unless ( ( my $commentChar = index($line, "#") ) < 0 ) {
391 3         15 $line = substr($line, 0, $commentChar);
392             }
393              
394             # trim trailing whitespace
395 25         119 $line =~ s/\ +$//;
396              
397 25         101 my ($sub) = $line =~ /sub ([^\s]+)/;
398 25         99 my $lineEnd = substr( $line, length($line) - 1 );
399              
400 25 100 100     130 if ( $lineEnd ne ";" and $lineEnd ne "}" ) {
401             # if this is an abstract, implementations are OK
402 3 50       25 next if $asAbstract;
403              
404             # ai. The sub has an implementation
405 0         0 error
406             "$interface is not a valid interface. $sub has an implementation";
407             }
408              
409             # strip any funny chars from the routine name
410 22         46 $sub =~ tr/\;\{\}//d;
411              
412 22         161 push @subs, $sub;
413             }
414             }
415              
416 10 50       31 if ( !$usesInterfaces ) {
417 0         0 error("Interface $interface does not use the interface module.");
418             }
419 10 50       452 if ( !$callsInterface ) {
420 0 0       0 error( ( $asAbstract ? "Abstract" : "Interface" ) . " $interface does not load the interface magic.");
421             }
422              
423 10         153 return @subs;
424             }
425              
426             # add a default constructor to the caller
427             sub makeMagicConstructor {
428 11 50   11 0 31 return if !$Class::Interface::AUTO_CONSTRUCTOR;
429              
430 0         0 my $caller = shift;
431              
432 0 0       0 unless ( defined &{ $caller."::new" } ) {
  0         0  
433 0         0 *{ $caller."::new"} = sub {
434 0   0 0   0 my $class = ref($_[0]) || $_[0]; shift;
  0         0  
435 0         0 my $self = bless({}, $class);
436              
437 0         0 my %value = @_;
438 0         0 foreach my $field ( keys %value ) {
439 0 0       0 $self->$field( $value{$field} ) if $self->can( $field )
440             }
441              
442 0         0 return $self
443 0         0 };
444             }
445             }
446              
447             # die
448             sub error(*) {
449 0     0 0 0 my $strings = join("", @_);
450              
451 0 0       0 if ( $Class::Interface::CONFESS == 1 ) {
452 0         0 confess $strings;
453             } else {
454 0         0 croak $strings;
455             }
456             }
457              
458             =pod
459              
460             =head1 MAGIC CONSTRUCTORS
461              
462             To add even more Java behaviour to perl...
463              
464             Extending or implementing classes that do not already have a constructor
465             can get one injected automaticly.
466              
467             The code for such a routine is as follows:
468              
469             sub new {
470             my $class = ref($_[0]) || $_[0]; shift;
471             my $self = bless({}, $class);
472              
473             my %value = @_;
474             foreach my $field ( keys %value ) {
475             $self->$field( $value{$field} ) if $self->can( $field )
476             }
477              
478             return $self
479             }
480              
481             In english: An object with a hashref is setup. The constructor can be called
482             like this:
483              
484             my $object = Object->new( attribute1 => "value",
485             attribute2 => [ qw(a b c)],
486             );
487              
488             if attributeX exists as an accessor routine in the object it will be set by
489             calling the actual routine.
490              
491             I would strongly advice using something like Class::AccessorMaker though...
492              
493             If you want magic constructors; set $Class::Interface::AUTO_CONSTRUCTOR to 1
494              
495             =head1 ERROR HANDLING
496              
497             If anything fails uses Carp::croak. Once you set $Class::Interface::CONFESS
498             to 1 it will spill the guts using confess.
499              
500             =head1 FAQ
501              
502             =over 4
503              
504             =item Q: Will it see the routines I create dynamicly?
505              
506             Using things like Class::AccessorMaker accessors are dynamcly created.
507             Class contracts can specify some getters to be present. Does Class::Interface
508             recognize them?
509              
510             =item A: Yes.
511              
512             The checks implements() and extends() perform happen well after use time. So
513             using Class::AccessorMaker is save. It performs it magic in use time. Any class
514             that will dynamicly create methods in use time should be usable with
515             Class::Interface.
516              
517             =back
518              
519             =head1 CAVEATS, BUGS, ETC.
520              
521             =head2 Order of annotations
522              
523             If your class extends an abstract which provides methods for an interface you
524             are implementing you must first call the &extends annotation.
525              
526             So:
527              
528             &extends('Runner');
529             &implements('Runnable');
530              
531             And not:
532              
533             &implements('Runnable');
534             &extends('Runner');
535              
536              
537             =head1 SEE ALSO
538              
539             L, L
540              
541             =head1 AUTHOR
542              
543             Hartog C. de Mik
544              
545             =head1 COPYRIGHT
546              
547             (cc-sa) 2008, Hartog C. de Mik
548              
549             cc-sa : L
550              
551             =cut
552              
553             1;
554              
555