Shows how to register filters and how to composite templates, css, and js files.
package Mojolicious::Plugin::MicroPipeline::CSSCompressor;
use Mojo::Base 'Mojolicious::Plugin';
use CSS::Compressor 'css_compress';
sub register {
my ($self, $app) = @_;
# Register "css_compressor" filter
$app->filter(css_compressor => sub { css_compress shift });
}
1;
package Mojolicious::Plugin::MicroPipeline;
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::Asset::Memory;
use Mojo::Home;
use Mojo::Loader;
use Mojo::Util 'md5_sum';
sub register {
my ($self, $app) = @_;
# List all static files
my @files
= map { @{Mojo::Home->new($_)->list_files} } @{$app->static->paths};
push @files,
map { keys %{Mojo::Loader->new->data($_)} } @{$app->static->classes};
# Register "filter" helper
my %filters;
$app->helper(
filter => sub {
my ($self, $name, $cb) = @_;
$filters{$name} = $cb;
}
);
# Register "asset" helper
my %assets;
$app->helper(
asset => sub {
my ($self, $name, $sources) = (shift, shift, shift);
# Generate "link" or "script" tag
unless ($sources) {
my $checksum = $assets{$name}{checksum};
$name =~ /^(.+)\.(\w+)$/;
return $self->stylesheet("/$1.$checksum.$2") if $2 eq 'css';
return $self->javascript("/$1.$checksum.$2");
}
# Concatenate
my $asset = '';
for my $source (@$sources) {
# Glob support
$source = quotemeta $source;
$source =~ s/\\\*/[^\/]+/;
# Check all files
for my $file (@files) {
next unless $file =~ /^$source$/;
$asset .= $self->app->static->file($file)->slurp;
}
}
# Filter
for my $filter (@_) { $asset = $filters{$filter}->($asset) }
# Store source with current checksum
$assets{$name} = {source => $asset, checksum => md5_sum($asset)};
}
);
# Asset dispatcher
$app->hook(
before_dispatch => sub {
my $self = shift;
# Match asset path with checksum
return
unless $self->req->url->path =~ /^\/?(.+)\.([[:xdigit:]]+)\.(\w+)$/;
return unless my $asset = $assets{"$1.$3"};
# Serve asset
$self->app->log->debug(qq/Serving asset "$1.$3" with checksum "$2"./);
$self->app->static->serve_asset($self,
Mojo::Asset::Memory->new->add_chunk($asset->{source}));
$self->tap(sub { shift->stash('mojo.static' => 1) })->rendered;
}
);
}
1;
use Mojolicious::Lite;
plugin 'MicroPipeline';
plugin 'MicroPipeline::CSSCompressor';
app->asset('app.js' => ['one.js', 'js/two.js']);
app->asset('app.css' => ['stylesheets/*.css'] => 'css_compressor');
get '/' => 'index';
app->start;
__DATA__
@@ index.html.ep
% title 'Hello!';
% layout 'default';
<div id="hello">Hello Mojo!</div>
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
%= asset 'app.css'
%= asset 'app.js'
</head>
<body><%= content %></body>
</html>
@@ one.js
alert('One!');
@@ js/two.js
alert('Two!');
@@ stylesheets/one.css
div {
color: #fff;
}
@@ stylesheets/two.css
body {
background-color: #000;
}
@@ stylesheets/three.css
#hello {
font: 0.9em 'Helvetica Neue', Helvetica, sans-serif;
}
@@ stylesheets/four.css
body {
margin: 0;
}
@@ stylesheets/five.css
#hello {
line-height: 1.5em;
}