Skip to content

Instantly share code, notes, and snippets.

@kraih
Created October 21, 2012 18:00
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save kraih/3927875 to your computer and use it in GitHub Desktop.
Save kraih/3927875 to your computer and use it in GitHub Desktop.
use Mojolicious::Lite;
plugin 'Pipeline';
plugin 'Pipeline::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;
}
package Mojolicious::Plugin::Pipeline::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::Pipeline;
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;
@kraih
Copy link
Author

kraih commented Oct 21, 2012

And this is the result.

<!DOCTYPE html>
<html>
  <head>
    <title>Hello!</title>
    <link href="/app.c79c59e35fa78a842395d5fe43c3d037.css" media="screen" rel="stylesheet" />
    <script src="/app.8aa93dcb2dd07d8260f90067a9909a12.js"></script>
  </head>
  <body><div id="hello">Hello Mojo!</div>
</body>
</html>

@alexbyk
Copy link

alexbyk commented Oct 22, 2012

Looks like very useful

@judofyr
Copy link

judofyr commented Oct 22, 2012

I think it would be useful if all assets are served from a single (configurable) path/domain (e.g. /assets). Then you could have a pre-compile step and serve the assets straight through Nginx/Apache without clobbering the top-level public-folder.

@cybersiddhu
Copy link

Would be an great feature that i love to see and use. Also it will be nice to have a default specification of the folder hierarchies to put the assets(js/css/images) and where the pre-compiled assets should land.

@MadMartigan
Copy link

In order to get IE to recognize a css file served via server_asset, I had to manually set the Content-Type header. Something like this....

if ($3 eq 'css') {
  $self->res->headers->content_type('text/css');
} elsif ($3 eq 'js') {
  $self->res->headers->content_type('application/javascript');
} 

I'm sure there is a better way to do it that I am unaware of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment