Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

         },
         {
            "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"
         },

META.yml  view on Meta::CPAN

      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_2017_12_01_day-1-getting-started  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/2017/12/01/day-1-getting-started/" property="og:url">
  <meta content="Day 1: Getting Started" property="og:title">
    <meta content="Hit the ground running with Mojolicious. A working application in minutes!" property="og:description">

devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash  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/2017/12/02/day-2-the-stash/" property="og:url">
  <meta content="Day 2: The Stash" property="og:title">
    <meta content="The stash is the most important data structure in Mojolicious." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash  view on Meta::CPAN

};
app-&gt;start;
</code></pre>

<h2>Stash Defaults</h2>

<p>In the above example we saw how you can set a stash value during a request to control the response.
Remember that the action callback is only called when a request comes in.
However, there is nothing special about the requst that we need to wait for to understand how to respond to it.</p>

<p>In Mojolicious, when establishing a route, we can also specify some default values to add to the stash on each request (unless they are changed).
These defaults are passed as a hash reference to the route constructor <code>get</code>.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/&#39; =&gt; {text =&gt; &#39;Hello 🌍 World!&#39;} =&gt; sub {
  my $c = shift;
};
app-&gt;start;
</code></pre>

<p>However now our action doesn&#39;t do anything, so we don&#39;t actually need it at all, and we are back to our original example.</p>

devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash  view on Meta::CPAN

};
app-&gt;start;
</code></pre>

<p>Here we see that <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Standard-placeholders">placeholder</a> values get merged into the stash.
We then can use them to render a more personalized response.
If you start the server and request <code>/Joel</code> in your browser you should see an application greeting me, or you can do it with your name.</p>

<p>If you tried to request <code>/</code> however, you would get a 404, not found.
The router doesn&#39;t want to handle this request without a value for the placeholder, so it assumes you wanted some other route to handle it.
While we could define another one for <code>/</code>, as we did before, we can do both at once by bringing back the defaults.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/:name&#39; =&gt; {name =&gt; &#39;🌍 world!&#39;} =&gt; sub {
  my $c = shift;
  my $name = $c-&gt;stash(&#39;name&#39;);
  $c-&gt;stash(text =&gt; &quot;hello $name&quot;);
};
app-&gt;start;
</code></pre>

<p>Now that the router knows what the default for <code>name</code> should be, it can now handle <code>/</code> as well as <code>/santa</code>!</p>

<h2>Stash Values in Templates</h2>

<p>Simple stash values, those that are only a single word (no punctuation) are also available in <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Embedded-Perl">templates</a>.
Here is the previous example using an &quot;inline template&quot;.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/:name&#39; =&gt; {name =&gt; &#39;🌍 world!&#39;} =&gt; sub {
  my $c = shift;
  $c-&gt;stash(inline =&gt; &#39;hello &lt;%= $name %&gt;&#39;);

devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash  view on Meta::CPAN

app-&gt;start;

__DATA__

@@ hello.html.ep
hello &lt;%= $name %&gt;
</code></pre>

<p>In the latter you see the first example of calling render with only one argument.
When it is called with an odd number of arguments, the first one is the identifier (name) of a template.
This is the same as stashing <code>template =&gt; &#39;hello&#39;</code>, which you could even do in the route defaults.</p>

<h2>Special/Reserved Stash Keys</h2>

<p>So there are probably a few people asking &quot;so the <code>text</code> stash value controls part of the rendering, does the <code>name</code> stash value do anything?&quot;
No, there are only a few stash values that have special meaning, they are documented on the <a href="http://mojolicious.org/perldoc/Mojolicious/Controller#stash">stash method</a> itself.
Those keys are</p>

<ul>
<li><code>action</code></li>
<li><code>app</code></li>

devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes  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/2017/12/03/day-3-using-named-routes/" property="og:url">
  <meta content="Day 3: Using Named Routes" property="og:title">
    <meta content="Avoid hard-coded URLs using Mojolicious&#39; named routes." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes  view on Meta::CPAN


            <div class="post-content">

              <section id="section-1">
                  <p>One of the things we know from years of programming is that you should never hard-code anything if you don&#39;t have to.
And yet far too many web application hard-code their urls, especially internal ones.
But what if you didn&#39;t have to?</p>

<p>Each Mojolicious route has its own <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Named-routes">name</a> which can be used to generate urls.
If you don&#39;t specify one, one is generated, but you shouldn&#39;t rely on that name, give it one that is meaningful and relevant to your purposes.
In lite apps, the name is the last parameter, after any defaults or callbacks.
(In a full app it is an attribute, but we&#39;ll talk about those in another post).</p>

<p>Then when you need a url, rather than hard-coding it, use <a href="http://mojolicious.org/perldoc/Mojolicious/Controller#url_for"><code>url_for</code></a> or related functionality to generate a url by name, you can even pass placeholder values if ...
Let&#39;s see how it works!</p>

              </section>
              <section id="section-2">
                  <h2>The North Pole Homepage</h2>

<p>Santa started out with a simple webapp.

devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-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="@joelaberger" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/04/day-4-dont-fear-the-full-app/" property="og:url">
  <meta content="Day 4: Don&#39;t Fear the Full App" property="og:title">
    <meta content="Growing from Lite to Full apps is easy. There&#39;s no magic to worry about." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands  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/2017/12/05/day-5-your-apps-built-in-commands/" property="og:url">
  <meta content="Day 5: Your App&#39;s Built-In Commands" property="og:title">
    <meta content="Exploring the built-in commands that come with every Mojolicious Application." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands  view on Meta::CPAN

  mojo generate lite_app
  ./myapp.pl daemon -m production -l http://*:8080
  ./myapp.pl get /foo
  ./myapp.pl routes -v

Tip: CGI and PSGI environments can be automatically detected very often and
    work without commands.

Options (for all commands):
  -h, --help          Get more information on a specific command
      --home &lt;path&gt;   Path to home directory of your application, defaults to
                      the value of MOJO_HOME or auto-detection
  -m, --mode &lt;name&gt;   Operating mode for your application, defaults to the
                      value of MOJO_MODE/PLACK_ENV or &quot;development&quot;

Commands:
cgi       Start application with CGI
cpanify   Upload distribution to CPAN
daemon    Start application with HTTP and WebSocket server
eval      Run code against application
generate  Generate files and directories from templates
get       Perform HTTP request
inflate   Inflate embedded files to real files

devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands  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/2017/12/06/day-6-adding-your-own-commands/" property="og:url">
  <meta content="Day 6: Adding Your Own Commands" property="og:title">
    <meta content="Adding commands to your app to ease administration and avoid auxiliary scripts." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands  view on Meta::CPAN


              </section>
              <section id="section-2">
                  <h2>What is a Command?</h2>

<p>Structurally, a command is just a class that inherits from <a href="http://mojolicious.org/perldoc/Mojolicious/Command">Mojolicious::Command</a> and implements a <code>run</code> method.
The method is passed an instance of the command class and the arguments given on the command line.
The command has the application as an attribute.
Just as the <code>eval</code> command demonstrated, the real power of the command comes from having access to an instance of the application, including its relevant configuration and methods.</p>

<p>By default your application looks for available commands in the namespace <code>Mojolicious::Command</code>.
As we saw before, several are available built-in to the system.
However others are available from CPAN, for example, I have a <a href="https://metacpan.org/pod/Mojolicious::Command::nopaste">nopaste clone</a> that when installed is available via your application or the <code>mojo</code> executable.</p>

<p>Your application can add to or even replace the default namespace search path by setting <a href="http://mojolicious.org/perldoc/Mojolicious#commands"><code>app-&gt;commands-&gt;namespaces</code></a>.
My <a href="https://metacpan.org/pod/Galileo">Galileo</a> CMS adds a command namespace so that its deploy command is available as <code>galileo deploy</code> but not as <code>mojo deploy</code>.
Meanwhile plugins that your app loads can add to the namespaces.
The <a href="http://mojolicious.org/perldoc/Minion">Minion</a> job queue is added to your application as a plugin, it appends <code>Minion::command</code> to your command namespaces so that your application has access to the minion commands like star...

<h2>Let&#39;s Build a Weather App</h2>

<p>Rather than give several small examples I&#39;m going to change it up this time and give one big example.
It will be ok if you don&#39;t understand every line, I&#39;m skipping a few pedagogical steps to make this example.</p>

<p>Why don&#39;t we build a weather caching app?

devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands  view on Meta::CPAN

<pre><code>package MyWeatherApp::Command::deploy;
use Mojo::Base &#39;Mojolicious::Command&#39;;

use Mojo::Util &#39;getopt&#39;;

has &#39;description&#39; =&gt; &#39;Deploy or update the MyWeatherApp schema&#39;;
has &#39;usage&#39; =&gt; &lt;&lt;&quot;USAGE&quot;;
$0 deploy [OPTIONS]
OPTIONS:
  -v , --verbose  the version to deploy
                  defaults to latest
USAGE

sub run {
  my ($self, @args) = @_;

  getopt(
    \@args,
    &#39;v|version=i&#39; =&gt; \my $version,
  );

devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page  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/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/" property="og:url">
  <meta content="Day 7: Using Template Variants For a Beta Landing Page" property="og:title">
    <meta content="Doug demonstrates using Mojolicious template variants to show a new landing page for a beta-testing website" property="og:description">

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  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/2017/12/08/day-8-mocking-a-rest-api/" property="og:url">
  <meta content="Day 8: Mocking a REST API" property="og:title">
    <meta content="Doug uses Mojolicious to quickly build a mock API for testing a front-end application." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test  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/2017/12/09/day-9-the-best-way-to-test/" property="og:url">
  <meta content="Day 9: The Best Way to Test" property="og:title">
    <meta content="An introduction to Test::Mojo, the testing framework for Mojolicious." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want  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/2017/12/10/day-10-give-the-customer-what-they-want/" property="og:url">
  <meta content="Day 10: Give the Customer What They Want" property="og:title">
    <meta content="Using content negotiation to create APIs that support multiple response formats." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want  view on Meta::CPAN

Perhaps the dot character is needed to indicate other things or perhaps it just looks weird for some requests.
In that case there is yet another mechanism.
For requests bearing a <code>format</code> query parameter, that value will be used.
A request to &#39;GET <code>/santa?format=json</code> will result in the same stash values as the previous example.</p>

<h3>How It Works</h3>

<p>By now you probably suspect, correctly, that the <code>format</code> stash value is the driver of Content Negotiation.
Other methods, which you will see later, will check that value in order to determine what should be rendered.</p>

<p>With that knowledge therefore this way you might guess, correctly, that if you&#39;d like to force a route to have a certain default format you can just put it into the route default stash values</p>

<pre><code>get &#39;/:name&#39; =&gt; {format =&gt; &#39;json&#39;} ...
</code></pre>

<p>In Mojolicious the overall <a href="http://mojolicious.org/perldoc/Mojolicious/Renderer#default_format">default format</a> is html, but of course can be changed.</p>

<pre><code>app-&gt;renderer-&gt;default_format(&#39;json&#39;);
</code></pre>

<p>There are also mechanisms to limit the format detection, but those are beyond the scope of this article.
The links above contain more.</p>

<p>Note also that the mappings between file extensions and MIME types obviously are in play here.
If you have special format types you can add them to the <a href="http://mojolicious.org/perldoc/Mojolicious#types">types</a> object on the application.</p>

<h2>Responding to the Format Request</h2>

devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators  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/2017/12/11/day-11-useragent-content-generators/" property="og:url">
  <meta content="Day 11: UserAgent Content Generators" property="og:title">
    <meta content="Using Mojo::UserAgent Content Generators to build request bodies." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators  view on Meta::CPAN


<pre><code>my $ua = Mojo::UserAgent-&gt;new;
my $tx = $ua-&gt;build_tx(POST =&gt; &#39;/url&#39;);
$tx-&gt;req-&gt;headers-&gt;header(&#39;Content-Type&#39;, &#39;text/plain&#39;);
$tx-&gt;req-&gt;body(&#39;some binary content&#39;);
$ua-&gt;start($tx);
</code></pre>

<p>A Content Generators is a shortcut to help build requests for certain types of content.
The previous example wasn&#39;t technically a Content Generator as these are indicated by a generator name and usually accept arguments.
That said, you can almost imagine that setting the body content is the default generator.</p>

<p>The simplest use of an actual Content Generator is the one the builds a JSON request.
A JSON post like</p>

<pre><code>$ua-&gt;post(&#39;/url&#39;, json =&gt; {some =&gt; [&#39;json&#39;, &#39;data&#39;]});
</code></pre>

<p>does two things, it builds the binary form of the body and it sets the <code>Content-Type</code> header.
To do it manually it would be either</p>

devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class  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/2017/12/12/day-12-more-than-a-base-class/" property="og:url">
  <meta content="Day 12: More Than a Base Class" property="og:title">
    <meta content="Mojo::Base is a streamlined base class with lots of extra goodies." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class  view on Meta::CPAN

If you know why you might want to use multipe-inheritance, you probably know how to get around this limitation as well.</p>

<h3>Attributes and Constructor</h3>

<p>Mojo::Base implements a class method which can be used to add attributes to the class (called <code>attr</code>).
While this is necessary for the implementation, this isn&#39;t the preferred usage.</p>

<p>The <code>has</code> keyword, added by the import above, gives us that nice declarative style that Perl users are familiar with from Moose and Moo.
The usage is different from those, owing to the limited choices available in declaration.
<code>has</code> takes a name or an array reference of name of the attribute(s) to declare.
It then optionally takes either a simple (non-reference) scalar default value or a callback to be used as a lazy builder.
When the lazy builder is called, it gets as an argument the instance itself.
That&#39;s it, clean and simple.</p>

<pre><code>package My::Class;
use Mojo::Base -base;

# attribute whose default is undef
has &#39;foo&#39;;

# two attribtues whose defaults are both 0
has [&#39;min&#39;, &#39;max&#39;] =&gt; 0;

# attribute that defaults to a new, empty hash reference
has &#39;data&#39; =&gt; sub { {} };

# attribute that uses its existing state to build
has &#39;double_max&#39; =&gt; sub {
  my $self = shift;
  return 2 * $self-&gt;max;
};
</code></pre>

<p>The callbacks are always lazy, meaning if the value of that attribute hasn&#39;t been established, either via the constructor or via a setter, then the default is used or the builder is run.</p>

<p>The default constructor (<code>new</code>), inherited from Mojo::Base, takes a hash reference or key-value pairs and uses them as initialization for the defined attributes.</p>

<pre><code>my $obj = My::Class-&gt;new(foo =&gt; &#39;bar&#39;, max =&gt; 10);
my $obj = My::Class-&gt;new({foo =&gt; &#39;bar&#39;, max =&gt; 10}); # same
</code></pre>

<p>Note that there is nothing to prevent you from passing data that isn&#39;t for a defined attribute (ie, the constructor isn&#39;t <a href="https://metacpan.org/pod/MooX::StrictConstructor">strict</a>).
Nor is there anything that declares a required attribute, though you can easily make one</p>

<pre><code>has &#39;username&#39; =&gt; sub { die &#39;username is required&#39; };
</code></pre>

devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-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="@joelaberger" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles/" property="og:url">
  <meta content="Day 13: More About Roles" property="og:title">
    <meta content="Investigating Mojo::Base&#39;s recently-added role support." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  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/2017/12/14/day-14-you-promised-to-call/" property="og:url">
  <meta content="Day 14: You Promised To Call!" property="og:title">
    <meta content="Learn about Promises and their new prominent role in Mojolicious." property="og:description">
    <meta content="https://mojolicious.io/blog/2017/12/14/day-14-you-promised-to-call/pinky_swear.jpg" property="og:image">

devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-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="@preaction" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app/" property="og:url">
  <meta content="Day 15: Start a New Yancy App" property="og:title">
    <meta content="Rapid-prototype a data-backed application using Mojolicious and Yancy." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  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/2017/12/16/day-16-the-secret-life-of-sessions/" property="og:url">
  <meta content="Day 16: The Secret Life of Sessions" property="og:title">
    <meta content="Use the session secret effectively to protect users without inconveniencing them." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  view on Meta::CPAN


</code></pre>

<p>When you run this application</p>

<pre><code>$ perl myapp.pl daemon
</code></pre>

<p>and visit <code>localhost:3000</code> you should see a counter that increments on each request.
That data is stored on the client (e.g. in the browser) between each request and is incremented on the server before sending it back to the client.
Each response is a new cookie with the new session data and a new cookie expiration time, <a href="http://mojolicious.org/perldoc/Mojolicious/Sessions#default_expiration">defaulting to one hour</a> from issue.
This also therefore implies that as long as you visit often enough (before any one cookie expires) and your data continues to validate against the secret, your session can last forever.</p>

<h2>Secret Security</h2>

<p>Now, one other thing you should see is that in your application&#39;s log output, you should have a message like</p>

<pre><code>Your secret passphrase needs to be changed
</code></pre>

<p>This happens because you are using the default secret for the application.
This default is just the name of the script, as you can see via the <a href="/blog/2017/12/05/day-5-your-apps-built-in-commands">eval command</a></p>

<pre><code>$ perl myapp.pl eval -V &#39;app-&gt;secrets&#39;
[
  &quot;myapp&quot;
]
</code></pre>

<p>This secret is not secure both because it is short and because it is easy to guess.
With a trivial application like this you might not need to worry about forgery, as you would with say a session that tracks user logins.
But who knows, perhaps you are going to award a prize to the user for the most requests made!

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  view on Meta::CPAN

<p>For your clients, this will have the jarring effect that all existing sessions will be invalidated.
In the example application the counter would be reset.
If instead the session were being used to keep users logged in, they would suddenly be logged out.
If it was for tracking a shopping cart ... no more shopping cart.</p>

<p>This can actually be useful even if your secret is safe but you want to force-invalidate sessions for some other reason, like say your application was generating corrupt data or worse.
Generally, however, this is something you&#39;d like to avoid.</p>

<h2>A Random Secret?</h2>

<p>Now perhaps you are asking yourself, if Mojolicious knows I&#39;m using the insecure default couldn&#39;t it just set a random secret?
Sure, and you could do so yourself too if you wanted.
Something as easy as this would set a random secret.</p>

<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;

<span class="hljs-comment"># NOT RECOMMENDED!</span><span class="hljs-comment">
</span>app-&gt;secrets([<span class="hljs-function">rand</span>]);

get &#39;<span class="hljs-string">/</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
  <span class="hljs-keyword">my</span> <span class="hljs-type">$c</span> = <span class="hljs-function">shift</span>;

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-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="@joelaberger" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/17/day-17-the-wishlist-app/" property="og:url">
  <meta content="Day 17: The Wishlist App" property="og:title">
    <meta content="Introducing template concepts with a group wishlist app." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN

  &lt;head&gt;
    &lt;meta charset=&quot;<span class="hljs-string">utf-8</span>&quot;&gt;
    &lt;meta http-equiv=&quot;<span class="hljs-string">X-UA-Compatible</span>&quot; content=&quot;<span class="hljs-string">IE=edge</span>&quot;&gt;
    &lt;meta name=&quot;<span class="hljs-string">viewport</span>&quot; content=&quot;<span class="hljs-string">width=device-width, initial-scale=1</span>&quot;&gt;

    &lt;title&gt;&lt;%= title %&gt;&lt;/title&gt;
    &lt;<span class="hljs-function">link</span> rel=&quot;<span class="hljs-string">stylesheet</span>&quot; type=&quot;<span class="hljs-string">text/css</span>&quot; href=&quot;<span class="hljs-string">//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/...
    %= content &#39;<span class="hljs-string">head</span>&#39;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;nav class=&quot;<span class="hljs-string">navbar navbar-default</span>&quot;&gt;
      &lt;div class=&quot;<span class="hljs-string">container</span>&quot;&gt;
        &lt;div class=&quot;<span class="hljs-string">navbar-header</span>&quot;&gt;
          &lt;a class=&quot;<span class="hljs-string">navbar-brand</span>&quot; href=&quot;<span class="hljs-string">/</span>&quot;&gt;
            Mojo Wishlist
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/nav&gt;
    &lt;div class=&quot;<span class="hljs-string">container</span>&quot;&gt;
      &lt;div id=&quot;<span class="hljs-string">sidebar</span>&quot; class=&quot;<span class="hljs-string">col-md-3</span>&quot;&gt;

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN

    &lt;/div&gt;

    %= content &#39;<span class="hljs-string">footer</span>&#39;
    %= content &#39;<span class="hljs-string">end</span>&#39;
  &lt;/body&gt;
&lt;/html&gt;


</code></pre>

<p><small>templates/layouts/default.html.ep</small></p>

<p>Here you can see that I include the <a href="https://getbootstrap.com/docs/3.3">Boostrap</a>.
You can also see a few bits of templating.
The first is that I use a <code>&lt;title&gt;</code> tag with the content <code>&lt;%= title %&gt;</code>.
This is a shortcut helper to get the value of the <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#title"><code>title</code> key of the stash</a>.
Well shall see in a moment how this value gets set.</p>

<p>The remaining template portions are each getting the contents of named content buffers.
While I establish five such inclusions, I will only actually use three: <code>head</code>, <code>sidebar</code> and the default unnamed buffer.</p>

<p>With the possible exception of <code>sidebar</code>, buffers like these are useful in almost all application layouts.
The <code>head</code> and <code>end</code> buffers let you add contents to those locations, especially useful to include extra stylesheets and javascript respectively.
The <code>footer</code> buffer would allow additions to be placed at the end of the body but before any javascript inclusions.</p>

<p>It is interesting to note that if we rendered this template directly, those buffers would all be empty.
Therefore the content put into them must come from someplace else.</p>

<h2>A Primary Template</h2>

<p>I mentioned before the the layout was like a container that would hold some inner content.
Let&#39;s consider the simplest such case.</p>

<p>When a user first accesses the site, the will be greeted with a login page.</p>

<p><img alt="Screen shot of the login page" src="login.png"></p>

<pre><code class="hljs">% title &#39;<span class="hljs-string">Welcome to the Wishlist!</span>&#39;;
% layout &#39;<span class="hljs-string">default</span>&#39;;

&lt;h2&gt;&lt;%= title %&gt;&lt;/h2&gt;

%= form_for &#39;<span class="hljs-string">login</span>&#39; =&gt; (method =&gt; &#39;<span class="hljs-string">POST</span>&#39;, class =&gt; &#39;<span class="hljs-string">form-inline</span>&#39;) =&gt; <span class="hljs-keyword">begin</span>
  &lt;div class=&quot;<span class="hljs-string">form-group</span>&quot;&gt;
    &lt;label&gt;Please <span class="hljs-function">tell</span> us your name to get started&lt;/label&gt;
    &lt;input name=&quot;<span class="hljs-string">name</span>&quot; class=&quot;<span class="hljs-string">form-control</span>&quot;&gt;
  &lt;/div&gt;
% <span class="hljs-keyword">end</span>


</code></pre>

<p><small>templates/login.html.ep</small></p>

<p>Immediately you can see that there are a few statements at the top.
These set the layout to our default one above and set the title key for the page.
It is important to realize that this page is rendered <strong>first</strong> before any layout is rendered.</p>

<p>After the template renders, Mojolicious will notice that a layout key was set and as a result it will render the layout with the result of the primary template rendering available as the default content buffer.
As we saw before, that content will be placed inside the <code>#main</code> section of the page.
However, in the process of rendering the primary template, the title was also set.
Since the layout is rendered afterwards, this value is now available to set the <code>&lt;title&gt;</code> tag at the top of the page.</p>

<p>While this may seem obvious, it is actually quite remarkable.
If the page had been rendered all in order, the value would not have been set in time to be used there at the top of the page.
Knowing the rendering order is therefore very important to understanding the rendering process.</p>

<h2>The Application</h2>

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN

The <code>/login</code> and <code>/logout</code> handlers, for example.</p>

<p>There are two routes for <code>/add</code> a <code>GET</code> and a <code>POST</code>.
<code>GET</code> requests are safe and will not change data, in this case the request triggers LinkEmbedder to fetch the information which is then displayed.</p>

<h4>Adding Items</h4>

<p><img alt="Screen shot of the add items page" src="add.png"></p>

<pre><code class="hljs">% title &#39;<span class="hljs-string">Add an item</span>&#39;;
% layout &#39;<span class="hljs-string">default</span>&#39;;

% content_for &#39;<span class="hljs-string">head</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
  &lt;style&gt;
    <span class="hljs-comment">#item-detail img {</span><span class="hljs-comment">
</span>      max-width: 40%;
      float: left;
      margin: <span class="hljs-number">20</span>px;
    }
  &lt;/style&gt;
% <span class="hljs-keyword">end</span>

% content_for &#39;<span class="hljs-string">sidebar</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
  &lt;div class=&quot;<span class="hljs-string">panel panel-default</span>&quot;&gt;
    &lt;div class=&quot;<span class="hljs-string">panel-heading</span>&quot;&gt;
      Add this item to your list?
    &lt;/div&gt;
    &lt;div class=&quot;<span class="hljs-string">panel-body</span>&quot;&gt;
      %= form_for &#39;<span class="hljs-string">add</span>&#39; =&gt; (method =&gt; &#39;<span class="hljs-string">POST</span>&#39;) =&gt; <span class="hljs-keyword">begin</span>
        %= hidden_field title =&gt; <span class="hljs-type">$link</span>-&gt;<span class="hljs-type">title</span><span class="hljs-type">
</span>        %= hidden_field url =&gt; <span class="hljs-type">$link</span>-&gt;<span class="hljs-type">url</span><span class="hljs-type">
</span>        &lt;input type=&quot;<span class="hljs-string">submit</span>&quot; value=&quot;<span class="hljs-string">Yes</span>&quot; class=&quot;<span class="hljs-string">btn btn-default</span>&quot;&gt;
        %= link_to &#39;<span class="hljs-string">No</span>&#39; =&gt; &#39;<span class="hljs-string">/</span>&#39;, (class =&gt; &#39;<span class="hljs-string">btn btn-default</span>&#39;)
      % <span class="hljs-keyword">end</span>
    &lt;/div&gt;
  &lt;/div&gt;
  %= include &#39;<span class="hljs-string">partial/log_out</span>&#39;
% <span class="hljs-keyword">end</span>

&lt;div class=&quot;<span class="hljs-string">panel panel-default</span>&quot;&gt;
  &lt;div id=&quot;<span class="hljs-string">item-detail</span>&quot; class=&quot;<span class="hljs-string">panel-body</span>&quot;&gt;
    &lt;%== <span class="hljs-type">$link</span>-&gt;<span class="hljs-type">html</span> %&gt;
  &lt;/div&gt;
&lt;/div&gt;

</code></pre>

<p><small>templates/add.html.ep</small></p>

<p>Beyond being interesting to see how the link is used to embed HTML into the page, we also see our first uses of named content buffers via <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#content_for"><code>content_for</cod...
These add styling that is specific to the page into the <code>&lt;head&gt;</code> tag and inject a panel into the sidebar.
Once this page renders, again before the layout is rendered, the content of that section is available in the <code>sidebar</code> buffer.</p>

<p>The result is a tiny form that contains the data to be stored if the user wants to add it to their wishlist.
Because the resulting main page might be quite large, and I want the user to have easy access to decide if they want to add the item, I&#39;ve placed it in the left hand column.
Perhaps this is bad UX, but for educational purposes, it shows how these buffers can be used.</p>

<p>We also see our first example of <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#include"><code>include</code></a>.
When called, the renderer immediately renders the template specified and returns the result.</p>

<pre><code class="hljs">&lt;div class=&quot;<span class="hljs-string">panel panel-default</span>&quot;&gt;
  &lt;div class=&quot;<span class="hljs-string">panel-body</span>&quot;&gt;
    &lt;ul class=&quot;<span class="hljs-string">nav nav-pills nav-stacked</span>&quot;&gt;
      %= t li =&gt; link_to &#39;<span class="hljs-string">Log Out</span>&#39; =&gt; &#39;<span class="hljs-string">logout</span>&#39;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;

</code></pre>

<p><small>templates/partial/log_out.html.ep</small></p>

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN


<h4>The List</h4>

<p>There are two routes that might render a wishlist.
The <code>/</code> route either allows the user to log in or if they are, displays their list.
There is a also a <code>/list/:name</code> route that renders any user&#39;s list by name.</p>

<p><img alt="Screen shot of the list page" src="list.png"></p>

<pre><code class="hljs">% title user-&gt;{name} . &quot;<span class="hljs-string">&#39;s List</span>&quot;;
% layout &#39;<span class="hljs-string">default</span>&#39;;

% content_for &#39;<span class="hljs-string">sidebar</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
  %= include &#39;<span class="hljs-string">partial/user_list</span>&#39;
  %= include &#39;<span class="hljs-string">partial/add_url</span>&#39;
  %= include &#39;<span class="hljs-string">partial/log_out</span>&#39;
% <span class="hljs-keyword">end</span>

&lt;h2&gt;&lt;%= title %&gt;&lt;/h2&gt;

&lt;table class=&quot;<span class="hljs-string">table</span>&quot;&gt;

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN

  &lt;/thead&gt;
  &lt;tbody&gt;
  % <span class="hljs-keyword">for</span> <span class="hljs-keyword">my</span> <span class="hljs-type">$key</span> (<span class="hljs-function">sort</span> <span class="hljs-function">keys</span> <span class="hljs-type">%</span>{ user-&gt;{items} }) ...
    % <span class="hljs-keyword">my</span> <span class="hljs-type">$item</span> = user-&gt;{items}{<span class="hljs-type">$key</span>};
    &lt;tr&gt;
      %= t td =&gt; <span class="hljs-type">$item</span>-&gt;{title}
      %= t td =&gt; link_to <span class="hljs-type">$item</span>-&gt;{url} =&gt; <span class="hljs-type">$item</span>-&gt;{url}
      % <span class="hljs-keyword">if</span> (session-&gt;{name} eq user-&gt;{name}) {
        %= t td =&gt; form_for &#39;<span class="hljs-string">remove</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
          %= hidden_field title =&gt; <span class="hljs-type">$key</span><span class="hljs-type">
</span>          &lt;input type=&quot;<span class="hljs-string">submit</span>&quot; value=&quot;<span class="hljs-string">Remove</span>&quot; class=&quot;<span class="hljs-string">btn btn-default</span>&quot;&gt;
        % <span class="hljs-keyword">end</span>
      % } <span class="hljs-keyword">else</span> {
        %= t td =&gt; form_for &#39;<span class="hljs-string">update</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
          %= hidden_field user  =&gt; user-&gt;{name}
          %= hidden_field title =&gt; <span class="hljs-type">$key</span><span class="hljs-type">
</span>          % <span class="hljs-keyword">if</span> (<span class="hljs-type">$item</span>-&gt;{purchased}) {
            %= hidden_field purchased =&gt; 0
            &lt;input type=&quot;<span class="hljs-string">submit</span>&quot; value=&quot;<span class="hljs-string">Mark Not Purchased</span>&quot; class=&quot;<span class="hljs-string">btn btn-default</span>&quot;&gt;
          % } <span class="hljs-keyword">else</span> {
            %= hidden_field purchased =&gt; <span class="hljs-number">1</span>
            &lt;input type=&quot;<span class="hljs-string">submit</span>&quot; value=&quot;<span class="hljs-string">Mark Purchased</span>&quot; class=&quot;<span class="hljs-string">btn btn-default</span>&quot;&gt;
          % }
        % <span class="hljs-keyword">end</span>
      % }
    &lt;/tr&gt;
  % }
  &lt;/tbody&gt;
&lt;/table&gt;


</code></pre>

devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app  view on Meta::CPAN

This is done by calling the <code>/update</code> method, the result of which will change the item&#39;s status and re-render the page.o</p>

<p>If it is their own page, they don&#39;t want to have the surprise spoiled see that someone has bought their present.
So we don&#39;t show the purchase state.
However perhaps they have changed their mind and no longer want that item.
In that case, they are presented with a remove button which calls to the <code>/remove</code> route.</p>

<p>Finally let&#39;s look at those last two partials.
There is a sidebar list of the users, so you can see everyone&#39;s list.</p>

<pre><code class="hljs">&lt;div class=&quot;<span class="hljs-string">panel panel-default</span>&quot;&gt;
  &lt;div class=&quot;<span class="hljs-string">panel-heading</span>&quot;&gt;
    Users&#39;<span class="hljs-string"> Lists</span><span class="hljs-string">
</span><span class="hljs-string">  &lt;/div&gt;</span><span class="hljs-string">
</span><span class="hljs-string">  &lt;div class=&quot;panel-body&quot;&gt;</span><span class="hljs-string">
</span><span class="hljs-string">    &lt;ul class=&quot;nav nav-pills nav-stacked&quot;&gt;</span><span class="hljs-string">
</span><span class="hljs-string">      % for my $name (sort keys %{ users() }) {</span><span class="hljs-string">
</span><span class="hljs-string">        %= t li =&gt; link_to $name =&gt; list =&gt; {name =&gt; $name}</span><span class="hljs-string">
</span><span class="hljs-string">      % }</span><span class="hljs-string">
</span><span class="hljs-string">    &lt;/ul&gt;</span><span class="hljs-string">
</span><span class="hljs-string">  &lt;/div&gt;</span><span class="hljs-string">
</span><span class="hljs-string">&lt;/div&gt;</span><span class="hljs-string">
</span><span class="hljs-string">
</span></code></pre>

<p><small>templates/partial/user_list.html.ep</small></p>

<p>And an input box that allows the user to submit a link to add to their wishlist.</p>

<pre><code class="hljs">&lt;div class=&quot;<span class="hljs-string">panel panel-default</span>&quot;&gt;
  &lt;div class=&quot;<span class="hljs-string">panel-heading</span>&quot;&gt;
    Add Item by URL
  &lt;/div&gt;
  &lt;div class=&quot;<span class="hljs-string">panel-body</span>&quot;&gt;
    %= form_for &#39;<span class="hljs-string">add</span>&#39; =&gt; <span class="hljs-keyword">begin</span>
      &lt;input name=&quot;<span class="hljs-string">url</span>&quot; class=&quot;<span class="hljs-string">form-control</span>&quot;&gt;
    % <span class="hljs-keyword">end</span>
  &lt;/div&gt;
&lt;/div&gt;

devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model  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/2017/12/18/day-18-the-wishlist-model/" property="og:url">
  <meta content="Day 18: The Wishlist Model" property="og:title">
    <meta content="Building a proper model layer for the Wishlist app." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model  view on Meta::CPAN


<span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> users (
  <span class="hljs-keyword">id</span> <span class="hljs-type">integer</span> <span class="hljs-keyword">primary</span> <span class="hljs-keyword">key</span> autoincrement,
  name text <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">unique</span>
);

<span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> items (
  <span class="hljs-keyword">id</span> <span class="hljs-type">integer</span> <span class="hljs-keyword">primary</span> <span class="hljs-keyword">key</span> autoincrement,
  title text,
  url text,
  purchased <span class="hljs-type">integer</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">default</span> <span class="hljs-number">0</span>,
  user_id <span class="hljs-type">integer</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span>,
  <span class="hljs-keyword">foreign</span> <span class="hljs-keyword">key</span>(user_id) <span class="hljs-keyword">references</span> users(<span class="hljs-keyword">id</span>)
);

<span class="hljs-comment">-- 1 down</span><span class="hljs-comment">
</span>
PRAGMA foreign_keys = <span class="hljs-keyword">OFF</span>;
<span class="hljs-keyword">drop</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">exists</span> users;
<span class="hljs-keyword">drop</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">exists</span> items;

devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model  view on Meta::CPAN

    <span class="hljs-type">$app</span>-&gt;<span class="hljs-type">home</span>-&gt;<span class="hljs-type">child</span>(&#39;<span class="hljs-string">wishlist.sql</span>&#39;)
  )-&gt;name(&#39;<span class="hljs-string">wishlist</span>&#39;);

  <span class="hljs-keyword">return</span> <span class="hljs-type">$sqlite</span>;
};

<span class="hljs-keyword">sub </span><span class="hljs-function">startup</span> {
  <span class="hljs-keyword">my</span> <span class="hljs-type">$app</span> = <span class="hljs-function">shift</span>;

  <span class="hljs-type">$app</span>-&gt;<span class="hljs-type">plugin</span>(&#39;<span class="hljs-string">Config</span>&#39; =&gt; {
    default =&gt; {},
  });

  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">my</span> <span class="hljs-type">$secrets</span> = <span class="hljs-type">$app</span>-&gt;<span class="hljs-type">config</span>-&gt;{secrets}) {
    <span class="hljs-type">$app</span>-&gt;<span class="hljs-type">secrets</span>(<span class="hljs-type">$secrets</span>);
  }

  <span class="hljs-type">$app</span>-&gt;<span class="hljs-type">helper</span>(<span class="hljs-function">link</span> =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> <span class="hljs-type">$c</span> = <span class="hljs-function">shift</span>;
    state <span class="hljs-type">$le</span> = LinkEmbedder-&gt;new;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$le</span>-&gt;<span class="hljs-type">get</span>(<span class="hljs-type">@_</span>);

devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model  view on Meta::CPAN


}

<span class="hljs-number">1</span>;

</code></pre>

<p><small>lib/Wishlist.pm</small></p>

<p>There is an application attribute which holds the Mojo::SQLite instance.
Its initializer pulls the name of the database file from configuration or defaults to <code>wishlist.db</code> as before.
Unlike with DBM::Deep we now also have to tell it where to find the migrations file.
To target these files we use the application&#39;s <a href="http://mojolicious.org/perldoc/Mojolicious#home"><code>home</code></a> object and <a href="http://mojolicious.org/perldoc/Mojo/File">Mojo::File</a> which is a topic for another day.</p>

<p>The application&#39;s <code>startup</code> method establishes a <code>model</code> helper which creates an instance of Wishlist::Model and attaches the Mojo::SQLite instance to it.
This is a very important concept because this very thin helper is what ties the model into the application as a whole.
Any part of the application that needs data from the model ends up using this helper.</p>

<p>For example, there are still the <code>user</code> and <code>users</code> helpers that behave just as their counterparts from yesterday.
This time however they work via the model to do their business.</p>

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  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/2017/12/19/day-19-make-your-app-installable/" property="og:url">
  <meta content="Day 19: Make Your App Installable" property="og:title">
    <meta content="A few simple changes to make your application installable." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  view on Meta::CPAN

N.B. there is even a DarkPAN tool written using Mojolicious called <a href="https://metacpan.org/pod/App::opan">opan</a>.</p>

<p>And hey if you needed even more more reason, it cleans up your project root directory somewhat too!</p>

              </section>
              <section id="section-2">
                  <h2>The Challenge</h2>

<p>The hardest part of this process is managing static files.
There are several Perl install tools that each handle static files slightly differently.
Some will happily bundle any files in the project directory, others will only bundle Perl files by default.</p>

<p>Then once installed that bundle location is usually different that what it was during development (even relative to the project).
It can be a challenge to make it so that you application can find those file both during development and after installation.</p>

<h2>TIMTOWTDI</h2>

<p>Before we get started, I should mention that there are many ways to accomplish this task.
If your application small and already located in a <code>script</code> directory and has no external static files, you&#39;re probably already done!
That isn&#39;t the case for most applications of any real size, however.</p>

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  view on Meta::CPAN


<pre><code>use File::ShareDir::Install;
install_share &#39;share&#39;;
</code></pre>

<p>And <a href="https://metacpan.org/pod/Dist::Zilla">Dist::Zilla</a> has a <a href="https://metacpan.org/pod/Dist::Zilla::Plugin::ShareDir">plugin</a>.</p>

<pre><code>[ShareDir]
</code></pre>

<p>whose default is <code>share</code>.
I&#39;m sure other installers have ways to handle it too.</p>

<h2>File::Share Saves the Day</h2>

<p>Since we earlier moved the location of templates and static files, we now need to tell the application where we put them.
The major innovation that makes this method possible and so painless is the module <a href="https://metacpan.org/pod/File::Share">File::Share</a>.
While it isn&#39;t <a href="https://metacpan.org/pod/File::ShareDir">the original module</a> used to locate share directories after installation, it is the best one since it also works before hand during development too.
To do so it uses the heuristic of looking to see if a directory exists named <code>share</code> in that particular location (and a few checks for sanity), thus the second reason we call the directory that.</p>

<p>When used we get the absolute path to the share directory of your distribution.

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  view on Meta::CPAN

</code></pre>

<p>Or you could allow them to be relative to the home.</p>

<pre><code>$templates = Mojo::File-&gt;new($templates);
if ($templates-&gt;is_abs) {
  $templates = $app-&gt;home-&gt;child(&quot;$templates&quot;);
}
</code></pre>

<p>Set these directories before the bundled versions so that if a file exists within them, they get used while defaulting to the bundled ones otherwise.</p>

<pre><code>if (-d $templates) {
  unshift @{ $app-&gt;renderer-&gt;paths }, $templates;
}
</code></pre>

<p>Note that you could use that kind of process to allow other configured files to be relative to the home.
The application home is a great place to put data like a sqlite database; the sqlite database file seen earlier, which as show had to be an absolute path.
Indeed such a transform exists already exists in the Wishlist app but was omitted above for clarity.</p>

devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing  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/2017/12/20/day-20-practical-testing/" property="og:url">
  <meta content="Day 20: Practical Testing" property="og:title">
    <meta content="Some practical tricks for testing real-world applications." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack  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="@CandyAngel_Nay" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/21/day-21-virtually-a-lumberjack/" property="og:url">
  <meta content="Day 21: Virtual(ly a) Lumberjack" property="og:title">
    <meta content="Using the Mojo toolkit to help reverse-engineer HMDs" property="og:description">

devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api  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="@jhthorsen" name="twitter:creator">
  <meta content="https://mojolicious.io/blog/2017/12/22/day-22-how-to-build-a-public-rest-api/" property="og:url">
  <meta content="Day 22: A RESTful API with OpenAPI" property="og:title">
    <meta content="How to build a public REST API for your Mojolicious application" property="og:description">

devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit  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/2017/12/23/day-23-one-liners-for-fun-and-profit/" property="og:url">
  <meta content="Day 23: One-Liners for Fun and Profit" property="og:title">
    <meta content="For those times when you just need a quick check or example." property="og:description">

devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up  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/2017/12/24/day-24-release-and-wrap-up/" property="og:url">
  <meta content="Day 24: Release and Wrap-Up" property="og:title">
    <meta content="The Wishlist app is released to CPAN and a wrap-up for the advent calendar." property="og:description">



( run in 0.610 second using v1.01-cache-2.11-cpan-0a6323c29d9 )