File Coverage

blib/lib/Template/Provider/DBI.pm
Criterion Covered Total %
statement 57 68 83.8
branch 16 34 47.0
condition 13 34 38.2
subroutine 8 8 100.0
pod 1 2 50.0
total 95 146 65.0


line stmt bran cond sub pod time code
1             package Template::Provider::DBI;
2              
3 1     1   604251 use DBI;
  1         3  
  1         42  
4 1     1   423 use DateTime::Format::DBI;
  1         520  
  1         27  
5 1     1   5 use base 'Template::Provider';
  1         2  
  1         837  
6              
7             our $VERSION = '0.04';
8              
9             sub _init
10             {
11             # check dbi specific params from args, then call super
12             # modified timestamps?
13             # precompiled?
14 1     1   543813 my ($self, $args) = @_;
15              
16 1         10 $self->{$_} = $args->{$_} for keys %$args;
17              
18 1 50 33     12 if($self->{DBI_DBH} && $self->{DBI_DSN})
19             {
20 0         0 return $self->error("DBI:Can't use DBI_DBH and DBI_DSN at the same time");
21             }
22              
23 1 50       5 if($self->{DBI_DSN})
24             {
25 0 0       0 $self->{DBI_DBH} = DBI->connect($self->{DBI_DSN}, $self->{DBI_USER}, $self->{DBI_PASSWD}) or return $self->error("DBI: Failed to connect to $self->{DBI_DSN}, $self->{DBI_USER}, $self->{DBI_PASSWD}, $DBI::errstr");
26             }
27              
28 1   50     6 $self->{DBI_TABLE} ||= 'templates';
29 1   50     7 $self->{DBI_MODIFIEDFIELD} ||= 'modified';
30 1   50     5 $self->{DBI_TMPLFIELD} ||= 'template';
31 1   50     5 $self->{DBI_FILEFIELD} ||= 'filename';
32 1   50     19 $self->{DBI_CURRENTTIME} ||= 'current_timestamp';
33             # $self->{DBI_QUERY} ||= "SELECT $self->{DBI_TMPLFIELD} FROM $self->{DBI_TABLE} WHERE $self->{DBI_FILEFIELD} = ?";
34            
35 1         2 eval {
36 1   33     16 $self->{DBI_DT} ||= DateTime::Format::DBI->new($self->{DBI_DBH}); };
37 1 50       236 if($@)
38             {
39 0         0 warn "DateTime::Format:: for $self->{DBI_DBH}->{Driver}->{Name} not found, no caching supported";
40 0         0 $self->{DBI_DT} = undef;
41 0         0 $self->{DBI_MODIFIEDFIELD} = '';
42             }
43              
44 1         3 my $modified = '';
45 1 50       4 $modified = ", $self->{DBI_MODIFIEDFIELD}" if($self->{DBI_MODIFIEDFIELD});
46 1   33     10 $self->{DBI_QUERY} ||= "SELECT $self->{DBI_TMPLFIELD} $modified FROM $self->{DBI_TABLE} WHERE $self->{DBI_FILEFIELD} = ?";
47 1 50       21 $self->{DBI_STH} = $self->{DBI_DBH}->prepare_cached($self->{DBI_QUERY}) or
48             return $self->error("DBI:Failed to prepare query: $DBI:errstr");
49 1         134 return $self->SUPER::_init();
50             }
51              
52             sub fetch
53             {
54             # called to fetch template by name
55             # return (undef. status_declined) on missing (and on error & tolerant)
56             # return ($error, status_error) on error
57              
58 1     1 1 19687 my ($self, $name) = @_;
59 1         4 my ($data, $error);
60             # we dont do refs or handles:
61 1 50       4 return (undef, Template::Constants::STATUS_DECLINED) if(ref($name));
62              
63             # Check if caching is allowed / file has been cached
64             # my $compiled = $self->_compiled_filename($name);
65 1 50 33     5 if(defined $self->{ SIZE } && $self->{ LOOKUP }->{ $name })
66             {
67 0         0 ($data, $error) = _fetch($name);
68             }
69             else
70             {
71 1         5 ($data, $error) = $self->_load($name);
72 1 50       13 ($data, $error) = $self->_compile($data) unless($error);
73 1 50       24494 $data = $self->_store($name, $data) unless($error);
74             }
75            
76 1         41 return ($data, $error);
77            
78             }
79              
80             sub _load
81             {
82 1     1   3 my ($self, $name) = @_;
83              
84 1         2 my $data = {};
85             # fetch template from db
86 1         91 $self->{DBI_STH}->execute($name);
87 1         29 my ($templ, $modified) = $self->{DBI_STH}->fetchrow_array();
88 1 50       5 return (undef, Template::Constants::STATUS_DECLINED)
89             if(!defined $templ);
90 1 50 33     9 if($modified && exists $self->{DBI_DT})
91             {
92             # No "modified" field used, as we have no DT::Format::X
93 1         6 $data->{time} = $self->convert_timestamp($modified);
94             }
95             else
96             {
97 0         0 $data->{time} = time();
98             }
99 1         18 $data->{load} = time();
100 1         10 $data->{name} = $name;
101 1         3 $data->{text} = $templ;
102              
103 1 50       4 my $err = $DBI::errstr if(!$templ);
104              
105 1         3 return ($data, $err);
106             }
107              
108             ## _store uses stat on the filename, bah
109             ## patch to call _mtime($name) ?
110              
111             sub _modified
112             {
113 1     1   19 my ($self, $name) = @_;
114              
115 1 50       8 if(!defined $self->{DBI_DT})
116             {
117 0         0 return time();
118             }
119              
120 1         20 my $sth = $self->{DBI_DBH}->prepare_cached(<<SQL);
121             SELECT $self->{DBI_MODIFIEDFIELD}
122             FROM $self->{DBI_TABLE}
123             WHERE $self->{DBI_FILEFIELD} = ?
124             SQL
125              
126 1         240 $sth->execute($name);
127 1         26 my ($result) = $sth->fetchrow_array();
128              
129 1   33     6 return $self->convert_timestamp($result) || $result;
130              
131             }
132              
133             sub convert_timestamp
134             {
135 2     2 0 18 my ($self, $timestamp) = @_;
136              
137 2 50       8 return time() if(!$timestamp);
138 2 50 33     45 if($self->{DBI_DT} && $self->{DBI_DT}->can('parse_timestamp'))
    50 33        
139             {
140 0         0 my $dt = $self->{DBI_DT}->parse_timestamp($timestamp);
141 0         0 return $dt->epoch;
142             }
143             elsif($self->{DBI_DT} && $self->{DBI_DT}->can('format_datetime'))
144             {
145 2         13 my $dt = $self->{DBI_DT}->parse_datetime($timestamp);
146 2         1544 return $dt->epoch;
147             }
148 0           return 0;
149             }
150              
151              
152             1;
153              
154             __END__
155              
156             =head1 NAME
157              
158             Template::Provider::DBI - A class to allow retrieval of templates from a DB
159              
160             =head1 VERSION
161              
162             This documentation refers to version 0.01.
163              
164             =head1 SYNOPSIS
165              
166             use Template::Provider::DBI;
167             use DBI;
168              
169             my $dbh = DBI->connect('dbi:SQLite:./mydatabase.db');
170             my $dbi = Template::Provider::DBI->new({ DBI_DBH => $dbh });
171              
172             my $tt = Template->new({ LOAD_TEMPLATES => [ $dbi ] });
173             $tt->process('mytemplate.tt', \%vars);
174              
175             =head1 DESCRIPTION
176              
177             This class adds a provider to Template Toolkit to retrieve templates from a
178             database of your choice. Using the LOAD_TEMPLATES option to L<Template>,
179             multiple providers can be created and used. The DBI provider searches for the
180             given template name, and returns DECLINED when it can't find it, to allow
181             other providers to be checked.
182              
183             =head2 Caching
184              
185             Caching is supported if L<DateTime::Format::DBI> supports your database. The
186             DateTime formatter/parser is used to convert timestamps out of the database
187             into epoch times for Template Toolkit. Caching is done through the usual
188             Template Provider method of storing the compiled template in a file.
189              
190             =head2 Usage
191              
192             To use this module, create an instance of it (see L<new> below), and pass it
193             to Template Toolkit's LOAD_TEMPLATES option. If you want to use other template
194             providers as well (even the default file template), then you need to also
195             create instances of them, and pass them to LOAD_TEMPLATES in the order you
196             would like them to be checked.
197              
198             =head1 SUBROUTINES/METHODS
199              
200             =head2 new (constructor)
201              
202             Parameters:
203             \%options
204              
205             Many options are supported, most have defaults:
206              
207             =over 4
208              
209             =item DBI_DBH
210              
211             A DBI database handle object, as returned by C<< DBI->connect >>. This
212             argument is optional if you are providing a DBI_DSN argument.
213              
214             =item DBI_DSN
215              
216             A database source name, to be passed to C<< DBI->connect >>. This will be used
217             to make and store a local database handle. It is optional if a DBI_DBH is
218             provided, if both are provided, an error is thrown.
219              
220             =item DBI_TABLE
221              
222             The name of the database table containing your templates. This will default to
223             'templates' if not provided.
224              
225             =item DBI_TMPLFIELD
226              
227             The name of the table field containing the actual template data. This will
228             default to 'template' if not provided.
229              
230             =item DBI_FILEFIELD
231              
232             The name of the table field containing the template filename. This will
233             default to 'filename'.
234              
235             =item DBI_MODIFIEDFIELD
236              
237             The name of the table field containing the timestamp or datetime of when the
238             template data was last modified. Defaults to 'modified' if not provided.
239              
240             =item DBI_DT
241              
242             If L<DateTime::Format::DBI> does not support your database, then you can try
243             getting around it by providing an object in the DBI_DT option that has a
244             "parse_timestamp" method, which will be passed the contents of your
245             DBI_MODIFIEDFIELD.
246              
247             =item DBI_QUERY
248              
249             The query used to retrieve the template data from the database will be create
250             simply as:
251              
252             SELECT $self->{DBI_TMPLFIELD}, $self->{DBI_MODIFIEDFIELD}$modified FROM $self->{DBI_TABLE} WHERE $self->{DBI_FILEFIELD} = ?;
253              
254             If you need a more complex query then provide it here, it should return at
255             least the template data field first, then the modify date field next (or
256             NULL), in that order.
257              
258             =back
259              
260             =head1 DEPENDENCIES
261              
262             Modules used, version dependencies, core yes/no
263              
264             DBI
265              
266             DateTime::Format::DBI
267              
268             =head1 NOTES
269              
270             DateTime::Format::DBI produces DBI warnings when used with SQLite. It calls
271             DBI::_dbtype_names($dbh); .. Not my fault, honest!
272              
273             =head1 BUGS AND LIMITATIONS
274              
275             None known currently, please email the author if you find
276             any.
277              
278             =head1 AUTHOR
279              
280             Jess Robinson <cpan@desert-island.demon.co.uk>
281              
282             =cut