view release on metacpan or search on metacpan
},
{
"class" : "Dist::Zilla::Plugin::ShareDir",
"name" : "@Author::PERLANCAR/@Filter/ShareDir",
"version" : "6.010"
},
{
"class" : "Dist::Zilla::Plugin::MakeMaker",
"config" : {
"Dist::Zilla::Role::TestRunner" : {
"default_jobs" : 1
}
},
"name" : "@Author::PERLANCAR/@Filter/MakeMaker",
"version" : "6.010"
},
{
"class" : "Dist::Zilla::Plugin::Manifest",
"name" : "@Author::PERLANCAR/@Filter/Manifest",
"version" : "6.010"
},
name: '@Author::PERLANCAR/@Filter/ExecDir'
version: '6.010'
-
class: Dist::Zilla::Plugin::ShareDir
name: '@Author::PERLANCAR/@Filter/ShareDir'
version: '6.010'
-
class: Dist::Zilla::Plugin::MakeMaker
config:
Dist::Zilla::Role::TestRunner:
default_jobs: 1
name: '@Author::PERLANCAR/@Filter/MakeMaker'
version: '6.010'
-
class: Dist::Zilla::Plugin::Manifest
name: '@Author::PERLANCAR/@Filter/Manifest'
version: '6.010'
-
class: Dist::Zilla::Plugin::ConfirmRelease
name: '@Author::PERLANCAR/@Filter/ConfirmRelease'
version: '6.010'
devdata/https_mojolicious.io_blog_2018_12_01_welcome-mojoconf-recap_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/01/welcome-mojoconf-recap/" property="og:url">
<meta content="Day 1: Welcome & MojoConf Recap" property="og:title">
<meta content="The 2018 Mojolicious Advent Calendar begins with a recap of MojoConf 2018." property="og:description">
devdata/https_mojolicious.io_blog_2018_12_02_automatic-reload-for-rapid-development_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/02/automatic-reload-for-rapid-development/" property="og:url">
<meta content="Day 2: Automatic Reload for Rapid Development" property="og:title">
<meta content="Learn how to reload both the server and client on changes during application development." property="og:description">
devdata/https_mojolicious.io_blog_2018_12_02_automatic-reload-for-rapid-development_ view on Meta::CPAN
template</a></p>
<pre><code>use Mojolicious::Lite;
plugin 'AutoReload';
get '/' => 'index';
app->start;
__DATA__
@@ layouts/default.html.ep
%= auto_reload
%= content
@@ index.html.ep
% layout 'default';
<h1>Hello, World!</h1>
</code></pre>
<p><a href="myapp.pl">Download the code here</a>. Now while we have our application open in
our browser, if the server is restarted, our browser will reload the page to
see the new app!</p>
<h2>How It Works</h2>
<p>This plugin is sheer elegance in its simplicity: When the browser loads the
devdata/https_mojolicious.io_blog_2018_12_02_automatic-reload-for-rapid-development_ view on Meta::CPAN
restarts, the WebSocket connection is broken. The client sees the broken
connection, waits a second for the server to start listening for connections
again, and then reloads the page.</p>
<h2>Disable In Production</h2>
<p>Once we switch from <code>morbo</code> to <code>hypnotoad</code>, we don't want the automatic reload
anymore. So, the plugin doesn't send the browser the JavaScript to build the
websocket. This is controlled with <a href="https://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial#Mode">the <code>mode</code>
attribute</a>.
When the <code>mode</code> is <code>development</code> (the default for <code>morbo</code>), the browser is told
to make the WebSocket. When the <code>mode</code> is anything else, no WebSocket is made.</p>
<p>Part of what makes Mojolicious so much fun is how easy it is. <a href="https://github.com/preaction/Mojolicious-Plugin-AutoReload/blob/v0.003/lib/Mojolicious/Plugin/AutoReload.pm#L56-L92">The entire
plugin is only 40 lines of
code</a>.</p>
<p>And now, with Mojolicious::Plugin::AutoReload, developing in Mojolicious is
just a little easier, and a little more fun!</p>
</section>
devdata/https_mojolicious.io_blog_2018_12_03_higher-order-promises_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@briandfoy_perl" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/03/higher-order-promises/" property="og:url">
<meta content="Day 3: Higher Order Promises" property="og:title">
<meta content="Create new, complex Promises by composing Promises" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_04_testing-hooks-and-helpers_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/04/testing-hooks-and-helpers/" property="og:url">
<meta content="Day 4: Testing Hooks and Helpers" property="og:title">
<meta content="How to easily test hooks and helpers using Test::Mojo" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_04_testing-hooks-and-helpers_ view on Meta::CPAN
);
$t->get_ok( '/test/exception' )->status_is( 500 );
my $log_content = path( 'exception.log' )->slurp;
like $log_content, qr{Exception}, 'exception is logged';
done_testing;
</code></pre>
<p>Sure, this is technically testing a route. But, it's useful to know that
I can edit my application after I load it (but before any routes are
exercised). I often spawn additional Test::Mojo objects, sometimes using
the default
<a href="https://mojolicious.org/perldoc/Mojo/HelloWorld">Mojo::HelloWorld</a>
application to test different plugins.</p>
<h1>Helpers</h1>
<p>Now, I could test my helpers in the exact same way: Set up a new route
that uses my helper and examine the result. But, testing helpers can be
even easier: I can just ask the app to give me a controller with <a href="https://mojolicious.org/perldoc/Mojolicious#build_controller">the
<code>build_controller</code>
method</a>.
devdata/https_mojolicious.io_blog_2018_12_04_testing-hooks-and-helpers_ view on Meta::CPAN
object, so I can set up the conditions for my test.</p>
<p>So, for example, if I have an authentication token in my configuration,
I could write a helper to check if my site visitor is trying to
authenticate.</p>
<pre><code>#!/usr/bin/env perl
use Mojolicious::Lite;
# Allow access via tokens
plugin Config => {
default => {
tokens => { }, # token => username
},
};
helper current_user => sub( $c ) {
my $auth = $c->req->headers->authorization;
return undef unless $auth;
my ( $token ) = $auth =~ /^Token\ (\S+)$/;
return undef unless $token;
return $c->app->config->{tokens}{ $token };
};
devdata/https_mojolicious.io_blog_2018_12_05_compound-selectors_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@briandfoy_perl" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/05/compound-selectors/" property="og:url">
<meta content="Day 5: Compound Selectors are Easier than Regexes" property="og:title">
<meta content="Extract HTML quickly and easily with Mojo::DOM" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_06_making-a-list-with-yancy_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/06/making-a-list-with-yancy/" property="og:url">
<meta content="Day 6: Making A List with Yancy" property="og:title">
<meta content="Santa's list building just got easier!" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_06_making-a-list-with-yancy_ view on Meta::CPAN
filter => {
is_nice => 0,
},
}, 'the_list.list';
</code></pre>
<p>Now we build our template. Yancy comes with a <a href="http://getbootstrap.com">Bootstrap
4</a> we can use to make a pretty list of names
and addresses.</p>
<pre><code>@@ layouts/default.html.ep
<head>
<script src="/yancy/jquery.js"></script>
<link rel="stylesheet" href="/yancy/bootstrap.css">
</head>
<body>
<main class="container">
%= content
</main>
</body>
@@ the_list.html.ep
% layout 'default';
<h1>Naughty List</h1>
<ul class="list-group">
% for my $item ( @$items ) {
<li class="list-group-item d-flex justify-content-between">
<div>
%= $item->{name}
<br/>
%= $item->{address}
</div>
% end
devdata/https_mojolicious.io_blog_2018_12_06_making-a-list-with-yancy_ view on Meta::CPAN
collection => 'the_list',
properties => [qw( is_delivered )],
forward_to => 'the_list.list',
}, 'the_list.deliver';
</code></pre>
<p>With the route configured, we need to add a form to our template. We'll
use <a href="https://mojolicious.org/perldoc/Mojolicious/Plugin/TagHelpers#form_for">the <code>form_for</code> helper from
Mojolicious</a>.
The form will display a yes/no toggle button for every row. Yancy is
secure by default, so we need to make sure that our form contains the
<a href="https://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Cross-site-request-forgery">CSRF token</a>
(using <a href="https://mojolicious.org/perldoc/Mojolicious/Plugin/TagHelpers#csrf_field">the <code>csrf_field</code> helper</a>)
to prevent cross-site requests.</p>
<pre><code>@@ the_list.html.ep
% layout 'default';
<h1>Naughty List</h1>
<ul class="list-group">
% for my $item ( @$items ) {
<li class="list-group-item d-flex justify-content-between">
<div>
%= $item->{name}
<br/>
%= $item->{address}
</div>
%= form_for 'the_list.deliver', $item, begin
devdata/https_mojolicious.io_blog_2018_12_07_openapi_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@ssoriche" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/07/openapi/" property="og:url">
<meta content="Day 7: MetaCPAN, Mojolicious and OpenAPI" property="og:title">
<meta content="Overview of how OpenAPI was integrated into MetaCPAN with Mojolicious." property="og:description">
devdata/https_mojolicious.io_blog_2018_12_07_openapi_ view on Meta::CPAN
See also `collapsed`
# The type of the value that the API accepts
type: string
# Define the attribute as required
required: true
# The rest of the parameters that the API accepts
- name: from
in: query
description: The offset to use in the result set
type: integer
# If the API applies a default to an attribute if it isn't specified.
# Let the us know what it is.
default: 0
- name: size
in: query
description: Number of results per page
type: integer
default: 20
- name: collapsed
in: query
description: |
Force a collapsed even when searching for a particular
distribution or module name.
type: boolean
</code></pre>
<h4>Defining the Response</h4>
devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="https://mojolicious.io/blog/2018/12/08/authenticating-with-ldap/" property="og:url">
<meta content="Day 8: Authenticating with LDAP" property="og:title">
<meta content="Learn how to add LDAP authentication to a Mojolicious full app" property="og:description">
<meta content="https://mojolicious.io/blog/2018/12/08/authenticating-with-ldap/banner.jpg" property="og:image">
devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_ view on Meta::CPAN
$sth->execute($username, $encrypted) or return;
</code></pre>
<p>Technically, AES is an encryption algorithm and SHA-2 is a hashing algorithm,
meaning that the transformation is effectively one-way and is more secure.
Here are a couple of modules that make it easier and safer:</p>
<p>A nice module out there for handling passwords is, well,
<a href="https://metacpan.org/pod/Passwords">Passwords</a>.
It's just a wrapper around some other modules that gives you a simple API
and will use <a href="https://metacpan.org/pod/Crypt::Eksblowfish::Bcrypt">Bcrypt</a> by default.
So if you've hashed your password with the <code>password_hash</code> function
and stored the <code>$hash</code> value in your database like this</p>
<pre><code>my $hash = password_hash($initial_password);
my $sth = $dbh->prepare('INSERT INTO user_passwd (username, password) VALUES (?, ?)');
$sth->do($username, $hash);
</code></pre>
<p>you should be ok to change the sub to</p>
devdata/https_mojolicious.io_blog_2018_12_09_add-a-theme-system-to-your-mojolicious-app_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="https://mojolicious.io/blog/2018/12/09/add-a-theme-system-to-your-mojolicious-app/" property="og:url">
<meta content="Day 9: Add a theme system to your Mojolicious app" property="og:title">
<meta content="https://mojolicious.io/blog/2018/12/09/add-a-theme-system-to-your-mojolicious-app/banner.jpg" property="og:image">
<meta content="summary_large_image" name="twitter:card">
devdata/https_mojolicious.io_blog_2018_12_09_add-a-theme-system-to-your-mojolicious-app_ view on Meta::CPAN
<p>Modifying the theme of a Mojolicious app is quite easy: add, modify or delete things in <code>public</code> and <code>templates</code>.
But all those direct modifications may not survive on update of the app: they will simply be erased by the files of the new version.</p>
<p>Let's see how we can provide a way to have a theme system in a Mojolicious application, that allows users to have a custom theme without pain and without risk of losing it on updates.</p>
</section>
<section id="section-2">
<h2>A fresh application</h2>
<p>When you create a new Mojolicious app with <code>mojo generate MyApplication</code>, these are the default directories that will serve files and their default contents to be served:</p>
<pre><code>$ tree public templates
public
âÂÂâÂÂâ index.html
templates
âÂÂâÂÂâ example
âÂÂààâÂÂâÂÂâ welcome.html.ep
âÂÂâÂÂâ layouts
âÂÂâÂÂâ default.html.ep
2 directories, 3 files
</code></pre>
<p><code>public</code> is where static files are stored, and <code>templates</code> is where templates are stored.</p>
<p>Those paths are registered in your Mojolicious application in <code>$app->static->paths</code> and <code>$app->renderer->paths</code>.
Luckily, those two objects are array references, so we can add or remove directories to them.</p>
<p>When serving a static file, our application search for the file in the first directory of the <code>$app->static->paths</code> array, and if it does not found it, search in the next directory, and so on.
It goes the same for template rendering.</p>
<h2>Let's change paths</h2>
<p>We could keep the <code>public</code> and <code>templates</code> default directories at the root of the application directory but I like to regroup all the themes-related stuff in a directory called <code>themes</code> and call my default themeâÂ...
<p>Create the new directories and move the default theme directories in it:</p>
<pre><code>$ mkdir -p themes/default
$ mv public templates themes/default
</code></pre>
<p>Then, we need to change the paths in our application.
Add this in <code>lib/MyApplication.pm</code>:</p>
<pre><code># Replace the default paths
$self->renderer->paths([$self->home->rel_file('themes/default/templates')]);
$self->static->paths([$self->home->rel_file('themes/default/public')]);
</code></pre>
<h2>Add a way to use another theme</h2>
<p>As said before, Mojolicious search for static files or templates in the first directory of the registered paths, and goes to next if it can't find the files or templates.</p>
<p>Thus, we need to add our new theme paths before the default ones.</p>
<p>Let's say that we created a <code>christmas</code> theme which files are in <code>themes/christmas/public</code> and which templates are in <code>themes/christmas/templates</code>.</p>
<p>Our snippet to add to the code becomes:</p>
<pre><code># Replace the default paths
$self->renderer->paths([$self->home->rel_file('themes/default/templates')]);
$self->static->paths([$self->home->rel_file('themes/default/public')]);
# Put the new theme first
unshift @{$self->renderer->paths}, $self->home->rel_file('themes/christmas/templates');
unshift @{$self->static->paths}, $self->home->rel_file('themes/christmas/public');
</code></pre>
<p>By doing that way, we can overload the default files.</p>
<p>You don't have to modify each file of the default theme to have a new theme: just copy the files you want to overload in your new theme directory and it will be used instead of the default one.</p>
<p>Let's say that you have a <code>background.png</code> file in your default theme:</p>
<pre><code>$ cd themes/default
$ tree public templates
public
âÂÂâÂÂâ background.png
âÂÂâÂÂâ index.html
templates
âÂÂâÂÂâ example
âÂÂààâÂÂâÂÂâ welcome.html.ep
âÂÂâÂÂâ layouts
âÂÂâÂÂâ default.html.ep
2 directories, 4 files
</code></pre>
<p>In order to overload it, you just have to have this:</p>
<pre><code>$ cd themes/christmas
$ tree public templates
public
âÂÂâÂÂâ background.png
devdata/https_mojolicious.io_blog_2018_12_09_add-a-theme-system-to-your-mojolicious-app_ view on Meta::CPAN
<h2>Using Mojolicious::Plugin::Config plugin</h2>
<p><a href="https://mojolicious.org/perldoc/Mojolicious/Plugin/Config">Mojolicious::Plugin::Config</a> comes with Mojolicious itself and is a great way to let users configure your application.
Why not using it to let them choose the theme they want?
In our example, the setting will unsurprisingly be named <code>theme</code>.</p>
<p>First, use the plugin:</p>
<pre><code># Mojolicious
my $config = $app->plugin('Config' => {
default => {
theme => 'default'
}
});
</code></pre>
<p>Note that I added a default value to the configuration of the plugin.
It makes sure that we will have a correct value for the chosen theme even if the user didn't choose one.</p>
<p>Now, we just have to use that configuration setting in our code:</p>
<pre><code># Replace the default paths
$self->renderer->paths([$self->home->rel_file('themes/default/templates')]);
$self->static->paths([$self->home->rel_file('themes/default/public')]);
# Do we use a different theme?
if ($config->{theme} ne 'default') {
# Put the new theme first
my $theme = $self->home->rel_file('themes/'.$config->{theme});
unshift @{$self->renderer->paths}, $theme.'/templates' if -d $theme.'/templates';
unshift @{$self->static->paths}, $theme.'/public' if -d $theme.'/public';
}
</code></pre>
<p>Note the <code>if -d $theme.'/templates'</code>: it prevents problems if the use made a typo in the name of the theme and allow to avoid creating both <code>templates</code> and <code>public</code> in the theme directory if you only need o...
<h2>Conclusion</h2>
<p>You are now providing a theme system in your application.
Users will now be able to change the style of it without fearing losing their changes on updates (though they will need to check the changes they made in case the default theme changed a lot).</p>
<p>You may even provides different themes yourself, like I did for my <a href="https://framagit.org/fiat-tux/hat-softwares/lstu">URL-shortening app, Lstu</a>àðÂÂÂ</p>
</section>
<small><p><a href="https://unsplash.com/photos/46juD4zY1XA">Photo</a> by <a href="https://unsplash.com/@davidpisnoy">David Pisnoy</a>, <a href="https://unsplash.com/license">Unsplash license</a> (quite similar to public domain)</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
devdata/https_mojolicious.io_blog_2018_12_10_minion-stands-alone_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/10/minion-stands-alone/" property="og:url">
<meta content="Day 10: Minion Stands Alone" property="og:title">
<meta content="Learn how to use the Minion job queue without needing a Mojolicious web application" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_11_who-watches-the-minions_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/11/who-watches-the-minions/" property="og:url">
<meta content="Day 11: Who Watches The Minions" property="og:title">
<meta content="Check the status of a Minion job queue using commands and the Minion::Admin UI" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_11_who-watches-the-minions_ view on Meta::CPAN
</section>
<section id="section-2">
<h2>Minion Jobs Command</h2>
<p>Minion comes with a <a href="https://mojolicious.org/perldoc/Minion/Command/minion/job"><code>job</code>
command</a> that
lists the jobs and their statuses.</p>
<pre><code>$ perl myapp.pl minion job
6 inactive default check_url
5 active default check_url
4 failed default check_url
3 failed default check_url
2 finished default check_url
1 finished default check_url
</code></pre>
<p>I can look at an individual job's information by passing in the job's
ID.</p>
<pre><code>$ perl minion.pl minion job 1
{
"args" => [
"http://mojolicious.org"
],
"attempts" => 1,
"children" => [],
"created" => "2018-11-23T19:15:47Z",
"delayed" => "2018-11-23T19:15:47Z",
"finished" => "2018-11-23T19:15:48Z",
"id" => 1,
"notes" => {},
"parents" => [],
"priority" => 0,
"queue" => "default",
"result" => "0.0716841220855713",
"retried" => undef,
"retries" => 0,
"started" => "2018-11-23T19:15:47Z",
"state" => "finished",
"task" => "check_url",
"worker" => 1
}
</code></pre>
devdata/https_mojolicious.io_blog_2018_12_12_dancer-and-minion_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@cromedome" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/12/dancer-and-minion/" property="og:url">
<meta content="Day 12: Using Minion in Dancer Apps" property="og:title">
<meta content="Overview of how to use Minion from within a Dancer application." property="og:description">
devdata/https_mojolicious.io_blog_2018_12_12_dancer-and-minion_ view on Meta::CPAN
use Moose;
use Minion;
use MyJob::Models::FooBar;
with 'MyJob::Roles::ConfigReader';
has 'runner' => (
is => 'ro',
isa => 'Minion',
lazy => 1,
default => sub( $self ) {
$ENV{ MOJO_PUBSUB_EXPERIMENTAL } = 1;
Minion->new( mysql => MyJob::DBConnectionManager->new->get_connection_uri({
db_type => 'feeds',
io_type => 'rw',
}));
},
);
</code></pre>
<p>We wrapped a simple Moose class around Minion to make it easy to add to any class or Dancer application with the extra functionality we wanted.</p>
<p>We ran into an issue at one point where jobs weren't running since we added them to a queue that no worker was configured to handle. To prevent this from happening to us again,
we added code to prevent us from adding code to a queue that didn't exist:</p>
<pre><code>my @QUEUE_TYPES = qw( default InstantXML PayrollXML ChangeRequest );
sub has_invalid_queues( $self, @queues ) {
return 1 if $self->get_invalid_queues( @queues );
return 0;
}
sub get_invalid_queues( $self, @queues ) {
my %queue_map;
@queue_map{ @QUEUE_TYPES } = ();
my @invalid_queues = grep !exists $queue_map{ $_ }, @queues;
return @invalid_queues;
}
</code></pre>
<p>With that in place, it was easy for our <code>queue_job()</code> method to throw an error if the developer tried to add a job to an invalid queue:</p>
<pre><code>sub queue_job( $self, $args ) {
my $job_name = $args->{ name } or die "queue_job(): must define job name!";
my $guid = $args->{ guid } or die "queue_job(): must have GUID to process!";
my $title = $args->{ title } // $job_name;
my $queue = $args->{ queue } // 'default';
my $job_args = $args->{ job_args };
die "queue_job(): Invalid job queue '$queue' specified" if $self->has_invalid_queues( $queue );
my %notes = ( title => $title, guid => $guid );
return $self->runner->enqueue( $job_name => $job_args => { notes => \%notes, queue => $queue });
}
</code></pre>
<h2>Creating Jobs</h2>
<p>In our base model class (Moose-based), we would create an attribute for our job runner:</p>
<pre><code>has 'job_runner' => (
is => 'ro',
isa => 'MyJob::JobQueue',
lazy => 1,
default => sub( $self ) { return MyJob::JobQueue->new->runner; },
);
</code></pre>
<p>And in the models themselves, creating a new queueable task was as easy as:</p>
<pre><code>$self->runner->add_task( InstantXML =>
sub( $job, $request_path, $guid, $company_db, $force, $die_on_error = 0 ) {
$job->note(
request_path => $request_path,
feed_id => 2098,
devdata/https_mojolicious.io_blog_2018_12_12_dancer-and-minion_ view on Meta::CPAN
to us:</p>
<pre><code># What port does the dashboard listen on?
dashboard_port: 4000
# Add the rest later.
dashboards:
UNKNOWN: http://localhost:3000/
DEV: http://my.development.host.tld:8001/
# Hosts that have no entry assume the default configuration
default:
max_children: 4
queues:
- default
# Host-specific settings
jcrome-precision-3510:
max_children: 8
queues:
- default
- InstantXML
- PayrollXML
- ChangeRequest
</code></pre>
<p>Our job queue workers look like:</p>
<pre><code>#!/usr/bin/env perl
use MyJob::Base;
devdata/https_mojolicious.io_blog_2018_12_12_dancer-and-minion_ view on Meta::CPAN
<p>Remember the YAML file we used to configure things up above? This last bit pulls the information for the host this worker is running on (<code>get_hostname()</code> is a home-grown
hostname function):</p>
<pre><code>sub get_hostconfig {
my $minion_config = MyJob::Config->new({ filename => "environments/minions.yml" })->config;
my $hostname = get_hostname();
if( exists $minion_config->{ $hostname }) {
return $minion_config->{ $hostname };
} else {
return $minion_config->{ default };
}
}
</code></pre>
<h2>Monitoring the Workers</h2>
<p>Our Minion dashboard was virtually identical to the one that @preaction posted in <a href="https://mojolicious.io/blog/2018/12/11/who-watches-the-minions/#section-2">Who Watches the Minions?</a>.
If you'd like to know more, I highly recommend reading his article.</p>
<h2>Outcome</h2>
devdata/https_mojolicious.io_blog_2018_12_13_taking-on-roles_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@briandfoy_perl" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/13/taking-on-roles/" property="og:url">
<meta content="Day 13: Taking on Roles" property="og:title">
<meta content="Need a little extra in your class?" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_14_a-practical-example-of-mojo-dom_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="https://mojolicious.io/blog/2018/12/14/a-practical-example-of-mojo-dom/" property="og:url">
<meta content="Day 14: A Practical Example of Mojo::DOM" property="og:title">
<meta content="Use Mojo::DOM to parse XML" property="og:description">
<meta content="https://mojolicious.io/blog/2018/12/14/a-practical-example-of-mojo-dom/banner.png" property="og:image">
devdata/https_mojolicious.io_blog_2018_12_15_practical-web-content-munging_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@swelljoe" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/15/practical-web-content-munging/" property="og:url">
<meta content="Day 15: Practical Web Content Munging" property="og:title">
<meta content="Working with ugly old websites using Mojo::UserAgent and Mojo::DOM" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_16_browser-diet_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="https://mojolicious.io/blog/2018/12/16/browser-diet/" property="og:url">
<meta content="Day 16: A pre-Christmas Diet for Mojolicious - A Children's Story" property="og:title">
<meta content="Setting the HTTP CacheControl header for your static files" property="og:description">
<meta content="https://mojolicious.io/blog/2018/12/16/browser-diet/squirrel.jpg" property="og:image">
devdata/https_mojolicious.io_blog_2018_12_16_browser-diet_ view on Meta::CPAN
<p>Sigh - this really is one demanding Sciurus vulgaris.</p>
<p>Well ... it <em>is</em> Christmas, but you'll need to install the
<a href="https://metacpan.org/pod/Mojolicious::Plugin::StaticCache">StaticCache plugin</a>
written for you only last year by
<a href="https://fiat-tux.fr/">Luc Didry</a>.
It sets the <code>Control-Cache</code> header for all static files served by Mojolicious.
With the <strong>nut.js</strong> and <strong>nut.css</strong> files in the <code>public</code> directory
(properly <a href="https://www.minifier.org/">minified</a> of course),
they should only be downloaded once and use the cached version until it expires.
The default <strong>max-age</strong> is 30 days and
if you want you can even cache during development with <code>even_in_dev => 1</code>.</p>
<p><img class="pull-right" src="speedtest_before_StaticCache.png"></p>
<p>The magpies in the forest had cluttered the calendar with 3 JavaScript libraries,
3 CSS files and 4 logos. Sure, the biggest and shiniest was only 66 kB
and the whole collection was a paltry 164 kB, but bandwidth is precious in the wilderness.
Before using the StaticCache plugin, the calendar rated a
<strong>92</strong> on Google's PageSpeed Insights.</p>
devdata/https_mojolicious.io_blog_2018_12_17_a-website-for-yancy_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/17/a-website-for-yancy/" property="og:url">
<meta content="Day 17: A Website For Yancy" property="og:title">
<meta content="Building a markdown-based site with Yancy" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_17_a-website-for-yancy_ view on Meta::CPAN
page" src="edit-index.png">
<img alt="Screenshot showing the Yancy editor listing the index page in the
database" src="list-index.png"></p>
<p>With our content created, I need to add a route to display it. Using the
<a href="https://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Wildcard-placeholders"><code>*</code> wildcard
placeholder</a>,
the route will match any path. I can then look up the page requested
from the database using the <a href="https://metacpan.org/pod/Yancy::Controller::Yancy/get">Yancy controller <code>get</code>
action</a>. I set
a default of "index" to pull our index page when users visit "/". Last,
the route will need a little bit of a template just to display the
page's HTML and a layout with some useful links and maybe some
<a href="http://getbootstrap.com">Bootstrap</a> to make things look a bit nicer.</p>
<pre><code>get '/*id' => {
id => 'index', # Default to index page
controller => 'yancy',
action => 'get',
collection => 'pages',
template => 'pages',
};
# Start the app. Must be the last code of the script
app->start;
__DATA__
@@ pages.html.ep
% layout 'default';
%== $item->{html}
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/yancy/bootstrap.css">
<title><%= title %></title>
</head>
<body>
<header>
<!-- Omitted for brevity -->
</header>
devdata/https_mojolicious.io_blog_2018_12_18_a-view-to-a-pod_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/18/a-view-to-a-pod/" property="og:url">
<meta content="Day 18: A View To A POD" property="og:title">
<meta content="A new POD viewer plugin for Mojolicious" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_18_a-view-to-a-pod_ view on Meta::CPAN
<p>Adding PODViewer to the existing site is easy!</p>
<pre><code>use Mojolicious::Lite;
plugin 'PODViewer';
app->start;
</code></pre>
<p>Now when I visit <a href="http://127.0.0.1:3000/perldoc">http://127.0.0.1:3000/perldoc</a> I see the POD for
<a href="http://mojolicious.org/perldoc">Mojolicious::Guides</a>. That's great and
all, but this is a documentation site for Yancy, not Mojolicious. Let's
adjust some configuration to make the default module Yancy, and only
allow viewing Yancy modules (trying to view another module will redirect
the user to <a href="http://metacpan.org">MetaCPAN</a>).</p>
<pre><code>use Mojolicious::Lite;
plugin 'PODViewer', {
default_module => 'Yancy',
allow_modules => [qw( Yancy Mojolicious::Plugin::Yancy )],
};
app->start;
</code></pre>
<p>There, now the Yancy docs are shown on the front page.</p>
<p><img alt="Screenshot of Yancy module documenation" src="okay-docs.png"></p>
<p>Finally, let's make it look a bit nicer: The documentation definitely
needs to use the default site layout, and a little additional CSS will
also make the documentation look a lot better!</p>
<pre><code>use Mojolicious::Lite;
plugin 'PODViewer', {
default_module => 'Yancy',
allow_modules => [qw( Yancy Mojolicious::Plugin::Yancy )],
layout => 'default',
};
app->start;
__DATA__
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/yancy/bootstrap.css">
<style>
h1 { font-size: 2.00rem }
h2 { font-size: 1.75rem }
h3 { font-size: 1.50rem }
h1, h2, h3 {
position: relative;
devdata/https_mojolicious.io_blog_2018_12_19_you-only-export-twice_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@preaction" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/19/you-only-export-twice/" property="og:url">
<meta content="Day 19: You Only Export Twice" property="og:title">
<meta content="Export content for static rendering" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_19_you-only-export-twice_ view on Meta::CPAN
<pre><code>$ cpanm Mojolicious::Command::export
</code></pre>
<p>Once it's installed, we now have the export command in our application which I
can use like any other Mojolicious command.</p>
<pre><code>$ ./myapp.pl export
</code></pre>
<p>By default, the export command tries to export the home page (<code>/</code>) and works
recursively from there. If I have pages that aren't linked from other places, I
should (a) probably add some links to that page, but (b) can just add it to the
list of pages to export:</p>
<pre><code>$ ./myapp.pl export / /private
</code></pre>
<p>Since I'm hosting this site under a directory in my personal website, I need to
use the <code>--base</code> option to rewrite all the internal links to the correct path,
and I can use the <code>--to</code> option to write directly to the web server's
directory:</p>
<pre><code>$ ./myapp.pl export --base /yancy --to /var/www/preaction.me/yancy
</code></pre>
<p>And, if I want, I can use <a href="https://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Adding-a-configuration-file">the Mojolicious Config
plugin</a>
to change the default settings, including what pages to export, the export
directory, and a base URL.</p>
<p>The best part is that the export command handles redirects. So, when we're
using <a href="http://metacpan.org/pod/Mojolicious::Plugin::PODViewer">the PODViewer
plugin</a> and get
redirected to <a href="http://metacpan.org">MetaCPAN</a>, the page gets updated with the
redirected location!</p>
<p>In the future it'd be nice if this command were made into a plugin so that it
could have hooks for customizing the exported content or additional checks for
devdata/https_mojolicious.io_blog_2018_12_20_testing-dancer_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/20/testing-dancer/" property="og:url">
<meta content="Day 20: Testing Dancer" property="og:title">
<meta content="Test your Dancer2 applications with Test::Mojo" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_21_a-little-christmas-template-cooking_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@briandfoy_perl" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/21/a-little-christmas-template-cooking/" property="og:url">
<meta content="Day 21: A Little Christmas Template Cooking" property="og:title">
<meta content="Using Mojo::Template for non-web applications." property="og:description">
devdata/https_mojolicious.io_blog_2018_12_21_a-little-christmas-template-cooking_ view on Meta::CPAN
my $now = localtime;
say $mt->render(<<~'EOF', $now->hms );
% my $time = shift;
<div>
Time: <%= $time %>
</div>
EOF
</code></pre>
<p>As seen above, by default, arguments are passed in to the template positionally, which is why the <code>shift</code> is there. I like to describe variables by name rather than their position. The <code>vars</code> attribute allows you to pass a ha...
<pre><code>use v5.26;
use Mojo::Template;
my $mt = Mojo::Template->new->vars(1);
use Time::Piece;
my $now = localtime;
say $mt->render(<<~'EOF', { time => $now->hms } );
devdata/https_mojolicious.io_blog_2018_12_22_use-carton-for-your-mojolicious-app-deployment_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="https://mojolicious.io/blog/2018/12/22/use-carton-for-your-mojolicious-app-deployment/" property="og:url">
<meta content="Day 22: Use Carton for your Mojolicious app deployment" property="og:title">
<meta content="https://mojolicious.io/blog/2018/12/22/use-carton-for-your-mojolicious-app-deployment/banner.jpg" property="og:image">
<meta content="summary_large_image" name="twitter:card">
devdata/https_mojolicious.io_blog_2018_12_22_use-carton-for-your-mojolicious-app-deployment_ view on Meta::CPAN
<p>For that, since we have a <code>cpanfile.snapshot</code> file from our development server, we can do:</p>
<pre><code>carton install --deployment
</code></pre>
<p>That installs the exact versions of modules listed in your snapshot.</p>
<h3>Features</h3>
<p>Per default, <code>carton install</code> will install all the <em>features</em> dependencies, but we can deactivate some:</p>
<pre><code>carton install --deployment --without mysql
</code></pre>
<p>In order to provide the correct version for all modules, even the optional ones, do a <code>carton install</code> on the development server, and use <code>--without</code> (note that this isn't <code>--without-feature</code> like <code>cpanm</...
<h3>Start your application</h3>
<p>In order to be able to use the <code>local</code> directory containing the dependencies, you can prefix your commands with <code>carton exec</code>.
So, to start a Mojolicious application with the built-in server <a href="https://mojolicious.org/perldoc/Mojo/Server/Hypnotoad">hypnotoad</a>, do:</p>
devdata/https_mojolicious.io_blog_2018_12_23_mojolicious-and-angular_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@sachindangol" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/23/mojolicious-and-angular/" property="og:url">
<meta content="Day 23: Mojolicious and Angular" property="og:title">
<meta content="Learn how to marry Mojolicious and Angular for building awesome web application" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_23_mojolicious-and-angular_ view on Meta::CPAN
It has many plugins, including easy implementation of <a href="https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI">OpenAPI</a>, <a href="https://metacpan.org/pod/Mojolicious::Plugin::OAuth2">OAuth</a>, utility modules and of many others on CPAN.</...
<p>One of the reasons you want to have this kind of web development set up is that front-end Angular developers and backend Mojolicious developers can work independently.</p>
<p>Angular is backend agnostic. Node.js Express is often used as backend for Angular, but we love Perl and Mojolicious.</p>
<p>We will see how these two can be married to make a web application today.</p>
</section>
<section id="section-2">
<p>I will be using the default auto-generated apps from both Mojolicious using <a href="https://mojolicious.org/perldoc/Mojolicious/Commands">mojo</a> and Angular using <a href="https://angular.io/cli">Angular CLI</a>.</p>
<h2>Generate Mojolicious Full App</h2>
<p>First I generate mojo full app using <code>mojo</code> CLI's <a href="https://mojolicious.org/perldoc/Mojolicious/Command/generate/app">generate app</a> command.</p>
<pre><code>Sachin@01:07 PM[~/workspace/project/mojo_angular]$ mojo generate app MojoAngularApp
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/script
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/script/mojo_angular_app
[chmod] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/script/mojo_angular_app 744
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/lib
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/lib/MojoAngularApp.pm
[exist] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/mojo_angular_app.conf
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/lib/MojoAngularApp/Controller
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/lib/MojoAngularApp/Controller/Example.pm
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/t
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/t/basic.t
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/public
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/public/index.html
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/templates/layouts
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/templates/layouts/default.html.ep
[mkdir] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/templates/example
[write] /Users/Sachin/workspace/project/mojo_angular/mojo_angular_app/templates/example/welcome.html.ep
Sachin@01:07 PM[~/workspace/project/mojo_angular]$
</code></pre>
<p>Now that the Mojolicious full-app is created, start <a href="https://mojolicious.org/perldoc/Mojo/Server/Hypnotoad">hypnotoad</a>, a production web server.</p>
<pre><code>Sachin@01:07 PM[~/workspace/project/mojo_angular]$ cd mojo_angular_app/
Sachin@01:08 PM[~/workspace/project/mojo_angular/mojo_angular_app]$ hypnotoad -f script/mojo_angular_app
[Sat Dec 15 13:08:52 2018] [info] Listening at "http://*:8080"
devdata/https_mojolicious.io_blog_2018_12_23_mojolicious-and-angular_ view on Meta::CPAN
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.08 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 16.3 kB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 3.67 MB [initial] [rendered]
ï½¢wdmï½£: Compiled successfully.
</code></pre>
<p>Open the browser to check Angular app:</p>
<p><img alt="basic angular app" src="angular_app.png"></p>
<p>Note that this page's content is coming from <code>NgDemo/src/index.html</code> where <code>app-root</code> selector html is content coming from <code>NgDemo/src/app/app.component.html</code>. Later we will be modifying <code>app.component.htm...
<h2>Make Mojolicious app serve an Angular app?</h2>
<p>First we'll compile the Angular app from <a href="https://www.typescriptlang.org/docs/home.html">TypeScript</a> to standard JavaScript.
<code>ng build</code> compiles an Angular app into an output directory named <code>dist</code> at the given output path.
Run <code>ng build</code> with <code>--base-href=./</code> so that base url inside the Angular app is set to the current directory for the application being built. This is very important so that later you do not waste time figuring out why Angular ro...
<pre><code>Sachin@02:06 PM[~/workspace/project/mojo_angular/NgDemo]$ ng build --base-href=./
Date: 2018-12-15T06:06:48.550Z
devdata/https_mojolicious.io_blog_2018_12_23_mojolicious-and-angular_ view on Meta::CPAN
this.adventDetail2018 = data;
});
}
}
Sachin@12:33 AM[~/workspace/project/mojo_angular/NgDemo/src/app]$
</code></pre>
<h4>Modify app.component.html file to show data</h4>
<p>Now we'll modify app.component.html file to show data fetched from the Mojolicious backend.
Replace the default template which was displaced earlier with our own html.
I have just looped through <code>adventDetail2018</code> variable, which consists of data from http get request, using <code>*ngFor</code> built in directive to form a <code>table</code>.</p>
<pre><code>Sachin@12:34 AM[~/workspace/project/mojo_angular/NgDemo/src/app]$ cat app.component.html
<div style="text-align:left">
<h1>
Welcome to {{ title }}!
</h1>
<h3>Mojolicious Advent Calendar 2018 Detail:</h3>
<table>
<thead>
devdata/https_mojolicious.io_blog_2018_12_24_async-await-the-mojo-way_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/24/async-await-the-mojo-way/" property="og:url">
<meta content="Day 24: Async/Await the Mojo Way" property="og:title">
<meta content="Announcing Mojo::AsyncAwait - Better non-blocking workflows for Mojolicious" property="og:description">
devdata/https_mojolicious.io_blog_2018_12_24_async-await-the-mojo-way_ view on Meta::CPAN
Instead, since it is written nonblocking, the requests are all made concurrently and the server is still free to respond to new clients.
And yet the code is still very easy to follow.</p>
<p>Note: the <a href="https://metacpan.org/pod/Mojolicious::Plugin::PromiseActions">PromiseActions</a> plugin automatically attaches error handlers to the controller action when it returns a promise; it is highly recommended when using async actions....
<h2>A Word on Implementation</h2>
<p>As stated before Mojo::AsyncAwait requires some mechanism to suspend the interpreter and resume it at that point later on.
Currently, the module uses the somewhat controversial module <a href="https://metacpan.org/pod/Coro">Coro</a> to do it.
As a bulwark against future implimentation changes, it comes with a pluggable backend system, not unlike how Mojo::IOLoop's pluggable reactor system works.
The default implementation may change and users may choose to use any available backend if they have a preference (once new ones come along, and others <strong>are</strong> in the works).</p>
<h2>Conclusion</h2>
<p>So now the formula is simple.</p>
<ul>
<li>Use libraries that return promises rather than take callbacks.</li>
<li>Use the <code>async</code> keyword when declaring functions that need to <code>await</code> promises.</li>
<li>Organize your promises using <a href="https://mojolicious.org/perldoc/Mojo/Promise#all">all</a>, <a href="https://mojolicious.org/perldoc/Mojo/Promise#race">race</a> (only wait for the first resolved promise) or some <a href="https://mojolicious....
</ul>
devdata/https_mojolicious.io_blog_2018_12_25_special-thanks_ view on Meta::CPAN
<!--[if lt IE 8 ]><html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8 ]><html class="no-js ie ie8" lang="en"> <![endif]-->
<!--[if (gte IE 8)|!(IE)]><!--><html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta content="" name="description">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<link href="/theme/css/default.css" rel="stylesheet">
<link href="/theme/css/layout.css" rel="stylesheet">
<link href="/theme/css/media-queries.css" rel="stylesheet">
<link href="/theme/css/statocles.css" rel="stylesheet">
<!-- twitter and opengraph -->
<meta content="summary" name="twitter:card">
<meta content="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2018/12/25/special-thanks/" property="og:url">
<meta content="Day 25: Special Thanks" property="og:title">
<meta content="Wrap up the 2018 Calendar with news and gratitude." property="og:description">