File Coverage

lib/DBIx/DR/PerlishTemplate.pm
Criterion Covered Total %
statement 322 386 83.4
branch 44 54 81.4
condition 7 15 46.6
subroutine 166 166 100.0
pod 0 12 0.0
total 539 633 85.1


line stmt bran cond sub pod time code
1 5     5   23367 use utf8;
  3         5  
  5         54  
2 4     5   69 use strict;
  6         97  
  5         54  
3 5     5   59 use warnings;
  3         5  
  5         105  
4              
5             package DBIx::DR::PerlishTemplate;
6 3     5   436 use Mouse;
  4         19158  
  4         16  
7 5     5   662 use Carp;
  3         4  
  5         179  
8 4     5   13 use Scalar::Util;
  6         106  
  5         96  
9 5     5   300 use DBIx::DR::ByteStream;
  3         3  
  5         3734  
10              
11             has line_tag => (is => 'rw', isa => 'Str', default => '%');
12             has open_tag => (is => 'rw', isa => 'Str', default => '<%');
13             has close_tag => (is => 'rw', isa => 'Str', default => '%>');
14             has quote_mark => (is => 'rw', isa => 'Str', default => '=');
15             has immediate_mark => (is => 'rw', isa => 'Str', default => '==');
16              
17             has sql => (is => 'ro', isa => 'Str', default => '');
18             has variables => (is => 'ro', isa => 'ArrayRef');
19              
20             has template => (is => 'rw', isa => 'Str', default => '');
21             has template_file => (is => 'rw', isa => 'Str', default => '');
22              
23             has stashes => (is => 'ro', isa => 'ArrayRef');
24             has pretokens => (is => 'ro', isa => 'ArrayRef');
25             has prepretokens => (is => 'ro', isa => 'ArrayRef');
26             has parsed_template => (is => 'ro', isa => 'Str', default => '');
27             has namespace => (is => 'rw', isa => 'Str',
28             default => 'DBIx::DR::PerlishTemplate::Sandbox');
29              
30              
31             has sql_utf8 => (is => 'ro', isa => 'Bool', default => 1);
32             sub _render {
33 62     62   94 my ($_PTPL) = @_;
34 60         43 my $_PTSUB;
35              
36 61 50       170 unless ($_PTPL->parsed_template) {
37 61         109 $_PTSUB = $_PTPL->{parsed_template} = $_PTPL->_parse;
38             } else {
39 2         59 $_PTSUB = $_PTPL->parsed_template;
40             }
41              
42 60         66 $_PTPL->{parsed_template} = $_PTSUB;
43              
44 4     5   65 my $esub = eval $_PTSUB;
  1     5   6  
  5     5   141  
  5     5   11  
  6     5   177  
  3     5   8  
  3     5   130  
  12     5   18  
  5     5   119  
  1     5   12  
  3     5   108  
  0     5   0  
  5     5   76  
  2     5   6  
  3     8   115  
  3     2   8  
  5     2   152  
  1     2   2  
  3     2   97  
  0     2   0  
  5     2   101  
  1     2   7  
  4     2   199  
  2     2   6  
  5     2   80  
  2     2   6  
  5     2   163  
  6     2   14  
  4     2   103  
  1     2   2  
  3     2   175  
  2     2   4  
  5     2   179  
  1     2   1  
  3     2   154  
  3     2   6  
  5     2   135  
  3     2   35  
  4     2   145  
  19     2   30  
  5     2   73  
  4     2   9  
  3     2   140  
  5     2   71  
  3     2   86  
  1     2   63  
  5     2   173  
  11     2   25  
  4     2   138  
  43     2   63  
  6     2   181  
  1     2   4  
  3     2   98  
  6     2   15  
  62     2   6437  
  1     2   3  
  1     2   52  
  0     1   0  
  1     1   3  
  0     1   0  
  1     1   37  
  4     1   5  
  1     1   30  
  0     1   0  
  1     1   32  
  0     1   0  
  1     1   29  
  0     1   0  
  1     1   28  
  1     1   3  
  1     1   45  
  0     1   0  
  1     1   3  
  0     1   0  
  1     1   36  
  2     1   5  
  1     1   31  
  0     1   0  
  1     1   34  
  0     1   0  
  1     1   27  
  0     1   0  
  1     1   35  
  1     1   3  
  1     1   46  
  0     1   0  
  1     1   4  
  0     1   0  
  1     1   37  
  0     1   0  
  1     1   43  
  0     1   0  
  1     1   47  
  0     1   0  
  1     1   34  
  0     1   0  
  1     1   29  
  0     1   0  
  1     1   54  
  0     1   0  
  1     1   4  
  1     1   11  
  1     1   36  
  11     1   15  
  1     1   67  
  0     1   0  
  1     1   35  
  0     1   0  
  1     1   31  
  0     1   0  
  1     1   28  
  0     1   0  
  1     1   68  
  0     1   0  
  1     1   4  
  1     1   3  
  1     1   53  
  11     1   16  
  1     1   37  
  0     1   0  
  1     1   30  
  0     1   0  
  1     1   28  
  0     1   0  
  1     1   29  
  0     1   0  
  1     1   66  
  0     1   0  
  1     1   3  
  0     1   0  
  1     1   38  
  0     1   0  
  1     1   30  
  0     1   0  
  1     1   27  
  0     1   0  
  1     1   26  
  0     1   0  
  1     1   30  
  0     1   0  
  1     1   76  
  0         0  
  1         5  
  1         2  
  1         31  
  11         25  
  1         35  
  0         0  
  1         25  
  0         0  
  1         30  
  0         0  
  1         46  
  0         0  
  1         78  
  0         0  
  1         4  
  1         2  
  1         37  
  4         7  
  1         34  
  0         0  
  1         32  
  0         0  
  1         34  
  0         0  
  1         32  
  0         0  
  1         48  
  1         3  
  1         4  
  1         3  
  1         33  
  11         17  
  1         31  
  0         0  
  1         27  
  0         0  
  1         26  
  0         0  
  1         25  
  0         0  
  1         62  
  0         0  
  1         3  
  0         0  
  1         41  
  0         0  
  1         37  
  0         0  
  1         33  
  0         0  
  1         31  
  0         0  
  1         28  
  0         0  
  1         108  
  0         0  
  1         3  
  1         3  
  1         55  
  2         4  
  1         36  
  0         0  
  1         29  
  1         3  
  1         30  
  0         0  
  1         29  
  0         0  
  1         33  
  0            
45 60 100       167 if (my $e = $@) {
46 9         107 my $do_croak;
47             my $template;
48 22 100       52 if ($_PTPL->template_file) {
49 5         67 $template = $_PTPL->template_file;
50             } else {
51 3         4 $do_croak = 1;
52 5         58 $template = 'inline template';
53             };
54 6         73 $e =~ s{ at .*?line (\d+)(\.\s*|,\s+.*?)?$}
55 8         124 [" at $template line " . ( $1 - $_PTPL->pre_lines )]gsme;
56              
57 8 50       22 if ($1) {
58 7         130 $e =~ s/\s*$/\n/g;
59 7 100       83 die $e unless $do_croak;
60 5         506 croak $e;
61             }
62              
63 0         0 croak "$e at $template";
64             }
65              
66 57         169 $_PTPL->{sql} = '';
67 70         96 $_PTPL->{variables} = [];
68              
69 56         122 $esub->( @{ $_PTPL->stashes } );
  54         1001  
70 55         383 1;
71             }
72              
73             sub render {
74 48     50 0 136 my ($self, $tpl, @args) = @_;
75 50         182 $self->{parsed_template} = '';
76 49         144 $self->template($tpl);
77 50         94 $self->template_file('');
78 48         52 $self->{stashes} = \@args;
79 50         197 $self->clean_namespace;
80 69         122 return $self->_render;
81             }
82              
83             sub render_file {
84 14     14 0 83 my ($self, $file, @args) = @_;
85 12 50 0     159 croak "File '@{[ $file // 'undef' ]}' not found or readable"
  2         62  
86             unless -r $file;
87 12         372 open my $fh, '<:raw', $file;
88 14         76 my $data;
89              
90 12         11 { local $/; $data = <$fh> }
  14         150  
  13         172  
91              
92 14 50       134 utf8::decode $data if $self->sql_utf8;
93              
94 13         21 $self->{parsed_template} = '';
95 15         102 $self->template_file($file);
96 14         25 $self->template($data);
97 13         54 $self->{stashes} = \@args;
98 16         36 $self->clean_namespace;
99 14         83 return $self->_render;
100             }
101              
102             sub clean_prepends {
103 48     50 0 50 my ($self) = @_;
104 50         121 $self->{pretokens} = [];
105 48         116 $self;
106             }
107              
108             sub clean_preprepends {
109 50     50 0 109 my ($self) = @_;
110 48         63 $self->{prepretokens} = [];
111 50         121 $self;
112             }
113              
114              
115             sub immediate {
116 269     270 0 237 my ($self, $str) = @_;
117 271 100       535 if (Scalar::Util::blessed $str) {
118 7 50       16 if ('DBIx::DR::ByteStream' eq Scalar::Util::blessed $str) {
    0          
119 8         38 $self->{sql} .= $str->content;
120             } elsif ($str->can('content')) {
121 0         0 $self->{sql} .= $str->content;
122             } else {
123 1         41 croak "Can't extract content from " . Scalar::Util::blessed $str;
124             }
125             } else {
126 265         322 $self->{sql} .= $str;
127             }
128 270         468 return DBIx::DR::ByteStream->new('');
129             }
130              
131             sub add_bind_value {
132 64     65 0 81 my ($self, @values) = @_;
133 65         119 push @{ $self->variables } => @values;
  63         174  
134             }
135              
136              
137             sub quote {
138 49     49 0 109 my ($self, $variable) = @_;
139              
140 48 100       117 if (Scalar::Util::blessed $variable) {
141 8 50       110 return $self->immediate($variable)
142             if 'DBIx::DR::ByteStream' eq Scalar::Util::blessed $variable;
143             }
144              
145 44         47 $self->{sql} .= '?';
146 44         117 $self->add_bind_value($variable);
147 43         82 return DBIx::DR::ByteStream->new('');
148             }
149              
150             sub _parse {
151 62     62   86 my ($self) = @_;
152              
153 61         108 my $result = '';
154              
155 61         133 my $immediate_mark = $self->immediate_mark;
156 63         108 my $quote_mark = $self->quote_mark;
157              
158             my $code_cb = sub {
159 72     74   64 my ($t) = @_;
160 74 50 33     331 return unless defined $t and length $t;
161              
162 72 100       207 if ($t =~ /^\Q$immediate_mark\E/) {
163 14         87 $result .= join '',
164             'immediate(',
165             substr($t, length($immediate_mark)),
166             ');';
167 13         17 return;
168             }
169              
170 62 100       198 if ($t =~ /^\Q$quote_mark\E/) {
171 37         107 $result .= join '',
172             'quote(',
173             substr($t, length($quote_mark)),
174             ');';
175 39         175 return;
176             }
177              
178 26         49 $result .= "$t;"; # always place ';' at end of code.
179 62         266 };
180              
181             my $text_cb = sub {
182 313     315   298 my ($content) = @_;
183 314 100 66     938 return unless defined $content and length $content;
184 250         220 $content =~ s/'/\\'/g;
185 249         403 $result .= "immediate('" . $content . "');";
186 62         151 };
187              
188 60         139 $self->_parse_ep($self->template, $text_cb, $code_cb);
189              
190 62         208 $result = join '',
191             'package ', $self->namespace, ';',
192             'BEGIN { ',
193             '*quote = sub { $_PTPL->quote(@_) };',
194             '*immediate = sub { $_PTPL->immediate(@_) };',
195             '};',
196             $self->preprepend,
197             'sub {', $self->prepend, $result, "\n}";
198              
199 61         389 return $result;
200             }
201              
202             sub _parse_ep {
203              
204 62     62   121 my ($self, $tpl, $text_cb, $code_cb) = @_;
205              
206             #---------------------------------------------------------
207             # единственные три переменные из self
208 61         89 my $line_tag = $self->line_tag;
209 62         145 my $open_tag = $self->open_tag;
210 60         87 my $close_tag = $self->close_tag;
211             # по идее это можно было оформить в виде независимого кода
212             #---------------------------------------------------------
213              
214 62         224 my @lines = split /\n/, $tpl;
215              
216 60         51 my $st = 'text';
217 63         101 my $code_text;
218              
219 60         136 for (my $i = 0; $i < @lines; $i++) {
220 182         231 local $_ = $lines[$i];
221              
222             CODE:
223 226 100       309 if ($st eq 'code') {
224 48 100       277 if (/^(.*?)\Q$close_tag\E(.*)/) {
225 42         53 $_ = $2;
226 44         160 $code_cb->($code_text . $1);
227 42         39 $code_text = undef;
228 44         96 $st = 'text';
229 42         128 goto ANYTEXT;
230             } else {
231 6         57 $code_text .= $_;
232 5         7 $code_text .= "\n";
233 6         128 next;
234             }
235             }
236              
237             TEXT_BEGIN:
238 177 100       721 if (/^(\s*)\Q$line_tag\E(.*)/) {
239 31         43 $text_cb->($1);
240 30 100       53 if ($i < $#lines) {
241 24         93 $code_cb->("$2\n");
242             } else {
243 10         18 $code_cb->($2);
244             }
245 31         104 next;
246             }
247              
248             ANYTEXT:
249 189 100       467 if (/^(.*?)\Q$open_tag\E(.*)/) {
250 43         115 $_ = $2;
251 42         61 $text_cb->($1);
252 43         108 $code_text = '';
253 42         33 $st = 'code';
254 43         199 goto CODE;
255             } else {
256 148         167 $text_cb->($_);
257 148 100       289 $text_cb->("\n") if $i < $#lines;
258 147         300 next;
259             }
260             }
261 61 50 33     161 $text_cb->("<%" . $code_text) if defined $code_text and length $code_text;
262             }
263              
264              
265             sub preprepend {
266 294     296 0 273 my ($self, @tokens) = @_;
267 295   100     463 $self->{prepretokens} ||= [];
268 297 100       383 push @{ $self->prepretokens } => map "$_;\n", @tokens if @tokens;
  229         604  
269 294 100       585 return join '' => @{ $self->prepretokens } if defined wantarray;
  67         288  
270             }
271              
272             sub prepend {
273 98     100 0 110 my ($self, @tokens) = @_;
274 99   50     189 $self->{pretokens} ||= [];
275 98 100       140 push @{ $self->pretokens } => map "$_;", @tokens if @tokens;
  33         131  
276 99 100       178 return join '' => @{ $self->pretokens } if defined wantarray;
  67         269  
277             }
278              
279              
280             sub pre_lines {
281 6     8 0 9 my ($self) = @_;
282 7         10 my $lines = 0;
283 7         13 $lines += @{[ /\n/g ]} for ($self->preprepend, $self->prepend);
  13         108  
284 9         38 return $lines;
285             }
286              
287             sub clean_prepend {
288 13     14 0 10729 my ($self) = shift;
289 12         21 $self->{pretokens} = [];
290             }
291              
292             sub clean_namespace {
293 61     62 0 124 my ($self) = @_;
294 60         100 my $sb = $self->namespace;
295              
296 3     5   15 no strict 'refs';
  5         61  
  3         213  
297 61         80 undef *{$sb . '::' . $_} for keys %{ $sb . '::' };
  60         393  
  178         737  
298             }
299              
300             1;
301              
302             =head1 NAME
303              
304             DBIx::DR::PerlishTemplate - template engine for L.
305              
306             =head1 COPYRIGHT
307              
308             Copyright (C) 2011 Dmitry E. Oboukhov
309             Copyright (C) 2011 Roman V. Nikolaev
310              
311             This program is free software, you can redistribute it and/or
312             modify it under the terms of the Artistic License.
313              
314             =cut
315              
316              
317              
318              
319