File Coverage

blib/lib/Parse/Template.pm
Criterion Covered Total %
statement 55 143 38.4
branch 2 32 6.2
condition 0 18 0.0
subroutine 16 25 64.0
pod 6 7 85.7
total 79 225 35.1


line stmt bran cond sub pod time code
1 2     2   1950 use strict;
  2         4  
  2         93  
2 2     2   12 use warnings;
  2         5  
  2         129  
3             require 5.006;
4             package Parse::Template;
5             $Parse::Template::VERSION = '3.08';
6            
7 2     2   22 use Carp;
  2         10  
  2         273  
8 2     2   14 use constant DEBUG => 0;
  2         4  
  2         148  
9 2     2   12 use vars qw/$AUTOLOAD/;
  2         3  
  2         201  
10             sub AUTOLOAD {
11 0     0   0 my($class, $part) = ($AUTOLOAD =~ /(.*)::(.*)$/);
12 2     2   11 no strict 'refs';
  2         3  
  2         257  
13 0   0 0   0 *$AUTOLOAD = sub { (ref $_[0] || $class)->eval("$part", @_) };
  0         0  
14 0         0 goto &$AUTOLOAD;
15             }
16            
17 2     2   2094 use Symbol qw(delete_package);
  2         2630  
  2         310  
18 0     0 0 0 { my $id = 0; sub getid { $id++ } }
19            
20             my $PACKAGE = __PACKAGE__;
21             sub new {
22 0     0 1 0 my $receiver = shift;
23 0         0 my $class = $PACKAGE . '::Sym' . getid();
24 0         0 my $self = bless {}, $class; # absolutely nothing in $self
25 2     2   15 no strict 'refs';
  2         4  
  2         299  
26 0         0 %{"${class}::template"} = (); # so no 'used only once' warning
  0         0  
27 0         0 ${"${class}::ancestor"} = ''; # so no 'used only once' warning
  0         0  
28            
29 0   0     0 @{"${class}::ISA"} = ref $receiver || $receiver;
  0         0  
30 0         0 ${"${class}::ancestor"} = $receiver; # reverse the destruction order
  0         0  
31 0         0 *{"${class}::AUTOLOAD"} = \&AUTOLOAD; # so no warning for procedural calls
  0         0  
32 0         0 %{"${class}::template"} = @_ ;
  0         0  
33 0         0 $self;
34             }
35 2     2   13 use constant TRACE_ENV => 0;
  2         4  
  2         241  
36             sub env {
37 0     0 1 0 my $self = shift;
38 0   0     0 my $class = ref $self || $self;
39 0         0 my $symbol = shift;
40 0 0       0 if ($symbol =~ /\W/) {
41 0         0 Carp::croak "invalid symbol name: $symbol"
42             }
43 2     2   11 no strict;
  2         5  
  2         1636  
44 0 0       0 if (@_) {
  0 0       0  
45 0         0 do {
46 0         0 my $value = shift;
47 0         0 print STDERR "${class}::$symbol\t$value\n" if TRACE_ENV;
48 0 0       0 if (ref $value) {
49 0         0 *{"${class}::$symbol"} = $value;
  0         0  
50             } else { # scalar value
51 0         0 *{"${class}::$symbol"} = \$value;
  0         0  
52             }
53 0 0       0 $symbol = shift if @_;
54 0 0       0 if ($symbol =~ /\W/) {
55 0         0 Carp::croak "invalid symbol name: $symbol";
56             }
57             } while (@_);
58             }
59             elsif (defined *{"${class}::$symbol"}) { # borrowed from Exporter.pm
60 0 0       0 return \&{"${class}::$symbol"} unless $symbol =~ s/^(\W)//;
  0         0  
61 0         0 my $type = $1;
62             return
63 0         0 $type eq '*' ? *{"${class}::$symbol"} :
  0         0  
64 0         0 $type eq "\$" ? \${"${class}::$symbol"} :
65 0         0 $type eq '%' ? \%{"${class}::$symbol"} :
66 0         0 $type eq '@' ? \@{"${class}::$symbol"} :
67             $type eq '&' ? \&{"${class}::$symbol"} :
68 0 0       0 do { Carp::croak("Can\'t find symbol: $type$symbol") };
  0 0       0  
    0          
    0          
    0          
69             }
70             else {
71 0         0 undef;
72             }
73             }
74             sub DESTROY {
75 0     0   0 print STDERR "destroy(@_): ", ref $_[0], "\n" if DEBUG;
76 0         0 delete_package(ref $_[0]);
77             }
78             # Purpose: validate the regexp and replace "!" by "\!", and "/" by "\/"
79             # if not already escaped
80             # Arguments: a regexp
81             # Returns: the preprocessed regexp
82             sub ppregexp {
83             # my $self = $_[0]; # useless
84 19     19 1 1513 my $regexp = $_[1];
85 19         28 eval { '' =~ /$regexp/ };
  19         235  
86 19 100       62 if ($@) {
87 1         11 $@ =~ s/\s+at\s+[^\s]+\s+line\s+\d+[.]\n$//; # annoying info
88 1         199 Carp::croak $@;
89             }
90 18         35 for ($regexp) {
91 18         211 s{
92             ( (?: \G | [^\\] ) (?: \\{2} )* ) # even number of back-slashes
93             ( [!/\"] ) # used delimiters
94             }{$1\\$2}xg;
95            
96             # replace back exceptions (?!...), (?
97 18         69 s{
98             ( \( \?
99             \\ # inserted by first replace
100             ( ! ) # delimiter
101             }{$1$2}xg; # remove back-slash
102             }
103 18         705 $regexp;
104             }
105             sub getPart {
106 0     0 1   my $self = shift;
107 0           my $part = shift;
108 0   0       my $class = ref $self || $self;
109 0           my $text = '';
110 2     2   46 no strict 'refs';
  2         3  
  2         311  
111 0 0         unless (defined($text = ${"${class}::template"}{$part})) {
  0            
112 0           my $parent = ${"${class}::ISA"}[0]; # delegation
  0            
113 0 0         unless (defined $parent) {
114 0           Carp::croak("'$part' template part is not defined");
115             }
116 0           $text = $parent->getPart($part);
117             }
118 0           $text;
119             }
120             sub setPart {
121 0     0 1   my $self = shift;
122 0           my $part = shift;
123 0   0       my $class = ref $self || $self;
124 2     2   11 no strict 'refs';
  2         15  
  2         932  
125 0           ${"${class}::template"}{$part} = shift;
  0            
126             }
127             $Parse::Template::CONFESS = 1;
128             my $Already_shown = 0;
129             my $__DIE__ = sub {
130             if (not($Parse::Template::CONFESS) and $Already_shown) {
131             # Reset when the eval() processing is finished
132             $Already_shown = 0 if defined($^S);
133             return;
134             }
135             # evaluated expressions are not always available in (caller(1))[6];
136             if (defined($1) and $1 ne '') {
137             my $expr = $1; # what is the template expression?
138             { package DB; # what is the part name?
139             @DB::caller = caller(1);
140             @DB::caller = caller(2) unless @DB::args;
141             };
142             #local $1;
143             $expr =~ s/package\s+${PACKAGE}::\w+\s*;//o;
144             my $line = 0;
145             $expr =~ s/^/sprintf "%2s ", ++$line/egm;
146             $expr =~ s/\n;$//;
147             my $part = defined $DB::args[1] ? $DB::args[1] : '';
148             if ($Already_shown) {
149             print STDERR "call from part '$part':\n$expr\n";
150             } else {
151             print STDERR "Error in part '$part':\n$expr\n";
152             }
153             }
154             else {
155             print STDERR "\$1 not defined";
156             }
157             print STDERR "\$1: $1\n";
158             # ignore Already_shown if you won't confess your exception
159             $Already_shown = 1 unless $Parse::Template::CONFESS;
160             };
161             $Parse::Template::SIG{__WARN__} = sub { # don't know how to suppress this:
162             print STDERR "$_[0]"
163             unless ($_[0] =~ /^Use of uninitialized value in substitution iterator/)
164             };
165            
166 2     2   12 use constant EVAL_TRACE => 0;
  2         4  
  2         103  
167 2     2   12 use constant SHOW_PART => 0;
  2         3  
  2         95  
168 2     2   11 use constant SIGN_PART => 0;
  2         6  
  2         990  
169             $Parse::Template::SIGN_START = "# Template %s {\n"; # not documented
170             $Parse::Template::SIGN_END = "# } Template %s\n"; # not documented
171             my $indent = 0;
172             my @part = ();
173             sub eval {
174 0     0 1   print STDERR do {
175             local $" = q!', '! ; '..' x ++$indent, "=>eval('@_')\n"
176             } if EVAL_TRACE;
177 0           my $self = shift;
178 0           my $part = shift; # can't declare $part in eval()
179 0           push @part, $part;
180 0   0       my $class = ref $self || $self;
181 0           my $text = $self->getPart($part);
182 0           print STDERR qq!$part content: $text\n! if SHOW_PART;
183 0           if (SIGN_PART) { # not documented
184             $text =~ s!^!sprintf $Parse::Template::SIGN_START, $part!e;
185             $text =~ s!$!sprintf $Parse::Template::SIGN_END, $part!e;
186             }
187 0           local $SIG{__DIE__} = $__DIE__;
188             # eval expression in class
189 0           $text =~ s( %% (.*?) %% ){ # the magical substitution
190 0           print STDERR '..' x $indent, "Eval part name: $part\n" if EVAL_TRACE;
191 0           print STDERR '..' x $indent, " expr: package $class;\n$1\n" if EVAL_TRACE;
192 0           "package $class; $1";
193             }eegsx;
194 0           print STDERR "after: $class - $1\n" if EVAL_TRACE;
195 0 0         die "$@" if $@; # caught by __DIE__
196 0           pop @part; $part = $part[-1];
  0            
197 0           --$indent if EVAL_TRACE;
198 0           $text;
199             }
200             1;
201             __END__