File Coverage

blib/lib/personal.pm
Criterion Covered Total %
statement 23 74 31.0
branch 1 28 3.5
condition 0 11 0.0
subroutine 7 15 46.6
pod 1 1 100.0
total 32 129 24.8


line stmt bran cond sub pod time code
1             package personal;
2              
3 1     1   8686 use 5.006_001;
  1         4  
  1         57  
4 1     1   7 use strict;
  1         3  
  1         44  
5 1     1   6 use File::Spec ();
  1         6  
  1         34  
6              
7 1     1   6 use constant IS_MODPERL => exists $ENV{MOD_PERL};
  1         2  
  1         1337  
8              
9             our $VERSION = '0.22';
10              
11             our $Cache = {};
12             our $Count = 1;
13              
14             # Public method
15             sub import : method {
16 1     1   35 shift->_export( qw(personal) );
17              
18 1 50       2156 if( @_ ) {
19 0         0 my $cache = _personal_cache();
20 0         0 my $class = shift;
21              
22 0 0       0 unless( $cache->{$class} ) {
23 0         0 my $project = {
24             class => $class,
25             relative => _class2relative($class)
26             };
27 0 0       0 unless( _search_package($project) ) {
28 0         0 _croak("Can't locate $project->{relative} in \@INC (\@INC contains: @INC)");
29             } else {
30 0         0 $project->{realname} = _class_newname();
31 0         0 _compile_package($project);
32              
33 0         0 $cache->{$class} = {
34             realname => $project->{realname}, mtime => $project->{filestat}[9],
35             absolute => $project->{absolute},
36             };
37             }
38             }
39              
40 0 0 0     0 if( @_
41             and $cache->{$class}{realname}->can('import') ) {
42 0         0 my @caller = caller;
43 0         0 my $cinfo = $cache->{$class};
44              
45 0         0 eval qq(#line $caller[2] $caller[1]
46             eval qq\(#line \$caller[2] \$caller[1]
47             package \$caller[0]; \$cinfo->{realname}->import(\\\@_);
48             \);
49             die \$@ if \$@;
50             );
51 0 0       0 die $@ if $@;
52             }
53             }
54             }
55              
56             # Public function
57             sub personal {
58 0     0 1 0 my $cache = _personal_cache();
59 0         0 my $class = shift;
60              
61 0 0       0 unless( exists $cache->{$class} ) {
62 0         0 _croak("Package $class was not loaded with 'use personal'");
63             } else {
64 0         0 return $cache->{$class}{realname};
65             }
66             }
67              
68             # Private function: _search_package(PROJECT)
69             # Searching package in @INC. Returns true on success
70             sub _search_package {
71 0     0   0 my $project = shift;
72              
73 0         0 foreach( @INC ) {
74 0         0 my $t_path = File::Spec->catfile($_, $project->{relative});
75              
76 0 0       0 if( -f $t_path ) {
77 0         0 $project->{filestat} = [ stat _ ];
78 0         0 $project->{absolute} = File::Spec->rel2abs($t_path);
79 0         0 return 1;
80             }
81             }
82 0         0 return undef;
83             }
84              
85             # Private function: _compile_package(PROJECT)
86             # Package compilation. Dieing on eny errors
87             sub _compile_package {
88 0     0   0 my $project = shift;
89 0         0 my $size = $project->{filestat}[7];
90 0         0 my $fh;
91             my $data;
92              
93 0 0 0     0 if( !open($fh, $project->{absolute})
    0          
    0          
94             or !binmode($fh) ) {
95 0         0 _croak("Error opening $project->{relative}");
96             }
97             elsif( read($fh, $data, $size) != $size ) {
98 0         0 _croak("Error reading $project->{relative}");
99             }
100             elsif( $data !~ s/^ *?package +?$project->{class}\b// ) {
101 0         0 _croak("Package $project->{class} ",
102             "is uncompatible with 'use personal'");
103             }
104             else {
105 0         0 my @caller = caller 1;
106 0         0 my $result = eval qq(#line $caller[2] $caller[1]
107             my \$result = eval qq\(#line 1 \$project->{relative}
108             package \$project->{realname} \$data
109             \);
110             warn \$@ if \$@;
111             \$result;
112             );
113 0 0       0 warn $@ if $@;
114              
115 0 0       0 unless( $result ) {
116 0         0 _carp("$project->{relative} did not return true value");
117             }
118 0 0 0     0 if( $@ or !$result ) {
119 0         0 _croak("Compilation failed on 'use personal'");
120             }
121             }
122             }
123              
124             # Private function: _class_newname()
125             # Generation of new unique personal class name
126             sub _class_newname {
127 0     0   0 return sprintf( '%s::_%012d', __PACKAGE__, $Count++ );
128             }
129              
130             # Private function: _class2relative(CLASS)
131             # Conversion class name to relative path of current FS
132             sub _class2relative {
133 0     0   0 return File::Spec->catfile( split('::', shift) ).'.pm';
134             }
135              
136             # Private function: _personal_cache()
137             # Returns reference to personal cache hash
138             sub _personal_cache {
139 0     0   0 my $area;
140              
141 0         0 if( IS_MODPERL ) {
142             my $r = Apache->request;
143             $area = $r->dir_config('PersonalArea');
144             $area = $r->filename unless defined($area);
145             }
146 0 0       0 $area = $0 unless defined($area);
147 0   0     0 return $Cache->{$area} ||= {};
148             }
149              
150             # Private method: SELF->_export( GLOB1, GLOB2, ... )
151             # Very simple exporter. Only for using from import() method in this package
152             sub _export : method {
153 1     1   3 my $source = shift;
154 1         3 my $destination = caller(1);
155              
156 1     1   7 no strict qw(refs);
  1         2  
  1         297  
157 1         3 foreach( @_ ) {
158 1         1 *{ "${destination}::$_" } = \&{ "${source}::$_" };
  1         9  
  1         5  
159             }
160             }
161              
162             # Private function: _carp(MESSAGE)
163             # Handling warnings
164             sub _carp {
165 0     0     require Carp; &Carp::carp;
  0            
166             }
167              
168             # Private function: _croak(REASON)
169             # Handling fatals
170             sub _croak {
171 0     0     require Carp; &Carp::croak;
  0            
172             }
173              
174             1;
175              
176             __END__