Combining several of my previous posts, let's build a quick site
with Mojolicious and Zurb Foundation. We
will use the AssetPack
plugin. Although we are serving fairly simple Javascript and CSS files
now, later we can switch to using Coffeescript, Less or Sass or anything
which AssetPack can pre-process.
First, install the AssetPack plugin and create a fresh "app" −
$ cpanm Mojolicious::Plugin::AssetPack
$ mojo generate app FoundationExample
Method 1 − Local copy of Foundation
Download the latest Foundation
and put the files under a foundation/ directory under our "app"
directory:
$ mkdir foundation
$ cd foundation
$ wget http://foundation.zurb.com/cdn/releases/foundation-5.5.2.zip
$ unzip foundation-5.5.2.zip
Now let's create a set of relative symbolic links for each file in the
Foundation directory:
$ mkdir public/foundation/
$ ln -s -r foundation/* public/foundation/
In lib/FoundationExample.pm, change the startup to −
# This method will run once at server start
sub startup {
my $self = shift;
$self->plugin("AssetPack");
my $source = "/foundation/";
$self->asset("foundation.css" => $source."css/foundation.min.css");
$self->asset("foundation.js" => $source."js/foundation.js");
$self->asset("modernizr.js" => $source."js/vendor/modernizr.js");
$self->asset("jquery.js" => $source."js/vendor/jquery.js");
# Router
my $r = $self->routes;
# Normal route to controller
$r->get('/')->to('example#welcome');
}
Continue with Both Methods, below.
Method 2 − CDN method
Use only the changes in lib/FoundationExample.pm shown above, but
replace the source with −
my $source = "https://cdnjs.cloudflare.com/ajax/libs/" .
"foundation/5.5.2/";
Continue with Both Methods, below.
Both Methods.
In lib/FoundationExample/Controller/Example.pm − change the welcome
routine to:
sub welcome {
my $self = shift;
$self->stash(copyright_holder => 'Someone');
$self->stash(keywords => 'something, somebody, somewhere');
$self->stash(author => 'William Lindley');
# Render template "example/welcome.html.ep" with message
$self->render(msg => 'Welcome to the Mojolicious real-time web framework!');
}
In templates/example/welcome.html.ep − change the layout to:
% layout 'zurbish';
and templates/layouts/zurbish.html.ep −
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Zurb Foundation stuff -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
%= asset 'foundation.css'
%= asset 'modernizr.js'
<!-- end Zurb -->
<title><%= title %></title>
<meta name="copyright" content="Copyright ⓒ <%= ((localtime())[5]+1900) %> <%= $copyright_holder %>" />
<meta name="keywords" content="<%= $keywords %>" />
<meta name="author" content="<%= $author %>" />
</head>
<body>
%= asset 'jquery.js'
%= asset 'foundation.js'
<script>
$(document).foundation();
</script>
<%= content %>
</body>
</html>
For more, see Jan Henning Thorsen's talk on
Mojolicious::Plugin::AssetPack
at MojoConf 2014.
Here we create an absolutely minimal multi-tenant webserver,
without even installing Apache or NginX. Although we can add either of
those later, with our content running under them as reverse proxies,
this tutorial lets you run one or more Mojolicious "apps" mounted under
a single toadfarm startup script, saving you the "default perl memory"
times the number of worker processes and apps.
We will b using Jan Henning Thorsen's Toadfarm:
Toadfarm is a module for configuring and starting your Mojolicious
applications. You can either combine multiple applications in one
script, or just use it as a init script.
First, perform a Debian Netinstall on a
virtual machine or virtual host. The only modification to all the
default selections is to enable only "Standard System Utilities" and
disable desktop, database, mail, print, file, and web server. We will
be building our own of all those!
Now boot up into that absolutely stock, base machine. For illustration,
we will assume your username is gronk
− change this as necessary.
First let's install a few system packages (ssh for access; curl for
installing Perlbrew et al; sudo for convenience;
build-essential for GCC, Make, and the compiler tools; and the no-X
version of emacs because I've
been using it since 1980):
$ su
# apt-get install sudo ssh curl build-essential emacs-nox
# usermod -a -G sudo <em>gronk</em> # exit
Now we install our Perlbrew environment as the user:
$ \curl -L http://install.perlbrew.pl | bash
$ echo source ~/perl5/perlbrew/etc/bashrc >> ~/.bash_profile
$ perlbrew install 5.22.0 -j5
The -j5 means use five processes for the Make. Then:
$ perlbrew switch 5.22.0
$ perlbrew install-cpanm
$ curl -L https://cpanmin.us | perl - -M https://cpan.metacpan.org -n Mojolicious
$ cpanm Toadfarm
jhthorsen explains how this goes together:
You can't run individual apps inside toadfarm as different user,
but you can start toadfarm as root and change to a different user.
[
This is true of Mojolicious generally
](https://metacpan.org/pod/Mojo::Server#user)
.
In particular, we create an Init script with a #! (hashbang) that
points to our user's "brewed" Perl. From the documentation:
Remember that the hashbang can be anything, so if you have Toadfarm
and Mojolicious running under
[
plenv
](https://github.com/tokuhirom/plenv)
or
Perlbrew
you need to change the hashbang part…
So we change our Init script to start with:
#!/home/<em>gronk</em>/perl5/perlbrew/perls/perl-5.22.0/bin/perl
Then in the Toadfarm script called by the Init script, which is started
as root, we do the switch to our user (called some-www-user in the
documentation, gronk here):
#/usr/bin/env perl
use Toadfarm -init;
# …
start [qw( http://*:80 https://*:443 )], user => "<em>gronk</em>", group => "<em>www</em>";
From the documentation:
Changing user has one implication: The workers will not use some-www-user's
secondary groups. So if "some-www-user" is part of the "www" and
"db" groups, then the process will only be run as "some-www-user"
part of the "www" group.
See also the IRC log for 26 April 2014.
I have gotten far enough into Mojolicious development to feel
rather baffled again. This post is a work in progress, to be edited and
filled in as I discover the answers.
According to the Rendering guide,
A skeleton for a full CPAN compatible plugin distribution can be
automatically generated.
We are going to create a plugin called StaticText, so let's do:
$ mojo generate plugin StaticText
What that actually does is the following actions inside your current
directory, as we can see from its output log:
[mkdir] ./Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin
[write] ./Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin/StaticText.pm
[mkdir] ./Mojolicious-Plugin-StaticText/t
[write] ./Mojolicious-Plugin-StaticText/t/basic.t
[exist] ./Mojolicious-Plugin-StaticText
[write] ./Mojolicious-Plugin-StaticText/Makefile.PL
Q: How exactly can I develop this inside an example application? The
documentation's standard load procedure:
$self->plugin('Mojolicious::Plugin::StaticText');
fails because it doesn't look in
Mojolicious-Plugin-StaticText/lib/Mojolicious/Plugin/ of course.
Q: How do I install my common personal plugins into my individual
"apps" ? Presumably I should be able to "git checkout" them directly
into... lib/Plugin/ ...? Obviously I'm not going to edit them into the
MyApp:: namespace, right?
Q: Should use the Mojolicious::Plugin namespace for plugins that I
consider to be eventual candidates for CPAN, or what is best practice
for naming my personal plugins that I plan on using across multiple
projects?
Q: I see how to pass parameters when loadiing a plugin, but does each
plugin get loaded, or registered, just once, or can I have multiple
instances of a plugin?
NOTE: Refer also to the Plugins document
Q: Where do I store "instance data" that is computed from the
configuration parameters I pass when loading a plugin?
A: Plugins are simply modules, not objects. If we want to save
configuration data at load-time, we could save that in an attribute of
the application like this: $app->attr('some_attribute_name' => value)
which we later access as just "$app->some_attribute_name".
A good example of how configuration parameters are passed, handled, and
saved is
http://search.cpan.org/~madcat/Mojolicious-Plugin-Database/lib/Mojolicious/Plugin/Database.pm
Behind the curtain: when you call attr, that actually builds and then
eval(s) a string which defines a function by the supplied name. That
function optionally sets (if called with a value), and always returns
the value of the named key in the object upon which you originally
called ->attr(). So in the case of doing $app->attr('foo') the function
would create and use $attr->{'foo'}.
Q: The Mojolicious::Plugins guide
shows the example code
$plugins->register_plugin('MyApp::Plugin::SomeThing', Mojolicious->new);
below the presumably required code:
use Mojolicious::Plugins;
my $plugins = Mojolicious::Plugins->new;
push @{$plugins->namespaces}, 'MyApp::Plugin';
Yet elsewhere, for example in
http://mojolicio.us/perldoc/Mojolicious/Plugin/JSONConfig , we see a
completely different style that runs with application start:
sub startup {
my $self = shift;
$self->plugin('Mojolicious::Plugin::StaticText');
What exactly is the difference between these two?
Q: In the version with
$plugins->register_plugin('MyApp::Plugin::SomeThing', Mojolicious->new);
what does the Mojolicious->new do? According to the documentation for
Mojolicious->new(), that
should "Construct a [whole other] new Mojolicious application and call
'startup'… set up logging… the renderer, static file server, default
set of plugins…" We can't seriously be loading a whole new "app" nested
inside of our current "app" can we? What's going on?
Q: After the name of the plugin, what happens to the remaining
arguments to $self->plugin()?
A: The optional second argument must be either a reference (to a
scalar, array, hash, subroutine, etc.) or a scalar. If it's just a
plain scalar such as 3, like the plugin's register() subroutine will be
called with a reference to a hash like this: {3, undef}. In general,
you will probably want to pass a reference to a hash that contains your
configuration parameters.
We are experiencing Perl's most successful period since the 1990s.
I-Programmer spoke with Sebastian Riedel, originator of the relatively
new Mojolicious system built on Perl.
…The truth is that installing Mojolicious is simply a very fast
and pleasant experience… we've done with tight integration of WebSockets
and event loops, [and] the ability to grow from single-file prototypes
to well structured web applications.
The first example application you encounter on
[
our website
](http://mojolicio.us)
… doesn't look very complicated at all. But once you start digging
a little deeper, you'll quickly realize how crazy (in a good way)
it really is, and how hard it would be to replicate with any other
web framework, in any language…
Read the rest of the story
at i-programmer, 11 December 2014.
Last time,
we created a site_design
template for Mojolicious
which built on a basic layout template for Zurb Foundation. On top of
this we can use our content-specific templates, with the two lower
templates nested below.
Here's an example that uses content from a WordPress™ Post. The
simplest way to retrieve the actual content of just a post from
WordPress is to use the rather new WP-API. This
was to be part of WP core in their version 4.1, and although its
appearance in core has been delayed, can be installed as the "WP REST
API" plugin from the WP repository.
Once the WP-API plugin is enabled, we can request a URI like one on
this site as:
http://blog.wlindley.com/wp-json/wp/posts/1265
That will return a JSON result with the rendered HTML (WordPress stores
post and page content in the database with newline breaks instead of
paragraph tags, and an assortment of other changes we do not want to
deal with here) as well as a variety of metadata about the post.
Unfortunately the current WP-API plugin does not let us request by post
name, only by internal ID. One hopes the API will be more complete when
it is fully released. Also beware the URI and request schema may differ
in a final release.
We can use Mojo::UserAgent
to retrieve the remote content and parse its JSON.
In a new file lib/MyApp/Controller/
then we add a controller. We use
the rendered HTML versions for the content, title, and excerpt, while
copying the other values with unmodified keys all to the stash, which
we can then reference in our template. The two templates notfound and
nopage (not shown here) are used when things go wrong.
package MyApp::Controller::Blogpage;
use Mojo::Base 'Mojolicious::Controller';
use Data::Dumper;
use Mojo::UserAgent;
# This action will render a template
sub retrieve {
my $self = shift;
my $ua = Mojo::UserAgent->new;
my $id = $self->param('id');
if (defined $id) {
$id =~ s/\D//g; # remove all non-digits
}
if (!$id) {
$self->redirect_to('notfound');
return;
}
my $tx = $ua->get("http://blog.wlindley.com/wp-json/wp/posts/${id}");
if ($tx->success) {
my $value = $tx->res->json;
# Render template "blogpage/retrieve.html.ep" with
# values from WP-API
$self->render((map { $_ => $value->{$_}->{rendered} }
qw/content title excerpt/) ,
(map { my $vname = $_;
$vname =~ s/^format\Z/wp_format/;
$vname => $value->{$_} }
qw/date type format comment_status id modified
ping_status featured_image author sticky
excerpt slug guid link/) );
} else {
$self->redirect_to('nopage');
}
}
1;
and create a template in templates/blogpage/retrieve.html.ep
. Note
how we are using the site_layout
template from last time:
% layout 'site_layout';
% title "$title";
<h1><%= $title =%></h1>
<blockquote>Published on <%= $date %></blockquote>
%== $content
Of course you can use any of the metadata tags which we copy in the
controller (featured_image might be an interesting one).
Then in our MyApp.pm we add this into the startup
subroutine:
$r->get('/blog')->to('blogpage#retrieve');
Now we can visit http://localhost:3000/blog?id=1265
and you should
see the content of the WordPress post, dropped into the content section
that you made in the Foundation style theme from last time.
Next time, we still plan to access a database.