File Coverage

lib/DBIx/DR/PlPlaceHolders.pm
Criterion Covered Total %
statement 126 129 97.6
branch 35 42 83.3
condition 10 18 55.5
subroutine 22 22 100.0
pod 1 6 16.6
total 194 217 89.4


line stmt bran cond sub pod time code
1 2     2   22980 use utf8;
  2         612  
  2         9  
2 2     2   46 use strict;
  2         0  
  2         27  
3 2     2   5 use warnings;
  2         2  
  2         48  
4              
5             package DBIx::DR::PlPlaceHolders;
6 2     2   669 use Mouse;
  2         36651  
  2         12  
7             extends 'DBIx::DR::PerlishTemplate';
8 2     2   953 use DBIx::DR::ByteStream;
  2         4  
  2         36  
9              
10 2     2   7 use Carp ();
  2         2  
  2         24  
11 2     2   5 use File::Spec ();
  2         2  
  2         18  
12 2     2   5 use Digest::MD5 ();
  2         2  
  2         25  
13 2     2   5 use Encode qw(encode);
  2         16  
  2         2067  
14              
15             has sql_dir => (is => 'bare', isa => 'Str');
16             has file_suffix => (is => 'rw', isa => 'Str', default => '.sql.ep');
17             has helpers => (is => 'ro', isa => 'HashRef', default => sub {{}});
18              
19             sub sql_dir {
20 28     28 0 977 my ($self, $dir) = @_;
21 28 100       70 if (defined $dir) {
    50          
22 2 100       126 Carp::croak "Diectory $dir is not found or a dir" unless -d $dir;
23 1         11 $self->{sql_dir} = File::Spec->rel2abs($dir);
24             } elsif(@_ >= 2) {
25 0         0 $self->{sql_dir} = undef;
26             }
27 27         225 return $self->{sql_dir};
28             }
29              
30             sub BUILD {
31 3     3 1 4 my ($self) = @_;
32              
33             # add default helpers
34             $self->set_helper(
35             include => sub {
36 1     1   1 my ($tpl, $file, @args) = @_;
37              
38 1         5 my $res = ref($self)->new(
39             pretokens => $self->prepretokens,
40             prepretokens => $self->prepretokens,
41             helpers => $self->helpers,
42             sql_dir => $self->sql_dir,
43             file_suffix => $self->file_suffix,
44             )->sql_transform(
45             -f => $file,
46             @args
47             );
48              
49              
50 1         5 $tpl->immediate($res->sql);
51 1         3 $tpl->add_bind_value($res->bind_values);
52 1         3 return DBIx::DR::ByteStream->new('');
53             },
54              
55             list => sub {
56 5     5   10 my ($tpl, @args) = @_;
57 5         25 $tpl->immediate(join ',' => map '?', @args);
58 5         15 $tpl->add_bind_value(@args);
59 5         11 return DBIx::DR::ByteStream->new('');
60             },
61              
62             hlist => sub {
63 6     6   12 my ($tpl, @args) = @_;
64 6 100       14 if ('ARRAY' eq ref $args[0]) {
65 5         5 my $filter = shift @args;
66 5         20 $tpl->immediate(
67             join ',' => (
68             '(' .
69             join(',' => ('?')x @$filter) .
70             ')'
71             )x @args
72             );
73 5         10 for my $a (@args) {
74 15         12 $tpl->add_bind_value( map { $a->{$_} } @$filter );
  26         46  
75             }
76 4         7 return DBIx::DR::ByteStream->new('');
77             }
78             $tpl->immediate(
79             join ',' => map {
80 1         2 '(' .
  3         10  
81             join(',' => ('?') x keys %$_) .
82             ')'
83             } @args
84             );
85 1         3 $tpl->add_bind_value(map { values %$_ } @args);
  3         5  
86 1         2 return DBIx::DR::ByteStream->new('');
87             },
88              
89             stacktrace => sub {
90 1     1   2 my ($tpl, $skip, $depth, $sep) = @_;
91              
92 1   50     2 $depth ||= 32;
93 1   50     8 $skip ||= 0;
94              
95 1         1 $skip += 7;
96 1         1 $depth += 6;
97 1 50       2 $sep = ", " unless defined $sep;
98              
99 1         1 my @stack;
100              
101 1 50       7 for (my $i = $skip ? $skip - 1 : 0; $i < $depth; $i++) {
102 1         8 my @line = caller $i;
103 1 50       2 last unless @line;
104 1         7 push @stack => sprintf '%s:%s', @line[1,2];
105             }
106 1         3 return DBIx::DR::ByteStream->new(join $sep, @stack);
107             },
108 3         67 );
109              
110 3         24 $self;
111             }
112              
113              
114             sub sql_transform {
115 50     50 0 14107 my $self = shift;
116 50         50 my ($sql, %opts);
117              
118 0         0 my $pt;
119              
120 50 100       101 if (@_ % 2) {
121 36         64 ($sql, %opts) = @_;
122 36         73 delete $opts{-f};
123             } else {
124 14         28 %opts = @_;
125 14 100       33 Carp::croak $self->usage unless $opts{-f};
126 13         20 my $file = $opts{-f};
127              
128 13 100 66     24 $file = File::Spec->catfile($self->sql_dir, $file)
129             if $self->sql_dir and $file !~ m{^/};
130 13         45 my $resuffix = quotemeta $self->file_suffix;
131 13 100 66     122 $file .= $self->file_suffix
132             if $self->file_suffix and $file !~ /$resuffix$/;
133              
134 13         262 my @fstat = stat $file;
135 13 100       124 Carp::croak "Can't find file $file" unless @fstat;
136 12         33 $opts{-f} = $file;
137             }
138              
139              
140 48   66     166 my $namespace = $opts{-f} || $sql;
141 48 100       132 $namespace = encode utf8 => $namespace if utf8::is_utf8($namespace);
142 48         198 $namespace = Digest::MD5::md5_hex($namespace);
143 48         92 $self->{namespace} = __PACKAGE__ . '::Sandbox::t' . $namespace;
144              
145 48         144 $self -> clean_prepends
146             -> clean_preprepends
147             ;
148              
149 48         44 for my $name (keys %{ $self->helpers }) {
  48         181  
150 228         493 $self->preprepend(
151             'BEGIN{ ' .
152             "*" . $name . '= sub {' .
153             '$_PTPL->call_helper(q{' . $name . '}, @_)' .
154             '} ' .
155             '}'
156             );
157             }
158              
159 48         56 my @args;
160 48         72 for (keys %opts) {
161 54 100       144 next unless /^\w/;
162 31         91 $self->prepend("my \$$_ = shift");
163 31         55 push @args, $opts{$_};
164             }
165              
166 48 100       73 if ($sql) {
167 36         81 $self->render($sql, @args);
168             } else {
169 12         39 $self->render_file($opts{-f}, @args);
170             }
171              
172 42         323 my $res =
173             DBIx::DR::PlPlaceHolders::TransformResult->new(rtemplate => $self);
174              
175             # clean memory
176 42         74 $self->{sql} = '';
177 42         62 $self->{variables} = [];
178              
179 42         119 $res;
180             }
181              
182              
183             sub call_helper {
184 17     17 0 34 my ($self, $name, @args) = @_;
185             Carp::croak "Helper '$name' is not found or has already been removed"
186 17 50       46 unless exists $self->helpers->{ $name };
187 17         43 $self->helpers->{ $name }->($self, @args);
188             }
189              
190              
191             sub set_helper {
192 6     6 0 491 my ($self, %opts) = @_;
193 6 100       16 Carp::croak $self->usage unless %opts;
194 5         17 while (my ($n, $s) = each %opts) {
195 15 50 33     68 Carp::croak $self->usage unless 'CODE' eq ref $s and $n =~ /^\w/;
196 15         61 $self->helpers->{ $n } = $s ;
197             }
198 5         9 $self;
199             }
200              
201             sub usage {
202 2     2 0 2 my ($self) = @_;
203 2         18 my @caller = caller 1;
204              
205 2 100       95 return 'Usage: $ph->sql_transform($sql | -f => $sql_file, ...)'
206             if $caller[3] =~ /sql_transform$/;
207 1 50       149 return 'Usage: $ph->set_helper($name => sub { ... })'
208             if $caller[3] =~ /set_helper$/;
209              
210 0         0 return $caller[3];
211             }
212              
213             package DBIx::DR::PlPlaceHolders::TransformResult;
214 2     2   14 use Mouse;
  2         2  
  2         10  
215              
216             has rtemplate => (is => 'ro', isa => 'Object', weak_ref => 1);
217             has sql => (is => 'ro', isa => 'Str');
218              
219             sub BUILD {
220 42     42   123 my ($self) = @_;
221 42         127 $self->{sql} = $self->rtemplate->sql;
222 42         114 $self->{bind_values} = $self->rtemplate->variables;
223             }
224              
225             sub bind_values {
226 43     43   13783 my ($self) = @_;
227 43 100       87 return @{ $self->{bind_values} } if wantarray;
  19         171  
228 24   50     84 return $self->{bind_values} || [];
229             }
230              
231             1;
232              
233             =head1 NAME
234              
235             DBIx::DR::PlPlaceHolders - template converter for L.
236              
237             =head1 COPYRIGHT
238              
239             Copyright (C) 2011 Dmitry E. Oboukhov
240             Copyright (C) 2011 Roman V. Nikolaev
241              
242             This program is free software, you can redistribute it and/or
243             modify it under the terms of the Artistic License.
244              
245             =cut
246