File Coverage

blib/lib/Fukurama/Class/Abstract.pm
Criterion Covered Total %
statement 72 73 98.6
branch 11 12 91.6
condition 6 9 66.6
subroutine 16 16 100.0
pod 2 2 100.0
total 107 112 95.5


line stmt bran cond sub pod time code
1             package Fukurama::Class::Abstract;
2 4     4   23440 use Fukurama::Class::Version(0.01);
  4         10  
  4         31  
3 4     4   24 use Fukurama::Class::Rigid;
  4         8  
  4         22  
4 4     4   27 use Fukurama::Class::Carp;
  4         8  
  4         48  
5 4     4   665 use Fukurama::Class::Tree();
  4         9  
  4         1381  
6              
7             my $CLASS = {};
8             my $DECORATED_SUBS = {};
9             our $DISABLE = 0;
10              
11             =head1 NAME
12              
13             Fukurama::Class::Abstract - Pragma to provide abstract classes
14              
15             =head1 VERSION
16              
17             Version 0.01 (beta)
18              
19             =head1 SYNOPSIS
20              
21             package MyClass;
22             use Fukurama::Class::Abstract;
23              
24             =head1 DESCRIPTION
25              
26             This pragma-like module provides functions to check the usage of all class-methods. All calls from childs,
27             which inherits from this class are ok, all other will croak at runtime.
28             Use Fukurama::Class instead, to get all the features for OO.
29              
30             =head1 CONFIG
31              
32             You can disable the whole behavior of this class by setting
33              
34             $Fukurama::Class::Abstract::DISABLE = 1;
35            
36             =head1 EXPORT
37              
38             All methods of your abstract class would be decorated with a caller-check method.
39              
40             =head1 METHODS
41              
42             =over 4
43              
44             =item abstract( abstract_class:STRING ) return:VOID
45              
46             Set the given class as abstract.
47              
48             =item run_check( ) return:VOID
49              
50             Helper method for static perl (see Fukurama::Class > BUGS)
51             This method decorates all non-special subroutines in the registered, abstract classes
52             that all calls would be checked.
53              
54             =back
55              
56             =head1 AUTHOR, BUGS, SUPPORT, ACKNOWLEDGEMENTS, COPYRIGHT & LICENSE
57              
58             see perldoc of L
59              
60             =cut
61              
62             # AUTOMAGIC void
63             sub import {
64 1     1   11 my $class = $_[0];
65            
66 1         5 my ($caller_class) = caller(0);
67 1         4 $class->abstract($caller_class);
68 1         124 return undef;
69             }
70             # STATIC void
71             sub abstract {
72 3     3 1 7 my $class = $_[0];
73 3         7 my $caller_class = $_[1];
74            
75 3         9 $CLASS->{$caller_class} = undef;
76 3         9 return 1;
77             }
78             # STATIC void
79             sub run_check {
80 5     5 1 17 my $class = $_[0];
81            
82 5 50       25 return if($DISABLE);
83 5         20 foreach my $obj_class (keys(%$CLASS)) {
84 4         7 foreach my $identifier (@{$class->_get_subs($obj_class)}) {
  4         17  
85 10         33 $class->_decorate_sub($obj_class, $identifier);
86             }
87             }
88 5         62 return;
89             }
90             # STATIC void
91             sub _decorate_sub {
92 10     10   17 my $class = $_[0];
93 10         14 my $obj_class = $_[1];
94 10         15 my $identifier = $_[2];
95            
96 10 100       34 return if(exists($DECORATED_SUBS->{$identifier}));
97 9         63 my ($subname) = $identifier =~ m/([^:]+)$/;
98 9 100       40 return if(Fukurama::Class::Tree->is_special_sub($subname));
99            
100 4     4   27 no strict 'refs';
  4         15  
  4         147  
101 4     4   20 no warnings 'redefine';
  4         7  
  4         1083  
102            
103 5         8 my $old = *{$identifier}{CODE};
  5         17  
104 5         28 *{$identifier} = sub {
105 10   33 10   4955 my $used_obj = ref($_[0]) || $_[0];
106            
107 10 100 100     171 if(!$used_obj || $used_obj eq $obj_class || !UNIVERSAL::isa($used_obj, $obj_class)) {
      66        
108 6         29 $class->_throw_error($used_obj, $obj_class, $identifier);
109             }
110 4         20 goto $old;
111 5         47 };
112            
113 5         17 $DECORATED_SUBS->{$identifier} = undef;
114 5         15 return;
115             }
116             # STATIC void
117             sub _throw_error {
118 6     6   11 my $class = $_[0];
119 6         11 my $obj_class = $_[1];
120 6         10 my $caller_class = $_[2];
121 6         11 my $identifier = $_[3];
122            
123 6 100       19 $obj_class = '' if(!defined($obj_class));
124 6         44 _croak("Abstract class '$obj_class' used in class '$caller_class'. Sub '$identifier' called.", 2);
125 0         0 return;
126             }
127             # STATIC array
128             sub _get_subs {
129 4     4   10 my $class = $_[0];
130 4         7 my $obj_class = $_[1];
131            
132 4     4   24 no strict 'refs';
  4         6  
  4         390  
133            
134 4         8 my $subs = [];
135 4         8 foreach my $name (%{$obj_class . '::'}) {
  4         22  
136 46         120 my $identifier = $obj_class . '::' . $name;
137 46 100       65 next if(!*{$identifier}{'CODE'});
  46         245  
138 10         28 push(@$subs, $identifier);
139             }
140 4         16 return $subs;
141             }
142              
143 4     4   21 no warnings 'void'; # avoid 'Too late to run CHECK/INIT block'
  4         8  
  4         249  
144              
145             # AUTOMAGIC void
146             CHECK {
147 4     4   1015 __PACKAGE__->run_check();
148             }
149             1;