Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017

 view release on metacpan or  search on metacpan

LICENSE  view on Meta::CPAN

    you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish, that
    in whole or in part contains the Program or any part thereof, either
    with or without modifications, to be licensed at no charge to all
    third parties under the terms of this General Public License (except
    that you may choose to grant warranty protection to some or all
    third parties, at your option).

    c) If the modified program normally reads commands interactively when
    run, you must cause it, when started running for such interactive use
    in the simplest and most usual way, to print or display an
    announcement including an appropriate copyright notice and a notice
    that there is no warranty (or else, saying that you provide a
    warranty) and that users may redistribute the program under these
    conditions, and telling the user how to view a copy of this General
    Public License.

    d) You may charge a fee for the physical act of transferring a
    copy, and you may at your option offer warranty protection in
    exchange for a fee.

LICENSE  view on Meta::CPAN

                     END OF TERMS AND CONDITIONS

        Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.

  To do so, attach the following notices to the program.  It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

LICENSE  view on Meta::CPAN

    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA


Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19xx name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License.  Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your

MANIFEST  view on Meta::CPAN

# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.010.
Changes
LICENSE
MANIFEST
META.json
META.yml
Makefile.PL
README
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up
devscripts/update

devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started  view on Meta::CPAN

  <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">
    <meta content="https://mojolicious.io/blog/2017/12/01/day-1-getting-started/1280px-Colorado_Springs_Hot_Air_Balloon_Competition.jpg" property="og:image">
    <meta content="summary_large_image" name="twitter:card">

  <script src="/theme/js/modernizr.js"></script>

      <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/sunburst.min.css" rel="stylesheet">

  <title>Day 1: Getting Started - mojolicious.io</title>
  <meta content="Joel Berger" name="author">
  <meta content="Statocles 0.093" name="generator">
  <link href="/static/favicon.ico" rel="shortcut icon">

devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started  view on Meta::CPAN


                  <time class="date" datetime="2017-12-01">Dec  1, 2017</time>
                  

              </p>

            </div>

              <div class="post-thumb">
                <!-- theme suggests 1300x500 -->
                <img alt="hot air ballons" src="/blog/2017/12/01/day-1-getting-started/1280px-Colorado_Springs_Hot_Air_Balloon_Competition.jpg">
              </div>

            <div class="post-content">

              <section id="section-1">
                  <h2>Start at the Beginning</h2>

<p>In this Advent Calendar series, some posts will be introductory, some will be advanced, some will be on new features.
Who knows what could be next?
But for now let&#39;s ensure a level playing field by working out how to get started.</p>

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

<p>Well, <a href="http://mojolicious.org">Mojolicious</a> is really two things.
First it is a powerful web-focused toolkit called Mojo.
Second it is a powerful web framework called Mojolicious.
The Mojolicious framework is built using the Mojo toolkit.</p>

devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started  view on Meta::CPAN

<p>or, you can install using any cpan client (we like <a href="https://metacpan.org/pod/App::cpanminus"><code>cpanm</code></a>) or using your system&#39;s package manager.</p>

<h2>Your First Application</h2>

<p>In the grand tradition of programming, the first thing we need to do is to run a hello world application.</p>

<p>Save the following as <code>hello.pl</code></p>

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

<p>This script simply</p>

<ul>
<li>imports Mojolicious (the lite version)</li>
<li>defines a GET handler to respond to requests with a unicode version of hello world</li>
<li>starts the application</li>
</ul>

<p>But before that&#39;s any use to us, we have to start a web server.</p>

<h2>Running Your Application</h2>

<p>Mojolicious applications (as we&#39;ll see in another post) are more than just web servers.
In order to use them as one, we need to start it as a web server.</p>

<p>Mojolicious comes with four built-in servers</p>

<ul>
<li><code>daemon</code>, single-threaded, the basis of all the others</li>
<li><code>morbo</code>, the development server, restarts on files changes</li>
<li><code>prefork</code>, optimized production server</li>
<li><code>hypnotoad</code>, like prefork but with hot-restart capability</li>
</ul>

<p><code>daemon</code> and <code>prefork</code> are application commands and are run like</p>

<pre><code>perl hello.pl daemon
</code></pre>

<p>These will work, but for development let&#39;s use <code>morbo</code>.
<code>morbo</code> and <code>hypnotoad</code> are their own scripts which take the application as an argument.
Start it by running</p>

<pre><code>morbo hello.pl
</code></pre>

<p>When it starts it should tell you to visit <code>http://127.0.0.1:3000</code>.
Open that url in your browser.
Your first advent treat should be waiting for you!</p>

<h2>Getting Help</h2>

<p>The documentation is available at <a href="http://mojolicious.org/perldoc">http://mojolicious.org/perldoc</a>.
You are especially encouraged to read the Tutorial and Guides in the order suggested there.</p>

<p>Read it carefully, unlike some documentation, it is written for brevity and conciseness.
Users accustomed to skimming documentation filled with fluff might need a second take.</p>

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

Let&#39;s look a little closer to see how it works</p>

              </section>
              <section id="section-2">
                  <h2>Using the Stash for Rendering Text</h2>

<p>In the previous post, we discussed the most simple &#39;Hello world&#39; application.</p>

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

<p>While that is a very simple working case, a more common example would look like</p>

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

<p>In this example, the <code>GET /</code> request is handled by an &quot;action callback&quot;.
A callback is function reference, intended to be called in the future; in this case the callback will be called when a client requests comes in that matches that type of request.</p>

<p>An action is called with one argument, called the <a href="http://mojolicious.org/perldoc/Mojolicious/Controller">controller</a>.
The controller is an object that represents our application&#39;s interaction with the current transaction.
It contains an object representing the <a href="http://mojolicious.org/perldoc/Mojo/Transaction">transaction</a>, which in turn holds objects for the <a href="http://mojolicious.org/perldoc/Mojo/Message/Request">request</a> and <a href="http://mojoli...
It has methods which can be used to generate responses, one of which is <code>render</code>, which you see above.
Here you see that we are going to render some text.</p>

<p>In truth though, most of the arguments to render are actually just merged into the stash.
Indeed the above example is the same as</p>

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

<p>What you see now is that Mojolicious looks to the stash to see how to render a response.
And indeed, if you don&#39;t call render, but it has enough information to render a response in the stash already, it will just do so.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/&#39; =&gt; sub {
  my $c = shift;
  $c-&gt;stash(text =&gt; &#39;Hello 🌍 World!&#39;);
};
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>

<pre><code>use Mojolicious::Lite;
get &#39;/&#39; =&gt; {text =&gt; &#39;hello 🌍 world!&#39;};
app-&gt;start;
</code></pre>

<h2>Using Placeholders</h2>

<p>We can take that example further and show how to make a more interesting greeting application, this time taking a name.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/:name&#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>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;);
};
app-&gt;start;
</code></pre>

<p>Or if you&#39;ll let me use a concept without fully introducing it, here is a template in the data section of your script.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/:name&#39; =&gt; {name =&gt; &#39;🌍 world!&#39;} =&gt; sub {
  my $c = shift;
  $c-&gt;render(&#39;hello&#39;);
};
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>

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

<p>Additionally all keys like <code>mojo.*</code> are reserved for internal use.
Most of those values are either useful in routing, templating, or rendering.</p>

<p>You&#39;ve seen <code>text</code>, which render a string by utf8 encoding it.
To render data in a binary format (or just text without being utf8 encoded) use the <code>data</code> key.
Both of those, as well as the <code>template</code> will be rendered with the content type <code>text/html</code>.
To use something different, you can specify it with the <code>format</code> key.</p>

<pre><code>use Mojolicious::Lite;
get &#39;/&#39; =&gt; {text =&gt; &#39;hello 🌍 world!&#39;, format =&gt; &#39;txt&#39;};
app-&gt;start;
</code></pre>

<p>Where the understood formats are listed <a href="http://mojolicious.org/perldoc/Mojolicious/Types#DESCRIPTION">here</a> (and more <a href="http://mojolicious.org/perldoc/Mojolicious/Types#DESCRIPTION">can be added</a>).</p>

<p>The others all have meanings, some of which you can probably figure out on your own, but this post has gone on long enough.
Those others will have to wait for another day.</p>

              </section>
              <small><p><a href="https://pixabay.com/en/bag-classic-leather-messenger-bag-1854148/">Image</a> in the Public Domain.</p>
</small>

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

                      <div class="about">
                        <h5>Joel Berger</h5>
                        <p>Joel has Ph.D. in Physics from the University of Illinois at Chicago.
He an avid Perl user and <a href="https://metacpan.org/author/JBERGER">author</a> and is a member of the Mojolicious Core Team.</p>

                      </div>

                  </div>

              <ul class="post-nav cf">
                  <li class="prev"><a href="/blog/2017/12/01/day-1-getting-started/index.html" rel="prev"><strong>Previous Article</strong> Day 1: Getting Started</a></li>
                  <li class="next"><a href="/blog/2017/12/03/day-3-using-named-routes/index.html" rel="next"><strong>Next Article</strong> Day 3: Using Named Routes </a></li>
              </ul>

            </div>

        </article>


      </div>

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

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.
It just had a main page and a page for him and his employees.
Since it was so simple, he didn&#39;t have any deeper paths, just <code>/</code> for the homepage and <code>/:name</code> for staff pages.
Of course what I&#39;m going to show you is a simplification, I can&#39;t show you Santa&#39;s full site, for obvious reasons.</p>

<pre><code>use Mojolicious::Lite;

get &#39;/:name&#39; =&gt; {template =&gt; &#39;staff&#39;} =&gt; &#39;staff&#39;;
get &#39;/&#39; =&gt; {template =&gt; &#39;home&#39;} =&gt; &#39;home&#39;;

app-&gt;start;

__DATA__

@@ staff.html.ep
&lt;p&gt;This is &lt;%= ucfirst $name %&gt;.&lt;/p&gt;

@@ home.html.ep
&lt;p&gt;Welcome to The North Pole!&lt;/p&gt;

&lt;p&gt;

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

The problem was that he hadn&#39;t planned his urls for that.
He could just keep all the existing ones and add a special cased <code>/toys</code> route, but it would look silly that way.
What he really needed to do was move all of the staff pages to keep the urls consistent.</p>

<pre><code>use Mojolicious::Lite;

get &#39;/toy/:toy_name&#39; =&gt; {template =&gt; &#39;toy&#39;} =&gt; &#39;toy&#39;;
get &#39;/meet/:name&#39; =&gt; {template =&gt; &#39;staff&#39;} =&gt; &#39;staff&#39;;
get &#39;/&#39; =&gt; {template =&gt; &#39;home&#39;} =&gt; &#39;home&#39;;

app-&gt;start;

__DATA__

@@ toy.html.ep
&lt;p&gt;Look at this amazing &lt;%= $toy_name %&gt;.&lt;/p&gt;

@@ staff.html.ep
&lt;p&gt;This is &lt;%= ucfirst $name %&gt;.&lt;/p&gt;

@@ home.html.ep

devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app  view on Meta::CPAN

Nothing could be further from the truth.
<a href="http://mojolicious.org/perldoc/Mojolicious/Lite">Mojolicious::Lite</a> is a very tiny wrapper around the so-called &quot;Full&quot; app architecture, giving it the approachable keyword syntax.</p>

<p>Because it is much nicer to have concise single-file examples for documentation most of Mojolicious&#39; documentation uses Lite syntax most of the time.
It is understandable that people worry about migrating (or as we call it &quot;growing&quot;) even once their apps would benefit from Object-Oriented structure; after all the docs seem geared towards Lite apps.
However, let those fears go, the transition is easy.
And once you understand it, the documentatation examples are trivial to translate.</p>

<p>Plus, Mojolicious comes with two forms of help when transitioning.
The first is the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Growing">Growing Guide</a> which covers everything this post will but from the perspective of porting an existing application (which I won&#39;t duplicate here).
The second is the <a href="http://mojolicious.org/perldoc/Mojolicious/Command/inflate">inflate command</a> which can even start you on the process by moving your templates from the data section and into their own files.</p>

<p>That said, in order to futher demystify things, I&#39;m going to cover some of the differences and pull back the curtain on the Lite syntax itself.</p>

              </section>
              <section id="section-2">
                  <h2>Let Me Convince You</h2>

<p>After repeated attempts to convince people that there is very little difference between the two, I&#39;ve found that there is one really great way to turn the conversation.
I show them the code.
No really <a href="https://github.com/kraih/mojo/blob/master/lib/Mojolicious/Lite.pm">take a look</a>.
As of this writing, Mojolicious::Lite is only 37 lines of code (as computed by David A. Wheeler&#39;s <a href="https://www.dwheeler.com/sloccount/">SLOCCount</a>)!
How much difference could there be in 37 lines of code?</p>

<p>Ok now that you believe me, let&#39;s talk about those few differences.</p>

<h2>The Script and the Class</h2>

<p>In a Lite script, your application logic lives right there in the script.
If a Full app, your logic goes in a separate class, mostly in the <code>startup</code> method, but remove <code>app-&gt;start</code> line.
While the first argument to a method (the invocant) is usually called <code>$self</code>, and you will see that, to keep things clear in this series I will always use <code>$app</code>.
So we have:</p>

<pre><code>sub startup {
  my $app = shift;
  ... # the rest of what was your script
}
</code></pre>

<p>Meanwhile the script that is run is just a few lines that start the app.
That script is always the same thing, having nothing to do with your app but the name of the class to invoke.
I just use the one at the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Growing#Script">end of the Growing Guide</a>.</p>

<h2>The Keywords</h2>

<p>Now that the code lives in the right place, it needs to be translated to be Object Oriented.
The first step is to place the logic into a method called <code>startup</code>, which takes the application object as its first argument.</p>

<p>There are really three types of keywords, those that are the application object or methods on the application, those that are methods on the router, and <code>group</code>.</p>

<p>The <code>app</code> keyword is just that invocant from before, so <code>app</code> becomes <code>$app</code>.
The keywords <code>helper</code>, <code>hook</code>, and <code>plugin</code> are just methods on the app, so <code>plugin ...</code> becomes <code>$app-&gt;plugin(...)</code>, etc.</p>

<p>The routing methods</p>

<ul>
<li><code>any</code></li>

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


              <div class="post-thumb">
                <!-- theme suggests 1300x500 -->
                <img alt="Space shuttle Atlantis liftoff" src="/blog/2017/12/05/day-5-your-apps-built-in-commands/1200px-Rocket_prolant.jpg">
              </div>

            <div class="post-content">

              <section id="section-1">
                  <p>I mentioned at the outset of this series that Mojolicious applications are more than just web servers.
I then showed how you can start a web server using the <a href="http://mojolicious.org/perldoc/Mojolicious/Command/daemon"><code>daemon</code></a> or <a href="http://mojolicious.org/perldoc/Mojolicious/Command/prefork"><code>prefork</code></a> comman...
In the previous post, I mentioned an <a href="http://mojolicious.org/perldoc/Mojolicious/Command/inflate"><code>inflate</code></a> command that can help you with growing your app from Lite to Full.</p>

<p>But there are other commands, built right in to your app, that can help you be more productive right away!</p>

              </section>
              <section id="section-2">
                  <h2>Command Basics</h2>

<p>Before I start, I want to briefly discuss the <a href="http://mojolicious.org/perldoc/mojo"><code>mojo</code></a> application/script that comes bundled with the Mojolicious distribution.
This command is a tiny Mojolicious app (actually another &quot;hello world&quot;) which can be thought of as the &quot;null app&quot;.
The built-in commands work both for your application and this null one, so use whichever is more appropriate.
When it doesn&#39;t matter which application runs a command, you can just use <code>mojo</code>.</p>

<p>Each command comes with a one-line description and a (possibly multi-line) usage statement.
To see the available commands, run <code>mojo help</code> and you will see all of the commands and their description.
You should see something like this:</p>

<pre><code>$ mojo help
Usage: APPLICATION COMMAND [OPTIONS]

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

<h3>The version Command</h3>

<p>The <a href="http://mojolicious.org/perldoc/Mojolicious/Command/version"><code>version</code></a> command is a simple utility to check your Mojolicious installation.</p>

<p>It outputs your current version of Perl and Mojolicious along with any installed optional libraries.
For example, you&#39;ll want to install <a href="https://metacpan.org/pod/IO::Socket::SSL">IO::Socket::SSL</a> if you want to fetch or serve sites over HTTPS.
It then checks to see if there is an updated version of Mojolicious available.</p>

<h3>The routes Command</h3>

<p>Once you started writing your application, you might want to introspect it a little bit, especially for debugging purposes.
The most straightforward command of that nature is <a href="http://mojolicious.org/perldoc/Mojolicious/Command/routes"><code>routes</code></a>.
Simply run it on your app to see what routes you have defined.</p>

<p>For example, we can run it on Santa&#39;s application from <a href="/blog/2017/12/03/day-3-using-named-routes">day 3</a>.</p>

<pre><code>$ perl santa.pl routes
/toy/:toy_name  GET  &quot;toy&quot;
/meet/:name     GET  &quot;staff&quot;
/               GET  &quot;home&quot;
</code></pre>

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

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?
I&#39;ve put the entire thing on github at <a href="https://github.com/jberger/MyWeatherApp">https://github.com/jberger/MyWeatherApp</a>.
I&#39;m going to copy portions of the code into this article for clarity, but consider that that site is probably the most up to date.</p>

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

From there you can get an API key, which will need to go in a configuration file.
Once you have it create a configuration file called <code>myweatherapp.conf</code> and fill it in like so:</p>

<pre><code>{
  appid =&gt; &#39;XXXXXXXXX&#39;,
}
</code></pre>

<h3>The Script</h3>

<p>First you will need the script, a wrapper to start the application.
Let&#39;s call it <a href="https://github.com/jberger/MyWeatherApp/blob/master/bin/myweatherapp">bin/myweatherapp</a>.
It should be exactly</p>

<pre><code>#!/usr/bin/env perl

use strict;
use warnings;

use FindBin;
BEGIN { unshift @INC, &quot;$FindBin::Bin/../lib&quot; }
use Mojolicious::Commands;

# Start command line interface for application
Mojolicious::Commands-&gt;start_app(&#39;MyWeatherApp&#39;);
</code></pre>

<h3>The Model</h3>

<p>Now let&#39;s make a model class.
A model is the business logic of any application.
It knows how to do the important work and should be free of anything to do with your actual site.</p>

<p>We&#39;ll store it in <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp/Model/Weather.pm">lib/MyWeatherApp/Model/Weather.pm</a>.</p>

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

It then requests the data from OpenWeatherMap.
The <code>result</code> method dies if there is a connection error.
For brevity I&#39;m being a little lax on other error checking.</p>

<p>The other two methods insert data into sqlite and recall it out again, again based on a search term.
This is basically just caching the data from OpenWeatherMap
Again for brevity I&#39;m only storing the term, the time, and the temperature.</p>

<h3>The Application</h3>

<p>Once we have an application we can start to make try it out a little.
The main class is at <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp.pm">lib/MyWeatherApp.pm</a>.</p>

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

use Mojo::SQLite;
use MyWeatherApp::Model::Weather;

has sqlite =&gt; sub {
  my $app = shift;
  my $file = $app-&gt;config-&gt;{file} // &#39;weather.db&#39;;
  my $sqlite = Mojo::SQLite-&gt;new(&quot;dblite:$file&quot;);
  $sqlite-&gt;migrations-&gt;from_data;
  return $sqlite;
};

sub startup {
  my $app = shift;

  $app-&gt;moniker(&#39;myweatherapp&#39;);
  $app-&gt;plugin(&#39;Config&#39;);

  push @{ $app-&gt;commands-&gt;namespaces }, &#39;MyWeatherApp::Command&#39;;

  $app-&gt;helper(&#39;weather&#39; =&gt; sub {
    my $c = shift;
    my $config = $c-&gt;app-&gt;config;

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

  temperature REAL NOT NULL
);

-- 1 down

DROP TABLE IF EXISTS weather;
</code></pre>

<p>This class does a few important things.
It defines a connection to a SQLite configurable database.
It then defines the all-important <code>startup</code> method.
This method is what is run when the application is first instantiated.</p>

<p>It defines the moniker, which in turn defines the configuration file name.
It then loads configuration; this is necessary since we need your appid for OpenWeatherMap.
It tells the app that we are going to define some application-specific commands under <a href="https://github.com/jberger/MyWeatherApp/tree/master/lib/MyWeatherApp/Command">MyWeatherApp::Command</a>.</p>

<p>Then there&#39;s something I haven&#39;t shown you before, a helper.
Helpers are like methods but they are available to the app and the controllers and even to templates.
They always receive a controller, even if they are called on the app.
Helpers are very useful for tying parts of your application together.

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


<p>Now that all that is done, we can try it out!</p>

<pre><code>$ perl bin/myweatherapp eval -V &#39;app-&gt;weather-&gt;fetch(&quot;Chicago&quot;)&#39;
</code></pre>

<p>If you&#39;ve configured your appid correctly you should get a dump of weather data about my home city.</p>

<h3>The Commands</h3>

<p>Well finally we have arrived at the whole reason we started this endeavour: the commands!</p>

<p>This example has two different uses for commands.
The first use is to deploy our database schema or to upgrade it should it change.
This would likely be run by an operations manager or configuration management software when installing or upgrading.
The command exists in <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp/Command/deploy.pm">lib/MyWeatherApp/Command/deploy.pm</a>.</p>

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

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

devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page  view on Meta::CPAN


<pre><code class="hljs"><span class="hljs-comment"># myapp.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
get &#39;<span class="hljs-string">/*path</span>&#39;, { path =&gt; &#39;<span class="hljs-string">index</span>&#39; }, <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">stash</span>( &#39;<span class="hljs-string">path</span>&#39; ),
        variant =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">app</span>-&gt;<span class="hljs-type">mode</span>,
    );
};
app-&gt;start;
</code></pre>

<p>The mode is set by passing the <code>-m beta</code> option to Mojolicious&#39;s <code>daemon</code> or
<code>prefork</code> command.</p>

<pre><code>$ perl myapp.pl daemon -m beta
</code></pre>

<p>This gives me the <a href="http://beta.cpantesters.org">new landing page for beta.cpantesters.org</a>.</p>

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
get &#39;<span class="hljs-string">/servers</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        json =&gt; [
            { ip =&gt; &#39;<span class="hljs-string">10.0.0.1</span>&#39;, os =&gt; &#39;<span class="hljs-string">Debian 9</span>&#39; },
            { ip =&gt; &#39;<span class="hljs-string">10.0.0.2</span>&#39;, os =&gt; &#39;<span class="hljs-string">Debian 8</span>&#39; }
        ],
    );
};
app-&gt;start;
</code></pre>

<p>Now I can fetch that JSON response by starting the web application and
going to <code>/servers</code> or by using the <code>get</code> command:</p>

<pre><code>$ perl test-api.pl get /servers
[{&quot;ip&quot;:&quot;10.0.0.1&quot;,&quot;os&quot;:&quot;Debian 9&quot;},{&quot;ip&quot;:&quot;10.0.0.2&quot;,&quot;os&quot;:&quot;Debian 8&quot;}

$ perl test-api.pl daemon
Server available at http://127.0.0.1:3000
</code></pre>

<p>That&#39;s pretty easy and shows how easy Mojolicious can be to get started.
But I have dozens of routes in my application! Combined with all the
possible data and its thousands of routes. How do I make all of them
work without copy-pasting code for every single route?</p>

<p>Let&#39;s match the whole path of the route and then create a template with
the given path. Mojolicious lets us match the whole path using the <code>*</code>
placeholder in the route path. Then we can use that path to look up the
template, which we&#39;ll put in the <code>__DATA__</code> section.</p>

<pre><code class="hljs"><span class="hljs-comment"># test-api.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
any &#39;<span class="hljs-string">/*path</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">stash</span>( &#39;<span class="hljs-string">path</span>&#39; ),
        <span class="hljs-function">format</span> =&gt; &#39;<span class="hljs-string">json</span>&#39;,
    );
};
app-&gt;start;
<span class="hljs-keyword">__DATA__</span>
@@ servers.json.ep
[
    { &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; },
    { &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }
]
</code></pre>

<p>Again, we can use the <code>get</code> command to test that we get the right data:</p>

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN


<pre><code class="hljs"><span class="hljs-comment"># test-api.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
any &#39;<span class="hljs-string">/*path</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">stash</span>( &#39;<span class="hljs-string">path</span>&#39; ),
        <span class="hljs-function">format</span> =&gt; &#39;<span class="hljs-string">json</span>&#39;,
    );
};
app-&gt;start;
<span class="hljs-keyword">__DATA__</span>
@@ servers.json.ep
[
    &lt;%== include &#39;servers/1&#39; %&gt;,
    &lt;%== include &#39;servers/2&#39; %&gt;
]
@@ servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN


<pre><code class="hljs"><span class="hljs-comment"># test-api.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
any &#39;<span class="hljs-string">/*path</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-function">join</span>( &#39;<span class="hljs-string">/</span>&#39;, <span class="hljs-function">uc</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">req</span>-&gt;<span class="hljs-type">m...
        <span class="hljs-function">format</span> =&gt; &#39;<span class="hljs-string">json</span>&#39;,
    );
};
app-&gt;start;
<span class="hljs-keyword">__DATA__</span>
@@ GET/servers.json.ep
[
    &lt;%== include &#39;GET/servers/1&#39; %&gt;,
    &lt;%== include &#39;GET/servers/2&#39; %&gt;
]
@@ GET/servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ GET/servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }
@@ POST/servers.json.ep
{ &quot;status&quot;: &quot;success&quot;, &quot;id&quot;: 3, &quot;server&quot;: &lt;%== $c-&gt;req-&gt;body %&gt; }
</code></pre>

<p>Now all our template paths start with the HTTP request method (<code>GET</code>),
allowing us to add different routes for <code>POST</code> requests and other HTTP
methods.</p>

<p>We also added a <code>POST/servers.json.ep</code> template that shows us getting
a successful response from adding a new server via the API. It even
correctly gives us back the data we submitted, like our original API
might do.</p>

<p>We can test our added <code>POST /servers</code> method with the <code>get</code> command
again:</p>

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

<pre><code class="hljs"><span class="hljs-comment"># test-api.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
any &#39;<span class="hljs-string">/*path</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-function">join</span>( &#39;<span class="hljs-string">/</span>&#39;, <span class="hljs-function">uc</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">req</span>-&gt;<span class="hljs-type">m...
        variant =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">app</span>-&gt;<span class="hljs-type">mode</span>,
        <span class="hljs-function">format</span> =&gt; &#39;<span class="hljs-string">json</span>&#39;,
    );
};
app-&gt;start;
<span class="hljs-keyword">__DATA__</span>
@@ GET/servers.json.ep
[
    &lt;%== include &#39;GET/servers/1&#39; %&gt;,
    &lt;%== include &#39;GET/servers/2&#39; %&gt;
]
@@ GET/servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ GET/servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

any &#39;<span class="hljs-string">/*path</span>&#39; =&gt; <span class="hljs-keyword">sub </span>{
    <span class="hljs-keyword">my</span> ( <span class="hljs-type">$c</span> ) = <span class="hljs-type">@_</span>;
    <span class="hljs-comment"># Allow preflight OPTIONS request for XmlHttpRequest to succeed</span><span class="hljs-comment">
</span>    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">rendered</span>( <span class="hljs-number">204</span> ) <span class="hljs-keyword">if</span> <span class="hljs-type">$c</span>-&gt;<span...
    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        template =&gt; <span class="hljs-function">join</span>( &#39;<span class="hljs-string">/</span>&#39;, <span class="hljs-function">uc</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">req</span>-&gt;<span class="hljs-type">m...
        variant =&gt; <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">app</span>-&gt;<span class="hljs-type">mode</span>,
        <span class="hljs-function">format</span> =&gt; &#39;<span class="hljs-string">json</span>&#39;,
    );
};
app-&gt;start;
<span class="hljs-keyword">__DATA__</span>
@@ GET/servers.json.ep
[
    &lt;%== include &#39;GET/servers/1&#39; %&gt;,
    &lt;%== include &#39;GET/servers/2&#39; %&gt;
]
@@ GET/servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ GET/servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }

devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test  view on Meta::CPAN

my $t = Test::Mojo-&gt;new($app);
</code></pre>

<h2>How It Works</h2>

<p>Test::Mojo contains an instance of <a href="http://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> which it uses to make requests.
What many people don&#39;t know is that Mojo::UserAgent can act as a <a href="http://mojolicious.org/perldoc/Mojo/UserAgent#server">server</a> for a Mojo application!
When the useragent gets a request for a relative url, (i.e. without a protocol or host), it uses this embedded server to fulfil the request.
This isn&#39;t just useful for Test::Mojo, but that is its primary purpose.</p>

<p>Many testing frameworks in Perl start some kind of fake server, mimicing the request/response cycle.
That works in blocking scenarios, but once you add non-blocking to the mix there is no substitute for a real server.
The useragent&#39;s server actually starts up on two different (local-only) ports, one for blocking requests and one for non-blocking.
Most people don&#39;t need to worry about that but for doing very complex things, knowing that might help.</p>

<p>When you make a request with Test::Mojo, its useragent will make the request, whether locally or externally and return to it the <a href="http://mojolicious.org/perldoc/Mojo/Transaction">transaction</a> object.
Test::Mojo then keeps that object in its <a href="http://mojolicious.org/perldoc/Test/Mojo#tx">tx</a> attribute for subsequent tests until the next request is made.
If none of the test methods that it provides will allow you to test what you need, you are welcome to fish the data out of that object.</p>

<p>When you use Test::More, when a test fails the test function returns a false value, allowing you to take some action on failure.</p>

<pre><code>is $answer, 42 or diag &#39;Deep Thought was wrong&#39;;
</code></pre>

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


any &#39;/table&#39; =&gt; sub {
  my $c = shift;
  my $data = [
    [qw/a b c d/],
    [qw/e f g h/],
  ];
  $c-&gt;reply-&gt;table($data);
};

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

<p>Of course under the hood this is using Content Negotiation and several other modules to provide CSV, HTML, JSON, text, XLS, and XLSX outputs.
It is configurable via several stash values that might be set.
If you&#39;d like to dig into that code to see how it works, please feel free.</p>

              </section>
              <small><p><a href="https://pxhere.com/en/photo/946984">Image</a> in the Public Domain.</p>
</small>

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

  &#39;some binary content&#39;
);
</code></pre>

<p>is equivalent to</p>

<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;]});

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

Adding content generators is easy too!
As seen in the <a href="http://mojolicious.org/perldoc/Mojo/UserAgent/Transactor#add_generator">documentation</a> adding a generator is as simple as adding a callback that will build the request.</p>

<p>To motivate this discussion, I&#39;ll introduce another module.
At work, I had to use <a href="http://xmlrpc.scripting.com/spec.html">XML-RPC</a> to interact with a remote service.
XML-RPC defines an XML schema for asking the service to call a method, just as you would locally, by method name and with some arguments.
It then returns a result or fault (exception).
These responses also contain arguments, that is to say, the response data.</p>

<p>Personally I find it is much easier to learn something new by seeing how it works.
I pulled <a href="https://metacpan.org/pod/XMLRPC::Fast">XMLRPC::Fast</a> from CPAN and started inspecting the code.
It started to make sense to me, but I noticed that it used <a href="https://metacpan.org/pod/XML::Parser">XML::Parser</a> for its XML.
Since Mojolicious has tools for that, I decided to continue learning by porting the code to <a href="http://mojolicious.org/perldoc/Mojo/Template">Mojo::Template</a> and <a href="http://mojolicious.org/perldoc/Mojo/DOM">Mojo::DOM</a>.</p>

<p>By the time I finished I had completely rewritten the module and decided that perhaps others would benefit from it in environments already using the Mojo stack.
So with much thanks to XMLRPC::Fast and its author Sébastien Aperghis-Tramoni I released my own as <a href="https://metacpan.org/pod/Mojo::XMLRPC">Mojo::XMLRPC</a>.
My module (as did the original) only has functions that build the XML payloads.
Therefore, to make a simple request, pass the result of encoding as XML-RPC as the body of a request, like so</p>

<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Base</span> -strict;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::UserAgent</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::XMLRPC</span> qw[encode_xmlrpc decode_xmlrpc];

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

  -&gt;dom
  -&gt;at(&#39;title&#39;)
  -&gt;text;
</code></pre>

<p>Perhaps this is too much adherence to fluent interfaces but as you progress, getting a nice long chain can really feel like an accomplishment!
If you&#39;d rather pass on this method, that&#39;s fine too.</p>

<h2>Roles</h2>

<p>While Mojo::Base has always tacitly supported roles via external modules, just recently has it started to offer explicit functionality in this area.
That said, I have lots ot say on the matter, so if you&#39;ll permit me, I&#39;m going to keep you in suspense until <a href="/blog/2017/12/13/day-13-more-about-roles">tomorrow</a>.</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=1592390">Image</a> by <a href="https://en.wikipedia.org/wiki/User:Mactographer" title="en:User:Mactographer">David Ball</a> - Original work, <a href="http://creativecomm...
</small>

              <p class="tags">
                <span>Tagged in </span>:
                  <a href="/blog/tag/advent/">advent</a>,
                  <a href="/blog/tag/fluent/">fluent</a>,

devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles  view on Meta::CPAN

You are encouraged to think of this new class as just the old one with more behaviors.
Usually however, the returned class is just used to instantiate an object anyway, so we can skip storing it.</p>

<pre><code>my $dom = Mojo::DOM-&gt;with_roles(&#39;Mojo::DOM::Role::FindCustom&#39;)-&gt;new(
  &#39;&lt;html&gt;&lt;a href=&quot;http://mojolicious.org&quot;&gt;Mojolicious&lt;/a&gt;&lt;/html&gt;&#39;
);
my $anchors = $dom-&gt;find_custom;
</code></pre>

<p>That looks less awkward, but now that&#39;s a lot to type before instantiating.
Remember that choice of package name for the role, turns out when you compose a role into a class whose name starts with <code>&lt;CLASS&gt;::Role::</code> there is a shortcut you can use.
Just replace all of that common prefix with a <code>+</code> sign!</p>

<pre><code>my $dom = Mojo::DOM-&gt;with_roles(&#39;+FindCustom&#39;)-&gt;new(
  &#39;&lt;html&gt;&lt;a href=&quot;http://mojolicious.org&quot;&gt;Mojolicious&lt;/a&gt;&lt;/html&gt;&#39;
);
my $anchors = $dom-&gt;find_custom;
</code></pre>

<p>And finally that looks quite nice.
It should be noted that this shortcut usage is different from some other plugin systems on CPAN, some of which use <code>+</code> to indicate a literal (ie non-shortened) class name.

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  view on Meta::CPAN

  });
}
</code></pre>

<p>If either the initial <code>get_p</code>, or either of the <code>then</code>s get rejected, then execution will skip to the <code>catch</code>. Another way to get this behaviour is to give a second code-ref to <code>then</code>.</p>

<p><code>finally</code> is given a code-ref which will be called with either the successful (i.e. resolved) value, or the failure (i.e. the rejection) value.</p>

<h2>The task at hand</h2>

<p>We have to synchronise the work between the multiple &quot;streams&quot; of execution, so that nothing gets missed, or done twice. Luckily, in the asynchronous but single-threaded context we have here, we can just pass around a reference to a sing...

<pre><code>#!/usr/bin/env perl

# cut down from https://stackoverflow.com/questions/15152633/perl-mojo-and-json-for-simultaneous-requests/15166898#15166898
sub usage { die &quot;Usage: bulkget-delay urlbase outdir suffixesfile\n&quot;, @_ };
# each line of suffixesfile is a suffix
# it gets appended to urlbase, then requested non-blocking
# output in outdir with suffix as filename

use Mojo::Base -strict;

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  view on Meta::CPAN

  my $url = $urlbase . $s;
  print &quot;getting $url\n&quot;;
  $ua-&gt;get_p($url)-&gt;then(sub {
    my ($tx) = @_;
    handle_result($outpath, $tx, $s);
    makepromise($urlbase, $ua, $suffixes, $outpath);
  });
}
</code></pre>

<p>Once each stream runs out of suffixes to process, it will finish. If we wanted to add the ability to add to the queue that could keep as many streams as we started, we would restructure so that each stream is subscribed to a queue, and if the queu...

<h2>See also</h2>

<ul>
<li>The Mojolicious Cookbook shows how to implement non-blocking requests <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Concurrent-blocking-requests">with promises</a>.</li>
<li>The new <a href="http://mojolicious.org/perldoc/Mojo/Promise">Mojo::Promise</a> class documentation.</li>
<li>This script is now available as a <code>Mojolicious::Command</code>: <a href="https://metacpan.org/pod/Mojolicious::Command::bulkget">Mojolicious::Command::bulkget</a>!</li>
</ul>

              </section>

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  view on Meta::CPAN

                      <div class="about">
                        <h5>Ed J</h5>
                        <p>Ed J (aka &quot;mohawk&quot; on IRC) has been using Perl for a long time. He is currently porting the reference <a href="http://graphql.org/">GraphQL</a> implementation from the <a href="http://graphql.org/graphql-js/">Java...

                      </div>

                  </div>

              <ul class="post-nav cf">
                  <li class="prev"><a href="/blog/2017/12/13/day-13-more-about-roles/index.html" rel="prev"><strong>Previous Article</strong> Day 13: More About Roles</a></li>
                  <li class="next"><a href="/blog/2017/12/15/day-15-start-a-new-yancy-app/index.html" rel="next"><strong>Next Article</strong> Day 15: Start a New Yancy App </a></li>
              </ul>

            </div>

        </article>


      </div>

      <div class="four columns end" id="secondary">

devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app  view on Meta::CPAN

  <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">
    <meta content="https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app/scaffold.jpg" property="og:image">
    <meta content="summary_large_image" name="twitter:card">

  <script src="/theme/js/modernizr.js"></script>

      <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/sunburst.min.css" rel="stylesheet">

  <title>Day 15: Start a New Yancy App - mojolicious.io</title>
  <meta content="Doug Bell" name="author">
  <meta content="Statocles 0.093" name="generator">
  <link href="/static/favicon.ico" rel="shortcut icon">

devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app  view on Meta::CPAN


                  <time class="date" datetime="2017-12-15">Dec 15, 2017</time>
                  

              </p>

            </div>

              <div class="post-thumb">
                <!-- theme suggests 1300x500 -->
                <img alt="Workers on a scaffold" src="/blog/2017/12/15/day-15-start-a-new-yancy-app/scaffold.jpg">
              </div>

            <div class="post-content">

              <section id="section-1">
                  <p><a href="http://metacpan.org/pod/Yancy">Yancy</a> is a new content management
plugin for the <a href="http://mojolicious.org">Mojolicious web framework</a>.
Yancy allows you to easily administrate your site’s content just by
describing it using <a href="http://json-schema.org">JSON Schema</a>. Yancy
supports <a href="http://metacpan.org/pod/Yancy::Backend">multiple backends</a>, so

devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app  view on Meta::CPAN

    },
};
</code></pre>

<p>Yancy will build us a rich form for our collection from the field types
we tell it. Some fields, like the <code>markdown</code> field, take additional
configuration: <code>x-html-field</code> tells the Markdown field where to save the
rendered HTML. There&#39;s plenty of customization options in <a href="http://metacpan.org/pod/Yancy#CONFIGURATION">the Yancy
configuration documentation</a>.</p>

<p>Now we can start up our app and go to <a href="http://127.0.0.1:3000/yancy">http://127.0.0.1:3000/yancy</a> to
manage our site&#39;s content:</p>

<pre><code>$ perl myapp.pl daemon
Server available at http://127.0.0.1:3000
</code></pre>

<p><img alt="Screen shot of adding a new blog item with Yancy" src="adding-item.png">
<img alt="Screen shot of Yancy after the new blog item is added" src="item-added.png"></p>

<p>Finally, we need some way to display our blog posts.  <a href="http://metacpan.org/pod/Mojolicious::Plugin::Yancy#HELPERS">Yancy provides

devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app  view on Meta::CPAN

    <span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(
        &#39;<span class="hljs-string">index</span>&#39;,
        posts =&gt; [ <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">yancy</span>-&gt;<span class="hljs-type">list</span>(
            &#39;<span class="hljs-string">blog</span>&#39;, {}, { order_by =&gt; { -desc =&gt; &#39;<span class="hljs-string">created</span>&#39; } },
        ) ],
    );
};
</code></pre>

<p>Now we just need an HTML template to go with our route! Here, I use the standard
<a href="http://getbootstrap.com/docs/4.0/getting-started/introduction/#starter-template">Bootstrap 4 starter template</a>
and add this short loop to render our blog posts:</p>

<pre><code>&lt;main role=&quot;main&quot; class=&quot;container&quot;&gt;
% for my $post ( @{ stash &#39;posts&#39; } ) {
    &lt;%== $post-&gt;{html} %&gt;
% }
&lt;/main&gt;
</code></pre>

<p><a href="04-template.pl">Now we have our completed application</a> and we can test
to see our blog post:</p>

<pre><code>$ perl myapp.pl daemon
Server available at http://127.0.0.1:3000
</code></pre>

<p><img alt="The rendered blog post with our template" src="blog-post.png"></p>

<p>Yancy provides a rapid way to get started building a Mojolicious
application (above Mojolicious’s already rapid development). Yancy
provides a basic level of content management so site developers can
focus on what makes their site unique.</p>

              </section>
              <small><p><a href="http://www.publicdomainpictures.net/view-image.php?image=6416">Image</a> in the public domain.</p>
</small>

              <p class="tags">
                <span>Tagged in </span>:

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

<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</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>;
  <span class="hljs-keyword">my</span> <span class="hljs-type">$count</span> = ++<span class="hljs-type">$c</span>-&gt;<span class="hljs-type">session</span>-&gt;{count};
  <span class="hljs-keyword">my</span> <span class="hljs-type">$message</span> = &quot;<span class="hljs-string">You have visited </span><span class="hljs-type">$count</span><span class="hljs-string"> time</span>&quot;;
  <span class="hljs-type">$message</span> .= &#39;<span class="hljs-string">s</span>&#39; <span class="hljs-keyword">unless</span> <span class="hljs-type">$count</span> == <span class="hljs-number">1</span>;
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(text =&gt; <span class="hljs-type">$message</span>);
};

app-&gt;start;

</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.

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

}

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>;
  <span class="hljs-keyword">my</span> <span class="hljs-type">$count</span> = ++<span class="hljs-type">$c</span>-&gt;<span class="hljs-type">session</span>-&gt;{count};
  <span class="hljs-keyword">my</span> <span class="hljs-type">$message</span> = &quot;<span class="hljs-string">You have visited </span><span class="hljs-type">$count</span><span class="hljs-string"> time</span>&quot;;
  <span class="hljs-type">$message</span> .= &#39;<span class="hljs-string">s</span>&#39; <span class="hljs-keyword">unless</span> <span class="hljs-type">$count</span> == <span class="hljs-number">1</span>;
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(text =&gt; <span class="hljs-type">$message</span>);
};

app-&gt;start;

</code></pre>

<p>If it finds a <code>secrets</code> parameter in your configuration, it will set it as the <code>secrets</code> on your application.
Since you have one in your new configuration file, it should set that property and the warning should go away.
Congratulations, you have a safer application already!</p>

<p>If sometime later, you suspect that someone has guessed your secret, or if your secret leaks out, you can change that secret and restart your application.
This will protect your application from malicious users.</p>

<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>

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

</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>;
  <span class="hljs-keyword">my</span> <span class="hljs-type">$count</span> = ++<span class="hljs-type">$c</span>-&gt;<span class="hljs-type">session</span>-&gt;{count};
  <span class="hljs-keyword">my</span> <span class="hljs-type">$message</span> = &quot;<span class="hljs-string">You have visited </span><span class="hljs-type">$count</span><span class="hljs-string"> time</span>&quot;;
  <span class="hljs-type">$message</span> .= &#39;<span class="hljs-string">s</span>&#39; <span class="hljs-keyword">unless</span> <span class="hljs-type">$count</span> == <span class="hljs-number">1</span>;
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">render</span>(text =&gt; <span class="hljs-type">$message</span>);
};

app-&gt;start;

</code></pre>

<p>So why isn&#39;t this recommended?
Because it would mean that each time you started the server you would get a new secret.
As with the case of changing your secret intentionally above, all existing sessions would be invalidated any time you wanted to reboot a server or restart the server process.
Additionally you could only use one server, any load balancing scenario would result in different secrets on different hosts, your users would randomly invalidate their sessions depending on which server they hit!</p>

<h2>Forward Secrecy</h2>

<p>Just as you have to change application passwords from time to time, so too you need to change your secret.
In a brute force scenario, a nefarious user could take one of their cookies and try to reverse engineer the secret that was used to generate it.</p>

<p>But wait!
You just said that changing the secret to prevent brute forcing will invalidate all of our sessions!</p>

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

<p>add that value to the configuration file</p>

<pre><code class="hljs">{
  secrets =&gt; [
    &#39;<span class="hljs-string">w8S4b+90CWwf</span>&#39;,
    &#39;<span class="hljs-string">yuIB7m88wS07</span>&#39;,
  ],
}
</code></pre>

<p>and restart the application.
Any requests with valid sessions will still work.
The reply they receive will contain a new session cookie, as always, but this time it will be issued using the new secret!</p>

<p>Requests issued by the old credentials will slowly be replaced by new ones as clients each make their first requests following the change.
Once you wait long enough that any valid session cookie would have expired, you can remove the old secret from the configuration and restart again.</p>

<h2>Restarting</h2>

<p>This is a good time to mention <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Hypnotoad"><code>hypnotoad</code></a>.
It is Mojolicious&#39; recommended production application server.
It has many of the same characteristics as the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Pre-forking"><code>prefork</code></a> server but with some tuned settings and one killer feature, <a href="http://mojolicious.org/perld...

<p>Note that on native Windows, <code>hypnotoad</code> will not work.
That said, it works great on the new <a href="https://blogs.msdn.microsoft.com/wsl/">Windows Subsystem for Linux</a>!</p>

<p>A major usage difference is that hypnotoad (for technical reasons) can&#39;t use command line parameters.
Instead it takes its parameters via configuration.
It also starts on port <code>8080</code> rather than <code>3000</code> to prevent accidentally exposing your development servers unexpectedly.
For now however, let&#39;s set it back, more for an example than any particular reason.</p>

<pre><code class="hljs">{
  secrets =&gt; [
    &#39;<span class="hljs-string">w8S4b+90CWwf</span>&#39;,
    &#39;<span class="hljs-string">yuIB7m88wS07</span>&#39;,
  ],
  hypnotoad =&gt; {
    <span class="hljs-function">listen</span> =&gt; [&#39;<span class="hljs-string">http://*:3000</span>&#39;],
  },
}
</code></pre>

<p>Rather than starting you application like</p>

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

<p>use the <code>hypnotoad</code> script</p>

<pre><code>$ hypnotoad myapp.pl
</code></pre>

<p>The application should again be available on port <code>3000</code>.</p>

<p>You&#39;ll see that rather than staying in the foreground like with <code>daemon</code> the command returns and you get your prompt back.
Don&#39;t worry, as long as it said that it started it should stay running.
To stop the application, run the same commmand but also pass a <code>-s</code> switch</p>

<pre><code>$ hypnotoad -s myapp.pl
</code></pre>

<p>The really interesting thing happens when restarting a running application.
Say you&#39;ve rolled your secrets and want to restart the application to use it.
Simply run the command as before, just like when you started it up.</p>

<p>Any requests currently being served by the old server processes will continue to be served by them (up to some timeout).
Any new requests will be served by new processes using the new configuration.
You don&#39;t cut off any users in the process.</p>

<p>Actually <code>hypnotoad</code> can be used to zero downtime restart for more reasons than just configuration changes.
It can handle changes to your application, updating dependencies, or even upgrading your version of Perl itself!</p>

<p>For now though, we&#39;re just satisfied that clients are none the wiser that you rolled the application secret out from under them without losing any connections nor invalidating any sessions.</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=54930070">Image</a> by <a class="external text" href="https://www.flickr.com/people/30478819@N08" rel="nofollow">Marco Verch</a> - <a class="external text" href="https:/...
</small>

              <p class="tags">
                <span>Tagged in </span>:

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

                      <div class="about">
                        <h5>Joel Berger</h5>
                        <p>Joel has Ph.D. in Physics from the University of Illinois at Chicago.
He an avid Perl user and <a href="https://metacpan.org/author/JBERGER">author</a> and is a member of the Mojolicious Core Team.</p>

                      </div>

                  </div>

              <ul class="post-nav cf">
                  <li class="prev"><a href="/blog/2017/12/15/day-15-start-a-new-yancy-app/index.html" rel="prev"><strong>Previous Article</strong> Day 15: Start a New Yancy App</a></li>
                  <li class="next"><a href="/blog/2017/12/17/day-17-the-wishlist-app/index.html" rel="next"><strong>Next Article</strong> Day 17: The Wishlist App </a></li>
              </ul>

            </div>

        </article>


      </div>

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


<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.

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

  }
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">redirect_to</span>(&#39;<span class="hljs-string">/</span>&#39;);
};

any &#39;<span class="hljs-string">/logout</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>;
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">session</span>(expires =&gt; <span class="hljs-number">1</span>);
  <span class="hljs-type">$c</span>-&gt;<span class="hljs-type">redirect_to</span>(&#39;<span class="hljs-string">/</span>&#39;);
};

app-&gt;start;

</code></pre>

<p><small>wishlist.pl</small></p>

<h3>Helpers</h3>

<p>I won&#39;t go into great detail today as much of the model logic will be replaced in tomorrow&#39;s article.
Still, in broad strokes, we define a persistent hash structure, the keys of which are users and the values are hashes of information.</p>

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

    -&gt;auto_migrate(<span class="hljs-number">1</span>);

  <span class="hljs-comment"># attach migrations file</span><span class="hljs-comment">
</span>  <span class="hljs-type">$sqlite</span>-&gt;<span class="hljs-type">migrations</span>-&gt;<span class="hljs-type">from_file</span>(
    <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>);
  }

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


</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>

<p>Finally the routes use the Full app declaration style but they do basically the same thing as before once they dispatch to their controllers.</p>

<h2>The List Controller</h2>

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


<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>

<p>If you&#39;ve read the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook">Cookbook</a>, you&#39;ve already seen that there is a section on <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Making-your-applicatio...
While that&#39;s true, it makes its recommendations without using external modules and for one specific installation tool.
It also assumes that your application <a href="http://mojolicious.org/perldoc/Mojolicious#home"><code>home</code></a> should be related to the installation directory, which isn&#39;t always the case.</p>

<p>Yours truly has even written a <a href="https://metacpan.org/pod/Mojolicious::Plugin::InstallablePaths">module</a> that was intended to ease this process somewhat.
While it does that, and I don&#39;t intend to deprecate it, I think there are even easier patterns now.</p>

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

    -&gt;auto_migrate(1);

  # attach migrations file
  $sqlite-&gt;migrations-&gt;from_file(
    $app-&gt;dist_dir-&gt;child(&#39;wishlist.sql&#39;)
  )-&gt;name(&#39;wishlist&#39;);

  return $sqlite;
};

sub startup {
  my $app = shift;

  $app-&gt;renderer-&gt;paths([
    $app-&gt;dist_dir-&gt;child(&#39;templates&#39;),
  ]);
  $app-&gt;static-&gt;paths([
    $app-&gt;dist_dir-&gt;child(&#39;public&#39;),
  ]);

  ...

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


<p>This also provides another conundrum, if the home is used to load the configuration, then it cannot be set by via the configuration.
No, the home really needs to be set by the user via the <code>MOJO_HOME</code> environment variable <a href="http://mojolicious.org/perldoc/Mojo/Home#detect">as documented</a>.
If this isn&#39;t desirable, some other environment variable could be used by providing your own in your class, overriding the one from the parent.</p>

<pre><code>has home =&gt; sub {
  Mojo::Home-&gt;new($ENV{WISHLIST_HOME});
};
</code></pre>

<p>Though <code>MOJO_HOME</code> is likely a fine place to start.</p>

<p>From there, you might (in some cases) want the users to be able to provide their own static files and/or templates.
Say if your application could be themed.
To do so, you could get values from the configuration and add them to the paths we set above.</p>

<p>Those might be raw paths.</p>

<pre><code>my $templates = $app-&gt;config-&gt;{theme}{templates};
</code></pre>

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


<p>I have made some of these changes to the <a href="https://github.com/jberger/Wishlist/compare/blog_post/sqlite_model...blog_post/installable">Wishlist App</a>.
You&#39;ll see that it it really isn&#39;t much to do.</p>

<p>You also see that I only used as much of these instructions as I needed; you can do the same.
There is no magic in any of what you&#39;ve seen (other than perhaps File::Share).
If you don&#39;t need to think about theming or customizing the environment variable, then don&#39;t worry about it.
But if you find yourself in that situation, you&#39;ll know you can make such features available to your users.</p>

<p>And hey, if it is a useful application, consider uploading it to CPAN.
Though I warn you, once you start contributing to CPAN it can be addictive!</p>

              </section>
              <small><p><a href="https://en.wikipedia.org/w/index.php?curid=31630711">Image</a> by Gnovick - Own work, <a href="https://creativecommons.org/licenses/by/3.0/" title="Creative Commons Attribution 3.0">CC BY 3.0</a>.</p>
</small>

              <p class="tags">
                <span>Tagged in </span>:
                  <a href="/blog/tag/advent/">advent</a>,
                  <a href="/blog/tag/wishlist/">wishlist</a>
              </p>

devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing  view on Meta::CPAN


done_testing;

</code></pre>

<p><small><a href="https://github.com/jberger/Wishlist/blob/blog_post/practical_testing/t/model.t">t/model.t</a></small></p>

<p>When called this way the passed hashref is loaded into the configuration rather than any configuration file.
Because of the way the <a href="https://github.com/jberger/Wishlist/blob/blog_post/practical_testing/lib/Wishlist.pm#L20-L26"><code>sqlite</code></a> attribute initializer was coded, the Mojo::SQLite special literal <code>:temp:</code> is passed thro...
Now I would never suggest that you write any code into your application that is specific to testing, however it is entirely reasonable to code around special literals that someone might need.
You could of course start a Wishlist server using a temporary database.</p>

<p>SQLite&#39;s in-memory (and Mojo::SQLite&#39;s on-disk temporary) databases are really handy for testing because they are automatically testing in isolation.
You don&#39;t have to worry about overwriting the existing database nor clearing the data at the end of your test.
Further, you can run your tests in <a href="https://metacpan.org/pod/Test::Harness#j&lt;n&gt;">parallel</a> to get a nice speedup in large test suites.</p>

<p>For databases that require a running server you have to be a little more careful, however isolated testing is still very possible.
For example, in Mojo::Pg you can set a <a href="http://mojolicious.org/perldoc/Mojo/Pg#search_path"><code>search_path</code></a> which isolates your test.</p>

<pre><code>my $t = Test::Mojo-&gt;new(
  &#39;MyApp&#39;,

devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack  view on Meta::CPAN

use Mojo::Base &#39;-base&#39;;

use Mojo::Asset::File;
use Mojo::IOLoop;
use Mojo::IOLoop::Stream;

use Mojo::Loader &#39;data_section&#39;;

has file_count =&gt; 0;

has file  =&gt; sub { shift-&gt;file_start(&#39;0_START.pgm&#39;) };
has loop  =&gt; sub { Mojo::IOLoop-&gt;singleton };
has stdin =&gt; sub { Mojo::IOLoop::Stream-&gt;new(\*STDIN)-&gt;timeout(0) };

__PACKAGE__-&gt;new-&gt;main;

sub main {
  my $self = shift;
  $self-&gt;stdin-&gt;on(read =&gt; sub { $self-&gt;stdin_read(@_) });
  $self-&gt;stdin-&gt;start;
  $self-&gt;loop-&gt;start unless $self-&gt;loop-&gt;is_running;
}

sub file_start {
  my ($self, $path) = @_;
  $self-&gt;file_count($self-&gt;file_count + 1);
  return Mojo::Asset::File-&gt;new(path =&gt; $path)-&gt;cleanup(0)-&gt;add_chunk(
    data_section(__PACKAGE__, &#39;pgm_header&#39;),
  );
};

sub stdin_read {
  my ($self, $stream, $bytes) = @_;

devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack  view on Meta::CPAN


  say $instruction-&gt;[1];
  $self-&gt;loop-&gt;timer(
    $instruction-&gt;[0] =&gt; sub { $self-&gt;instruction_show },
  );

  $self-&gt;loop-&gt;timer($instruction-&gt;[0] / 2, sub {
    my $type = (split / /, $self-&gt;instructions-&gt;[0][1])[1];
    my $file_count = $self-&gt;file_count;
    my $path = sprintf &#39;%i_%s.pgm&#39;, $file_count, uc $type;
    $self-&gt;file( $self-&gt;file_start($path) );
  }) if $instruction-&gt;[1] eq &#39;Steady&#39;;
}
</code></pre>

<p>As you can see, we ask the loop to stop gracefully when there are no more
instructions.
Otherwise, we output the instruction and then schedule the next instruction
to be done in the amount of seconds we specified in the script.</p>

<p>If the action is to steady the HMD, we know we are going to start another
action shortly so, in half of the wait time, we start a new file.
This should mean that the end and start of each file, the HMD is not moving
(much).</p>

<p>Then we need to start walking through this script by adding this before
starting the event loop in <em>main</em>:</p>

<pre><code>$self-&gt;loop-&gt;timer(0 =&gt; sub { $self-&gt;instruction_show });
</code></pre>

<p>Running this now prompts the user to move the HMD while outputting the packet
data in an image named after the movement that image represents!</p>

<h2>Get Your Head(er) Checked</h2>

<p>As mentioned, the PGM header is broken as it is supposed to contain the number

devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api  view on Meta::CPAN


<p>While the remainder of this article will focus on Full apps, you can also use
the plugin for Lite apps as well. In that case, the key to getting your routes
recognised is to make them have the right path (in this case, <code>/echo</code> - it will
get moved by the plugin under <code>/api</code>) and the right <code>name</code> (the third parameter
to eg <code>get</code>) to match either the <code>x-mojo-name</code> or <code>operationId</code>.</p>

<h2>How to use the specification in your Mojolicious application</h2>

<p>The example application will use the specification above to generate routes and
input/output validation rules. To start off, we can use the
<a href="http://mojolicious.org/perldoc/Mojolicious/Command/generate/app"><code>generate</code></a>
command to create the app:</p>

<pre><code>$ mojo generate app MyApp
</code></pre>

<p>Check it works right by running the provided test:</p>

<pre><code>$ prove -l t
</code></pre>

<p>After that, edit <code>lib/MyApp.pm</code> to make it look like this:</p>

<pre><code>package MyApp;
use Mojo::Base &quot;Mojolicious&quot;;

sub startup {
  my $self = shift;

  # Load the &quot;api.yaml&quot; specification from the public directory
  $self-&gt;plugin(OpenAPI =&gt; {spec =&gt; $self-&gt;static-&gt;file(&quot;api.yaml&quot;)-&gt;path});
}

1;
</code></pre>

<p>Then copy/paste the specification from above and save it to <code>public/api.yaml</code>.</p>

devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api  view on Meta::CPAN


<p>Yay! We see that the same input data was echoed back to us, without any error
message. Mission accomplished!</p>

<h2>See also</h2>

<p>Did you find OpenAPI interesting? Check out these resources to find out more:</p>

<ul>
<li><a href="https://metacpan.org/release/Mojolicious-Plugin-OpenAPI">Mojolicious::Plugin::OpenAPI</a>
contains guides and synopsis to get you started.</li>
<li><a href="https://metacpan.org/pod/OpenAPI::Client">OpenAPI::Client</a>&#39;s manual
contains more information about how the client side works.</li>
<li><a href="https://swagger.io/docs/specification/about/">Swagger&#39;s about page</a> has
information about the specification and OpenAPI.</li>
<li><a href="https://www.openapis.org/">OAI</a> is the official OpenAPI resource page.</li>
<li><a href="https://metacpan.org/pod/JSON::Validator">JSON::Validator</a> is the &quot;brain&quot;
behind both the plugin and client side in Perl. Check it out, if you&#39;re
interested in JSON-Schema.</li>
</ul>

devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit  view on Meta::CPAN

<pre><code>perl -Mojo -E &#39;print r({hello =&gt; &quot;world&quot;})&#39;
perl -Mojo -E &#39;print j({hello =&gt; &quot;world&quot;})&#39;
</code></pre>

<p>The <code>j</code> function is more interesting in that if you give it a string, it will <a href="http://mojolicious.org/perldoc/Mojo/JSON#decode_json">decode it from JSON</a> and return a data structure.</p>

<pre><code>echo &#39;{&quot;hello&quot;:&quot;world&quot;}&#39; | perl -Mojo -E &#39;print j(&lt;&gt;)-&gt;{hello}&#39;
</code></pre>

<p>On their own, these aren&#39;t that interesting.
But when combined with others, you can start to have fun.</p>

<h3>Making Requests</h3>

<p>Most of the functions are dedicated to making HTTP requests.
There are functions for</p>

<ul>
<li>get - <code>g</code></li>
<li>head - <code>h</code></li>
<li>post - <code>p</code></li>

devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit  view on Meta::CPAN

<pre><code>perl -Mojo -E &#39;print j({sites =&gt; b(f(shift)-&gt;slurp)-&gt;split(&quot;\n&quot;)-&gt;map(&quot;trim&quot;)-&gt;sort})&#39; sites
</code></pre>

<h3>The Lite App</h3>

<p>If all of that weren&#39;t enough for you, remember that your one-liner is also a Lite app.
This means that you get all of the Lite app keywords.
I like to show the Mojolicious error page, featuring the glorious Fail Raptor, to the uninitiated.
While I could find the image in the repo, just making a &quot;fail app&quot; is so much easier!</p>

<pre><code>perl -Mojo -E &#39;get &quot;/&quot; =&gt; sub { die }; app-&gt;start&#39; daemon -m production
</code></pre>

<p>If you haven&#39;t seen him, you need to run that one-liner to see him for yourself!</p>

<p>You do still have to call <code>app-&gt;start</code> as ever, and the application gets called with commands, just like any application.
Call it with <code>daemon</code> and since we want to show the production version of the page, force it into production mode.</p>

<p>There is an even shorter way to declare a one-liner application too.
Since one-liners are likely to be used immediately, and routing conditions aren&#39;t so important, there is one ojo keyword to shorten things.
That keyword is <code>a</code>, whose name should cause you to think of Lite&#39;s <a href="http://mojolicious.org/perldoc/Mojolicious/Lite#any"><code>any</code></a> keyword.</p>

<p>It has a one important differences besides just the shorter name: it returns the application rather than the route instance, allowing you to chain the call to <code>start</code>.
Another handy trick is that, when using <code>ojo</code>, actions expose the controller instance as <code>$_</code> so you don&#39;t have to unpack the arguments to get access to it.
(If you have a recent enough Perl, you can also use signatures on your functions automagically too.)</p>

<pre><code>perl -Mojo -E &#39;a(&quot;/&quot; =&gt; sub { $_-&gt;render(text =&gt; scalar localtime) })-&gt;start&#39; get /
</code></pre>

<p>Since all the commands work, using the Lite app and the <code>get</code> command together can mean that you see the results of a request to your one-liner application right there on your terminal!</p>

<p>I use this functionality all the time to quickly demonstrate some concept; sometimes to others or sometimes to myself.
What happens if I set this stash parameter?
Will a route condition do what I expect?
Indeed the Mojolicious core team members often make one-liner applications to paste into IRC when we want to reproduce some bug or discuss some change.</p>

<h3>Benchmarking</h3>

devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit  view on Meta::CPAN


<h2>Conclusion</h2>

<p>Making <code>ojo</code> one-liners can be great to experiment with new concepts, demonstrate problems, fetch and work with data, and many other tasks.
You might use them in non-web one-liners that need JSON or Data::Dumper or perhaps MMojo::Collection for chaining.
(Speaking of chaining, for bonus points, check out <a href="https://metacpan.org/pod/ojoBox">ojoBox</a> for <a href="https://metacpan.org/pod/Mojo::Autobox">autoboxing Perl types</a>, making even cooler chains!)</p>

<p>These one-liners are not going to be everyone&#39;s cup of tea.
If these don&#39;t seem like your&#39;s you can completely ignore them.</p>

<p>However, once you start using them, I think you&#39;ll find yourself using them often.</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=21375125">Image</a> by © <a href="http://www.royan.com.ar" rel="nofollow">Jorge Royan</a>, <a href="https://creativecommons.org/licenses/by-sa/3.0" title="Creative Co...
</small>

              <p class="tags">
                <span>Tagged in </span>:
                  <a href="/blog/tag/advent/">advent</a>,
                  <a href="/blog/tag/command/">command</a>,
                  <a href="/blog/tag/debugging/">debugging</a>,

devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up  view on Meta::CPAN


<p>But for all this ease and convenience there is a price.
Personal data is being taken, mined, and sold.
People are buying products that require internet connections but whose manufacturers will drop support before the hardware fails.
Statements you make and pictures you share will live on on the internet forever, even if you change.</p>

<h3>Fighting Back</h3>

<p>I would never say that the bad outweighs the good, but I do wish people would be more intentional about these types of choices online.
Yet that almost cannot happen until they have alternatives that they control.
To that end I have been trying in the past year to start to take control of my digital life where possible.
I have deployed <a href="https://nextcloud.com/">NextCloud</a> on a private server to keep my files and share with people I choose.
I wrote an application called <a href="https://github.com/jberger/CarPark">CarPark</a> to control my garage door via a RaspberryPi and even spoke about it at <a href="https://www.youtube.com/watch?v=aJc5yYONBBc">The Perl Conference</a>; it isn&#39;t ...

<p>And that brings us to the <a href="https://github.com/jberger/Wishlist">Wishlist</a> application.
Several sites, some large and some small, offer similar services: tracking your wishlists and aiding gift buying, all for free.
However, this too must also come at a cost.</p>

<p>In this article I&#39;m announcing that after adding a few tweaks around user registration, I have now released the Wishlist application to <a href="https://metacpan.org/pod/Wishlist">CPAN</a>.
I hope you can use it to keep a slightly tighter grasp on your digital footprint without sacrificing utility.</p>

<p>You can deploy it on a VPS, like <a href="https://www.linode.com/">Linode</a> or <a href="https://www.scaleway.com/">Scaleway</a>.
You could even use it on that old desktop computer in your home office as long as you are careful about network security.
Speaking of which, <a href="https://letsencrypt.org/">Let&#39;s Encrypt</a> finally makes SSL practical for personal users, try <a href="https://certbot.eff.org">certbot</a> or my <a href="https://metacpan.org/pod/Mojolicious::Plugin::ACME">plugin</a...
Once you have one service running, it will become progressively easier to host more and more of your own personal cloud applications.</p>

<p>I&#39;d love to see if Wishlist and CarPark and other such applications could put you back in the driver&#39;s seat for your digital lives.</p>

<h3>A Call to Action</h3>

<p>That said they aren&#39;t nearly good enough.
They need more tests, more documentation, more functionality.
It would be great if they could support plugins and theming.
Wishlist is reasonably easy to deploy but it could always be easier via tools like Docker etc.

devscripts/update  view on Meta::CPAN

my %add_modules = (
    # not linked
    #"04" => ["Mojo::DOM"],
);

my %typos = (
    #'Perl::PrereqScanner::NotSoLite' => 'Perl::PrereqScanner::NotQuiteLite',
);

my %daily_urls = (
    "01" => "https://mojolicious.io/blog/2017/12/01/day-1-getting-started",
    "02" => "https://mojolicious.io/blog/2017/12/02/day-2-the-stash",
    "03" => "https://mojolicious.io/blog/2017/12/03/day-3-using-named-routes",
    "04" => "https://mojolicious.io/blog/2017/12/04/day-4-dont-fear-the-full-app",
    "05" => "https://mojolicious.io/blog/2017/12/05/day-5-your-apps-built-in-commands",
    "06" => "https://mojolicious.io/blog/2017/12/06/day-6-adding-your-own-commands",
    "07" => "https://mojolicious.io/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page",
    "08" => "https://mojolicious.io/blog/2017/12/08/day-8-mocking-a-rest-api",
    "09" => "https://mojolicious.io/blog/2017/12/09/day-9-the-best-way-to-test",
    "10" => "https://mojolicious.io/blog/2017/12/10/day-10-give-the-customer-what-they-want",
    "11" => "https://mojolicious.io/blog/2017/12/11/day-11-useragent-content-generators",
    "12" => "https://mojolicious.io/blog/2017/12/12/day-12-more-than-a-base-class",
    "13" => "https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles",
    "14" => "https://mojolicious.io/blog/2017/12/14/day-14-you-promised-to-call",
    "15" => "https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app",
    "16" => "https://mojolicious.io/blog/2017/12/16/day-16-the-secret-life-of-sessions",
    "17" => "https://mojolicious.io/blog/2017/12/17/day-17-the-wishlist-app",
    "18" => "https://mojolicious.io/blog/2017/12/18/day-18-the-wishlist-model",
    "19" => "https://mojolicious.io/blog/2017/12/19/day-19-make-your-app-installable",
    "20" => "https://mojolicious.io/blog/2017/12/20/day-20-practical-testing",
    "21" => "https://mojolicious.io/blog/2017/12/21/day-21-virtually-a-lumberjack",
    "22" => "https://mojolicious.io/blog/2017/12/22/day-22-how-to-build-a-public-rest-api",
    "23" => "https://mojolicious.io/blog/2017/12/23/day-23-one-liners-for-fun-and-profit",
    "24" => "https://mojolicious.io/blog/2017/12/24/day-24-release-and-wrap-up",
);

lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_01.pm  view on Meta::CPAN

package Acme::CPANModules::Import::MojoliciousAdvent::2017_12_01;

our $DATE = '2018-12-30'; # DATE
our $VERSION = '0.001'; # VERSION

our $LIST = {description=>"This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/01/day-1-getting-started] (retrieved on 2018-12-30). Visit the URL for the full contents.",entries=>[{module=>"App::cpanmin...

1;
# ABSTRACT: Modules mentioned in Mojolicious Advent Calendar 2017 (day 01)

__END__

=pod

=encoding UTF-8

=head1 NAME

Acme::CPANModules::Import::MojoliciousAdvent::2017_12_01 - Modules mentioned in Mojolicious Advent Calendar 2017 (day 01)

=head1 VERSION

This document describes version 0.001 of Acme::CPANModules::Import::MojoliciousAdvent::2017_12_01 (from Perl distribution Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017), released on 2018-12-30.

=head1 DESCRIPTION

This module is generated by extracting module names mentioned in L<https://mojolicious.io/blog/2017/12/01/day-1-getting-started> (retrieved on 2018-12-30). Visit the URL for the full contents.

Modules mentioned in Mojolicious Advent Calendar 2017 (day 01).

This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/01/day-1-getting-started] (retrieved on 2018-12-30). Visit the URL for the full contents.

=head1 INCLUDED MODULES

=over

=item * L<App::cpanminus>

=back

=head1 HOMEPAGE

lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_15.pm  view on Meta::CPAN

package Acme::CPANModules::Import::MojoliciousAdvent::2017_12_15;

our $DATE = '2018-12-30'; # DATE
our $VERSION = '0.001'; # VERSION

our $LIST = {description=>"This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app] (retrieved on 2018-12-30). Visit the URL for the full contents.",entries=>[{module=>"Mojo:...

1;
# ABSTRACT: Modules mentioned in Mojolicious Advent Calendar 2017 (day 15)

__END__

=pod

=encoding UTF-8

=head1 NAME

Acme::CPANModules::Import::MojoliciousAdvent::2017_12_15 - Modules mentioned in Mojolicious Advent Calendar 2017 (day 15)

=head1 VERSION

This document describes version 0.001 of Acme::CPANModules::Import::MojoliciousAdvent::2017_12_15 (from Perl distribution Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017), released on 2018-12-30.

=head1 DESCRIPTION

This module is generated by extracting module names mentioned in L<https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app> (retrieved on 2018-12-30). Visit the URL for the full contents.

Modules mentioned in Mojolicious Advent Calendar 2017 (day 15).

This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/15/day-15-start-a-new-yancy-app] (retrieved on 2018-12-30). Visit the URL for the full contents.

=head1 INCLUDED MODULES

=over

=item * L<Mojo::Pg>

=item * L<Mojo::Pg::Migrations>

=item * L<Mojolicious::Lite>



( run in 0.752 second using v1.01-cache-2.11-cpan-0d8aa00de5b )