1package Plack::Middleware::File::Sass;
2
3use strict;
4use 5.008_001;
5our $VERSION = '0.03';
6
7use parent qw(Plack::Middleware);
8use Plack::Util::Accessor qw(sass syntax);
9use Plack::Util;
10use IPC::Open3 qw(open3);
11use Carp ();
12
13my $text_sass;
14my %valid = (sass => 1, scss => 1);
15
16sub prepare_app {
17    my $self = shift;
18
19    $self->syntax("sass") unless defined $self->syntax;
20    $valid{$self->syntax} or Carp::croak("Unsupported syntax: ", $self->syntax);
21
22    my $sass = `sass -v`;
23    if ($sass && $sass =~ /Sass 3/) {
24        $self->sass(\&sass_command);
25    } elsif (eval { require Text::Sass }) {
26        $self->sass(\&sass_perl);
27    } else {
28        Carp::croak("Can't find sass gem nor Text::Sass module");
29    }
30}
31
32sub sass_command {
33    my($syntax, $body) = @_;
34
35    my $pid = open3(my $in, my $out, my $err,
36          "sass", "--stdin", ($syntax eq 'scss' ? '--scss' : ()));
37    print $in $body;
38    close $in;
39
40    my $buf = join '', <$out>;
41    waitpid $pid, 0;
42
43    return $buf;
44}
45
46sub sass_perl {
47    my($syntax, $body) = @_;
48
49    my $method = "${syntax}2css";
50    $text_sass ||= Text::Sass->new;
51    $text_sass->$method($body);
52}
53
54sub call {
55    my($self, $env) = @_;
56
57    my $syntax = $self->syntax;
58
59    # Sort of depends on how App::File works
60    my $orig_path_info = $env->{PATH_INFO};
61    if ($env->{PATH_INFO} =~ s/\.css$/.$syntax/i) {
62        my $res = $self->app->($env);
63
64        return $res unless ref $res eq 'ARRAY';
65
66        if ($res->[0] == 200) {
67            my $sass; Plack::Util::foreach($res->[2], sub { $sass .= $_[0] });
68            my $css = $self->sass->($syntax, $sass);
69
70            my $h = Plack::Util::headers($res->[1]);
71            $h->set('Content-Type' => 'text/css');
72            $h->set('Content-Length' => length $css);
73
74            $res->[2] = [ $css ];
75        } elsif ($res->[0] == 404) {
76            $env->{PATH_INFO} = $orig_path_info;
77            $res = $self->app->($env);
78        }
79
80        return $res;
81    }
82
83    return $self->app->($env);
84}
85
861;
87__END__
88
89=encoding utf-8
90
91=for stopwords
92
93=head1 NAME
94
95Plack::Middleware::File::Sass - Sass and SCSS support for all Plack frameworks
96
97=head1 SYNOPSIS
98
99  use Plack::App::File;
100  use Plack::Builder;
101
102  builder {
103      mount "/stylesheets" => builder {
104          enable "File::Sass";
105          Plack::App::File->new(root => "./stylesheets");
106      };
107  };
108
109  # Or with Middleware::Static
110  enable "File::Sass", syntax => "scss";
111  enable "Static", path => qr/\.css$/, root => "./static";
112
113=head1 DESCRIPTION
114
115Plack::Middleware::File::Sass is a Plack middleware component that
116works with L<Plack::App::File> or L<Plack::Middleware::Static> to
117compile L<Sass|http://sass-lang.com/> templates into CSS stylesheet in
118every request.
119
120When a request comes in for I<.css> file, this middleware changes the
121internal path to I<.sass> or I<.scss>, depending on the configuration,
122in the same directory. If the Sass template is found, a new CSS
123stylesheet is built on memory and served to the browsers.  Otherwise,
124it falls back to the original I<.css> file in the directory.
125
126This middleware should be very handy for the development. While Sass
127to CSS rendering is reasonably fast, for the production environment
128you might want to precompile Sass templates to CSS files on disk and
129serves them with a real web server like nginx or lighttpd.
130
131=head1 SASS BACKENDS
132
133If you have the sass gem version higher than 3 installed and have the
134C<sass> executable available in your PATH, this module automatically
135uses the command to convert Sass or SCSS into CSS. If the command is
136not available and you have L<Text::Sass> perl module available, it
137will be used. Otherwise you'll get an exception during the
138initialization of this middleware component.
139
140=head1 OPTIONS
141
142=over 4
143
144=item syntax
145
146Defines which syntax to use. Valid values are I<sass> and
147I<scss>. Defaults to I<sass>.
148
149=back
150
151=head1 AUTHOR
152
153Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
154
155=head1 LICENSE
156
157This library is free software; you can redistribute it and/or modify
158it under the same terms as Perl itself.
159
160=head1 SEE ALSO
161
162L<Plack::App::File> L<Text::Sass> L<http://sass-lang.com/>
163
164=cut
165