File Coverage

blib/lib/Plack/Middleware/Compile.pm
Criterion Covered Total %
statement 39 39 100.0
branch 9 12 75.0
condition 5 8 62.5
subroutine 9 9 100.0
pod 1 1 100.0
total 63 69 91.3


line stmt bran cond sub pod time code
1             package Plack::Middleware::Compile;
2             BEGIN {
3 1     1   1705 $Plack::Middleware::Compile::VERSION = '0.01';
4             }
5              
6             #ABSTRACT: Compile HAML/SASS/Coffeescript/whatever on demand
7              
8 1     1   9 use warnings;
  1         2  
  1         28  
9 1     1   4 use strict;
  1         2  
  1         60  
10              
11 1     1   6 use base 'Plack::Middleware';
  1         1  
  1         111  
12 1     1   5 use Plack::Util::Accessor qw(pattern lib blib mime map compile);
  1         1  
  1         9  
13              
14 1     1   97 use File::Spec;
  1         2  
  1         91  
15              
16             =head1 NAME
17              
18             Plack::Middleware::Compile
19              
20             =head1 VERSION
21              
22             version 0.01
23              
24             =head1 SYNOPSIS
25              
26             use Plack::Builder;
27              
28             builder {
29             enable 'Compile' => (
30             pattern => qr{\.coffee$},
31             lib => 'coffee',
32             blib => 'js',
33             mime => 'text/plain',
34             map => sub {
35             my $filename = shift;
36             $filename =~ s/coffee$/js/;
37             return $filename;
38             },
39             compile => sub {
40             my ($in, $out) = @_;
41             system("coffee --compile --stdio < $in > $out");
42             }
43             );
44             }
45              
46             =head1 DESCRIPTION
47              
48             Enable this middleware to serve compiled content (Coffeescript -> Javascript,
49             Sass -> CSS, HAML -> HTML, etc). The content will only be compiled when the
50             source is changed.
51              
52             =head1 CONFIGURATION
53              
54             =head2 pattern
55              
56             A regex which will be matched against PATH_INFO to determine if the middleware
57             should handle this request.
58              
59             =head2 lib
60              
61             A directory in which to find the source files.
62              
63             =head2 blib
64              
65             An output directory to send the compiled files to. This will be the same as
66             your lib directory if you don't specify it.
67              
68             =head2 mime
69              
70             The mime type to serve the files as. Defaults to 'text/plain'.
71              
72             =head2 map
73              
74             A function that maps input filenames to output filenames.
75              
76             =head2 compile
77              
78             A function that takes the input and output filenames as arguments and produces
79             the compiled file from the input.
80              
81             =cut
82              
83             sub _text {
84 1     1   3 my ($code, $text) = @_;
85 1     1   6 use bytes;
  1         2  
  1         8  
86             [
87 1         11 $code,
88             [
89             'Content-Type' => 'text/plain',
90             'Content-Length' => length($text),
91             ],
92             [ $text ]
93             ];
94             }
95              
96             sub call {
97 6     6 1 1074278 my ($self, $env) = @_;
98 6         15 my $in = $env->{PATH_INFO};
99            
100 6 100       28 return $self->app->($env) unless $in =~ $self->pattern;
101              
102 5         171 my $lib = $self->lib;
103 5   33     38 my $blib = $self->blib || $lib;
104 5         55 my $out = File::Spec->catfile($blib, $self->map->($in));
105 5         181 $in = File::Spec->catfile($lib, $in);
106 5 100       182 return _text(404, 'Not Found') unless (-r $in);
107              
108 4         132 my @os = stat($out);
109 4         77 my @is = stat($in);
110 4 100 100     24 if ( !@os || $is[9] > $os[9] ) {
111 3         7 eval { $self->compile->($in, $out) };
  3         14  
112 3 50       712 return _text(500, $@) if $@;
113 3         63 @os = stat($out);
114             }
115            
116 4 50       88 return _text(404, 'Not Found') unless (-r $out);
117 4 50       160 return _text(500, $!) unless open my $fh, '<', $out;
118              
119             return [
120 4   50     24 200,
121             [
122             'Content-Length' => $os[7],
123             'Content-Type' => $self->mime || 'text/plain',
124             ],
125             $fh
126             ];
127             }
128              
129             1;