File Coverage

blib/lib/Template/Provider/HTTP.pm
Criterion Covered Total %
statement 42 71 59.1
branch 11 32 34.3
condition 4 8 50.0
subroutine 8 8 100.0
pod n/a
total 65 119 54.6


line stmt bran cond sub pod time code
1             package Template::Provider::HTTP;
2 2     2   2286 use base qw( Template::Provider );
  2         6  
  2         242  
3              
4 2     2   14 use strict;
  2         4  
  2         96  
5 2     2   32 use warnings;
  2         4  
  2         76  
6              
7 2     2   2398 use LWP::UserAgent;
  2         108092  
  2         2068  
8              
9             our $VERSION = 0.05;
10              
11             =head1 NAME
12              
13             Template::Provider::HTTP - fetch templates from a webserver
14              
15             =begin html
16              
17            
18             Build status
19            
20              
21             =end html
22              
23             =head1 SYNOPSIS
24              
25             use Template;
26             use Template::Provider::HTTP;
27              
28             my %provider_config = (
29             INCLUDE_PATH => [
30             "/some/local/path", # file
31             "http://svn.example.com/svn/templates/", # url
32             ],
33             );
34              
35             my $tt = Template->new(
36             { LOAD_TEMPLATES => [
37             Template::Provider::HTTP->new( \%provider_config ),
38             Template::Provider->new( \%provider_config ),
39             ],
40             }
41             );
42              
43             # now use $tt as normal
44             $tt->process( 'my_template.html', \%vars );
45              
46             =head1 DESCRIPTION
47              
48             Templates usually live on disk, but this is not always ideal. This module lets
49             you serve your templates over HTTP from a webserver.
50              
51             For our purposes we wanted to access the latest templates from a Subversion
52             repository and have them update immediately.
53              
54             ABSOLUTE = 1 when passed to the constructor acts as a helper to support full
55             path to your http template. "Full" path begins at the domain
56             name(omit http://):
57              
58             use Template;
59             use Template::Provider::HTTP;
60            
61             my $tt = Template->new( { LOAD_TEMPLATES => [
62             Template::Provider::HTTP->new( ABSOLUTE => 1 ) ], } );
63             $tt->process( 'www.example.com/templates/my_template.html', \%vars );
64              
65             EXPAND_RELATIVE = 1 when passed to the constructor will attempt to expand
66             relative paths in the source document into absolute paths. For example:
67             href="../../main.css" will turn into:
68             href="http://www.someurl.tld/some/path/../../main.css"
69              
70             =head1 NOTE
71              
72             Currently there is NO caching, so the webserver will get multiple hits every
73             time that a template is requested. Patches welcome.
74              
75             =head1 METHODS
76              
77             This module is a very thin layer on top of L - please see the documentation there for full details.
78              
79             =head1 PRIVATE METHODS
80              
81             =head2 _init
82              
83             Does some setup. Notably goes through the C and removes anything
84             that does not start with C.
85              
86             =cut
87              
88             sub _init {
89 1     1   105691 my ( $self, $params ) = @_;
90              
91 1         96 $self->SUPER::_init($params);
92              
93             my @path
94 1 50       158 = grep {m{ \A http s? :// \w }xi} @{ $self->{INCLUDE_PATH} || [] };
  3         52  
  1         38  
95 1 50       8 push( @path, "http:" ) if $self->{ABSOLUTE};
96 1         3 $self->{INCLUDE_PATH} = \@path;
97            
98 1         11 $self->{UA} = $params->{UA};
99 1         12 $self->{EXPAND_RELATIVE} = $params->{EXPAND_RELATIVE};
100              
101 1         19 return $self;
102              
103             }
104              
105             =head2 _ua
106              
107             Returns a L object, or a cached one if it has already been
108             called.
109              
110             =cut
111              
112             sub _ua {
113 9     9   19 my $self = shift;
114 9   66     133 return $self->{UA} ||= LWP::UserAgent->new;
115             }
116              
117             =head2 _template_modified
118              
119             Returns the current time if the request is a success, otherwise undef. Could be
120             smartened up with a bt of local caching.
121              
122             =cut
123              
124             #------------------------------------------------------------------------
125             # _template_modified($path)
126             #
127             # Returns the last modified time of the $path.
128             # Returns undef if the path does not exist.
129             # Override if templates are not on disk, for example
130             #------------------------------------------------------------------------
131              
132             sub _template_modified {
133 7     7   441789 my $self = shift;
134              
135 7   50     36 my $template = shift || return;
136 7         56 $template =~ s{http:/}{http://};
137              
138 7 50       31 $self->debug("_template_modified( '$template' )") if $self->{DEBUG};
139              
140 7 100       46 return $self->_ua->get($template)->is_success ? time : undef;
141             }
142              
143             =head2 _template_content
144              
145             Returns the content from the request, or an error.
146              
147             =cut
148              
149             #------------------------------------------------------------------------
150             # _template_content($path)
151             #
152             # Fetches content pointed to by $path.
153             # Returns the content in scalar context.
154             # Returns ($data, $error, $mtime) in list context where
155             # $data - content
156             # $error - error string if there was an error, otherwise undef
157             # $mtime - last modified time from calling stat() on the path
158             #------------------------------------------------------------------------
159              
160             sub _template_content {
161 2     2   16173 my $self = shift;
162              
163 2         5 my $path = shift;
164 2         11 $path =~ s{http:/}{http://};
165 2 50       12 $self->debug("_template_content( '$path' )") if $self->{DEBUG};
166              
167 2 50       7 return ( undef, "No path specified to fetch content from " )
168             unless $path;
169              
170 2         5 my $data;
171             my $mod_date;
172 0         0 my $error;
173 0         0 my $res;
174              
175 2 50       98 if ( $path =~ m{ \A http s? :// \w }xi ) {
176 2         15 $res = $self->_ua->get($path);
177              
178 2 50       13925 if ( $res->is_success ) {
179 2         36 $data = $res->decoded_content;
180 2         2359 $mod_date = time;
181             } else {
182 0         0 $error = "error with request: " . $res->status_line;
183             }
184             } else {
185 0         0 $error = 'NOT A URL';
186             }
187              
188 2 50 33     36 if( !$error && $self->{EXPAND_RELATIVE} ) {
189 0         0 my $urlbase = $res->base;
190 0 0       0 if( $urlbase !~ m/\/$/ ) {
191 0         0 my @chunks = split /\/+/, $urlbase;
192 0         0 delete $chunks[ scalar( @chunks ) - 1 ];
193 0         0 delete $chunks[0];
194            
195 0         0 $urlbase = "http://";
196 0         0 foreach my $chunk ( @chunks ) {
197 0 0       0 if( $chunk ) {
198 0         0 $urlbase .= "$chunk/";
199             }
200             }
201             }
202              
203 0         0 my @path_chunks = split( /\/+/, $urlbase );
204 0         0 my $domain = "http://" . $path_chunks[1];
205              
206 0         0 my @dbl_matches = $data =~ m/"([^ ]+)"/g;
207 0         0 my @sgl_matches = $data =~ m/'([^ ]+)'/g;
208              
209 0 0       0 foreach my $path ( grep { $_ && /^\./ } @dbl_matches ) {
  0         0  
210 0         0 $data =~ s/"$path"/"$urlbase$path"/g;
211             }
212            
213 0 0       0 foreach my $path ( grep { $_ && /^\./ } @sgl_matches ) {
  0         0  
214 0         0 $data =~ s/'$path'/'$urlbase$path'/g;
215             }
216              
217 0 0       0 foreach my $path ( grep { $_ && /^\// } @dbl_matches ) {
  0         0  
218 0         0 $data =~ s/"$path"/"$domain$path"/g;
219             }
220            
221 0 0       0 foreach my $path ( grep { $_ && /^\// } @sgl_matches ) {
  0         0  
222 0         0 $data =~ s/'$path'/'$domain$path'/g;
223             }
224             }
225              
226             return wantarray
227 2 50       40 ? ( $data, $error, $mod_date )
228             : $data;
229             }
230              
231             =head1 SEE ALSO
232              
233             L - which this module inherits from.
234              
235             =head1 BUGS AND REPO
236              
237             This code is hosted on GitHub:
238              
239             code: https://github.com/evdb/template-provider-http
240              
241             bugs: https://github.com/evdb/template-provider-http/issues
242              
243             =head1 AUTHOR
244              
245             Edmund von der Burg C<>
246              
247             =head1 THANKS
248              
249             Developed whilst working at Foxtons for an internal system there and released
250             with their blessing.
251              
252             Kevin Kane (https://github.com/klkane) added support for C 1>.
253              
254             =head1 GOD SPEED
255              
256             TT3 - there has to be a better way than this :)
257              
258             =head1 LICENSE
259              
260             Sam as Perl.
261              
262             =cut
263              
264             1;