# Plack-Middleware-Assets **Repository Path**: mirrors_gitpan/Plack-Middleware-Assets ## Basic Information - **Project Name**: Plack-Middleware-Assets - **Description**: No description available - **Primary Language**: Unknown - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-10-20 - **Last Updated**: 2025-06-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README package Plack::Middleware::Assets; # ABSTRACT: Concatenate and minify JavaScript and CSS files use strict; use warnings; use base 'Plack::Middleware'; use Plack::Util::Accessor qw( separator filter content minify files key mtime type type_class expires extension ); use Digest::MD5 qw(md5_hex); use HTTP::Date (); use Class::Load (); sub new { my $class = shift; my $self = $class->SUPER::new(@_); # set defaults $self->separator(1) unless ( defined $self->separator ); $self->minify(1) unless ( defined $self->minify ); $self->filter(1) unless ( defined $self->filter ); $self->_build_content; return $self; } sub _build_content { my $self = shift; local $/; my $type = $self->type || ( ( grep {/\.css$/} @{ $self->files } ) ? 'css' : ( grep {/\.js$/} @{ $self->files } ) ? 'js' : 'plain' ); $self->type($type); my $class = __PACKAGE__ . "::Type::$type"; eval { Class::Load::load_class($class) } or die "$class could not be loaded: $@"; $self->type_class($class); my $separator = $self->separator; # use default comment format unless format was specified $separator = $class->separator if $separator && $separator !~ /%s/; $self->content( join( "\n", map { open my $fh, '<', $_ or die "$_: $!"; ( $separator ? sprintf( $separator, $_ ) : '' ) . <$fh> } @{ $self->files } ) ); $self->content( $self->_transform('filter') ) if $self->filter; $self->content( $self->_transform('minify') ) if $self->minify; $self->key( md5_hex( $self->content ) ); my @mtime = map { ( stat($_) )[9] } @{ $self->files }; $self->mtime( ( reverse( sort(@mtime) ) )[0] ); } sub _transform { my ( $self, $transform ) = @_; return $self->content if ( $transform ne 'minify' && $ENV{PLACK_ENV} && $ENV{PLACK_ENV} eq 'development' ); no strict 'refs'; my $run = $self->$transform; my $method; if ( ref $run eq 'CODE' ) { $method = $run; } elsif ( $run && $self->type_class->can($transform) ) { $method = $self->type_class . "::$transform"; } return $self->content unless ($method); local $_ = $self->content; return $method->($_); } sub serve { my $self = shift; my $type = $self->type; return [ 200, [ 'Content-Type' => $self->type_class->content_type, 'Content-Length' => length( $self->content ), 'Last-Modified' => HTTP::Date::time2str( $self->mtime ), 'Expires' => HTTP::Date::time2str( time + ( $self->expires || 2592000 ) ), ], [ $self->content ] ]; } sub call { my $self = shift; my $env = shift; if ( $ENV{PLACK_ENV} && $ENV{PLACK_ENV} eq 'development' ) { my @mtime = map { ( stat($_) )[9] } @{ $self->files }; $self->_build_content if ( $self->mtime < ( reverse( sort(@mtime) ) )[0] ); } $env->{'psgix.assets'} ||= []; my $extension = $self->extension || $self->type_class->extension; my $url = '/_asset/' . $self->key . '.' . $extension; push( @{ $env->{'psgix.assets'} }, $url ); return $self->serve if $env->{PATH_INFO} eq $url; return $self->app->($env); } package Plack::Middleware::Assets::Type::plain; use strict; use warnings; sub content_type {'text/plain'} sub separator { } sub extension {'txt'} package Plack::Middleware::Assets::Type::css; use strict; use warnings; use base 'Plack::Middleware::Assets::Type::plain'; use CSS::Minifier::XS qw(minify); sub content_type {'text/css'} sub separator {"/* %s */\n"} sub extension {'css'} package Plack::Middleware::Assets::Type::js; use strict; use warnings; use base 'Plack::Middleware::Assets::Type::plain'; use JavaScript::Minifier::XS qw(minify); sub content_type {'application/javascript'} sub separator {"/* %s */\n"} sub extension {'js'} 1; __END__ =head1 SYNOPSIS # in app.psgi use Plack::Builder; builder { enable Assets => ( files => [] ); enable Assets => ( files => [], minify => 0 ); $app; }; # or customize your assets as desired: builder { # concatenate sass files and transform them into css enable Assets => ( files => [], type => 'css', filter => sub { Text::Sass->new->sass2css(shift) }, minify => 0 ); # pass a coderef for a custom minifier enable Assets => ( files => [], filter => sub {uc}, minify => sub { s/ +/\t/g; $_ } ); # concatenate any arbitrary content type enable Assets => ( files => [], type => 'less' ); $app; }; # since this module ships only with the types css and js, # you have to implement the less type yourself: package Plack::Middleware::Assets::Type::less; use base 'Plack::Middleware::Assets::Type::css'; use CSS::Minifier::XS qw(minify); use CSS::LESSp (); sub filter { CSS::LESSp->parse(@_) } # $env->{'psgix.assets'}->[0] points at the first asset. =head1 DESCRIPTION Plack::Middleware::Assets concatenates JavaScript and CSS files and minifies them. A C digest is generated and used as the unique url to the asset. For instance, if the first C is C, then the unique C url can be used in a single HTML C