view release on metacpan or search on metacpan
123456789101112131415161718192021222324252627282930313233343536373839404142# 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
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
time
class=
"date"
datetime=
"2017-12-01"
>Dec 1, 2017</
time
>
</p>
</div>
<div class=
"post-thumb"
>
<!-- theme suggests 1300x500 -->
<img alt=
"hot air ballons"
src=
"/blog/2017/12/01/day-1-getting-started/1280px-Colorado_Springs_Hot_Air_Balloon_Competition.jpg"
>
</div>
<div class=
"post-content"
>
<section id=
"section-1"
>
<h2>Start at the Beginning</h2>
<p>In this Advent Calendar series, some posts will be introductory, some will be advanced, some will be on new features.
Who knows what could be
next
?
But
for
now let&
#39;s ensure a level playing field by working out how to get started.</p>
devdata/https_mojolicious.io_blog_2017_12_01_day-1-getting-started view on Meta::CPAN
225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268<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"
>
</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
271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_02_day-2-the-stash view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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&
#39;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
320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363<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"
>
</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
366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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&
#39;t have to.
And yet far too many web application hard-code their urls, especially internal ones.
But what
if
you didn&
#39;t have to?</p>
<p>Each Mojolicious route
has
its own <a href=
"http://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Named-routes"
>name</a> which can be used to generate urls.
devdata/https_mojolicious.io_blog_2017_12_03_day-3-using-named-routes view on Meta::CPAN
227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269I also like to separate concerns.
Anyway, you can choose whichever you like best.
After all, there&
#39;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"
>
</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&
#39;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
272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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&
#39; 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
147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185<p>After repeated attempts to convince people that there is very little difference between the two, I&
#39;ve found that there is one really great way to turn the conversation.
I show them the code.
No really <a href=
"https://github.com/kraih/mojo/blob/master/lib/Mojolicious/Lite.pm"
>take a look</a>.
As of this writing, Mojolicious::Lite is only 37 lines of code (as computed by David A. Wheeler&
#39;s <a href="https://www.dwheeler.com/sloccount/">SLOCCount</a>)!
How much difference could there be in 37 lines of code?</p>
<p>Ok now that you believe me, let&
#39;s talk about those few differences.</p>
<h2>The Script and the Class</h2>
<p>In a Lite script, your application logic lives right there in the script.
If a Full app, your logic goes in a separate class, mostly in the <code>startup</code> method, but remove <code>app-
>
;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
295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338With 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"
>
</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&
#39;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
341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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
216217218219220221222223224225226227228229230231232233234235236<p>It outputs your current version of Perl and Mojolicious along
with
any installed optional libraries.
For example, you&
#39;ll want to install <a href="https://metacpan.org/pod/IO::Socket::SSL">IO::Socket::SSL</a> if you want to fetch or serve sites over HTTPS.
It then checks to see
if
there is an updated version of Mojolicious available.</p>
<h3>The routes Command</h3>
<p>Once you started writing your application, you might want to introspect it a little bit, especially
for
debugging purposes.
The most straightforward command of that nature is <a href=
"http://mojolicious.org/perldoc/Mojolicious/Command/routes"
><code>routes</code></a>.
Simply run it on your app to see what routes you have
defined
.</p>
<p>For example, we can run it on Santa&
#39;s application from <a href="/blog/2017/12/03/day-3-using-named-routes">day 3</a>.</p>
<pre><code>$ perl santa.pl routes
/toy/:toy_name GET
"
;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
320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372<pre><code>perl myapp.pl
eval
&
#39;app->pg->migrations->migrate'
</code></pre>
<p>Or just check that the database is reachable.</p>
<pre><code>perl myapp.pl
eval
-V &
#39;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"
>
</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&
#39;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
375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
<
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&
#39;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&
#39;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&
#39;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
196197198199200201202203204205206207208209210211212213214215216BEGIN {
unshift
@INC
,
"
;
$FindBin::Bin
/../lib
"
; }
# Start command line interface for application
Mojolicious::Commands-
>
;start_app(&
#39;MyWeatherApp');
</code></pre>
<h3>The Model</h3>
<p>Now let&
#39;s make a model class.
A model is the business logic of any application.
It knows how to
do
the important work and should be free of anything to
do
with
your actual site.</p>
<p>We&
#39;ll store it in <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp/Model/Weather.pm">lib/MyWeatherApp/Model/Weather.pm</a>.</p>
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
493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535Indeed they are one of
my
very favorite things about Mojolicious.
Perhaps you can
tell
?</p>
</section>
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"
>
</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&
#39;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
538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_07_day-7-using-template-variants-for-a-beta-landing-page view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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"
>
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
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
197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247<
;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&
#39;t the
first
time
I&
#39;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&
#39;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
feature, but that&
#39;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>
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
250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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
291292293294295296297298299300301302303304305306307308309310311<pre><code>$ perl test-api.pl get -M POST -c &
#39;{ "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 &
#39;<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
382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424<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>
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
427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
<
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&
#39;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&
#39;ll give a quick overview of how to use Test::Mojo and some of its methods.
Rest assured you&
#39;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
244245246247248249250251252253254255256257258259260261262263264265266267268269270<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&
#39;ve already seen and in the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Testing">testin...
That said
for
the sake explication, let&
#39;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(&
#39;/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(&
#39;/login', form => {user => 'me', pass => 'secr3t'});
$t
-
>
;put_ok(&
#39;/inventory/12345', json => {type => 'widget', value => 'tons'});
</code></pre>
<p>Or you can submit raw data as a trailing argument</p>
$t
-
>
;post_ok(&
#39;/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
355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396<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&
#39;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"
>
</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
399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_10_day-10-give-the-customer-what-they-want view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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
198199200201202203204205206207208209210211212213214215216217218If 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&
#39;s examine this as a test.
Imagine a test
for
an application that returns information about Santa&
#39;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 &
#39;<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 =
>
; &
#39;<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
299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340<p>Of course under the hood this is using Content Negotiation and several other modules to provide CSV, HTML, JSON, text, XLS, and XLSX outputs.
It is configurable via several stash
values
that might be set.
If you&
#39;d like to dig into that code to see how it works, please feel free.</p>
</section>
</small>
<p 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"
>
</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
343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138
<
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&
#39;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&
#39;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
174175176177178179180181182183184185186187188189190191192193
encode_json({some =
>
; [&
#39;json', 'data']})
);
</code></pre>
<p>or a similar example to the above using <code>build_tx</code>.
I think you&
#39;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&
#39;s documented example is</p>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368However, 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&
#39;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"
>
</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
371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131
<
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&
#39;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&
#39;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
200201202203204205206207208209210211212213214215216217218219220<p>This adds the <code>
has
</code> keyword to your
package
which, as we will see soon, declares the class&
#39;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>
</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&
#39;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&
#39;t the preferred usage.</p>
<p>The <code>
has
</code> keyword, added by the
import
above, gives us that nice declarative style that Perl users are familiar
with
from Moose and Moo.
The usage is different from those, owing to the limited choices available in declaration.
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
-
>
;at(&
#39;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&
#39;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&
#39;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"
>
</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
358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
<
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&
#39;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&
#39;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
242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
$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&
#39;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&
#39;s own role handling and even inspired adding it to the core.</p>
<p>As I&
#39;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!
use
Test::More;
use
Test::Mojo;
my
$t
= Test::Mojo
-
>
;with_roles(&
#39;+PSGI', '+TestDeep', '+Debug')
-
>
;new(&
#39;/path/to/app.psgi');
$t
-
>
;get_ok(&
#39;/')
-
>
;text_deeply(
&
#39;nav a',
[
qw( Home Blog Projects About Contact )
],
&
#39;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
291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332<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"
>
</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
335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
91011121314151617181920212223242526272829303132<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
35363738394041424344454647484950515253545556575859606162636465666768</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call view on Meta::CPAN
77787980818283848586878889909192939495969798
</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
111112113114115116117118119120121122123124125126127128129130131
<
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
212213214215216217218219220221222223224225226227228229230231<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&
#39;t work?</h2>
<p>In the above, we assumed that everything worked. What
if
it doesn&
#39;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
304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345<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
348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
<
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"
>
Yancy allows you to easily administrate your site’s content just by
your site&
#39;s content can be in
</section>
<section id=
"section-2"
>
<h2>Demonstration</h2>
<p>For an demonstration application, let’s create a simple blog using
First we need to create a database schema
for
our
blog posts. Let&
#39;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( &
#39;<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>
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&
#39;s our configured Yancy <code>blog</code> collection:</p>
<pre><code class=
"hljs"
>plugin Yancy =
>
; {
collections =
>
; {
blog =
>
; {
required =
>
; [ &
#39;<span class="hljs-string">title</span>', '<span class="hljs-string">markdown</span>', '<span class="hljs-string">html</span>' ],
properties =
>
; {
id =
>
; {
type =
>
; &
#39;<span class="hljs-string">integer</span>',
readOnly =
>
; <span class=
"hljs-number"
>1</span>,
},
title =
>
; {
type =
>
; &
#39;<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
225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319rendered HTML. There&
#39;s plenty of customization options in <a href="http://metacpan.org/pod/Yancy#CONFIGURATION">the Yancy
configuration documentation</a>.</p>
<p>Now we can start up
our
app and go to <a href=
"http://127.0.0.1:3000/yancy"
>http://127.0.0.1:3000/yancy</a> to
manage
our
site&
#39;s content:</p>
<pre><code>$ perl myapp.pl daemon
Server available at http://127.0.0.1:3000
</code></pre>
<p><img alt=
"Screen shot of adding a new blog item with Yancy"
src=
"adding-item.png"
>
<img alt=
"Screen shot of Yancy after the new blog item is added"
src=
"item-added.png"
></p>
<p>Finally, we need some way to display
our
blog posts. <a href=
"http://metacpan.org/pod/Mojolicious::Plugin::Yancy#HELPERS"
>Yancy provides
helpers to access
our
data</a>. Let&
#39;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&
#39;ve got a blog, we will order by the creation date, descending.</p>
<pre><code class=
"hljs"
>get &
#39;<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>(
&
#39;<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>(
&
#39;<span class="hljs-string">blog</span>', {}, { order_by => { -desc => '<span class="hljs-string">created</span>' } },
) ],
);
};
</code></pre>
<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 &
#39;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>
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
322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
<
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&
#39;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&
#39;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&
#39;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
167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202<pre><code>$ perl myapp.pl daemon
</code></pre>
<p>and visit <code>localhost:3000</code> you should see a counter that increments on
each
request.
That data is stored on the client (e.g. in the browser) between
each
request and is incremented on the server
before
sending it back to the client.
Each response is a new cookie
with
the new session data and a new cookie expiration
time
, <a href=
"http://mojolicious.org/perldoc/Mojolicious/Sessions#default_expiration"
>defaulting to one hour</a> from issue.
This also therefore implies that as long as you visit often enough (
before
any one cookie expires) and your data continues to validate against the secret, your session can
last
forever.</p>
<h2>Secret Security</h2>
<p>Now, one other thing you should see is that in your application&
#39;s log output, you should have a message like</p>
<pre><code>Your secret passphrase needs to be changed
</code></pre>
<p>This happens because you are using the
default
secret
for
the application.
This
default
is just the name of the script, as you can see via the <a href=
"/blog/2017/12/05/day-5-your-apps-built-in-commands"
>
eval
command</a></p>
<pre><code>$ perl myapp.pl
eval
-V &
#39;app->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&
#39;s play it safe.</p>
<p>The secret isn&
#39;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
235236237238239240241242243244245246247248249250251252253254255<p>If it finds a <code>secrets</code> parameter in your configuration, it will set it as the <code>secrets</code> on your application.
Since you have one in your new configuration file, it should set that property and the warning should go away.
Congratulations, you have a safer application already!</p>
<p>If sometime later, you suspect that someone
has
guessed your secret, or
if
your secret leaks out, you can change that secret and restart your application.
This will protect your application from malicious users.</p>
<p>For your clients, this will have the jarring effect that all existing sessions will be invalidated.
In the example application the counter would be
reset
.
If instead the session were being used to keep users logged in, they would suddenly be logged out.
If it was
for
tracking a shopping cart ...
no
more shopping cart.</p>
<p>This can actually be useful even
if
your secret is safe but you want to force-invalidate sessions
for
some other reason, like
say
your application was generating corrupt data or worse.
Generally, however, this is something you&
#39;d like to avoid.</p>
<h2>A Random Secret?</h2>
<p>Now perhaps you are asking yourself,
if
Mojolicious knows I&
#39;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
311312313314315316317318319320321322323324325326327328329330331<p>Requests issued by the old credentials will slowly be replaced by new ones as clients
each
make their first requests following the change.
Once you
wait
long enough that any valid session cookie would have expired, you can remove the old secret from the configuration and restart again.</p>
<h2>Restarting</h2>
<p>This is a good
time
to mention <a href=
"http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Hypnotoad"
><code>hypnotoad</code></a>.
It is Mojolicious&
#39; recommended production application server.
It
has
many of the same characteristics as the <a href=
"http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Pre-forking"
><code>prefork</code></a> server but
with
some tuned settings and one killer feature, <a href="http://mojolicious.org/perld...
<p>Note that on native Windows, <code>hypnotoad</code> will not work.
That said, it works great on the new <a href=
"https://blogs.msdn.microsoft.com/wsl/"
>Windows Subsystem
for
Linux</a>!</p>
<p>A major usage difference is that hypnotoad (
for
technical reasons) can&
#39;t use command line parameters.
Instead it takes its parameters via configuration.
It also starts on port <code>8080</code> rather than <code>3000</code> to prevent accidentally exposing your development servers unexpectedly.
For now however, let&
#39;s set it back, more for an example than any particular reason.</p>
<pre><code class=
"hljs"
>{
secrets =
>
; [
&
#39;<span class="hljs-string">w8S4b+90CWwf</span>',
&
#39;<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
367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408It can handle changes to your application, updating dependencies, or even upgrading your version of Perl itself!</p>
<p>For now though, we&
#39;re just satisfied that clients are none the wiser that you rolled the application secret out from under them without losing any connections nor invalidating any sessions.</p>
</section>
<small><p><a href=
"https://commons.wikimedia.org/w/index.php?curid=54930070"
>Image</a> by <a class=
"external text"
href=
"https://www.flickr.com/people/30478819@N08"
rel=
"nofollow"
>Marco Verch</a> - <a class=
"external text"
href="https:/...
</small>
<p class=
"tags"
>
<span>Tagged in </span>:
<a href=
"/blog/tag/advent/"
>advent</a>,
<a href=
"/blog/tag/session/"
>session</a>
</p>
<div class=
"bio cf"
>
<div class=
"gravatar"
>
</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
411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
<
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&
#39;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&
#39;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&
#39;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
213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251The <code>footer</code> buffer would allow additions to be placed at the end of the body but
before
any javascript inclusions.</p>
<p>It is interesting to note that
if
we rendered this template directly, those buffers would all be empty.
Therefore the content put into them must come from someplace
else
.</p>
<h2>A Primary Template</h2>
<p>I mentioned
before
the the layout was like a container that would hold some inner content.
Let&
#39;s consider the simplest such case.</p>
<p>When a user first accesses the site, the will be greeted
with
a login page.</p>
<p><img alt=
"Screen shot of the login page"
src=
"login.png"
></p>
<pre><code class=
"hljs"
>% title &
#39;<span class="hljs-string">Welcome to the Wishlist!</span>';
% layout &
#39;<span class="hljs-string">default</span>';
<
;h2
>
;
<
;%= title %
>
;
<
;/h2
>
;
%= form_for &
#39;<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
278279280281282283284285286287288289290291292293294295296297298
<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 &
#39;<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} ? &
#39;<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 &
#39;<span class="hljs-string">/list/:name</span>' => '<span class="hljs-string">list</span>';
get &
#39;<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>(&
#39;<span class="hljs-...
<span class=
"hljs-type"
>
$c
</span>-
>
;<span class=
"hljs-type"
>render</span>(&
#39;<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
315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
<span class=
"hljs-type"
>
$item
</span>-
>
;{purchased} = <span class=
"hljs-type"
>
$c
</span>-
>
;<span class=
"hljs-type"
>param</span>(&
#39;<span class="hljs-string">purchased</span>');
<span class=
"hljs-type"
>
$c
</span>-
>
;<span class=
"hljs-type"
>redirect_to</span>(&
#39;<span class="hljs-string">list</span>', name => <span class="hljs-type">$user</span>->{name});
};
post &
#39;<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>(&
#39;<span class="hljs-string">title</span>&#...
<span class=
"hljs-type"
>
$c
</span>-
>
;<span class=
"hljs-type"
>redirect_to</span>(&
#39;<span class="hljs-string">/</span>');
};
post &
#39;<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>(&
#39;<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>(&
#39;<span class="hljs-string">/</span>');
};
any &
#39;<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>(&
#39;<span class="hljs-string">/</span>');
};
app-
>
;start;
</code></pre>
<p><small>wishlist.pl</small></p>
<h3>Helpers</h3>
<p>I won&
#39;t go into great detail today as much of the model logic will be replaced in tomorrow'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&
#39;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&
#39;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 &
#39;<span class="hljs-string">Add an item</span>';
% layout &
#39;<span class="hljs-string">default</span>';
devdata/https_mojolicious.io_blog_2017_12_17_day-17-the-wishlist-app view on Meta::CPAN
392393394395396397398399400401402403404405406407408409410411
<
;/div
>
;
<
;div class=
"
;<span class=
"hljs-string"
>panel-body</span>
"
;
>
;
%= form_for &
#39;<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 &
#39;<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 &
#39;<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
419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468<p>The result is a tiny form that contains the data to be stored
if
the user wants to add it to their wishlist.
Because the resulting main page might be quite large, and I want the user to have easy access to decide
if
they want to add the item, I&
#39;ve placed it in the left hand column.
Perhaps this is bad UX, but
for
educational purposes, it shows how these buffers can be used.</p>
<p>We also see
our
first example of <a href=
"http://mojolicious.org/perldoc/Mojolicious/Plugin/DefaultHelpers#include"
><code>include</code></a>.
When called, the renderer immediately renders the template specified and returns the result.</p>
<pre><code class=
"hljs"
>
<
;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 &
#39;<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&
#39;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&
#39;s list by name.</p>
<p><img alt=
"Screen shot of the list page"
src=
"list.png"
></p>
<pre><code class=
"hljs"
>% title user-
>
;{name} .
"
;<span class=
"hljs-string"
>&
#39;s List</span>";
% layout &
#39;<span class="hljs-string">default</span>';
% content_for &
#39;<span class="hljs-string">sidebar</span>' => <span class="hljs-keyword">begin</span>
%= include &
#39;<span class="hljs-string">partial/user_list</span>'
%= include &
#39;<span class="hljs-string">partial/add_url</span>'
%= include &
#39;<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
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605<p>As I said
before
, I&
#39;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 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"
>
</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
608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
<
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&
#39;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&
#39;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&
#39;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&
#39;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&
#39;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&
#39;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
210211212213214215216217218219220221222223224225226227228229230<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&
#39;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&
#39;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> &
#39;<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
411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
});
<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>(&
#39;<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} ? &
#39;<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>(&
#39;<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>(&
#39;<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>(&
#39;<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>(&
#39;<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>(&
#39;<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>(&
#39;<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>(&
#39;<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
496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
);
<span class=
"hljs-type"
>
$c
</span>-
>
;<span class=
"hljs-type"
>redirect_to</span>(&
#39;<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"
>
</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
555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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&
#39;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
159160161162163164165166167168169170171172173174175176177178179It 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&
#39;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 =
>
; &
#39;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
196197198199200201202203204205206207208209210211212213214215The major innovation that makes this method possible and so painless is the module <a href=
"https://metacpan.org/pod/File::Share"
>File::Share</a>.
While it isn&
#39;t <a href="https://metacpan.org/pod/File::ShareDir">the original module</a> used to locate share directories after installation, it is the best one since it also works before hand during development too.
To
do
so it uses the heuristic of looking to see
if
a directory
exists
named <code>share</code> in that particular location (and a few checks
for
sanity), thus the second reason we call the directory that.</p>
<p>When used we get the absolute path to the share directory of your distribution.
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>
...
use
File::Share;
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
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&
#39;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&
#39;ve seen (other than perhaps File::Share).
If you don&
#39;t need to think about theming or customizing the environment variable, then don't worry about it.
But
if
you find yourself in that situation, you&
#39;ll know you can make such features available to your users.</p>
<p>And hey,
if
it is a useful application, consider uploading it to CPAN.
Though I
warn
you, once you start contributing to CPAN it can be addictive!</p>
</section>
<small><p><a href=
"https://en.wikipedia.org/w/index.php?curid=31630711"
>Image</a> by Gnovick - Own work, <a href=
"https://creativecommons.org/licenses/by/3.0/"
title=
"Creative Commons Attribution 3.0"
>CC BY 3.0</a>.</p>
</small>
<p class=
"tags"
>
<span>Tagged in </span>:
<a href=
"/blog/tag/advent/"
>advent</a>,
<a href=
"/blog/tag/wishlist/"
>wishlist</a>
</p>
<div class=
"bio cf"
>
<div class=
"gravatar"
>
</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
364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
101112131415161718192021222324252627282930313233<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=
"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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137
<
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&
#39;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
175176177178179180181182183184185186187188189190191192193194195196197
title =
>
; &
#39;<span class="hljs-string">Dark Matter</span>',
url =
>
; &
#39;<span class="hljs-string">lordnibbler.org</span>',
},
],
}, &
#39;<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&
#39;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&
#39;t have to worry about overwriting the existing database nor clearing the data at the end of your test.
Further, you can run your tests in <a href=
"https://metacpan.org/pod/Test::Harness#j<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
212213214215216217218219220221222223224225226227228229230231232233234235And 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&
#39;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>.
As I mentioned
before
you can also mock out services by attaching tiny Mojolicious applications to a UserAgent&
#39;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
265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
-
>
;element_exists(
&
#39;<span class="hljs-string">form input[type="hidden"][name="url"][value="coolbeans.notasite"]</span>'
);
is <span class=
"hljs-type"
>
$url
</span>, &
#39;<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&
#39;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"
>
</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
327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132
<
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
134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174<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&
#39;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
</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!
to capture and output the packet data.</p>
<pre><code>tshark -i usbmon1 -Y &
#39;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
401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461The 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&
#39;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
<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
464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131
<
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"
>
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
374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417behind both the plugin and client side in Perl. Check it out,
if
you&
#39;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"
>
</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
420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
<
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
214215216217218219220221222223224225226227228229230231232233234</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 &
#39;print r({ g("mojolicious.io/blog")->dom->find("a")->map(sub{ $_->text => $_->{href} })->each })'
</code></pre>
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
337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380If these don&
#39;t seem like your's you can completely ignore them.</p>
<p>However, once you start using them, I think you&
#39;ll find yourself using them often.</p>
</section>
<small><p><a href=
"https://commons.wikimedia.org/w/index.php?curid=21375125"
>Image</a> by © <a href=
"http://www.royan.com.ar"
rel=
"nofollow"
>Jorge Royan</a>, <a href=
"https://creativecommons.org/licenses/by-sa/3.0"
title="Creative Co...
</small>
<p class=
"tags"
>
<span>Tagged in </span>:
<a href=
"/blog/tag/advent/"
>advent</a>,
<a href=
"/blog/tag/command/"
>command</a>,
<a href=
"/blog/tag/debugging/"
>debugging</a>,
<a href=
"/blog/tag/lite/"
>lite</a>
</p>
<div class=
"bio cf"
>
<div class=
"gravatar"
>
</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
383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
101112131415161718192021222324252627282930313233<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
36373839404142434445464748495051525354555657585960616263646566676869</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>
<ul>
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
78798081828384858687888990919293949596979899
</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
112113114115116117118119120121122123124125126127128129130131132133134135136137138139
<
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...
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
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252All 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"
>
</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
255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351<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>
</ul>
<ul class=
"footer-social"
>
<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>
</ul>
devscripts/update view on Meta::CPAN
1213141516171819202122232425262728293031323334353637383940414243444546474849505152535455my
%add_modules
= (
# not linked
#"04" => ["Mojo::DOM"],
);
my
%typos
= (
#'Perl::PrereqScanner::NotSoLite' => 'Perl::PrereqScanner::NotQuiteLite',
);
my
%daily_urls
= (
"07"
=>
"https://mojolicious.io/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page"
,
);
gen_curried_sub(
'App::CreateAcmeCPANModulesImportModules::create_acme_cpanmodules_import_modules'
,
{
modules
=> [
{
name
=>
'2017'
,
extract_urls
=> [
map
{
$daily_urls
{
$_
}}
"01"
..
"24"
],
devscripts/update view on Meta::CPAN
697071727374757677787980
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
1234567891011121314151617181920212223242526272829303132333435363738394041our
$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
1234567891011121314151617181920212223242526272829303132333435363738394041our
$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
1234567891011121314151617181920212223242526272829303132333435363738394041our
$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
1234567891011121314151617181920212223242526272829303132333435363738394041our
$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