view release on metacpan or search on metacpan
# 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
dist.ini
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_01.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_02.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_03.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_04.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_05.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_06.pm
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_09.pm
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
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
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's ensure a level playing field by working out how to get started.</p>
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started view on Meta::CPAN
<p>If you still have trouble, we have a mailing list and an IRC channel ready to help!
Find us at <a href="http://mojolicious.org/perldoc#SUPPORT">http://mojolicious.org/perldoc#SUPPORT</a>!</p>
</section>
<small><p><a href="https://commons.wikimedia.org/wiki/File:Colorado_Springs_Hot_Air_Balloon_Competition.jpg">Image by DarlArthurS</a> licensed under the <a class="extiw" href="https://en.wikipedia.org/wiki/en:Creative_Commons" title="w:...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/installing/">installing</a>,
<a href="/blog/tag/hello-world/">hello world</a>,
<a href="/blog/tag/lite/">lite</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/11/30/mojolicious-advent-2017/index.html" rel="prev"><strong>Previous Article</strong> Mojolicious Advent 2017</a></li>
<li class="next"><a href="/blog/2017/12/02/day-2-the-stash/index.html" rel="next"><strong>Next Article</strong> Day 2: The Stash </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash 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/02/day-2-the-stash/" property="og:url">
<meta content="Day 2: The Stash" property="og:title">
<meta content="The stash is the most important data structure in Mojolicious." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/02/day-2-the-stash/bag-1854148_1920.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 2: The Stash - 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_02_day-2-the-stash view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
<time class="date" datetime="2017-12-02">Dec 2, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="leather bag" src="/blog/2017/12/02/day-2-the-stash/bag-1854148_1920.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>In Mojolicious, when processing a request and preparing a response one of the most important concepts is "the stash".
Since it is a non-blocking framework, your code can't use global variables to store any state during processing.
If you did and some other code were to run, it could very easily get cross-talk between requests.</p>
<p>The stash is the place you can store information while you process it.
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
<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>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/hello-world/">hello world</a>,
<a href="/blog/tag/lite/">lite</a>,
<a href="/blog/tag/rendering/">rendering</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes 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/03/day-3-using-named-routes/" property="og:url">
<meta content="Day 3: Using Named Routes" property="og:title">
<meta content="Avoid hard-coded URLs using Mojolicious' named routes." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/03/day-3-using-named-routes/my_name_is.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 3: Using Named Routes - 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_03_day-3-using-named-routes view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
<time class="date" datetime="2017-12-03">Dec 3, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt=""my name is" stickers" src="/blog/2017/12/03/day-3-using-named-routes/my_name_is.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>One of the things we know from years of programming is that you should never hard-code anything if you don't have to.
And yet far too many web application hard-code their urls, especially internal ones.
But what if you didn't have to?</p>
<p>Each Mojolicious route has its own <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Named-routes">name</a> which can be used to generate urls.
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
I also like to separate concerns.
Anyway, you can choose whichever you like best.
After all, there's more than one way to do it!</p>
</section>
<small><p><a href="https://flickr.com/photos/94599716@N06/15283079263">Image</a> by <a href="https://www.flickr.com/people/94599716@N06">Travis Wise</a> licensed under the <a class="extiw" href="https://en.wikipedia.org/wiki/en:Creative...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/lite/">lite</a>,
<a href="/blog/tag/routing/">routing</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/02/day-2-the-stash/index.html" rel="prev"><strong>Previous Article</strong> Day 2: The Stash</a></li>
<li class="next"><a href="/blog/2017/12/04/day-4-dont-fear-the-full-app/index.html" rel="next"><strong>Next Article</strong> Day 4: Don't Fear the Full App </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-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="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2017/12/04/day-4-dont-fear-the-full-app/" property="og:url">
<meta content="Day 4: Don't Fear the Full App" property="og:title">
<meta content="Growing from Lite to Full apps is easy. There's no magic to worry about." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/04/day-4-dont-fear-the-full-app/arucaria.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 4: Don't Fear the Full App - 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_04_day-4-dont-fear-the-full-app view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
<time class="date" datetime="2017-12-04">Dec 4, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Arucaria trees in Curitiba Brazil" src="/blog/2017/12/04/day-4-dont-fear-the-full-app/arucaria.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>One of the most common misconceptions about Mojolicious is that there is a large difference between the declarative "Lite" apps that we have seen so far and large-scale structured "Full" apps.
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 "Full" 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' documentation uses Lite syntax most of the time.
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
<p>After repeated attempts to convince people that there is very little difference between the two, I'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'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'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->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->plugin(...)</code>, etc.</p>
<p>The routing methods</p>
<ul>
<li><code>any</code></li>
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
With that, I encourage you to go back and read the <a href="http://mojolicious.org/perldoc#TUTORIAL">Tutorial and Guides</a> and realize that everything that looks like Lite apps is really just as true for Full ones.</p>
</section>
<small><p>Image is copyright (c) 2013 Joel Berger.
It shows a pair of Arucaria trees in the city of Curitiba, during YAPC::Brasil 2013.
It is licensed under a <a href="http://creativecommons.org/licenses/by-sa/4.0/" rel="license">Creative Commons Attribution-ShareAlike 4.0 International License</a>.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/lite/">lite</a>,
<a href="/blog/tag/full/">full</a>,
<a href="/blog/tag/growing/">growing</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/03/day-3-using-named-routes/index.html" rel="prev"><strong>Previous Article</strong> Day 3: Using Named Routes</a></li>
<li class="next"><a href="/blog/2017/12/05/day-5-your-apps-built-in-commands/index.html" rel="next"><strong>Next Article</strong> Day 5: Your App's Built-In Commands </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands 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/05/day-5-your-apps-built-in-commands/" property="og:url">
<meta content="Day 5: Your App's Built-In Commands" property="og:title">
<meta content="Exploring the built-in commands that come with every Mojolicious Application." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/05/day-5-your-apps-built-in-commands/1200px-Rocket_prolant.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 5: Your App's Built-In Commands - 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_05_day-5-your-apps-built-in-commands view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
<time class="date" datetime="2017-12-05">Dec 5, 2017</time>
</p>
</div>
<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>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
<p>It outputs your current version of Perl and Mojolicious along with any installed optional libraries.
For example, you'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'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 "toy"
/meet/:name GET "staff"
/ GET "home"
</code></pre>
<p>This shows you the three routes that were defined.
It shows the paths for each route including their placeholders, that all three handle GET, and their route name.
Using this output is especially helpful when using named routes, as we learned in that post; all the information you need is right in that table!</p>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
<pre><code>perl myapp.pl eval 'app->pg->migrations->migrate'
</code></pre>
<p>Or just check that the database is reachable.</p>
<pre><code>perl myapp.pl eval -V 'app->pg->db->query("SELECT NOW()")->hash'
</code></pre>
<p>These last two database examples assumed that your app was using <a href="http://mojolicious.org/perldoc/Mojo/Pg">Mojo::Pg</a> but similar one-liners could work for any database that your app knows about.</p>
<p>There really is nothing like debugging or administering your application without having to copy and paste a bunch of your logic from your app to some script.
Although if you really find yourself using the <code>eval</code> command for the same tasks often ... well that should wait until tomorrow.</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=44576486">Image</a> by <a href="//commons.wikimedia.org/w/index.php?title=User:Gsaisudha75&action=edit&redlink=1" title="User:Gsaisudha75 (page does not exist)">...
</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>,
<a href="/blog/tag/administration/">administration</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/04/day-4-dont-fear-the-full-app/index.html" rel="prev"><strong>Previous Article</strong> Day 4: Don't Fear the Full App</a></li>
<li class="next"><a href="/blog/2017/12/06/day-6-adding-your-own-commands/index.html" rel="next"><strong>Next Article</strong> Day 6: Adding Your Own Commands </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands 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/06/day-6-adding-your-own-commands/" property="og:url">
<meta content="Day 6: Adding Your Own Commands" property="og:title">
<meta content="Adding commands to your app to ease administration and avoid auxiliary scripts." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/06/day-6-adding-your-own-commands/1280px-Brightly_lit_STS-135_on_launch_pad_39a.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 6: Adding Your Own Commands - 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_06_day-6-adding-your-own-commands view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
<time class="date" datetime="2017-12-06">Dec 6, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Space shuttle Atlantis prepared for liftoff (night)" src="/blog/2017/12/06/day-6-adding-your-own-commands/1280px-Brightly_lit_STS-135_on_launch_pad_39a.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Everyone has written those one-off administration or check scripts.
There are probably a few cluttering your project root or bin directory right now.
Those have a problem beyond just the clutter: duplication.</p>
<p>Programmers hate duplication because of skew.
If code gets improved in one place, it is unlikely to be improved in all places, unless there is only the one.
So that script you wrote a while back, the one with the database connection you hand-rolled, is that still correct?</p>
<p>In the <a href="/blog/2017/12/05/day-5-your-apps-built-in-commands">previous article in this series</a> I talked about the built-in commands available to your application.
The final command was <a href="http://mojolicious.org/perldoc/Mojolicious/Command/eval"><code>eval</code></a>.
I mentioned that when combined with predefined behaviors, the command could be great for administrative tasks.
That's true, but you need to know what to eval in order to do so.</p>
<p>To formalize that process, we can go one step further: defining our own commands.
By doing this your application's administative behaviors can take arguemnts and provide optional switches as well as give usage messages.
In this way these administative commands decouple themselves from knowledge of the application's internals and become useful to a broader set of users.</p>
</section>
<section id="section-2">
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
use Mojolicious::Commands;
# Start command line interface for application
Mojolicious::Commands->start_app('MyWeatherApp');
</code></pre>
<h3>The Model</h3>
<p>Now let'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'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>
<pre><code>package MyWeatherApp::Model::Weather;
use Mojo::Base -base;
use Carp ();
use Mojo::URL;
use Mojo::UserAgent;
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
Indeed they are one of my very favorite things about Mojolicious.
Perhaps you can tell?</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=15739462">Image</a>
by Bill Ingalls - <a href="http://www.flickr.com/photos/nasahqphoto/5914101671/in/photostream/" rel="nofollow">http://www.flickr.com/photos/nasahqphoto/5914101671/in/photostream/</a>, Public Domain.</p>
</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/example/">example</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/05/day-5-your-apps-built-in-commands/index.html" rel="prev"><strong>Previous Article</strong> Day 5: Your App's Built-In Commands</a></li>
<li class="next"><a href="/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/index.html" rel="next"><strong>Next Article</strong> Day 7: Using Template Variants For a Beta Landing Page </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page 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/07/day-7-using-template-variants-for-a-beta-landing-page/" property="og:url">
<meta content="Day 7: Using Template Variants For a Beta Landing Page" property="og:title">
<meta content="Doug demonstrates using Mojolicious template variants to show a new landing page for a beta-testing website" property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/1280px-Single_yellow_tulip_in_a_field_of_red_tulips.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 7: Using Template Variants For a Beta Landing Page - 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_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
<time class="date" datetime="2017-12-07">Dec 7, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Single yellow tulip in a field of red tulips" src="/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/1280px-Single_yellow_tulip_in_a_field_of_red_tulips.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p><a href="http://cpantesters.org">CPAN Testers</a> is a pretty big project with a long,
storied history. At its heart is a data warehouse holding all the test reports
made by people installing CPAN modules. Around that exists an ecosystem of
tools and visualizations that use this data to provide useful insight into the
status of CPAN distributions.</p>
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
<p>This is the main CPAN Testers application.</p>
</code></pre>
<p>Template variants are a useful feature in some edge cases, and this isn't the
first time I've found a good use for them. I've also used them to provide a
different layout template in "development" mode to display a banner saying
"You're on the development site". Useful for folks who are undergoing user
acceptance testing. The best part is that if the desired variant for that
specific template is not found, Mojolicious falls back to the main template. I
built a mock JSON API application which made extensive use of this fallback
feature, but that's another blog post for another time.</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=2147460">Image</a> by Photo by and (c)2007 <a href="//commons.wikimedia.org/wiki/User:Jina_Lee" title="User:Jina Lee">Jina Lee</a> - <span class="int-own-work" lang="en"...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/rendering/">rendering</a>,
<a href="/blog/tag/templates/">templates</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/4bdc4922059d58a0fcbf8f35652dc254.png">
</div>
<div class="about">
<h5>Doug Bell</h5>
<p>Doug (<a href="http://preaction.me">preaction</a>) is a long time Perl user.
He is the current maintainer of <a href="http://www.cpantesters.org/">CPAN Testers</a> and the author of many <a href="https://metacpan.org/author/PREACTION">CPAN modules</a> including the <a href="http://preaction.me/statocles/">Statocles</a> blog e...
</div>
</div>
<ul class="post-nav cf">
<li class="prev"><a href="/blog/2017/12/06/day-6-adding-your-own-commands/index.html" rel="prev"><strong>Previous Article</strong> Day 6: Adding Your Own Commands</a></li>
<li class="next"><a href="/blog/2017/12/08/day-8-mocking-a-rest-api/index.html" rel="next"><strong>Next Article</strong> Day 8: Mocking a REST API </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api 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/08/day-8-mocking-a-rest-api/" property="og:url">
<meta content="Day 8: Mocking a REST API" property="og:title">
<meta content="Doug uses Mojolicious to quickly build a mock API for testing a front-end application." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/08/day-8-mocking-a-rest-api/1280px-crash-test-dummies.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 8: Mocking a REST API - 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_08_day-8-mocking-a-rest-api view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
<time class="date" datetime="2017-12-08">Dec 8, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Two crash test dummies" src="/blog/2017/12/08/day-8-mocking-a-rest-api/1280px-crash-test-dummies.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>One of my applications is a pure-JavaScript UI for a JSON API. This UI
is an entirely different project that communicates with a public API
using an OpenAPI specification.</p>
<p>Our public API is huge and complex: To set up the public API, I need
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
<pre><code>$ perl test-api.pl get -M POST -c '{ "ip": "10.0.0.3" }' /servers
{ "status": "success", "id": 3, "server": { "ip": "10.0.0.3" } }
</code></pre>
<p>Now what if I want to test what happens when the API gives me an error?
Mojolicious has an easy way to layer on additional templates to use for
certain routes: <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Template-variants">Template
variants</a>.
These variant templates will be used instead of the original template,
but only if they are available. Read more on <a href="/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/">how to use template
variants yesterday on the advent
calendar</a>.</p>
<p>By setting the template variant to the application "mode", we can easily
switch between multiple sets of templates by adding <code>-m <mode></code> to the
command we run.</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 '<span class="hljs-string">/*path</span>' => <span class="hljs-keyword">sub </span>{
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
<p>Now I have 20 lines of code that can be made to mock any JSON API
I write. Mojolicious makes everything easy!</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=37564272">Image</a> by Calspan Corporation, National Highway Traffic Safety Administration - Public Domain.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/mocking/">mocking</a>,
<a href="/blog/tag/api/">api</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/4bdc4922059d58a0fcbf8f35652dc254.png">
</div>
<div class="about">
<h5>Doug Bell</h5>
<p>Doug (<a href="http://preaction.me">preaction</a>) is a long time Perl user.
He is the current maintainer of <a href="http://www.cpantesters.org/">CPAN Testers</a> and the author of many <a href="https://metacpan.org/author/PREACTION">CPAN modules</a> including the <a href="http://preaction.me/statocles/">Statocles</a> blog e...
</div>
</div>
<ul class="post-nav cf">
<li class="prev"><a href="/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/index.html" rel="prev"><strong>Previous Article</strong> Day 7: Using Template Variants For a Beta Landing Page</a></li>
<li class="next"><a href="/blog/2017/12/09/day-9-the-best-way-to-test/index.html" rel="next"><strong>Next Article</strong> Day 9: The Best Way to Test </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test 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/09/day-9-the-best-way-to-test/" property="og:url">
<meta content="Day 9: The Best Way to Test" property="og:title">
<meta content="An introduction to Test::Mojo, the testing framework for Mojolicious." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/09/day-9-the-best-way-to-test/1280px-CSIRO_ScienceImage_2798_Testing_in_the_Laboratory.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 9: The Best Way to Test - 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_09_day-9-the-best-way-to-test view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
<time class="date" datetime="2017-12-09">Dec 9, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Woman using chemistry lab equipment" src="/blog/2017/12/09/day-9-the-best-way-to-test/1280px-CSIRO_ScienceImage_2798_Testing_in_the_Laboratory.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Ok so it is a bit of a click-bait headline.
But that doesn't mean I don't believe it.</p>
<p><a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a> is a test framework for websites and related technologies.
While its true that there are many such tools, this one gets its power comes from combining so many of the tools that Mojolicious provides.
A full non-blocking web server, including websockets, an <a href="http://mojolicious.org/perldoc/Mojo/IOLoop">event loop</a>, an <a href="http://mojolicious.org/perldoc/Mojo/DOM">XML/HTML DOM parser</a>, <a href="http://mojolicious.org/perldoc/Mojo/J...
Further, with the recent additions in support of <a href="http://mojolicious.org/perldoc/Mojo/Base#with_roles">roles</a> (which will be discussed in a future post), Test::Mojo is becoming an extensible testing platform.</p>
<p>In this article, I'll give a quick overview of how to use Test::Mojo and some of its methods.
Rest assured you'll see more of it as the series continues.</p>
</section>
<section id="section-2">
<h2>Getting Started</h2>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
<h2 id="making-requests">Making Requests</h2>
<p>Now that we have a running application and a tester to test it, what can we do?
The Mojolicious documentation has lots of examples both in the <a href="http://mojolicious.org/perldoc/Test/Mojo">class documenation</a> that we've already seen and in the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Testing">testin...
That said for the sake explication, let's see a few things.</p>
<p>You can make requests with most of the same arguments as to <a href="http://mojolicious/perldoc/Mojo/UserAgent#METHODS">Mojo::UserAgent</a>.
These can include headers as a hash reference:</p>
<pre><code>$t->get_ok('/login', {'X-Application-Auth' => 'custom value'});
</code></pre>
<p>Requests with JSON, form, for multipart data are built via <a href="http://mojolicious.org/perldoc/Mojo/UserAgent/Transactor#GENERATORS">content generators</a>.
You can <a href="http://mojolicious.org/perldoc/Mojo/UserAgent/Transactor#add_generator">add your own</a> generator too; perhaps the subject of another article if there is interest.</p>
<pre><code>$t->post_ok('/login', form => {user => 'me', pass => 'secr3t'});
$t->put_ok('/inventory/12345', json => {type => 'widget', value => 'tons'});
</code></pre>
<p>Or you can submit raw data as a trailing argument</p>
<pre><code>use Mojo::XMLRPC 'encode_xmlrpc'; # from CPAN
$t->post_ok('/xmlrpc', encode_xmlrpc(call => 'mymethod', 'myarg'));
</code></pre>
<p>There are methods for all the standard HTTP methods.
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
<p>As I said before, the Mojolicious documentation has lots of examples both in the <a href="http://mojolicious.org/perldoc/Test/Mojo">class documenation</a> and in the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Testing">testing guide...
Check those out while you wait, if you've liked what you've seen above.</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=35474503">Image</a> by CSIRO, <a href="http://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/testing/">testing</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/08/day-8-mocking-a-rest-api/index.html" rel="prev"><strong>Previous Article</strong> Day 8: Mocking a REST API</a></li>
<li class="next"><a href="/blog/2017/12/10/day-10-give-the-customer-what-they-want/index.html" rel="next"><strong>Next Article</strong> Day 10: Give the Customer What They Want </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want 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/10/day-10-give-the-customer-what-they-want/" property="og:url">
<meta content="Day 10: Give the Customer What They Want" property="og:title">
<meta content="Using content negotiation to create APIs that support multiple response formats." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/10/day-10-give-the-customer-what-they-want/cafe-wood-vintage-retro-seat-restaurant-946984-pxhere.com.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 10: Give the Customer What They Want - 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_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
<time class="date" datetime="2017-12-10">Dec 10, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Wood bar counter with chairs" src="/blog/2017/12/10/day-10-give-the-customer-what-they-want/cafe-wood-vintage-retro-seat-restaurant-946984-pxhere.com.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Writing an API can be as easy as taking the results of some database query and presenting it to the user.
A more advanced one can often present the data in one of multiple formats.
The user can then specify which format they want.</p>
<p>JSON is currently the most popular format for new APIs.
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
If you have special format types you can add them to the <a href="http://mojolicious.org/perldoc/Mojolicious#types">types</a> object on the application.</p>
<h2>Responding to the Format Request</h2>
<p>There are two methods which help render what the client wants: <a href="http://mojolicious.org/perldoc/Mojolicious/Controller#respond_to"><code>respond_to</code></a> and <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#acc...
<p>The former, <code>respond_to</code>, is much more high level and should be your go-to choice.
It takes key value pairs where the keys are the file types that should be handled (in extension format).
The values are either stash values that should be used when rendering or else a callback to be invoked.</p>
<p>Since I showed you <a href="/blog/2017/12/09/day-9-the-best-way-to-test">yesterday</a> how to use <a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>, let's examine this as a test.
Imagine a test for an application that returns information about Santa's Reindeer.</p>
<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
<span class="hljs-comment"># This could be a db lookup</span><span class="hljs-comment">
</span>helper '<span class="hljs-string">reindeer</span>' => <span class="hljs-keyword">sub </span>{
<span class="hljs-keyword">my</span> (<span class="hljs-type">$c</span>, <span class="hljs-type">$name</span>) = <span class="hljs-type">@_</span>;
<span class="hljs-keyword">my</span> <span class="hljs-type">%reindeer</span> = (
rudolph => {
name => '<span class="hljs-string">Rudolph</span>',
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
<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'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>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/rendering/">rendering</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/09/day-9-the-best-way-to-test/index.html" rel="prev"><strong>Previous Article</strong> Day 9: The Best Way to Test</a></li>
<li class="next"><a href="/blog/2017/12/11/day-11-useragent-content-generators/index.html" rel="next"><strong>Next Article</strong> Day 11: UserAgent Content Generators </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators 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/11/day-11-useragent-content-generators/" property="og:url">
<meta content="Day 11: UserAgent Content Generators" property="og:title">
<meta content="Using Mojo::UserAgent Content Generators to build request bodies." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/11/day-11-useragent-content-generators/artist-painting-1459778857j86.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 11: UserAgent Content Generators - 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_11_day-11-useragent-content-generators view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
<time class="date" datetime="2017-12-11">Dec 11, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Artist painting" src="/blog/2017/12/11/day-11-useragent-content-generators/artist-painting-1459778857j86.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>We have already seen <a href="http://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> used to make HTTP requests in this series.
In fact we've already seen how you can use <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#USER-AGENT">Content Generators</a> to build requests <a href="/blog/2017/12/09/day-9-the-best-way-to-test#making-requests">in tests</a>...
But we didn't look at how they work or how you can add new ones.</p>
</section>
<section id="section-2">
<h2>Using Content Generators</h2>
<p>The UserAgent, and more specifically its <a href="http://mojolicious.org/perl/Mojo/UserAgent/Transactor">Transactor</a>, help you by making it easy to create HTTP requests.
Consider the most basic request with a body, a <code>POST</code> with a binary body, maybe ASCII text.
In that case, the request</p>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
encode_json({some => ['json', 'data']})
);
</code></pre>
<p>or a similar example to the above using <code>build_tx</code>.
I think you'll agree that the generator form is much easier to read and "does what you mean".</p>
<p>At the time of this writing, Mojo::UserAgent comes with three <a href="http://mojolicious.org/perldoc/Mojo/UserAgent/Transactor#tx">built-in Content Generators</a>, including the <code>json</code> one as we've already seen.</p>
<p>The <code>form</code> generator creates urlencoded or multipart requests depending on the data passed.
The form generator is, unsurprisingly, useful for submittng forms, often used to login to sites, search for content or upload files.
It is even smart enought to use query parameters for <code>GET</code> and <code>HEAD</code> requests (which cannot take a body), while using body parameters for others.</p>
<p>Finally, the recently-added <code>multipart</code> generator is for building your own generic multipart requests.
Though not common, some APIs allow or even require users to upload multiple files in the same request.</p>
<p>This was the case presented to us by a user not too long ago.
They were interacting with the <a href="https://developers.google.com/drive/v3/web/multipart-upload">Google Drive API</a> that wanted them to upload a file as part of a multipart message with a JSON document attached containing metadata.
The overall request was to be marked at <a href="https://tools.ietf.org/html/rfc2387"><code>multipart/related</code></a> while each part should have its own <code>Content-Type</code>.
Google's documented example is</p>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
However, once defined, it does cut down on repeated code for subsequent requests.
Thus the benefit grows the more times it is used.
In a larger code base, that adherence to the DRY mantra (Don't Repeat Yourself) might be invaluable.</p>
</section>
<small><p><a href="http://www.publicdomainpictures.net/view-image.php?image=157945&picture=artist-painting">Image</a> in the Public Domain.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/useragent/">useragent</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/10/day-10-give-the-customer-what-they-want/index.html" rel="prev"><strong>Previous Article</strong> Day 10: Give the Customer What They Want</a></li>
<li class="next"><a href="/blog/2017/12/12/day-12-more-than-a-base-class/index.html" rel="next"><strong>Next Article</strong> Day 12: More Than a Base Class </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class 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/12/day-12-more-than-a-base-class/" property="og:url">
<meta content="Day 12: More Than a Base Class" property="og:title">
<meta content="Mojo::Base is a streamlined base class with lots of extra goodies." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/12/day-12-more-than-a-base-class/1280px-GG-ftpoint-bridge-2.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 12: More Than a Base Class - 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_12_day-12-more-than-a-base-class view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
<time class="date" datetime="2017-12-12">Dec 12, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Golden Gate Bridge viewed and Fort Point" src="/blog/2017/12/12/day-12-more-than-a-base-class/1280px-GG-ftpoint-bridge-2.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Through this series, you've seen the module <a href="http://mojolicious.org/perldoc/Mojo/Base">Mojo::Base</a> referenced several times, though briefly and mostly in passing.
It shouldn't be taken lightly however, it packs a lot of punch in one import statement!
Nearly every file in the Mojolicious distribution uses it, either directly or indirectly.
So what is it?</p>
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
<p>This adds the <code>has</code> keyword to your package which, as we will see soon, declares the class's attributes.
This will cause your new class to inherit from Mojo::Base, meaning it will get the methods from Mojo::Base as well, which you will also see.
Of course, the module also acquires the pragmas and functionality listed above and may add <code>-signatures</code> if desired.</p>
<p>If you want your class to derive from some other parent class, you can pass that name rather than <code>-base</code>.</p>
<pre><code>package My::Class::Subclass;
use Mojo::Base 'My::Class';
</code></pre>
<p>You saw this usage quite a bit in the Full app example on <a href="/blog/2017/12/06/day-6-adding-your-own-commands">Day 6</a>.
Mojo::Base only supports single-inheritance because we don't want to encourage bad practices.
If you know why you might want to use multipe-inheritance, you probably know how to get around this limitation as well.</p>
<h3>Attributes and Constructor</h3>
<p>Mojo::Base implements a class method which can be used to add attributes to the class (called <code>attr</code>).
While this is necessary for the implementation, this isn't the preferred usage.</p>
<p>The <code>has</code> keyword, added by the import above, gives us that nice declarative style that Perl users are familiar with from Moose and Moo.
The usage is different from those, owing to the limited choices available in declaration.
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
->at('title')
->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'd rather pass on this method, that'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'll permit me, I'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>,
<a href="/blog/tag/roles/">roles</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/11/day-11-useragent-content-generators/index.html" rel="prev"><strong>Previous Article</strong> Day 11: UserAgent Content Generators</a></li>
<li class="next"><a href="/blog/2017/12/13/day-13-more-about-roles/index.html" rel="next"><strong>Next Article</strong> Day 13: More About Roles </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles 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/13/day-13-more-about-roles/" property="og:url">
<meta content="Day 13: More About Roles" property="og:title">
<meta content="Investigating Mojo::Base's recently-added role support." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles/amsterdam_hats.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 13: More About Roles - 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_13_day-13-more-about-roles view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
<time class="date" datetime="2017-12-13">Dec 13, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Pile of hats" src="/blog/2017/12/13/day-13-more-about-roles/amsterdam_hats.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Before we get ahead of ourselves, what are roles?
Briefly stated, roles enable composing functionality (usually methods) into a class without without implying a change of inheritence.
Said another way, roles let you specify what a class does without changing what it is.
For a better description, check out Toby Inkster's article <a href="http://radar.oreilly.com/2014/01/horizontal-reuse-an-alternative-to-inheritance.html">Horizontal Reuse: An Alternative to Inheritance</a>.</p>
<p>An important utility of roles is that you can easily use more than one role at the same time in the same consuming class.
With inheritance, especially of third-party functionality, you have to choose one set of extensions to utilize.
This is because the author of the subclass establishes the inheritance.
In roles, the user determines which roles to compose into the base class.</p>
<p><a href="/blog/2017/12/12/day-12-more-than-a-base-class">Yesterday</a> I ended the discussion of <a href="http://mojolicious.org/perldoc/Mojo/Base">Mojo::Base</a> before discussing the roles support.
Added in several installments between Mojolicious versions <a href="https://metacpan.org/release/SRI/Mojolicious-7.40">7.40</a> and <a href="https://metacpan.org/release/SRI/Mojolicious-7.55">7.55</a>, this role support is one of the most recently ad...
The role handling comes from <a href="https://metacpan.org/pod/Role::Tiny">Role::Tiny</a> which is an optional dependency in Mojolicious, but is required in order to use the functionality that I will describe.</p>
</section>
<section id="section-2">
<p>This is not to say that roles couldn't be or weren't used in Mojolicious before then, only that Mojo::Base didn't include any special handling to make it user-friendly.
Prior to the functionality being formally available from Mojo::Base, a few roles were available for Mojo stack classes on CPAN.
To my knowledge they all used Role::Tiny, but they had to roll their own composition mechanisms, presenting some barrier to use.</p>
<h2>Creating Roles</h2>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
$dom->find_custom->each(sub{ say });
}
</code></pre>
<h2>Roles on CPAN</h2>
<p>The first part of the ecosystem to embrace roles was testing.
Given the interface of <a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>, adding additional test methods isn't as easy as is generally true of other testers in the <a href="https://metacpan.org/pod/Test::More">Test::More</a> lands...
Roles fill that gap nicely, and therefore such roles predated Mojo::Base's own role handling and even inspired adding it to the core.</p>
<p>As I've mentioned <a href="/blog/2017/12/09/day-9-the-best-way-to-test">earlier in this series</a>, <a href="https://metacpan.org/pod/Test::Mojo::Role::Debug">Test::Mojo::Role::Debug</a> adds methods to run a callback on failed tests.
<a href="https://metacpan.org/pod/Test::Mojo::Role::TestDeep">Test::Mojo::Role::TestDeep</a> lets you test responses with the comparison functions from <a href="https://metacpan.org/pod/Test::Deep">Test::Deep</a>.
<a href="https://metacpan.org/pod/Test::Mojo::Role::PSGI">Test::Mojo::Role::PSGI</a> lets you use Test::Mojo with non-Mojolicious web frameworks!
You can even use them all together.</p>
<pre><code>use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
use Test::Deep; # for its keywords
my $t = Test::Mojo
->with_roles('+PSGI', '+TestDeep', '+Debug')
->new('/path/to/app.psgi');
$t->get_ok('/')
->text_deeply(
'nav a',
[qw( Home Blog Projects About Contact )],
'nav link text matches site section titles',
)->d(sub{ diag $t->tx->req->body });
</code></pre>
<p>This example loads a hypothetical psgi application from the filesystem, tests it for text elements and if that test fails dumps the response body to the console.</p>
<p>There are three modules (only two on CPAN so far) that let you run javascript on pages via external processes.
<a href="https://metacpan.org/pod/Test::Mojo::Role::Phantom">Test::Mojo::Role::Phantom</a> and <a href="https://metacpan.org/pod/Test::Mojo::Role::Selenium">Test::Mojo::Role::Selenium</a> use <a href="http://phantomjs.org/">PhantomJS</a> and <a href=...
PhantomJS is abandoned however so <a href="https://github.com/jberger/Mojo-Chrome">Test::Mojo::Role::Chrome</a> is in development on my Github and will replace the PhantomJS role.</p>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
<p>... as well as others.
And the list is sure to grow now that Mojo::Base can natively compose these roles.</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=15179120">Image</a> é Jorge Royanà/à<a class="external free" href="http://www.royan.com.ar" rel="nofollow">http://www.royan.com.ar</a>, <a href="https://creativ...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/roles/">roles</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/12/day-12-more-than-a-base-class/index.html" rel="prev"><strong>Previous Article</strong> Day 12: More Than a Base Class</a></li>
<li class="next"><a href="/blog/2017/12/14/day-14-you-promised-to-call/index.html" rel="next"><strong>Next Article</strong> Day 14: You Promised To Call! </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call 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="https://mojolicious.io/blog/2017/12/14/day-14-you-promised-to-call/" property="og:url">
<meta content="Day 14: You Promised To Call!" property="og:title">
<meta content="Learn about Promises and their new prominent role in Mojolicious." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/14/day-14-you-promised-to-call/pinky_swear.jpg" property="og:image">
<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 14: You Promised To Call! - mojolicious.io</title>
<meta content="Ed J" name="author">
<meta content="Statocles 0.093" name="generator">
<link href="/static/favicon.ico" rel="shortcut icon">
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
<time class="date" datetime="2017-12-14">Dec 14, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Two hands with interlocked pinkies, a pinky swear" src="/blog/2017/12/14/day-14-you-promised-to-call/pinky_swear.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>A new feature of <a href="http://mojolicious.org/">Mojolicious</a>, as of <a href="https://metacpan.org/release/SRI/Mojolicious-7.49">7.49</a>, is the implementation of the <a href="https://promisesaplus.com/implementations#in-ot...
</section>
<section id="section-2">
<h2>Background</h2>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
<p>Another option for dealing with a number of concurrent activities, if you just want the first one that completes, is <a href="http://mojolicious.org/perldoc/Mojo/Promise#race"><code>race</code></a>.</p>
<h2>What if something doesn't work?</h2>
<p>In the above, we assumed that everything worked. What if it doesn't? Promises as a standard offer two other methods: <code>catch</code>, and <code>finally</code>.</p>
<p><code>catch</code> is given a code-ref, which will be called when a Promise is "rejected". When things work as above, each Promise is "resolved". That means the value it was resolved with gets passed to the next <code>then</cod...
<pre><code>sub fetchpage {
$ua->get_p($url)->then(sub { ... })->then(sub { ... })->catch(sub {
# either log, or report, or something else
});
}
</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>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
<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>
<small><p><a href="https://www.flickr.com/photos/elsabordelossegundos/15418211523">Image</a> by <a href="https://www.flickr.com/photos/elsabordelossegundos/">mariadelajuana</a>, <a href="https://creativecommons.org/licenses/by/2.0/">CC ...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/non-blocking/">non-blocking</a>,
<a href="/blog/tag/promises/">promises</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/c6e74e4e0ab61c61db655f0e60c9f8a6.jpg">
</div>
<div class="about">
<h5>Ed J</h5>
<p>Ed J (aka "mohawk" 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_14_day-14-you-promised-to-call view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
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
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
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
your site's content can be in
<a href="http://metacpan.org/pod/Yancy::Backend::Pg">Postgres</a>,
<a href="http://metacpan.org/pod/Yancy::Backend::Mysql">MySQL</a>, and
<a href="http://metacpan.org/pod/Yancy::Backend::Dbic">DBIx::Class</a>.</p>
</section>
<section id="section-2">
<h2>Demonstration</h2>
<p>For an demonstration application, letâÂÂs create a simple blog using
<a href="http://mojolicious.org/perldoc/Mojolicious/Lite">Mojolicious::Lite</a>.
First we need to create a database schema for our blog posts. Let's use
<a href="http://metacpan.org/pod/Mojo::Pg">Mojo::Pg</a> and its <a href="http://metacpan.org/pod/Mojo::Pg::Migrations">migrations
feature</a> to create a table
called "blog" with fields for an ID, a title, a date, some markdown, and
some HTML.</p>
<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>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Pg</span>;
<span class="hljs-keyword">my</span> <span class="hljs-type">$pg</span> = <span class="hljs-function">Mojo::Pg</span>->new( '<span class="hljs-string">postgres://localhost/blog</span>' );
<span class="hljs-type">$pg</span>-><span class="hljs-type">migrations</span>-><span class="hljs-type">from_data</span>-><span class="hljs-type">migrate</span>;
<span class="hljs-keyword">__DATA__</span>
@@ migrations
-- 1 up
CREATE TABLE blog (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
created TIMESTAMP NOT NULL DEFAULT NOW(),
markdown TEXT NOT NULL,
html TEXT NOT NULL
);
-- 1 down
DROP TABLE blog;
</code></pre>
<p>Next we add <a href="http://metacpan.org/pod/Mojolicious::Plugin::Yancy">the Yancy
plugin</a> and tell it
about our backend and data. Yancy deals with data as a set of
collections which contain items. For a relational database like
Postgres, a collection is a table, and an item is a row in that table.</p>
<p>Yancy uses a JSON schema to describe each item in a collection.
For our <code>blog</code> collection, we have five fields:</p>
<ol>
<li><code>id</code> which is an auto-generated integer and should be read-only</li>
<li><code>title</code> which is a free-form string which is required</li>
<li><code>created</code> which is an ISO8601 date/time string, auto-generated</li>
<li><code>markdown</code> which is a required Markdown-formatted string</li>
<li><code>html</code>, a string which holds the rendered Markdown and is also required</li>
</ol>
<p>Here's our configured Yancy <code>blog</code> collection:</p>
<pre><code class="hljs">plugin Yancy => {
backend => '<span class="hljs-string">pg://localhost/blog</span>',
collections => {
blog => {
required => [ '<span class="hljs-string">title</span>', '<span class="hljs-string">markdown</span>', '<span class="hljs-string">html</span>' ],
properties => {
id => {
type => '<span class="hljs-string">integer</span>',
readOnly => <span class="hljs-number">1</span>,
},
title => {
type => '<span class="hljs-string">string</span>',
},
created => {
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app view on Meta::CPAN
rendered HTML. There'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'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
helpers to access our
data</a>. Let's
use the <code>list</code> helper to display a list of blog posts. This helper takes
a collection name and gives us a list of items in that collection. It
also allows us to search for items and order them to our liking. Since
we've got a blog, we will order by the creation date, descending.</p>
<pre><code class="hljs">get '<span class="hljs-string">/</span>' => <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>-><span class="hljs-type">render</span>(
'<span class="hljs-string">index</span>',
posts => [ <span class="hljs-type">$c</span>-><span class="hljs-type">yancy</span>-><span class="hljs-type">list</span>(
'<span class="hljs-string">blog</span>', {}, { order_by => { -desc => '<span class="hljs-string">created</span>' } },
) ],
);
};
</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><main role="main" class="container">
% for my $post ( @{ stash 'posts' } ) {
<%== $post->{html} %>
% }
</main>
</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>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/app/">app</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/4bdc4922059d58a0fcbf8f35652dc254.png">
</div>
<div class="about">
<h5>Doug Bell</h5>
<p>Doug (<a href="http://preaction.me">preaction</a>) is a long time Perl user.
He is the current maintainer of <a href="http://www.cpantesters.org/">CPAN Testers</a> and the author of many <a href="https://metacpan.org/author/PREACTION">CPAN modules</a> including the <a href="http://preaction.me/statocles/">Statocles</a> blog e...
</div>
</div>
<ul class="post-nav cf">
<li class="prev"><a href="/blog/2017/12/14/day-14-you-promised-to-call/index.html" rel="prev"><strong>Previous Article</strong> Day 14: You Promised To Call!</a></li>
<li class="next"><a href="/blog/2017/12/16/day-16-the-secret-life-of-sessions/index.html" rel="next"><strong>Next Article</strong> Day 16: The Secret Life of Sessions </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
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions 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/16/day-16-the-secret-life-of-sessions/" property="og:url">
<meta content="Day 16: The Secret Life of Sessions" property="og:title">
<meta content="Use the session secret effectively to protect users without inconveniencing them." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/16/day-16-the-secret-life-of-sessions/scrabble.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 16: The Secret Life of Sessions - 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_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
<time class="date" datetime="2017-12-16">Dec 16, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Pile of Scrabble tiles" src="/blog/2017/12/16/day-16-the-secret-life-of-sessions/scrabble.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>As you all know, HTTP is a stateless protocol.
In Mojolicious applications the session is used to maintain state between requests.
These sessions are managed by the application's <a href="http://mojolicious.org/perldoc/Mojolicious/#sessions">session manager</a>.</p>
<p>During each request, the <a href="http://mojolicious.org/perldoc/Mojolicious/Controller#session">session</a> is just another hash reference attached to the controller, in some ways like the <a href="/blog/2017/12/02/day-2-the-stash">stash</a>, exc...
Mojolicious does this by encoding the structure, first as JSON then Base64.
It then signs the resulting string using HMAC-SHA1 and the application's <a href="http://mojolicious.org/perldoc/Mojolicious#secrets">secret</a> to prevent tampering and stores it as a cookie on the response to the client.</p>
<p>On subsequent requests, the client sends the cookie along with the request (as cookies do).
Mojolicious then checks if the document and signature validate against the secret, if so the cookie is decoded and made available again via the session method.</p>
<p>Two important things to note.
First, though the data is safe from tampering, it isn't encrypted; a savvy user can decode the cookie and see the stored data, so don't put anything in it that shouldn't be seen.
Second, this is only useful if the secret is strong and safe.
If not, the client could forge a cookie that appeared to come from your application, possibly with catastrophic results!
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
<pre><code>$ perl myapp.pl daemon
</code></pre>
<p>and visit <code>localhost:3000</code> you should see a counter that increments on each request.
That data is stored on the client (e.g. in the browser) between each request and is incremented on the server before sending it back to the client.
Each response is a new cookie with the new session data and a new cookie expiration time, <a href="http://mojolicious.org/perldoc/Mojolicious/Sessions#default_expiration">defaulting to one hour</a> from issue.
This also therefore implies that as long as you visit often enough (before any one cookie expires) and your data continues to validate against the secret, your session can last forever.</p>
<h2>Secret Security</h2>
<p>Now, one other thing you should see is that in your application's log output, you should have a message like</p>
<pre><code>Your secret passphrase needs to be changed
</code></pre>
<p>This happens because you are using the default secret for the application.
This default is just the name of the script, as you can see via the <a href="/blog/2017/12/05/day-5-your-apps-built-in-commands">eval command</a></p>
<pre><code>$ perl myapp.pl eval -V 'app->secrets'
[
"myapp"
]
</code></pre>
<p>This secret is not secure both because it is short and because it is easy to guess.
With a trivial application like this you might not need to worry about forgery, as you would with say a session that tracks user logins.
But who knows, perhaps you are going to award a prize to the user for the most requests made!
Let's play it safe.</p>
<p>The secret isn't something you need to remember, it just has to be hard to guess.
So I suggest you pick a random one.
You could generate 12 characters of random text using</p>
<pre><code>$ </dev/urandom base64 | head -c 12
yuIB7m88wS07
</code></pre>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
<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'd like to avoid.</p>
<h2>A Random Secret?</h2>
<p>Now perhaps you are asking yourself, if Mojolicious knows I'm using the insecure default couldn't it just set a random secret?
Sure, and you could do so yourself too if you wanted.
Something as easy as this would set a random secret.</p>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
<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' 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'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's set it back, more for an example than any particular reason.</p>
<pre><code class="hljs">{
secrets => [
'<span class="hljs-string">w8S4b+90CWwf</span>',
'<span class="hljs-string">yuIB7m88wS07</span>',
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
It can handle changes to your application, updating dependencies, or even upgrading your version of Perl itself!</p>
<p>For now though, we'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>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/session/">session</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-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="@joelaberger" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2017/12/17/day-17-the-wishlist-app/" property="og:url">
<meta content="Day 17: The Wishlist App" property="og:title">
<meta content="Introducing template concepts with a group wishlist app." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/17/day-17-the-wishlist-app/xmas_tree.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 17: The Wishlist App - 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_17_day-17-the-wishlist-app view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<time class="date" datetime="2017-12-17">Dec 17, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Living room, Christmas tree, and presents" src="/blog/2017/12/17/day-17-the-wishlist-app/xmas_tree.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>For today's article, I really wanted to demonstrate concepts using a practical example appliation.
It is possible I let the exaxmple get away from me!</p>
<p>In today's article I indend to show how to use template composition techniques to produce a wishlist app.
We will cover <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Layouts">layouts</a>, <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Partial-templates">partial templates</a>, <a href="http://mojolicious.org/pe...
<p>The data model is admittedly rough, however I think my plan will be to make that a feature and not a bug.
Today we will example concepts mostly relating to the templates, then in tomorrows post I will migrate the model from using the simplistic persistence of <a href="https://metacpan.org/pod/DBM::Deep">DBM::Deep</a> that it uses now to <a href="https://...
</section>
<section id="section-2">
<p>At that point I hope to put the application into a repository of its own.
In the meantime however, you can see the application in the source for this <a href="https://github.com/MojoliciousDotIO/mojolicious.io/tree/master/blog/2017/12/17/day-17-the-wishlist-app/wishlist">article</a>.
To run it, you will need to install two additional modules, DBM::Deep and <a href="https://metacpan.org/pod/LinkEmbedder">LinkEmbedder</a>.</p>
<pre><code>$ cpanm Mojolicious DBM::Deep LinkEmbedder
</code></pre>
<h2>Layouts</h2>
<p>Most web sites have a defined style and layout between pages.
A header bar, a sidebar for navigation, a footer.
The content of each might change slightly between pages but the similarity is remarkable.</p>
<p>Do the developers copy and paste this logic between pages?
Certainly not!</p>
<p>The first tool of the trade is a layout template.
This is a template that will contain the results of rendering some inner template.
This will usually contain the outermost tags, like <code><html></code>, <code><head></code>, and <code><body></code>.
They will likely also establish any structure that exists on all of the pages, like navigation and sidebar sections.</p>
<p>Let's look at the layout that our wishlist application</p>
<pre><code class="hljs"><!DOCTYPE html>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
The <code>footer</code> buffer would allow additions to be placed at the end of the body but before any javascript inclusions.</p>
<p>It is interesting to note that if we rendered this template directly, those buffers would all be empty.
Therefore the content put into them must come from someplace else.</p>
<h2>A Primary Template</h2>
<p>I mentioned before the the layout was like a container that would hold some inner content.
Let's consider the simplest such case.</p>
<p>When a user first accesses the site, the will be greeted with a login page.</p>
<p><img alt="Screen shot of the login page" src="login.png"></p>
<pre><code class="hljs">% title '<span class="hljs-string">Welcome to the Wishlist!</span>';
% layout '<span class="hljs-string">default</span>';
<h2><%= title %></h2>
%= form_for '<span class="hljs-string">login</span>' => (method => '<span class="hljs-string">POST</span>', class => '<span class="hljs-string">form-inline</span>') => <span class="hljs-keyword">begin</span>
<div class="<span class="hljs-string">form-group</span>">
<label>Please <span class="hljs-function">tell</span> us your name to get started</label>
<input name="<span class="hljs-string">name</span>" class="<span class="hljs-string">form-control</span>">
</div>
% <span class="hljs-keyword">end</span>
</code></pre>
<p><small>templates/login.html.ep</small></p>
<p>Immediately you can see that there are a few statements at the top.
These set the layout to our default one above and set the title key for the page.
It is important to realize that this page is rendered <strong>first</strong> before any layout is rendered.</p>
<p>After the template renders, Mojolicious will notice that a layout key was set and as a result it will render the layout with the result of the primary template rendering available as the default content buffer.
As we saw before, that content will be placed inside the <code>#main</code> section of the page.
However, in the process of rendering the primary template, the title was also set.
Since the layout is rendered afterwards, this value is now available to set the <code><title></code> tag at the top of the page.</p>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<span class="hljs-type">$name</span> ||= <span class="hljs-type">$c</span>-><span class="hljs-type">stash</span>->{name} || <span class="hljs-type">$c</span>-><span class="hljs-type">session</span>->{name};
<span class="hljs-keyword">return</span> {} <span class="hljs-keyword">unless</span> <span class="hljs-type">$name</span>;
<span class="hljs-keyword">return</span> <span class="hljs-type">$c</span>-><span class="hljs-type">users</span>->{<span class="hljs-type">$name</span>} ||= {
name => <span class="hljs-type">$name</span>,
items => {},
};
};
get '<span class="hljs-string">/</span>' => <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">$template</span> = <span class="hljs-type">$c</span>-><span class="hljs-type">session</span>->{name} ? '<span class="hljs-string">list</span>' : '<span class="hljs-strin...
<span class="hljs-type">$c</span>-><span class="hljs-type">render</span>(<span class="hljs-type">$template</span>);
};
get '<span class="hljs-string">/list/:name</span>' => '<span class="hljs-string">list</span>';
get '<span class="hljs-string">/add</span>' => <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">$link</span> = <span class="hljs-type">$c</span>-><span class="hljs-type">link</span>(<span class="hljs-type">$c</span>-><span class="hljs-type">param</span>('<span class="hljs-...
<span class="hljs-type">$c</span>-><span class="hljs-type">render</span>('<span class="hljs-string">add</span>', <span class="hljs-function">link</span> => <span class="hljs-type">$link</span>);
};
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<span class="hljs-type">$item</span>->{purchased} = <span class="hljs-type">$c</span>-><span class="hljs-type">param</span>('<span class="hljs-string">purchased</span>');
<span class="hljs-type">$c</span>-><span class="hljs-type">redirect_to</span>('<span class="hljs-string">list</span>', name => <span class="hljs-type">$user</span>->{name});
};
post '<span class="hljs-string">/remove</span>' => <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-function">delete</span> <span class="hljs-type">$c</span>-><span class="hljs-type">user</span>->{items}{<span class="hljs-type">$c</span>-><span class="hljs-type">param</span>('<span class="hljs-string">title</span>&#...
<span class="hljs-type">$c</span>-><span class="hljs-type">redirect_to</span>('<span class="hljs-string">/</span>');
};
post '<span class="hljs-string">/login</span>' => <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">if</span> (<span class="hljs-keyword">my</span> <span class="hljs-type">$name</span> = <span class="hljs-type">$c</span>-><span class="hljs-type">param</span>('<span class="hljs-string">name</span>')) {
<span class="hljs-type">$c</span>-><span class="hljs-type">session</span>->{name} = <span class="hljs-type">$name</span>;
}
<span class="hljs-type">$c</span>-><span class="hljs-type">redirect_to</span>('<span class="hljs-string">/</span>');
};
any '<span class="hljs-string">/logout</span>' => <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>-><span class="hljs-type">session</span>(expires => <span class="hljs-number">1</span>);
<span class="hljs-type">$c</span>-><span class="hljs-type">redirect_to</span>('<span class="hljs-string">/</span>');
};
app->start;
</code></pre>
<p><small>wishlist.pl</small></p>
<h3>Helpers</h3>
<p>I won't go into great detail today as much of the model logic will be replaced in tomorrow'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>
<p>Once you login, your name is stored in the <a href="http://mojolicious.org/perldoc/Mojolicious/Controller#session"><code>session</code></a> and in this hash.
While I haven't followed the <a href="https://mojolicious.io/blog/2017/12/16/day-16-the-secret-life-of-sessions/">best practices for sessions from yesterday</a>, you certainly could and should if this data mattered to you.
But also, no authentication is attempted, this is a personal wishlist app, hopefully none of your friends are going to play the Grinch on you!</p>
<p>The <code>user</code> helper is especially clever.
You can pass it a name for lookup, if that isn't provided then a name is looked for in the stash and the session in turn.
In this way you are looking up a specific user, the user being referenced by the page, or the logged in user.</p>
<p>There is also a helper that uses <a href="https://metacpan.org/pod/LinkEmbedder">LinkEmbedder</a> to look up information about a link and return it.
That is used when a user pastes a url that they want to add to their list.
LinkEmbedder will fetch that page and scrape it for metadata using several open protocols and falling back onto heuristics if possible.
It will then return the information and an short HTML representation of that resource.</p>
<h3>Routes</h3>
<p>The routes are mostly self explanatory, even if their code is not.
The <code>/login</code> and <code>/logout</code> handlers, for example.</p>
<p>There are two routes for <code>/add</code> a <code>GET</code> and a <code>POST</code>.
<code>GET</code> requests are safe and will not change data, in this case the request triggers LinkEmbedder to fetch the information which is then displayed.</p>
<h4>Adding Items</h4>
<p><img alt="Screen shot of the add items page" src="add.png"></p>
<pre><code class="hljs">% title '<span class="hljs-string">Add an item</span>';
% layout '<span class="hljs-string">default</span>';
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
</div>
<div class="<span class="hljs-string">panel-body</span>">
%= form_for '<span class="hljs-string">add</span>' => (method => '<span class="hljs-string">POST</span>') => <span class="hljs-keyword">begin</span>
%= hidden_field title => <span class="hljs-type">$link</span>-><span class="hljs-type">title</span><span class="hljs-type">
</span> %= hidden_field url => <span class="hljs-type">$link</span>-><span class="hljs-type">url</span><span class="hljs-type">
</span> <input type="<span class="hljs-string">submit</span>" value="<span class="hljs-string">Yes</span>" class="<span class="hljs-string">btn btn-default</span>">
%= link_to '<span class="hljs-string">No</span>' => '<span class="hljs-string">/</span>', (class => '<span class="hljs-string">btn btn-default</span>')
% <span class="hljs-keyword">end</span>
</div>
</div>
%= include '<span class="hljs-string">partial/log_out</span>'
% <span class="hljs-keyword">end</span>
<div class="<span class="hljs-string">panel panel-default</span>">
<div id="<span class="hljs-string">item-detail</span>" class="<span class="hljs-string">panel-body</span>">
<%== <span class="hljs-type">$link</span>-><span class="hljs-type">html</span> %>
</div>
</div>
</code></pre>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<p>The result is a tiny form that contains the data to be stored if the user wants to add it to their wishlist.
Because the resulting main page might be quite large, and I want the user to have easy access to decide if they want to add the item, I've placed it in the left hand column.
Perhaps this is bad UX, but for educational purposes, it shows how these buffers can be used.</p>
<p>We also see our first example of <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#include"><code>include</code></a>.
When called, the renderer immediately renders the template specified and returns the result.</p>
<pre><code class="hljs"><div class="<span class="hljs-string">panel panel-default</span>">
<div class="<span class="hljs-string">panel-body</span>">
<ul class="<span class="hljs-string">nav nav-pills nav-stacked</span>">
%= t li => link_to '<span class="hljs-string">Log Out</span>' => '<span class="hljs-string">logout</span>'
</ul>
</div>
</div>
</code></pre>
<p><small>templates/partial/log_out.html.ep</small></p>
<p>While our application doesn't do so, calls to <code>include</code> can take arguments that they see in the stash.
They can also add content to named content buffers, just like the primary template can.
All our logout "partial" template does is generate a nicely formatted link to the log out route.
The name partial indicates that, like layout, this template is not intended to be rendered on its own.
The utility of making this its own template is that many pages can use that same partial to render that same log out link.</p>
<h4>The List</h4>
<p>There are two routes that might render a wishlist.
The <code>/</code> route either allows the user to log in or if they are, displays their list.
There is a also a <code>/list/:name</code> route that renders any user's list by name.</p>
<p><img alt="Screen shot of the list page" src="list.png"></p>
<pre><code class="hljs">% title user->{name} . "<span class="hljs-string">'s List</span>";
% layout '<span class="hljs-string">default</span>';
% content_for '<span class="hljs-string">sidebar</span>' => <span class="hljs-keyword">begin</span>
%= include '<span class="hljs-string">partial/user_list</span>'
%= include '<span class="hljs-string">partial/add_url</span>'
%= include '<span class="hljs-string">partial/log_out</span>'
% <span class="hljs-keyword">end</span>
<h2><%= title %></h2>
<table class="<span class="hljs-string">table</span>">
<thead>
<tr>
<td>Title</td>
<td><span class="hljs-function">Link</span></td>
<td>Action</td>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<p>As I said before, I'm looking forward to making a more complete application with proper storage for tomorrow.
That said, the code shown today already works and is quite useful, even for as small as it is!</p>
</section>
<small><p><a href="https://pxhere.com/en/photo/1056078">Image</a> in the Public Domain.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/rendering/">rendering</a>,
<a href="/blog/tag/templates/">templates</a>,
<a href="/blog/tag/example/">example</a>,
<a href="/blog/tag/wishlist/">wishlist</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/16/day-16-the-secret-life-of-sessions/index.html" rel="prev"><strong>Previous Article</strong> Day 16: The Secret Life of Sessions</a></li>
<li class="next"><a href="/blog/2017/12/18/day-18-the-wishlist-model/index.html" rel="next"><strong>Next Article</strong> Day 18: The Wishlist Model </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model 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/18/day-18-the-wishlist-model/" property="og:url">
<meta content="Day 18: The Wishlist Model" property="og:title">
<meta content="Building a proper model layer for the Wishlist app." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/18/day-18-the-wishlist-model/van_gogh_still_life.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 18: The Wishlist Model - 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_18_day-18-the-wishlist-model view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
<time class="date" datetime="2017-12-18">Dec 18, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Still Life: Vase with Pink Roses by Vincent van Gogh" src="/blog/2017/12/18/day-18-the-wishlist-model/van_gogh_still_life.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p><a href="https://mojolicious.io/blog/2017/12/17/day-17-the-wishlist-app/">Yesterday</a> we discussed templates features like layouts, partial templates, and content buffers.
We motivated the discussion by introducing a fully functioning example application that tracked user's Christmas wishlists.
That application did lack sophistication in the area of data storage, using <a href="https://metacpan.org/pod/DBM::Deep">DBM::Deep</a> for quickly getting a basic persistence layer.
While that worked well enough to demonstrate template functionality it was no triumph of the model layer.
Indeed some very hack techniques are used, especially in manipulating wishlist items, since there was no unique record ids.</p>
<p>Well as promised I have created a <a href="https://github.com/jberger/Wishlist">repository</a> for the application on Github.
I have also added several tags.
While development on the application may continue, those tags will remain for future readers.</p>
<p>The initial form of the application (as seen in yesterday's post) is tagged <a href="https://github.com/jberger/Wishlist/tree/blog_post/dbm_deep"><code>blog_post/dbm_deep</code></a>.
You are then invited to step through the commits <a href="https://github.com/jberger/Wishlist/compare/blog_post/dbm_deep...blog_post/full_app">from that one to <code>blog_post/full_app</code></a> to follow along as I port it from a Lite to a Full app...
<p>This article will briefly discuss the application as it exists in the next tag, <a href="https://github.com/jberger/Wishlist/tree/blog_post/sqlite_model"><code>blog_post/sqlite_model</code></a>.
At this point I have replaced DBM::Deep with <a href="https://metacpan.org/pod/Mojo::SQLite">Mojo::SQLite</a>, written a rudimentary model layer for it, and connected the two with the application via helpers.
Let's see how that improves the application and in the meantime, get a look at idiomatic database access in Mojolicious!</p>
</section>
<section id="section-2">
<h2>Model View Controller</h2>
<p>Most modern web applications adhere to a pattern called <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">Model View Controller</a> or MVC.
Much has been written about MVC, more than could be conveyed here.
Quickly though, the view is how data is displayed, the template.
The model is the database, both read and write access, and how to manipulated it.
This is usually thought of as the "business logic".
Finally the controller is supposed to be the minimal amount of logic that can be used to connect the two and process web requests.</p>
<p>In Perl almost all relational database access is via <a href="https://metacpan.org/pod/DBI">DBI</a> whether directly or indirectly.
However this isn't a model as such, a model really needs to have defined data layout (schema) and manipulation.</p>
<p>Many Perl users turn to <a href="https://metacpan.org/pod/DBIx::Class">DBIx::Class</a>, an <a href="https://en.wikipedia.org/wiki/Object-relational_mapping">Object-Relational Mapper</a> (ORM), and this is a great choice.
It hides most of the SQL and creates classes for the tables.
However some developers like staying a little closer to the SQL.</p>
<h2>Mojo-Flavored DBI</h2>
<p>The Mojolicious community has several modules that live partway between DBI and DBIx::Class.
I lovingly call them "Mojo-Flavored DBI" collectively.
The first of these was <a href="https://metacpan.org/pod/Mojo::Pg">Mojo::Pg</a> for <a href="https://www.postgresql.org/">PostgreSQL</a>.
Quickly, copycat modules were made, <a href="https://metacpan.org/pod/Mojo::mysql">Mojo::mysql</a> for <a href="https://www.mysql.com/">MySQL</a> and <a href="https://metacpan.org/pod/Mojo::SQLite">Mojo::SQLite</a> for the embedded database <a href="...
<p>These are attractive because they are lightweight in comparison to ORMs.
They feature schema migration management and similar <a href="https://mojolicious.io/blog/2017/12/12/day-12-more-than-a-base-class/">fluent interfaces</a> as Mojolicious.
They handle connection pooling and nonblocking access (emulated in the case of SQLite).
In recent versions, they also wrap <a href="https://metacpan.org/pod/SQL::Abstract">SQL::Abstract</a> which can be used to simplify certain common actions.
SQL::Abstract is also used by DBIx::Class, so as you'd expect, these have a feel similar to an ORM.</p>
<p>Going forward with this article, I will use Mojo::SQLite since it doesn't require an external database.</p>
<h2>The Schema</h2>
<p>The first thing we need to establish is the database schema; the collection of tables and their columns.
In Mojo-Flavored DBI these are collected into one file, broken up by comments.
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
<p>Here you can see how to move from the empty version 0 up to version 1.
You can also define how to move back down though it is ok to ignore those and not support downgrading.</p>
<p>The schema we define mimics the one we used yesterday.
Users have names.
Items have titles, urls, purchased state (SQLite doesn't have a boolean) and a reference to the user that requested it.</p>
<h2>The Model Class</h2>
<p>I extracted the business logic from the <a href="https://metacpan.org/pod/SQL::Abstract">original application's controller actions</a>, anything that handled persistence, and moved them to a dedicated class, <a href="https://github.com/jberger...
<pre><code class="hljs"><span class="hljs-keyword">package</span> <span class="hljs-function">Wishlist::Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Base</span> -base;
<span class="hljs-keyword">use</span> Carp ();
has sqlite => <span class="hljs-keyword">sub </span>{ <span class="hljs-function">Carp::croak</span> '<span class="hljs-string">sqlite is required</span>' };
<span class="hljs-keyword">sub </span><span class="hljs-function">add_user</span> {
<span class="hljs-keyword">my</span> (<span class="hljs-type">$self</span>, <span class="hljs-type">$name</span>) = <span class="hljs-type">@_</span>;
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
});
<span class="hljs-type">$app</span>-><span class="hljs-type">helper</span>(users => <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">return</span> <span class="hljs-type">$c</span>-><span class="hljs-type">model</span>-><span class="hljs-type">list_user_names</span>;
});
<span class="hljs-keyword">my</span> <span class="hljs-type">$r</span> = <span class="hljs-type">$app</span>-><span class="hljs-type">routes</span>;
<span class="hljs-type">$r</span>-><span class="hljs-type">get</span>('<span class="hljs-string">/</span>' => <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">$template</span> = <span class="hljs-type">$c</span>-><span class="hljs-type">session</span>->{name} ? '<span class="hljs-string">list</span>' : '<span class="hljs-str...
<span class="hljs-type">$c</span>-><span class="hljs-type">render</span>(<span class="hljs-type">$template</span>);
});
<span class="hljs-type">$r</span>-><span class="hljs-type">get</span>('<span class="hljs-string">/list/:name</span>')->to(template => '<span class="hljs-string">list</span>')->name('<span class="hljs-string">list</sp...
<span class="hljs-type">$r</span>-><span class="hljs-type">get</span>('<span class="hljs-string">/add</span>')->to('<span class="hljs-string">List#show_add</span>')->name('<span class="hljs-string">show_add</span>')...
<span class="hljs-type">$r</span>-><span class="hljs-type">post</span>('<span class="hljs-string">/add</span>')->to('<span class="hljs-string">List#do_add</span>')->name('<span class="hljs-string">do_add</span>');
<span class="hljs-type">$r</span>-><span class="hljs-type">post</span>('<span class="hljs-string">/update</span>')->to('<span class="hljs-string">List#update</span>')->name('<span class="hljs-string">update</span>')...
<span class="hljs-type">$r</span>-><span class="hljs-type">post</span>('<span class="hljs-string">/remove</span>')->to('<span class="hljs-string">List#remove</span>')->name('<span class="hljs-string">remove</span>')...
<span class="hljs-type">$r</span>-><span class="hljs-type">post</span>('<span class="hljs-string">/login</span>')->to('<span class="hljs-string">Access#login</span>')->name('<span class="hljs-string">login</span>');
<span class="hljs-type">$r</span>-><span class="hljs-type">any</span>('<span class="hljs-string">/logout</span>')->to('<span class="hljs-string">Access#logout</span>')->name('<span class="hljs-string">logout</span>'...
}
<span class="hljs-number">1</span>;
</code></pre>
<p><small>lib/Wishlist.pm</small></p>
<p>There is an application attribute which holds the Mojo::SQLite instance.
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
);
<span class="hljs-type">$c</span>-><span class="hljs-type">redirect_to</span>('<span class="hljs-string">/</span>');
}
<span class="hljs-number">1</span>;
</code></pre>
<p><small>lib/Wishlist/Controller/List.pm</small></p>
<p>While all the same business logic is accomplished, this time the semantic model methods are used rather than manipulating the data directly.
THe methods establish what they want to be done not how to do it.
This is much better MVC and will serve you better in the long run.</p>
<p>So is this the end of our discussion of the Wishlist app?
Who can say?</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=10510831">Image</a> "Still Life: Vase with Pink Roses" by <a class="extiw" href="https://en.wikipedia.org/wiki/en:Vincent_van_Gogh" title="w:en:Vincent van Gog...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/model/">model</a>,
<a href="/blog/tag/example/">example</a>,
<a href="/blog/tag/wishlist/">wishlist</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/17/day-17-the-wishlist-app/index.html" rel="prev"><strong>Previous Article</strong> Day 17: The Wishlist App</a></li>
<li class="next"><a href="/blog/2017/12/19/day-19-make-your-app-installable/index.html" rel="next"><strong>Next Article</strong> Day 19: Make Your App Installable </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable 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/19/day-19-make-your-app-installable/" property="og:url">
<meta content="Day 19: Make Your App Installable" property="og:title">
<meta content="A few simple changes to make your application installable." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/19/day-19-make-your-app-installable/container_ship.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 19: Make Your App Installable - 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_19_day-19-make-your-app-installable view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
<time class="date" datetime="2017-12-19">Dec 19, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Container ship loading at the dock" src="/blog/2017/12/19/day-19-make-your-app-installable/container_ship.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Thus far we have always run our applications from the local directory.
That is usually the project root directory and/or the repository checkout.
But did you know that with only a few changes you can make your application installable like other Perl modules?</p>
<p>While, you must do this if you want to upload your application to CPAN, even if you don't intend to do that, it still has benefits.
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
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'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't intend to deprecate it, I think there are even easier patterns now.</p>
<h2>The Share Directory</h2>
<p>Perl has a lesser known and somewhat informal functionality for bundling static files called a "share directory".
Create a directory in your project root called <code>share</code> which will serve for this purpose.
Then move the <code>templates</code> and <code>public</code> directories from your project root into that directory.
You should also move any other static files like say database migration files (e.g. <code>wishlist.sql</code> from <a href="https://mojolicious.io/blog/2017/12/18/day-18-the-wishlist-model/">yesterday</a>).</p>
<p>Although each install tool has different ways of specifying where the share directory is located during the development phase, none is espectially difficult to work with.
One reason I chose the name <code>share</code> is because my preferred installation tool <a href="https://metacpan.org/pod/Module::Build::Tiny">Module::Build::Tiny</a> (which I use via <a href="https://metacpan.org/pod/App::ModuleBuildTiny">App::Modu...
The others are configurable in the install scripts themselves (Makefile.PL/Build.PL/dist.ini).
For <a href="https://metacpan.org/pod/Module::Build">Module::Build</a>, you set the <a href="https://metacpan.org/pod/Module::Build::API#share_dir"><code>share_dir</code></a> parameter</p>
<pre><code>share_dir => 'share'
</code></pre>
<p>For <a href="https://metacpan.org/pod/ExtUtils::MakeMaker">ExtUtils::MakeMaker</a> use <a href="https://metacpan.org/pod/File::ShareDir::Install">File::ShareDir::Install</a></p>
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
The major innovation that makes this method possible and so painless is the module <a href="https://metacpan.org/pod/File::Share">File::Share</a>.
While it isn't <a href="https://metacpan.org/pod/File::ShareDir">the original module</a> used to locate share directories after installation, it is the best one since it also works before hand during development too.
To do so it uses the heuristic of looking to see if a directory exists named <code>share</code> in that particular location (and a few checks for sanity), thus the second reason we call the directory that.</p>
<p>When used we get the absolute path to the share directory of your distribution.
Usually the name of the distribution is the name of your main module with the <code>::</code> replaced by <code>-</code>.</p>
<h2>Use in Mojolicious Apps</h2>
<p>To use File::Share in a Mojolicious (Full) app I recommend wrapping it in a <a href="http://mojolicious.org/perldoc/Mojo/File">Mojo::File</a> object and storing it in an attirbute on the app.
The attribute can be named anything, perhaps even as simple as <code>files</code>, though for the <a href="https://github.com/jberger/Wishlist/blob/blog_post/installable/lib/Wishlist.pm#L10-L14">Wishlist app</a> I have used the name <code>dist_dir</c...
<p>Why <code>dist_dir</code>?
Well that is the name of the function provided by File::Share.</p>
<pre><code>package Wishlist;
use Mojo::Base 'Mojolicious';
...
use File::Share;
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
unshift @{ $app->renderer->paths }, $templates;
}
</code></pre>
<p>Note that you could use that kind of process to allow other configured files to be relative to the home.
The application home is a great place to put data like a sqlite database; the sqlite database file seen earlier, which as show had to be an absolute path.
Indeed such a transform exists already exists in the Wishlist app but was omitted above for clarity.</p>
<h2>In Conclusion</h2>
<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'll see that it it really isn'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've seen (other than perhaps File::Share).
If you don't need to think about theming or customizing the environment variable, then don't worry about it.
But if you find yourself in that situation, you'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>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/18/day-18-the-wishlist-model/index.html" rel="prev"><strong>Previous Article</strong> Day 18: The Wishlist Model</a></li>
<li class="next"><a href="/blog/2017/12/20/day-20-practical-testing/index.html" rel="next"><strong>Next Article</strong> Day 20: Practical Testing </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing 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/20/day-20-practical-testing/" property="og:url">
<meta content="Day 20: Practical Testing" property="og:title">
<meta content="Some practical tricks for testing real-world applications." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/20/day-20-practical-testing/lava.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 20: Practical Testing - 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_20_day-20-practical-testing view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
<time class="date" datetime="2017-12-20">Dec 20, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Taking a sample of lava at Kilauea" src="/blog/2017/12/20/day-20-practical-testing/lava.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Back on <a href="/blog/2017/12/09/day-9-the-best-way-to-test">Day 9</a> we discussed testing and especially <a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>.
Today I want to just briefly talk about some practical things that can come up when testing real world applications.
Once again the discussion will be motivated by the <a href="https://github.com/jberger/Wishlist">Wishlist</a> application that we've been developing these past few days.</p>
</section>
<section id="section-2">
<h2>Configuration Overrides</h2>
<p>In the Day 9 article I mentioned that the Test::Mojo <a href="http://mojolicious.org/perldoc/Test/Mojo#new">constructor</a> could be passed configuration overrides.
In this example we can see how that override lets us ensure that we are testing on a fresh and isolated database.</p>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
title => '<span class="hljs-string">Dark Matter</span>',
url => '<span class="hljs-string">lordnibbler.org</span>',
},
],
}, '<span class="hljs-string">correct initial user state</span>';
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's in-memory (and Mojo::SQLite's on-disk temporary) databases are really handy for testing because they are automatically testing in isolation.
You don'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<n>">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>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
And you have to clean up after yourself otherwise the next time the test is run it will be affected by the remaining data.</p>
<h2>Mocking Helpers</h2>
<p>If you have done any testing you've probably dealt with mocking, but if you haven't, mocking is the act of replacing functionality from essentially unrelated code with test-specific code.
Doing this lets you test one section of code (called a unit) in isolation from others.</p>
<p>Everyone has their favorite mock library.
There are so many tastes and styles that in the end Many people make their own, including <a href="https://metacpan.org/pod/Mock::MonkeyPatch">yours truly</a>.
Of course you can use those libraries in Mojolicious when appropriate.
As I mentioned before you can also mock out services by attaching tiny Mojolicious applications to a UserAgent's <a href="http://mojolicious.org/perldoc/Mojo/UserAgent#server"><code>server</code></a> attribute or making an entire external service...
<p>In some cases however, the natural place to mock is in the place of a helper.
When you think about it, this is actually rather obvious since helpers are often the glue used in Mojolicious applications to combine disparate code, like models or in this case the <a href="https://github.com/jberger/Wishlist/blob/blog_post/practica...
<p>To test this we could actually do any of the mentioned options from mocking <a href="https://metacpan.org/pod/LinkEmbedder#get">LinkEmbedder->get</a> to attaching a mock service.
That said it is sufficient here to just replace the helper, which is as easy as assigning over it.</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">Test::More</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Test::Mojo</span>;
<span class="hljs-keyword">my</span> <span class="hljs-type">$t</span> = <span class="hljs-function">Test::Mojo</span>->new(
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
->element_exists(
'<span class="hljs-string">form input[type="hidden"][name="url"][value="coolbeans.notasite"]</span>'
);
is <span class="hljs-type">$url</span>, '<span class="hljs-string">coolbeans.notasite</span>', '<span class="hljs-string">correct site was requested</span>';
done_testing;
</code></pre>
<p><small><a href="https://github.com/jberger/Wishlist/blob/blog_post/practical_testing/t/embed.t">t/embed.t</a></small></p>
<p>Because the template expects the result to be an object we have to build a tiny class to contain our mock results.
Also whenever you are mocking, it is important to check the input your mock received as well as the results that the calling code derives from your mock return value.</p>
<p>In the test you can also see some examples of how to use selectors to test for both text and attribute values.
The text test is especially important because it shows that the html value that I got back from the LinkEmbedder isn't being escaped by the template and will render as HTML to the client.</p>
<p>A few more tests and some documentation and our application will really be taking final shape!</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=11337658">Image</a> by Hawaii Volcano Observatory, USGS - <a class="external text" href="http://hvo.wr.usgs.gov/kilauea/update/archive/2009/2009_Jun-Oct.html" rel="nofol...
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/mocking/">mocking</a>,
<a href="/blog/tag/testing/">testing</a>,
<a href="/blog/tag/wishlist/">wishlist</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/19/day-19-make-your-app-installable/index.html" rel="prev"><strong>Previous Article</strong> Day 19: Make Your App Installable</a></li>
<li class="next"><a href="/blog/2017/12/21/day-21-virtually-a-lumberjack/index.html" rel="next"><strong>Next Article</strong> Day 21: Virtual(ly a) Lumberjack </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack 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="@CandyAngel_Nay" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2017/12/21/day-21-virtually-a-lumberjack/" property="og:url">
<meta content="Day 21: Virtual(ly a) Lumberjack" property="og:title">
<meta content="Using the Mojo toolkit to help reverse-engineer HMDs" property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/21/day-21-virtually-a-lumberjack/vr.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 21: Virtual(ly a) Lumberjack - mojolicious.io</title>
<meta content="CandyAngel" name="author">
<meta content="Statocles 0.093" name="generator">
<link href="/static/favicon.ico" rel="shortcut icon">
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
<time class="date" datetime="2017-12-21">Dec 21, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Woman wearing VR goggles outside" src="/blog/2017/12/21/day-21-virtually-a-lumberjack/vr.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>What do you do when you want to split up a stream of data in real-time while
giving the user instructions?</p>
<p>This is just what I wanted to do to aid in reverse-engineering the USB
protocol of Virtual Reality devices known as Head Mounted Displays (HMD), for
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
<p>HMDs are used to create virtual reality environments. When worn, two slightly
different images are drawn to each side of the screen, with each side visible
to only one eye. This imitates binocular vision and creates an image with a
feeling of depth. By tracking the rotation of the unit, the user can then look
around this environment.</p>
<p>The recent resurgence of Virtual Reality devices can be attributed to the Rift
DK1, released by Oculus in March 2013.</p>
<p>By logging the packets generated during each movement, we can compare the
content of each log to identify which bytes are related to which action. Such
movements include roll (tilting head side-to-side), pitch (looking up and
down) and yaw (turning left/right). Though position isn't tracked, we also
look for sway (left-right translation), surge (back and forth) and heave (up
and down) information as this is used in combination with the other values for
accurate tracking of rotation.</p>
<p>Mojo is an amazing toolkit for web development, as shown in previous
calendar entries, but using components of it can also solve problems in other
non-web spaces like these. Why use Mojo for this? Because it makes it <em>easy</em>.</p>
</section>
<section id="section-2">
<h2>Down By The Riverside</h2>
<p>First of all, we need the stream of data.
Initially, I used <code>tshark</code> for this, though another member of the OpenHMD team
built another application, OpenHMD-Dumper, which has both a device selection
interface and opens the device to read the packets, which makes this much
easier.
Doing so allows us to ask others who have HMDs to send us these logs, so we
can add driver support for devices none of the team have access to!
Otherwise, we use something else to open the device and read data and <code>tshark</code>
to capture and output the packet data.</p>
<pre><code>tshark -i usbmon1 -Y 'usb.src == "1.22.1"' -T fields -e usb.capdata
</code></pre>
<p>You may need to change your buffering to line-mode. You can do so by prefixing
<code>stdbuf -oL</code> like so:</p>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
The simple file format makes it very easy to graph the data with gnuplot.
In the worst/simple case scenario, you can simply graph at each offset and
various sizes until you see a (rough) sine wave, indicating you've got the
right offset and number of bytes for that part of the puzzle!</p>
<h2>Moving On To Greater Things</h2>
<p>This tool could easily be improved (and will be, when it is next called upon).
For example, both the extraction and fixing of the headers could be made into
commands, as demonstrated in
<a href="https://mojolicious.io/blog/2017/12/06/day-6-adding-your-own-commands/">Day 6</a>.</p>
<p>It could also be made into a proper web application where the user can select
the action they want to do and an animation shows the movement that is wanted
(not everyone is familiar with nautical terms after all!).
Perhaps even with a device select?
A UI that allows the user to select the offset, size and unpack method and
render the graphs as appropriate could cut down the time to analyse.
Better yet, if it could detect which combinations result in a rough sine wave,
it could narrow down and present the user with likely candidates for each
movement..</p>
<p>.. but those are improvements for another day!</p>
</section>
<small><p><a href="https://www.pexels.com/photo/sky-woman-clouds-girl-123335/">Image</a> by Bradley Hook, in the Public Domain.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/non-web/">non-web</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="/static/avatar_candyangel.jpg">
</div>
<div class="about">
<h5>CandyAngel</h5>
<p>CandyAngel is a Perl developer who tends to demonstrate her love of any particular piece of software by making it do things it was never intended to do.. yet still do it well.</p>
</div>
</div>
<ul class="post-nav cf">
<li class="prev"><a href="/blog/2017/12/20/day-20-practical-testing/index.html" rel="prev"><strong>Previous Article</strong> Day 20: Practical Testing</a></li>
<li class="next"><a href="/blog/2017/12/22/day-22-how-to-build-a-public-rest-api/index.html" rel="next"><strong>Next Article</strong> Day 22: A RESTful API with OpenAPI </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api 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="@jhthorsen" name="twitter:creator">
<meta content="https://mojolicious.io/blog/2017/12/22/day-22-how-to-build-a-public-rest-api/" property="og:url">
<meta content="Day 22: A RESTful API with OpenAPI" property="og:title">
<meta content="How to build a public REST API for your Mojolicious application" property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/22/day-22-how-to-build-a-public-rest-api/hammock_beach.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 22: A RESTful API with OpenAPI - mojolicious.io</title>
<meta content="Jan Henning Thorsen" name="author">
<meta content="Statocles 0.093" name="generator">
<link href="/static/favicon.ico" rel="shortcut icon">
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
<time class="date" datetime="2017-12-22">Dec 22, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="A hammock on a beach" src="/blog/2017/12/22/day-22-how-to-build-a-public-rest-api/hammock_beach.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>The <a href="https://www.openapis.org/">OpenAPI</a> Specification (formerly named
Swagger) is an API description format for REST APIs. An API specification
written using the the rules set by the Open API Initiative can be used to
describe:</p>
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
behind both the plugin and client side in Perl. Check it out, if you're
interested in JSON-Schema.</li>
</ul>
</section>
<small><p><a href="https://www.pexels.com/photo/hammock-palm-trees-bungalows-bora-bora-104750">Image</a> by Chris McClave, in the Public Domain.</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/api/">api</a>,
<a href="/blog/tag/openapi/">openapi</a>,
<a href="/blog/tag/rest/">rest</a>,
<a href="/blog/tag/swagger/">swagger</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/806800a3aeddbad6af673dade958933b">
</div>
<div class="about">
<h5>Jan Henning Thorsen</h5>
<p>Jan (aka batman) is a self taught Perl hacker and a member of the Mojolicious Core Team. He likes to work with anything that has to do with web - both frontend and backend. Got more questions? Send him an <a href="mailto:jh...
</div>
</div>
<ul class="post-nav cf">
<li class="prev"><a href="/blog/2017/12/21/day-21-virtually-a-lumberjack/index.html" rel="prev"><strong>Previous Article</strong> Day 21: Virtual(ly a) Lumberjack</a></li>
<li class="next"><a href="/blog/2017/12/23/day-23-one-liners-for-fun-and-profit/index.html" rel="next"><strong>Next Article</strong> Day 23: One-Liners for Fun and Profit </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit 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/23/day-23-one-liners-for-fun-and-profit/" property="og:url">
<meta content="Day 23: One-Liners for Fun and Profit" property="og:title">
<meta content="For those times when you just need a quick check or example." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/23/day-23-one-liners-for-fun-and-profit/chess.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 23: One-Liners for Fun and Profit - 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_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
<time class="date" datetime="2017-12-23">Dec 23, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Old man playing timed chess" src="/blog/2017/12/23/day-23-one-liners-for-fun-and-profit/chess.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Perl is well-known for its <a href="http://www.catonmat.net/download/perl1line.txt">one-liners</a>: short programs written as part of the <a href="http://perldoc.perl.org/perlrun.html">command line invocation of the interpreter</...
Certainly every programmer or sysadmin has the need, from time to time, to do a quick one-off task programmatically.
Such tasks can be done with a full script, to be sure, but once you get the hang of writing them, one-liners can save the time and hassle of actually doing so.</p>
<p>These tasks may include removing unwanted lines from files, collecting data from logs, or even a quick proof-of-concept of something that would become a script later.
They can read lines in files, even multiple files, can operate on files in-place, can read from STDIN as a pipe.
But while one-liners have been tools of the trade for these activities, certainly no such thing would be practical for web tasks, right?</p>
<p>But of course, on <a href="https://mojolicious.io/blog/2017/12/05/day-5-your-apps-built-in-commands/">day 5</a> and <a href="https://mojolicious.io/blog/2017/12/06/day-6-adding-your-own-commands/">day 6</a> of this series that we saw that we can b...
We have even seen how to use the <a href="http://mojolicious.org/perldoc/Mojolicious/Command/eval">eval</a> command to run a one-liner against your app.
So could we take this further?</p>
<p>Could we do remote data fetching and manipulation as a one-liner?
Could we build an entire web application as a one-liner?
Would I be asking if the answer was no?</p>
</section>
<section id="section-2">
<h2>A Note on Readability</h2>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
</code></pre>
<p>This simple request is something you could have done with the <a href="http://mojolicious.org/perldoc/Mojolicious/Command/get">get</a> command,</p>
<pre><code>mojo get mojolicious.org title text
</code></pre>
<p>however the one liner form can let you do much more, like complex mapping.
For example, to get the text and link from each link on a site as a data structure.</p>
<pre><code>perl -Mojo -E 'print r({ g("mojolicious.io/blog")->dom->find("a")->map(sub{ $_->text => $_->{href} })->each })'
</code></pre>
<p>You can also use Perl's looping constructs.
Perhaps you have a file full of sites for which you want to get some data.</p>
<pre><code>$ cat sites
mojolicious.org
mojolicious.io
mojocasts.com
</code></pre>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
If these don't seem like your's you can completely ignore them.</p>
<p>However, once you start using them, I think you'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>,
<a href="/blog/tag/lite/">lite</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/22/day-22-how-to-build-a-public-rest-api/index.html" rel="prev"><strong>Previous Article</strong> Day 22: A RESTful API with OpenAPI</a></li>
<li class="next"><a href="/blog/2017/12/24/day-24-release-and-wrap-up/index.html" rel="next"><strong>Next Article</strong> Day 24: Release and Wrap-Up </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up 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/24/day-24-release-and-wrap-up/" property="og:url">
<meta content="Day 24: Release and Wrap-Up" property="og:title">
<meta content="The Wishlist app is released to CPAN and a wrap-up for the advent calendar." property="og:description">
<meta content="https://mojolicious.io/blog/2017/12/24/day-24-release-and-wrap-up/tree_unfocused.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 24: Release and Wrap-Up - 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_24_day-24-release-and-wrap-up view on Meta::CPAN
</head>
<body>
<header>
<div class="row">
<div class="twelve columns">
<div class="logo">
<a href="/index.html">
<h3 style="color: #fff">mojolicious.io</h3>
</a>
</div>
<nav id="nav-wrap">
<a class="mobile-btn" href="#nav-wrap" title="Show navigation">Show navigation</a>
<a class="mobile-btn" href="#" title="Hide navigation">Hide navigation</a>
<ul class="nav" id="nav">
<!-- li.current is given a different styling -->
<li><a href="/blog">Blog</a></li>
<li><span><a href="/">Advent Calendar</a></span>
<ul>
<li><a href="/">2018</a></li>
<li><a href="/page/advent/2017">2017</a></li>
</ul>
</li>
<li><span><a href="https://mojolicious.org">mojolicious.org</a></span>
<ul>
<li><a href="http://mojolicious.org/perldoc">Documentation</a></li>
<li><a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Tutorial">Tutorial</a></li>
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
</div>
</header>
<div id="page-title">
<div class="row">
<div class="ten columns centered text-center">
<h1>Mojo Wonk Blog<span>.</span></h1>
<p>A semi-offical blog dedicated to the Mojolicious web framework</p>
</div>
</div>
</div>
<div class="content-outer">
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
<time class="date" datetime="2017-12-24">Dec 24, 2017</time>
</p>
</div>
<div class="post-thumb">
<!-- theme suggests 1300x500 -->
<img alt="Out of focus image of lit Christmas tree" src="/blog/2017/12/24/day-24-release-and-wrap-up/tree_unfocused.jpg">
</div>
<div class="post-content">
<section id="section-1">
<p>Over the course of this advent calendar, we have discussed the <a href="https://mojolicious.io/blog/tag/wishlist/">Wishlist example application</a> several times.
We used it to motivate discussions about <a href="/blog/2017/12/17/day-17-the-wishlist-app/">templates</a>, <a href="/blog/2017/12/18/day-18-the-wishlist-model/">models</a>, <a href="/blog/2017/12/19/day-19-make-your-app-installable/">installable app...
In this post I want to use it somewhat differently.
I would like to motivate you, the reader, into action in a few different ways.</p>
</section>
<section id="section-2">
<h3>The Double-Edged Sword</h3>
<p>The internet has made so many amazing things possible.
Old friendships are kept alive.
Personal photographs can be stored forever and shared with friends.
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
All of the documentation is online at <a href="http://mojolicious.org/perldoc">http://mojolicious.org/perldoc</a>.</p>
<h2>Happy holidays from the Mojolicious community!</h2>
</section>
<small><p><a href="https://www.pexels.com/photo/art-blurred-blurry-bokeh-383646/">Image</a> by Tim Mossholder, in the Public Domain</p>
</small>
<p class="tags">
<span>Tagged in </span>:
<a href="/blog/tag/advent/">advent</a>,
<a href="/blog/tag/wishlist/">wishlist</a>
</p>
<div class="bio cf">
<div class="gravatar">
<img alt="author image" src="https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147">
</div>
<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/23/day-23-one-liners-for-fun-and-profit/index.html" rel="prev"><strong>Previous Article</strong> Day 23: One-Liners for Fun and Profit</a></li>
<li class="next"><a href="/blog/2018/03/18/graphql-openapi/index.html" rel="next"><strong>Next Article</strong> Mojolicious, OpenAPI - and GraphQL </a></li>
</ul>
</div>
</article>
</div>
<div class="four columns end" id="secondary">
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
<div class="widget widget_tag_cloud">
<h5 class="widget-title">Tags</h5>
<div class="tagcloud cf">
<a href="/blog/tag/administration/">administration</a>
<a href="/blog/tag/advent/">advent</a>
<a href="/blog/tag/angular/">Angular</a>
<a href="/blog/tag/api/">api</a>
<a href="/blog/tag/app/">app</a>
<a href="/blog/tag/authentication/">authentication</a>
<a href="/blog/tag/caching/">caching</a>
<a href="/blog/tag/carton/">carton</a>
<a href="/blog/tag/command/">command</a>
<a href="/blog/tag/css/">css</a>
<a href="/blog/tag/dancer/">dancer</a>
<a href="/blog/tag/debugging/">debugging</a>
<a href="/blog/tag/deployment/">deployment</a>
<a href="/blog/tag/development/">development</a>
<a href="/blog/tag/documentation/">documentation</a>
<a href="/blog/tag/example/">example</a>
<a href="/blog/tag/fluent/">fluent</a>
<a href="/blog/tag/full/">full</a>
<a href="/blog/tag/graphql/">graphql</a>
<a href="/blog/tag/growing/">growing</a>
<a href="/blog/tag/headers/">headers</a>
<a href="/blog/tag/hello-world/">hello world</a>
<a href="/blog/tag/html/">html</a>
<a href="/blog/tag/installing/">installing</a>
<a href="/blog/tag/javascript/">JavaScript</a>
<a href="/blog/tag/ldap/">LDAP</a>
<a href="/blog/tag/lite/">lite</a>
<a href="/blog/tag/minion/">minion</a>
<a href="/blog/tag/mocking/">mocking</a>
<a href="/blog/tag/model/">model</a>
<a href="/blog/tag/mojoconf/">mojoconf</a>
<a href="/blog/tag/non-blocking/">non-blocking</a>
<a href="/blog/tag/non-web/">non-web</a>
<a href="/blog/tag/openapi/">openapi</a>
<a href="/blog/tag/promises/">promises</a>
<a href="/blog/tag/psgi/">psgi</a>
<a href="/blog/tag/rendering/">rendering</a>
<a href="/blog/tag/rest/">rest</a>
<a href="/blog/tag/roles/">roles</a>
<a href="/blog/tag/routing/">routing</a>
<a href="/blog/tag/session/">session</a>
<a href="/blog/tag/swagger/">swagger</a>
<a href="/blog/tag/templates/">templates</a>
<a href="/blog/tag/testing/">testing</a>
<a href="/blog/tag/theme/">theme</a>
<a href="/blog/tag/useragent/">useragent</a>
<a href="/blog/tag/wishlist/">wishlist</a>
<a href="/blog/tag/xml/">xml</a>
<a href="/blog/tag/yancy/">yancy</a>
</div>
</div>
</aside>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="twelve columns">
<ul class="footer-nav">
<li><a href="/blog">Blog.</a></li>
<li><a href="/">Advent Calendar.</a></li>
<li><a href="https://mojolicious.org">mojolicious.org.</a></li>
</ul>
<ul class="footer-social">
<li><a href="https://github.com/mojolicious/mojo"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/search?q=%23mojolicious"><i class="fa fa-twitter"></i></a></li>
<li><a href="/blog/index.rss"><i class="fa fa-rss"></i></a></li>
</ul>
<ul class="copyright">
<li>Copyright é 2017 Joel Berger</li>
<li><a href="https://github.com/MojoliciousDotIO/mojolicious.io">Contribute to this site on Github</a></li>
<li>Design by <a href="http://www.styleshout.com/">Styleshout</a></li>
<li>Made with <a href="http://preaction.me/statocles">Statocles</a></li>
<li>Powered by <a href="http://www.perl.org">Perl</a></li>
</ul>
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",
);
gen_curried_sub(
'App::CreateAcmeCPANModulesImportModules::create_acme_cpanmodules_import_modules',
{
modules => [
{
name => '2017',
url => "https://mojolicious.io/page/advent/2017/",
extract_urls => [map {$daily_urls{$_}} "01".."24"],
devscripts/update view on Meta::CPAN
ignore_empty => 1,
namespace => 'Acme::CPANModules::Import::MojoliciousAdvent',
user_agent => 'Mozilla/5.0',
dist_dir => "$Bin/..",
},
'app',
);
Perinci::CmdLine::Any->new(
url => '/main/app',
log => 1,
)->run;
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_02.pm view on Meta::CPAN
package Acme::CPANModules::Import::MojoliciousAdvent::2017_12_02;
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/02/day-2-the-stash] (retrieved on 2018-12-30). Visit the URL for the full contents.",entries=>[{module=>"Mojo::Message::Req...
1;
# ABSTRACT: Modules mentioned in Mojolicious Advent Calendar 2017 (day 02)
__END__
=pod
=encoding UTF-8
=head1 NAME
Acme::CPANModules::Import::MojoliciousAdvent::2017_12_02 - Modules mentioned in Mojolicious Advent Calendar 2017 (day 02)
=head1 VERSION
This document describes version 0.001 of Acme::CPANModules::Import::MojoliciousAdvent::2017_12_02 (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/02/day-2-the-stash> (retrieved on 2018-12-30). Visit the URL for the full contents.
Modules mentioned in Mojolicious Advent Calendar 2017 (day 02).
This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/02/day-2-the-stash] (retrieved on 2018-12-30). Visit the URL for the full contents.
=head1 INCLUDED MODULES
=over
=item * L<Mojo::Message::Request>
=item * L<Mojo::Message::Response>
=item * L<Mojo::Transaction>
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_03.pm view on Meta::CPAN
package Acme::CPANModules::Import::MojoliciousAdvent::2017_12_03;
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/03/day-3-using-named-routes] (retrieved on 2018-12-30). Visit the URL for the full contents.",entries=>[{module=>"Mojo::URL...
1;
# ABSTRACT: Modules mentioned in Mojolicious Advent Calendar 2017 (day 03)
__END__
=pod
=encoding UTF-8
=head1 NAME
Acme::CPANModules::Import::MojoliciousAdvent::2017_12_03 - Modules mentioned in Mojolicious Advent Calendar 2017 (day 03)
=head1 VERSION
This document describes version 0.001 of Acme::CPANModules::Import::MojoliciousAdvent::2017_12_03 (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/03/day-3-using-named-routes> (retrieved on 2018-12-30). Visit the URL for the full contents.
Modules mentioned in Mojolicious Advent Calendar 2017 (day 03).
This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/03/day-3-using-named-routes] (retrieved on 2018-12-30). Visit the URL for the full contents.
=head1 INCLUDED MODULES
=over
=item * L<Mojo::URL>
=item * L<Mojolicious::Controller>
=item * L<Mojolicious::Plugin::TagHelpers>
lib/Acme/CPANModules/Import/MojoliciousAdvent/2017_12_04.pm view on Meta::CPAN
package Acme::CPANModules::Import::MojoliciousAdvent::2017_12_04;
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/04/day-4-dont-fear-the-full-app] (retrieved on 2018-12-30). Visit the URL for the full contents.",entries=>[{module=>"Mojol...
1;
# ABSTRACT: Modules mentioned in Mojolicious Advent Calendar 2017 (day 04)
__END__
=pod
=encoding UTF-8
=head1 NAME
Acme::CPANModules::Import::MojoliciousAdvent::2017_12_04 - Modules mentioned in Mojolicious Advent Calendar 2017 (day 04)
=head1 VERSION
This document describes version 0.001 of Acme::CPANModules::Import::MojoliciousAdvent::2017_12_04 (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/04/day-4-dont-fear-the-full-app> (retrieved on 2018-12-30). Visit the URL for the full contents.
Modules mentioned in Mojolicious Advent Calendar 2017 (day 04).
This list is generated by extracting module names mentioned in [https://mojolicious.io/blog/2017/12/04/day-4-dont-fear-the-full-app] (retrieved on 2018-12-30). Visit the URL for the full contents.
=head1 INCLUDED MODULES
=over
=item * L<Mojolicious::Command::inflate>
=item * L<Mojolicious::Lite>
=back