<?xml version="1.0" encoding="us-ascii"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Perl Advent Calendar 2019</title><id>http://perladvent.org/2019/</id><link href="http://perladvent.org/2019/atom.xml" rel="self"/><updated>2026-05-28T21:06:53Z</updated><author><name>Mark Fowler</name></author><generator uri="https://metacpan.org/pod/XML::Atom::SimpleFeed" version="0.905">XML::Atom::SimpleFeed</generator><entry><title>Enjoy Christmas</title><link href="http://perladvent.org/2019/2019-12-25.html"/><id>http://perladvent.org/2019/2019-12-25.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;It&#38;#39;s been twenty years since I published the first Perl Advent Calendar.&lt;/p&gt;

&lt;p&gt;I was at a Perl Monger meeting this summer when someone asked me if I had some advice after all this time. Maybe: Steal from the best but make it your own? That&#38;#39;s after all what I do - I wait for people to do all the hard work of writing the code and all I do is write about what they&#38;#39;ve been doing. I have it easy. They deserve all the praise.&lt;/p&gt;

&lt;p&gt;So in the interests of giving good advice &lt;b&gt;and&lt;/b&gt; stealing from the best, I&#38;#39;m going to steal from &lt;a href=&#34;https://www.chicagotribune.com/columns/chi-schmich-sunscreen-column-column.html&#34;&gt;Mary Schmich&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;Ladies-and-Gentlemen-of-Christmas-2019&#34;&gt;Ladies and Gentlemen of Christmas 2019&lt;/h3&gt;

&lt;p&gt;Enjoy Christmas. If I could offer you only one tip for the future, Enjoying Christmas would be it. The long term benefits of friends and family have been proved by scientists whereas the rest of my advice has no basis more reliable than my own meandering experience. I will dispense this advice now&lt;/p&gt;

&lt;p&gt;Enjoy the power and beauty of creating new things; or never mind. You will not understand the power and beauty of creating new things until they have become commonplace. But trust me, in 20 years you&#38;#39;ll look back at code you have written recall in a way you can&#38;rsquo;t grasp now how much possibility lay before you and how fabulous it was. You are more able that you imagine&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t worry about the future; or worry, but know that worrying is as effective as trying to get your code to pass tests by running the same test twentieth time without changing it. The real troubles in your project are apt to be things that never crossed your worried mind; the kind that blindside you five minutes after you deploy&lt;/p&gt;

&lt;p&gt;Deploy one thing user facing every day that scares you&lt;/p&gt;

&lt;p&gt;Release often&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t be reckless with other people&#38;rsquo;s code bases; don&#38;rsquo;t put up with people who are reckless with yours&lt;/p&gt;

&lt;p&gt;Write documentation&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t waste your time on platform jealousy; sometimes your language is ahead, sometimes it&#38;#39;s behind. The race is long, and in the end, it&#38;rsquo;s only with your own development that matters&lt;/p&gt;

&lt;p&gt;Remember the github stars you receive; forget the trolls. If you succeed in doing this, tell me how&lt;/p&gt;

&lt;p&gt;Keep your old release notes. Throw away your old bug reports&lt;/p&gt;

&lt;p&gt;Refactor&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t feel guilty if you don&#38;rsquo;t know what stack to use. The most interesting people I know didn&#38;rsquo;t know at 22 what they would end up coding in. Some of the most interesting 40-year-olds I know still don&#38;rsquo;t&lt;/p&gt;

&lt;p&gt;Write tests&lt;/p&gt;

&lt;p&gt;Be kind to your typing knuckles, you&#38;rsquo;ll miss them when when they&#38;#39;re gone&lt;/p&gt;

&lt;p&gt;Maybe you&#38;rsquo;ll IPO, maybe you won&#38;rsquo;t Maybe you&#38;rsquo;ll be acquired, maybe you won&#38;rsquo;t Maybe you&#38;rsquo;ll sunset your service at 40 Maybe you&#38;rsquo;ll open source, have to ask for help on gofundme, and enjoy every minute. Whatever you do, don&#38;rsquo;t congratulate yourself too much, or berate yourself either. What technology, protocol, or company that comes out on top is mostly chance; And it won&#38;#39;t last.&lt;/p&gt;

&lt;p&gt;Enjoy your editor. Use it every way you can. Don&#38;rsquo;t be afraid of it, or what other people think of it. It&#38;rsquo;s the greatest tool you&#38;rsquo;ll ever own.&lt;/p&gt;

&lt;p&gt;Blog, even if you think you have nothing to talk about&lt;/p&gt;

&lt;p&gt;Read the documentation, even if you ignore it&lt;/p&gt;

&lt;p&gt;Do not read too much into the hot new thing; It will only make you feel outdated&lt;/p&gt;

&lt;p&gt;Get to know your mentors; you never know when they&#38;rsquo;ll be gone for good&lt;/p&gt;

&lt;p&gt;Be nice to your bug reporters; they are your best form of feedback and the People most likely to stick with your project long term&lt;/p&gt;

&lt;p&gt;Understand that while users come and go, the precious few who are passionate you should hold on to&lt;/p&gt;

&lt;p&gt;Work hard to bridge the gaps in geography and lifestyle, because the more you grow the more you need perspectives that are not like yours&lt;/p&gt;

&lt;p&gt;Write pure machine code once, but stop before you become obsessive&lt;/p&gt;

&lt;p&gt;Write Excel macros once, but stop before you forget the fundamentals&lt;/p&gt;

&lt;p&gt;Explore new code&lt;/p&gt;

&lt;p&gt;Accept certain inalienable truths: new technologies will age, companies will fail, you too will get old-- and when you do, you&#38;rsquo;ll fantasize that when you were young technologies were everlasting, companies were indestructible and newbies repeated their elders&lt;/p&gt;

&lt;p&gt;Respect people in the community&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t expect anyone else to make your code work for you&lt;/p&gt;

&lt;p&gt;Maybe you have a cushy job, maybe you have a support team; but you never know when either one might run out&lt;/p&gt;

&lt;p&gt;Don&#38;rsquo;t mess too much with your stack at one time, or by the time you&#38;#39;re ready to deploy, you&#38;#39;ll need to start over&lt;/p&gt;

&lt;p&gt;Be careful whose advice you buy, but be patient with those who supply it Advice is a form of nostalgia. Dispensing it is a way of fishing the past from the disposal, wiping it off, painting over the ugly parts and recycling it for more than it&#38;rsquo;s worth&lt;/p&gt;

&lt;p&gt;But trust me on enjoying Christmas&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-25T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>We Wish You a Meme Christmas</title><link href="http://perladvent.org/2019/2019-12-24.html"/><id>http://perladvent.org/2019/2019-12-24.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;center&gt;&lt;img src=&#34;confession_bear.jpg&#34; alt=&#34;Confession Bear Meme: Sometimes I write advent calendar entries for what cool thing it creates rather than how cool the Perl it took to write it is&#34; width=&#34;460&#34; height=&#34;480&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;It&#38;#39;s true. I&#38;#39;ve featured &lt;a href=&#34;http://perladvent.org/2018/2018-12-19.html&#34;&gt;cat pictures&lt;/a&gt; and &lt;a&gt;animated gifs&lt;/a&gt;. I&#38;#39;ve created &lt;a href=&#34;http://perladvent.org/2018/2018-12-17.html&#34;&gt;Christmas playlists&lt;/a&gt;. I&#38;#39;ve shown you images of &lt;a href=&#34;http://perladvent.org/2019/2019-12-05.html&#34;&gt;Christmas lights&lt;/a&gt;. I&#38;#39;ve even had an excuse to &lt;a href=&#34;http://perladvent.org/2019/2019-12-08.html&#34;&gt;embedded entire Chistmas movies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now...it&#38;#39;s finally time to create memes.&lt;/p&gt;

&lt;h3 id=&#34;Imager&#34;&gt;Imager&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Imager&#34;&gt;Imager&lt;/a&gt; is a handy dandy image drawing library in Perl that we&#38;#39;re going to be using for our Meme creation needs.&lt;/p&gt;

&lt;p&gt;Imager has about a gazillion methods to do pretty much every standard graphics manipulation you can imagine. But, shockingly, it doesn&#38;#39;t have a way to draw standard meme text (Impact font, white text, black outline.) We can fix that by adding our own plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager::MemeString&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager::Font&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# the Imager way is to directly declare functions in the main&lt;br /&gt;# &#39;Imager&#39; namespace.  Here we&#39;re using the full package-qualified&lt;br /&gt;# syntax for naming a function to place &#39;meme_string&#39; directly in&lt;br /&gt;# the &#39;Imager&#39; namespace rather than our own.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager::meme_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # meme text is upper case!&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;uc&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;delete&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # Impact is the &#38;quot;classic&#38;quot; font for Memes. This should be the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# location for the ttf file on disk - this below is the standard&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# location on macOS Catalina.&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$font&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager::Font&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/System/Library/Fonts/Supplemental/Impact.ttf&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # by default text will be centered around the middle of the image&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;delete&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getwidth&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$y&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;delete&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # these are the defaults - you can override this by just&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# specifying different things in the arguments.&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%defaults&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;font&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$font&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;aa&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;halign&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;center&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # We&#39;re cheating here.  If we were doing this properly we&#39;d&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# render the text to a different &#38;quot;layer&#38;quot; image and then use an&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# algoritm to outline it black.  What we do instead is simplier:&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# We just draw the same text over and over with horizontal and&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# vertical offsets.&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$xoffset&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;-3&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;..&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$yoffset&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;-3&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;..&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;align_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$xoffset&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$y&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$yoffset&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;%defaults&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;%args&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;black&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # draw the same text one last time in white&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;align_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$y&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;%defaults&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;%args&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;white&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now all we need is a wrapper script that loads our meme template, uses this new method to draw the meme text, and saves it out again:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager::MemeString&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$template&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Forgot to pass template filename!&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$output&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Forgot to pass output filename!&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Imager&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Read in the template image. The Imager object will have the same&lt;br /&gt;# dimensions as the file we just read in&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$template&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;errstr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# draw the meme string.  Because we&#39;re only passing in &#38;quot;y&#38;quot; not &#38;quot;x&#38;quot;&lt;br /&gt;# it&#39;ll automatically be centred horizontally&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;meme_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, World&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;y&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# and save the new image out&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;jpeg&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Cannot write: &#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$img&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;errstr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Okay, bring on the memes! (email me links to any you create!)&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;ggg.jpg&#34; alt=&#34;Good Guy Greg Meme: Good guy CPAN authors give me lots to write about in Advent Calendar&#34; width=&#34;460&#34; height=&#34;460&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;center&gt;&lt;img src=&#34;penguin.jpg&#34; alt=&#34;Penguin Awesome-Awkward Meme: Finally completed 24 Advent articles. What&#39;ll I do with my free time now?&#34; widthB=&#34;460&#34; height=&#34;461&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;center&gt;&lt;img src=&#34;yall.jpg&#34; alt=&#34;Y&#39;All Meme: Y&#39;All got any more of those days of advent?&#34; width=&#34;460&#34; height=&#34;361&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;center&gt;&lt;img src=&#34;successkid.jpg&#34; alt=&#34;Success Kid Meme: Have time to turn best entries into modules&#34; width=&#34;460&#34; height=&#34;460&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;center&gt;&lt;img src=&#34;distractedboyfriend.jpg&#34; alt=&#34;Distracted Boyfriend Meme: Christmas / Me / Using a computer&#34; width=&#34;460&#34; height=&#34;307&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;/div&gt;</summary><updated>2019-12-24T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Testing our Impatience</title><link href="http://perladvent.org/2019/2019-12-23.html"/><id>http://perladvent.org/2019/2019-12-23.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&#38;quot;I can make your tests take half the time&#38;quot;, was the claim that Garland Snowboughs had made.&lt;/p&gt;

&lt;p&gt;Twice as fast? Rustic Mullingfig had hardly started building out the implementation of his Christmas Tree class. It wasn&#38;#39;t possible that he&#38;#39;d made the code go slow yet - there really wasn&#38;#39;t much of it.&lt;/p&gt;

&lt;p&gt;&#38;quot;Go on then mate, I&#38;#39;ll bet you a mince pie that you can&#38;#39;t manage it.&#38;quot;&lt;/p&gt;

&lt;p&gt;The bet was on.&lt;/p&gt;

&lt;h3 id=&#34;The-Tests-in-Question&#34;&gt;The Tests in Question&lt;/h3&gt;

&lt;p&gt;There really wasn&#38;#39;t much to the code yet. So far Rustic had only implemented the planted attribute (that defaulted to the time the object was created) and some stringification.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XmasTree&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moose&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;planted&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;overload&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;quot;&#38;quot;&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;as_string&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;as_string&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;A Xmas Tree, planted on @{[ $self-&#38;gt;planted ]}&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And he&#38;#39;d written two basic tests - one for the stringification:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XmasTree&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# look, a year 2020 bug!&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;like&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XmasTree&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;regexp&#34;&gt;qr/An Xmas Tree, planted on 2019/&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And one for the planted attribute itself:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XmasTree&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XmasTree&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;planted&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;prop&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;blessed&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DateTime&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # look, a year 2020 bug!&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;word&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;year&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2019&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These ran pretty darn quickly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ yath -Ilib t

    ** Defaulting to the &#38;#39;test&#38;#39; command **

    ( PASSED )  job  1    t/planted.t
    ( PASSED )  job  2    t/stringification.t

    ================================================================================

    Run ID: A60F80C6-251F-11EA-ADBC-83BD753448D3

    All tests were successful!


    1.17659s on wallclock (0.19 usr 0.02 sys + 0.93 cusr 0.11 csys = 1.25 CPU)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How is Garland going to speed those tests up?&lt;/p&gt;

&lt;h3 id=&#34;Faster&#34;&gt;Faster&lt;/h3&gt;

&lt;p&gt;The first thing Garland changed was to add the &lt;code&gt;-P&lt;/code&gt; option to &lt;code&gt;yath&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yath&lt;/code&gt; is the Test2 test harness (yath stands for Yet Another Test Harness.) Each test it runs forks a new process, executes the test script, and gathers the STDERR and STDOUT of the child process in the parent test harness process to work out the results.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-P&lt;/code&gt; option tells the harness to load the mentioned module &lt;i&gt;before&lt;/i&gt; it forks. So by specifying &lt;code&gt;-PDateTime&lt;/code&gt; we only have to load DateTime once in the parent process, not twice, once in each child process as it loads&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ yath -PDateTime -PMoose  -Ilib t

    ** Defaulting to the &#38;#39;test&#38;#39; command **

    ( PASSED )  job  1    t/planted.t
    ( PASSED )  job  2    t/stringification.t

    ================================================================================

    Run ID: D10B83C4-251F-11EA-BDAA-9B6707E666E2

    All tests were successful!


    0.90175s on wallclock (0.19 usr 0.02 sys + 0.68 cusr 0.10 csys = 0.99 CPU)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whoo! That only took 77% of the time it previously did...but Garland had promised more! Can we make it faster still?&lt;/p&gt;

&lt;h3 id=&#34;Faster-Still&#34;&gt;Faster Still&lt;/h3&gt;

&lt;p&gt;The key realization is that not only that DateTime and other CPAN modules we use do not change between the different test scripts we execute, they don&#38;#39;t change between different &lt;i&gt;runs of the same test script&lt;/i&gt;. Or, to put it another way...we want to load them into memory once and then every time after (until we eventually upgrade our Perl modules) we won&#38;#39;t have to run them again.&lt;/p&gt;

&lt;p&gt;To do this &lt;code&gt;yath&lt;/code&gt; has a &lt;i&gt;daemon&lt;/i&gt; mode. You can start it up by using the &lt;code&gt;start&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ yath start -PDateTime -PMoose
    Waiting for runner...

    Persistent runner started!
    Runner PID: 3199
    Runner dir: /tmp/yath-test-3198-JClS5vpt
    Runner logs:
    standard output: /tmp/yath-test-3198-JClS5vpt/output.log
    standard  error: /tmp/yath-test-3198-JClS5vpt/error.log

    Use `yath watch` to monitor the persistent runner

    0.58685s on wallclock (0.19 usr 0.02 sys + 0.00 cusr 0.00 csys = 0.21 CPU)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now whenever we run yath from this directory it&#38;#39;ll contact this daemon and have &lt;i&gt;it&lt;/i&gt; run the tests instead - and its already loaded the modules!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ yath t

    ** Persistent runner detected, defaulting to the &#38;#39;run&#38;#39; command **

    ( PASSED )  job 3485-1    t/planted.t
    ( PASSED )  job 3485-2    t/stringification.t

    ================================================================================

    Run ID: D682502A-2520-11EA-8563-D1A3990446BB

    All tests were successful!


    0.35264s on wallclock (0.18 usr 0.03 sys + 0.00 cusr 0.00 csys = 0.21 CPU)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Okay, that took less than 30% of the original time it took - over three times as fast!&lt;/p&gt;

&lt;p&gt;Of course, this speed up will become more significant the more stable unchanging CPAN modules we pull in as our codebase becomes bigger.&lt;/p&gt;

&lt;p&gt;We can even make this easier by having a &lt;i&gt;preload&lt;/i&gt; module that we use whenever we get ready to start testing.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;reloadStandard&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moose&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DateTime&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ yath start -PreloadStandard
    ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But more importantly, what this means is that Garland is owed some mince pies!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-23T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Comparing More</title><link href="http://perladvent.org/2019/2019-12-22.html"/><id>http://perladvent.org/2019/2019-12-22.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Cinnamon Candybubbles has been really interested in the Wise Old Elf&#38;#39;s &lt;a href=&#34;http://www.perladvent.org/2019/2019-12-21.html&#34;&gt;lecture yesterday&lt;/a&gt; on Test2. She&#38;#39;d been particularly intregied by the comparison operators that allow you to build a complex datastructure of comparisons to pass into &lt;code&gt;is&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Test2::Tools::Compare&#34;&gt;Test2::Tools::Compare&lt;/a&gt; lists a whole bunch of these operators that are immediately available in &lt;a href=&#34;https://metacpan.org/module/Test2::V0&#34;&gt;Test2::V0&lt;/a&gt;. But what she wanted to extend the operators?&lt;/p&gt;

&lt;p&gt;What Cinnamon wants to do is write a simple comparison function that she can use to check if a value is one of Santa&#38;#39;s reindeer or not.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Tools::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;reindeer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Donner&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Prancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Robbie&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;driver&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Santa&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# insist that all reindeer are Santa&#39;s reindeer&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;hash&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;reindeer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;all_items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;santas_reindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;());&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How can she do this?&lt;/p&gt;

&lt;h3 id=&#34;Writing-a-Test2::Tools-Class&#34;&gt;Writing a Test2::Tools Class&lt;/h3&gt;

&lt;p&gt;Test2&#38;#39;s standard extension mechanism is to provide tools in the &lt;code&gt;Test2::Tools&lt;/code&gt; namespace that provide functions that interoperate with existing Test2 functionality. These classes don&#38;#39;t need to subclass any particular class nor have any specific functions or method defined - they can access anything they need to about the Test2 context by using the &lt;code&gt;context&lt;/code&gt; function from &lt;a href=&#34;https://metacpan.org/module/Test2::API&#34;&gt;Test2::API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this case Cinnamon isn&#38;#39;t even going to do that - she&#38;#39;s not actually writing any code that performs a test per se. She&#38;#39;s providing a function that the tests in &lt;a href=&#34;https://metacpan.org/module/Test2::Tools::Compare&#34;&gt;Test2::Tools::Compare&lt;/a&gt; can use to compare data structures.&lt;/p&gt;

&lt;p&gt;Writing a Test2::Tools class that exports comparison functions isn&#38;#39;t that hard - it&#38;#39;s just like any other Exporter based code.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Tools::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Exporter&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( import )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;our&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@EXPORT&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( santas_reindeer )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# we&#39;re going to re-use existing Test2 comparison classes&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::Set&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::String&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# return the same as in_set(qw(Rudolph Dasher Dancer...))&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;santas_reindeer&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$set&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::Set&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;checks&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::String&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Rudolph Dasher Dancer Prancer Vixen Comet Cupid Donner Blitzen&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$set&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_reduction&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;any&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$set&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This works, and correctly identifies that &lt;code&gt;Robbie&lt;/code&gt; is not one of Santa&#38;#39;s reindeer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$  perl -Ilib /tmp/testing.t
    not ok 1
    # Failed test at /tmp/testing.t line 17.
    # +-------------------------+-----------------------+----+---------+--------+
    # | PATH                    | GOT                   | OP | CHECK   | LNs    |
    # +-------------------------+-----------------------+----+---------+--------+
    # |                         | HASH(0x7f9fab817670)  |    | &#38;lt;HASH&#38;gt;  | 14, 17 |
    # | {reindeer}              | ARRAY(0x7f9fab817658) |    | &#38;lt;ARRAY&#38;gt; | 15     |
    # | {reindeer}[2] &#38;lt;Check 0&#38;gt; | Robbie                | eq | Rudolph |        |
    # | {reindeer}[2] &#38;lt;Check 1&#38;gt; | Robbie                | eq | Dasher  |        |
    # | {reindeer}[2] &#38;lt;Check 2&#38;gt; | Robbie                | eq | Dancer  |        |
    # | {reindeer}[2] &#38;lt;Check 3&#38;gt; | Robbie                | eq | Prancer |        |
    # | {reindeer}[2] &#38;lt;Check 4&#38;gt; | Robbie                | eq | Vixen   |        |
    # | {reindeer}[2] &#38;lt;Check 5&#38;gt; | Robbie                | eq | Comet   |        |
    # | {reindeer}[2] &#38;lt;Check 6&#38;gt; | Robbie                | eq | Cupid,  |        |
    # | {reindeer}[2] &#38;lt;Check 7&#38;gt; | Robbie                | eq | Donner  |        |
    # | {reindeer}[2] &#38;lt;Check 8&#38;gt; | Robbie                | eq | Blitzen |        |
    # +-------------------------+-----------------------+----+---------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Though it&#38;#39;s a little on the verbose side - the Cinnamon doesn;t really want to know all the names that don&#38;#39;t match, they want to simply know that it doesn&#38;#39;t match any of Santa&#38;#39;s reindeer&lt;/p&gt;

&lt;h3 id=&#34;Writing-a-Custom-Comparison&#34;&gt;Writing a Custom Comparison&lt;/h3&gt;

&lt;p&gt;The tools we previously wrote simply re-used existing comparisons. How about Cinnamon writes her own comparison operator, so she can have full control over the output?&lt;/p&gt;

&lt;p&gt;This makes her Tools module considerable simplier at least..&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Tools::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Exporter&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( import )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;our&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@EXPORT&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( santas_reindeer )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;santas_reindeer&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::SantasReindeer&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Test2::Compare::SantasReindeer should be a subclass of the &lt;a href=&#34;https://metacpan.org/module/Test2::Compare::Base&#34;&gt;Test2::Compare::Base&lt;/a&gt; class. A simple implementation would be:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;base&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(Test2::Compare::Base)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::Util&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( any )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Santa&#39;s Reindeer&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;verify&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%params&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # if this value didn&#39;t exist at all, need to return false&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$params&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # return true if and only if what we got matches&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;any&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$params&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;got&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Rudolph Dasher Dancer Prancer Vixen Comet Cupid Donner Blitzen&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Cinnamon had to implement two methods: &lt;code&gt;name&lt;/code&gt; (which returns the name of the test that will be shown in output) and &lt;code&gt;verify&lt;/code&gt; (which returns true or false if it matches or not).&lt;/p&gt;

&lt;p&gt;The output is much more readable now&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ $  perl -Ilib /tmp/testing.t
    not ok 1
    # Failed test at /tmp/testing.t line 17.
    # +---------------+-----------------------+------------------+--------+
    # | PATH          | GOT                   | CHECK            | LNs    |
    # +---------------+-----------------------+------------------+--------+
    # |               | HASH(0x7f917f001870)  | &#38;lt;HASH&#38;gt;           | 14, 17 |
    # | {reindeer}    | ARRAY(0x7f917f001858) | &#38;lt;ARRAY&#38;gt;          | 15     |
    # | {reindeer}[2] | Robbie                | Santa&#38;#39;s Reindeer |        |
    # +---------------+-----------------------+------------------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Embracing-The-Negative&#34;&gt;Embracing The Negative&lt;/h3&gt;

&lt;p&gt;Ever since the cuban missile crisis Grand-Santa hasn&#38;#39;t been allowed to fly the sliegh any more. This is easy to express in a test with the &lt;code&gt;!&lt;/code&gt; operator:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Grand-Santa&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# no Grand-Santa!&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;hash&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;Grand-Santa&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This helpfully prints out useful info in the diagnostics making it clear that the test was negated too.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    # Seeded srand with seed &#38;#39;20191220&#38;#39; from local date.
    not ok 1
    # Failed test at - line 12.
    # +----------+----------------------+----+-------------+-------+
    # | PATH     | GOT                  | OP | CHECK       | LNs   |
    # +----------+----------------------+----+-------------+-------+
    # |          | HASH(0x7fa50d817040) |    | &#38;lt;HASH&#38;gt;      | 9, 12 |
    # | {driver} | Grand-Santa          | ne | Grand-Santa | 10    |
    # +----------+----------------------+----+-------------+-------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What if Cinnamon does&#38;#39;t want any of Santa&#38;#39;s reindeer on our sleigh? It should be as simple as writing the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Tools::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;training flight&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;reindeer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Donner&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Prancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Robbie&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;driver&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Arthur&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# insist that all reigndeer aren&#39;t Santa&#39;s&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ds&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;hash&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;reindeer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;all_items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;santas_reindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;());&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Does that work?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl -Ilib /tmp/testing.t
    # Seeded srand with seed &#38;#39;20191220&#38;#39; from local date.
    not ok 1
    # Failed test at /tmp/testing.t line 18.
    # +---------------+-----------------------+----+---------+--------+
    # | PATH          | GOT                   | OP | CHECK   | LNs    |
    # +---------------+-----------------------+----+---------+--------+
    # |               | HASH(0x7f9501017670)  |    | &#38;lt;HASH&#38;gt;  | 15, 18 |
    # | {reindeer}    | ARRAY(0x7f9501017658) |    | &#38;lt;ARRAY&#38;gt; | 16     |
    # | {reindeer}[0] | Donner                | eq |         |        |
    # | {reindeer}[1] | Prancer               | eq |         |        |
    # | {reindeer}[2] | Robbie                | eq |         |        |
    # +---------------+-----------------------+----+---------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ooops, Cinnamon now checking that the names are equal to false. What changes does she have to make?&lt;/p&gt;

&lt;p&gt;The Test2::Compare::SantasReindeer needs to overload the &lt;code&gt;!&lt;/code&gt; operator, save that in the instance, and then the code can decide what comparison to run in &lt;code&gt;verify&lt;/code&gt; later on.&lt;/p&gt;

&lt;p&gt;Thankfully, Cinnamon doesn&#38;#39;t have to code all of that - the overloading aspect can be handled just by loading &lt;a href=&#34;https://metacpan.org/module/Test2::Compare::Negatable&#34;&gt;Test2::Compare::Negatable&lt;/a&gt; which will set the &lt;code&gt;+NEGATED&lt;/code&gt; attribute for us.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::SantasReindeer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;base&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(Test2::Compare::Base)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::Util&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( any none )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Overloads &#39;!&#39; for us.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::Compare::Negatable&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@REINDEER&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Rudolph Dasher Dancer Prancer Vixen Comet Cupid Donner Blitzen&lt;br /&gt;)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Santa&#39;s Reindeer&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;verify&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%params&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # Always check if $got exists! This method must return false if no&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# value at all was received.&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$params&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;none&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$params&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;got&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@REINDEER&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;NEGATE&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;any&lt;/span&gt;  &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$params&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;got&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@REINDEER&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now Cinnamon&#38;#39;s test works as it should:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    mark@Tarrant:~/tmp$  perl -Ilib /tmp/testing.t
    # Seeded srand with seed &#38;#39;20191220&#38;#39; from local date.
    not ok 1
    # Failed test at /tmp/testing.t line 18.
    # +---------------+-----------------------+------------------+--------+
    # | PATH          | GOT                   | CHECK            | LNs    |
    # +---------------+-----------------------+------------------+--------+
    # |               | HASH(0x7f857200ea70)  | &#38;lt;HASH&#38;gt;           | 15, 18 |
    # | {reindeer}    | ARRAY(0x7f857200ea58) | &#38;lt;ARRAY&#38;gt;          | 16     |
    # | {reindeer}[0] | Donner                | Santa&#38;#39;s Reindeer |        |
    # | {reindeer}[1] | Prancer               | Santa&#38;#39;s Reindeer |        |
    # +---------------+-----------------------+------------------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But the output is still a little confusing; Currently the diagnostics are saying that the problem is that Donner and Prancer aren&#38;#39;t Santa&#38;#39;s Reindeer when the point of the test was that they are, but we don&#38;#39;t want them to be.&lt;/p&gt;

&lt;p&gt;Cinnamon has one more method to override in the class to get the output she desires:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;operator&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;NEGATE&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;isn&#39;t one of&#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;is one of&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the output looks like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ perl -Ilib /tmp/testing.t
    not ok 1
    # Failed test at /tmp/testing.t line 18.
    # +---------------+-----------------------+--------------+------------------+--------+
    # | PATH          | GOT                   | OP           | CHECK            | LNs    |
    # +---------------+-----------------------+--------------+------------------+--------+
    # |               | HASH(0x7ff5e2802870)  |              | &#38;lt;HASH&#38;gt;           | 15, 18 |
    # | {reindeer}    | ARRAY(0x7ff5e2802858) |              | &#38;lt;ARRAY&#38;gt;          | 16     |
    # | {reindeer}[0] | Donner                | isn&#38;#39;t one of | Santa&#38;#39;s Reindeer |        |
    # | {reindeer}[1] | Prancer               | isn&#38;#39;t one of | Santa&#38;#39;s Reindeer |        |
    # +---------------+-----------------------+--------------+------------------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ perl -Ilib /tmp/testing.t
    not ok 1
    # Failed test at /tmp/testing.t line 18.
    # +---------------+-----------------------+-----------+------------------+--------+
    # | PATH          | GOT                   | OP        | CHECK            | LNs    |
    # +---------------+-----------------------+-----------+------------------+--------+
    # |               | HASH(0x7f8687017670)  |           | &#38;lt;HASH&#38;gt;           | 15, 18 |
    # | {reindeer}    | ARRAY(0x7f8687017658) |           | &#38;lt;ARRAY&#38;gt;          | 16     |
    # | {reindeer}[2] | Robbie                | is one of | Santa&#38;#39;s Reindeer |        |
    # +---------------+-----------------------+-----------+------------------+--------+&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Comparatively-Good&#34;&gt;Comparatively Good&lt;/h3&gt;

&lt;p&gt;Cinnamon Candybubbles ended up with a comparison that was not only quick to type, but more importantly very easy to understand when looking at the diagnostic output.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-22T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Test2: Test Harder (but easier)</title><link href="http://perladvent.org/2019/2019-12-21.html"/><id>http://perladvent.org/2019/2019-12-21.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&#38;quot;Right, my fellow Elves, calm down&#38;quot;, the Wise Old Elf shouted over the rabble gathed in the elf lecture hall, &#38;quot;Today I&#38;#39;m going to be talking to you about Testing&#38;quot;&lt;/p&gt;

&lt;p&gt;There were loud groans from the audience. They&#38;#39;d been writing tests for &lt;i&gt;years&lt;/i&gt;. It was like teaching their Grandmother to suck Eggnog.&lt;/p&gt;

&lt;p&gt;&#38;quot;Now now...how many of you are familiar with the latest changes in Test2?&#38;quot;&lt;/p&gt;

&lt;p&gt;Less noise now. Maybe there was something here they could learn after all.&lt;/p&gt;

&lt;p&gt;&#38;quot;There&#38;#39;s so much to cover. I could tell you about the underlying event system that allows decoupling of parts of the test system. I could talk about the new structure of creating interoperable test frameworks. I could talk to about the new harness features...but today, today I&#38;#39;m going to talk to you about &lt;a href=&#34;https://metacpan.org/module/Test2::V0&#34;&gt;Test2::V0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Test2::V0 is like a smorgasbord of all the good bits of Test2 wrapped up in one delectable package. When you might previously grab Test::More, you should grab Test2::V0. Because...because it not only uses all of the cool tech on its backend, but because it&#38;#39;s so darn handy to use.&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Making-the-Easy-Stuff-Easy:-Strictness-Warnings-and-UTF8&#34;&gt;Making the Easy Stuff Easy: Strictness, Warnings and UTF8&lt;/h3&gt;

&lt;p&gt;&#38;quot;Lazyness! One of the greatest virtue of the Perl programmer! Why type a lot when you can type a little? Why not have sensible defaults.&#38;quot;&lt;/p&gt;

&lt;p&gt;There were plenty of nodding heads now. Writing tests is a Cost of Doing Business - so making it as simple as possible to do the right thing without thinking about it is critical.&lt;/p&gt;

&lt;p&gt;The Wise Old Elf threw the most basic Perl script up on the screen.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test::More&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;tests&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Do we want to build a \N{SNOWMAN}?&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;Now here&#38;#39;s the same code written in an idiomatic fashion with Test2::V0.&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Do you want to build a \N{SNOWMAN}?&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;Who can spot the difference?&#38;quot;&lt;/p&gt;

&lt;p&gt;Tiramisu Shinytree raised her hand. &#38;quot;There&#38;#39;s no strict or warnings!&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Correct! Test2::V0 (like Moose, Mojolicious, and many other common tools) turns those on for you automatically. Anything else?&#38;quot;&lt;/p&gt;

&lt;p&gt;It was Bluebell Brandycake&#38;#39;s turn. &#38;quot;There&#38;#39;s no plan!&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Well yes, Test2 allows you to use &lt;code&gt;done_testing&lt;/code&gt; rather than having to count each and every test and declare a plan, but modern versions of Test::More allow that too. Anything else? Anyone spot the bug?&#38;quot;&lt;/p&gt;

&lt;p&gt;Silence. Can you spot it? The Wise Old Elf decided to show the class the output of the scripts.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell $ perl /tmp/more.t
    1..1
    Wide character in print at /opt/perl/bin/lib/site_perl/5.28.1/Test2/Formatter/TAP.pm line 144.
    ok 1 - Do you want to build a X?

    shell $ perl /tmp/2v0.t
    ok 1 - Do you want to build a X?
    1..1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The crowd groaned. They&#38;#39;d forgotten Fowler&#38;#39;s Rule on Unicode (there&#38;#39;s always another unicode bug, you just haven&#38;#39;t found it yet)&lt;/p&gt;

&lt;p&gt;&#38;quot;Yep. Test2::V0 also reconfigures the output handles to encode as UTF-8 which I bet you always forget - until you see that warning message.&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Deep-Testing&#34;&gt;Deep Testing&lt;/h3&gt;

&lt;p&gt;&#38;quot;The next thing you&#38;#39;ll notice is that Test2::V0 based tests are, well, naturally &lt;i&gt;deeper&lt;/i&gt; than their Test::More counterparts. Where in Test::More if you wanted to test data structures you&#38;#39;d have to use &lt;code&gt;is_deeply&lt;/code&gt;...&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test::More&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;tests&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Reindeer::Santa&lt;/span&gt;  &lt;span class=&#34;words&#34;&gt;qw(original_team)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@results&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;original_team&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is_deeply&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@original_tam&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Dasher&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Dancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Prancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Vixen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Comet&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Cupid&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Donner&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Blitzen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;check reindeer&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;With Test2::V0 you&#38;#39;d just use &lt;code&gt;is&lt;/code&gt;.&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Reindeer::Santa&lt;/span&gt;  &lt;span class=&#34;words&#34;&gt;qw(original_team)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@results&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;original_team&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@original_tam&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Dasher&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Dancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Prancer&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Vixen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Comet&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Cupid&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Donner&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Blitzen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;check reindeer&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Wise Old Elf went onto explain it was so much more. To demonstrate, he set a little programming exercise.&lt;/p&gt;

&lt;p&gt;Write a test that checks the output is a hashref that has three entries in it. The first is a name (which must be a string). The second is an age which must be equal to eighteen or the string &#38;quot;adult&#38;quot;. The third is an info array that has four elements: A true value, an instance of Snowman, an instance that has an &lt;code&gt;as_string&lt;/code&gt; method that returns &lt;code&gt;Anna&lt;/code&gt;, and finally something that is a valid RFC 1123 date time. The top level hash can have any other alphanumeric keys except &lt;code&gt;error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The elves spent a long time producing page after page of code. There were debates. Arguments. And thirty minutes later no-one was done.&lt;/p&gt;

&lt;p&gt;They were quite shocked when the Wise Old elf showed them his simple solution:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;hash&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;   # insist that name is a string (not a ref, etc!)&lt;br /&gt;&lt;/span&gt;   &lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;regexp&#34;&gt;qr//&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;   # insist that the person&#39;s age 18 (or 18.0, etc) or &#38;quot;adult&#38;quot;&lt;br /&gt;&lt;/span&gt;   &lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;age&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;in_set&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;adult&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;   # info is an array&lt;br /&gt;&lt;/span&gt;   &lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;array&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;       # the first element must be a true value&lt;br /&gt;&lt;/span&gt;       &lt;span class=&#34;word&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;       # the second element must be an instance of Snowman&lt;br /&gt;&lt;/span&gt;       &lt;span class=&#34;word&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;blessed&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Snowman&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;       # the third element must return &#39;Anna&#39; when as_string is called on it&lt;br /&gt;&lt;/span&gt;       &lt;span class=&#34;word&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;as_string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Anna&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;       # the fourth element must be parseable as a RFC 1123 time&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the validator sub must return true if passes, false if not&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# (and Try::Tiny&#39;s try returns undef on exception)&lt;br /&gt;&lt;/span&gt;       &lt;span class=&#34;word&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;validator&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;DateTime::Format::HTTP&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parse_datetime&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;       # and there must only be four elements&lt;br /&gt;&lt;/span&gt;       &lt;span class=&#34;word&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;   # we don&#39;t want a field called &#38;quot;error&#38;quot;&lt;br /&gt;&lt;/span&gt;   &lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DNE&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# does not exist&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;   # allow any other fields that keys are alphanumeric&lt;br /&gt;&lt;/span&gt;   &lt;span class=&#34;word&#34;&gt;all_keys&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;regexp&#34;&gt;qr/^[A-Za-z0-9]+$/aa&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;compare&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Exception-Checking&#34;&gt;Exception Checking&lt;/h3&gt;

&lt;p&gt;&#38;quot;Sparkle Candytoes, what are an elf&#38;#39;s favorite colors?&#38;quot;&lt;/p&gt;

&lt;p&gt;Sparkle beamed. &#38;quot;Red and Green, Wise Old Elf&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Correct! When we write good tests we don&#38;#39;t just test the &lt;i&gt;green&lt;/i&gt; path - what our code does wh its supposed to, we also check the &lt;i&gt;red&lt;/i&gt; path. Does it handle hte errors correctly&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;We want to make sure that our code throws the right exceptions. Without the exception breaking our test of course! Test2::V0 has its own exception capturing code:&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;autodie&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dies&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;lt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;file-that-does-not-exist&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;open throws exception on non existent file&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;Since &lt;code&gt;dies&lt;/code&gt; returns the actual exception thrown we can improve the test, by checking if the exception contained the right string:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    like(dies {
       open my $fh, &#38;quot;&#38;lt;&#38;quot;, &#38;quot;file-that-does-not-exist&#38;quot;;
    }, qr/No such file/, &#38;#39;open throws exception on non existent file&#38;#39;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or even check that something dies with the right kind of errors&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    isa_ok(dies {
        Mojo::Exception-&#38;gt;throw(&#38;#39;boom&#38;#39;)
    }, &#38;#39;Mojo::Exception&#38;#39;, &#38;#39;exception was an instance of Mojo::Exception);&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Subtests&#34;&gt;Subtests&lt;/h3&gt;

&lt;p&gt;&#38;quot;Now class, who can spot the problem with this code?&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test::More&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;horse&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cart&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cart&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No hands up. &#38;quot;Cinnamon Candybubbles, how about you&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;I think it works, Wise Old Elf&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh, it does, it does. It just looks confusing&#38;quot;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    # Subtest: horse
        not ok 1 - cart
        #   Failed test &#38;#39;cart&#38;#39;
        #   at - line 4.
        #          got: undef
        #     expected: &#38;#39;cart&#38;#39;
        1..1
        # Looks like you failed 1 test of 1.
    not ok 1 - horse
    #   Failed test &#38;#39;horse&#38;#39;
    #   at - line 5.
    1..1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;You have to read the comments to figure out what&#38;#39;s going on. The cart is literally before the horse.&#38;quot;&lt;/p&gt;

&lt;p&gt;The Wise Old Elf showed them the same code in Test2::V0:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Test2::V0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# only this line changed!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;horse&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$cart&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cart&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cart&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But the output is very different&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    not ok 1 - horse {
        not ok 1 - cart
        # Failed test &#38;#39;cart&#38;#39;
        # at - line 5.
        # +---------+-------+
        # | GOT     | CHECK |
        # +---------+-------+
        # | &#38;lt;UNDEF&#38;gt; | cart  |
        # +---------+-------+
        1..1
    }
    # Failed test &#38;#39;horse&#38;#39;
    # at - line 6.
    1..1&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;And-More&#34;&gt;And More&lt;/h3&gt;

&lt;p&gt;&#38;quot;Now class, I&#38;#39;d like to talk to you about...&#38;quot;, began the Wise Old Elf, only to be interrupted by the ringing of the lunch bell. Test2 is a big topic. More will have to wait for another day...&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-21T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Controlling your Terminal with Perl</title><link href="http://perladvent.org/2019/2019-12-20.html"/><id>http://perladvent.org/2019/2019-12-20.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;a href=&#34;https://iterm2.com/&#34;&gt;iTerm&lt;/a&gt; is a heck of a terminal emulator for macOS. Not only is it rock solid, fast, and efficient - it&#38;#39;s also exteremly powerful - and can both be controlled &lt;b&gt;by Perl&lt;/b&gt; and &lt;b&gt;can control Perl&lt;/b&gt;.&lt;/p&gt;

&lt;h3 id=&#34;Are-We-Nearly-There-Yet&#34;&gt;Are We Nearly There Yet?&lt;/h3&gt;

&lt;p&gt;In addition to the standard control sequences iTerm supports &lt;a href=&#34;https://iterm2.com/documentation-escape-codes.html&#34;&gt;Proprietary Escape Codes&lt;/a&gt; that can get it to do non-standard things a real dumb terminal would never be able to do.&lt;/p&gt;

&lt;p&gt;For example, my standard wrapper for running tests in Perl looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   perl -e &#38;#39;print &#38;quot;\e]50;ClearScrollback\a&#38;quot;&#38;#39; &#38;amp;&#38;amp;
   yath -v -Ilib t/01mytest.t \
   &#38;amp;&#38;amp; perl -e &#38;#39;print &#38;quot;\e]9;\nALL OK\a&#38;quot;&#38;#39; || perl -e &#38;#39;print &#38;quot;\e]9;\nFAILED\a&#38;quot;&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That first sends the control sequence to clear the screen and delete all history (so scrolling to the top takes me to the top of the test.) It then runs the tests, and I can go make a cup of tea, because the script will send a notification that&#38;#39;ll bleep as soon as tests are done running to tell me if it&#38;#39;s succeeded or failed.&lt;/p&gt;

&lt;h3 id=&#34;Clickable-Links&#34;&gt;Clickable Links&lt;/h3&gt;

&lt;p&gt;Another neat trick that iTerm supports allowing programs to markup their output so that text becomes clickable. For example this creates a clickable link to google about Perl.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Search more about &#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$href&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://google.com?q=Perl&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$text&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Perl&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;\e]8;;$href\a$text\e]8;;\a&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;.\n&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, the links don&#38;#39;t have to be pages on the web - you can trigger anything that has a URL scheme on macOS. Here I can click on the bullet point links to add them to OmniFocus:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;* &#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;clickable_link&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Buy Milk&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;omnifocus:///add?name=Buy%20milk&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;* &#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;clickable_link&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Debug Program&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;omnifocus:///add?name=Debug%20Program&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;clickable_link&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$href&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;\e]8;;$href\a$text\e]8;;\a&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Clickable-Recognizable-Text&#34;&gt;Clickable Recognizable Text&lt;/h3&gt;

&lt;p&gt;So far we&#38;#39;ve been talking about having the programs running in the terminal in control. What if we want to do it the other way round - configure the terminal to react to things in the terminal and decide what to do itself?&lt;/p&gt;

&lt;p&gt;This kind of thing is configured in the &lt;i&gt;Profiles&lt;/i&gt; section of iTerm&#38;#39;s preferences. iTerm can be configured to switch to any profile you create automatically. For example here I&#38;#39;ve configured it to switch to this profile whenever I&#38;#39;m in the &lt;code&gt;RuleTheWorld&lt;/code&gt; project dir in my homedir.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;autoswitch.png&#34; width=&#34;542&#34; height=&#34;507&#34; &gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Now I can click on the &lt;code&gt;Edit&lt;/code&gt; button below &lt;code&gt;Smart Selection&lt;/code&gt; to define some new selection rules. Let&#38;#39;s create one for Perl error messages.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;perlerr.jpg&#34; width=&#34;804&#34; height=&#34;354&#34; &gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Now if if hit &lt;code&gt;Edit&lt;/code&gt; I can start defining context menu opens for when I right click on anything that matches the regex. The first menu item I create can also be activated by cmd-clicking on the match.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;actions.jpg&#34; width=&#34;564&#34; height=&#34;282&#34; &gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Now when I command-click on an error message it&#38;#39;ll open the file in VSCode at the right line number.&lt;/p&gt;

&lt;h3 id=&#34;Triggers&#34;&gt;Triggers&lt;/h3&gt;

&lt;p&gt;Triggers are similar to clickable text, except you don&#38;#39;t have to click on them - they happen immediately as the terminal sees the text being sent to it.&lt;/p&gt;

&lt;p&gt;For example, you might recognize the message Hypnotoad prints out when starting up and open the browser window. Or you might have a command on the remove server print out something (like a file name) in a recognizable format and have the local machine do something like it (such as trigger a script to download that file and open in the editor).&lt;/p&gt;

&lt;p&gt;One of my all time favorite hacks is to have iTerm color code the backgrounds of my Perl error messages so they&#38;#39;re easy to spot.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;highlight.png&#34; width=&#34;558&#34; height=&#34;108&#34; &gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;I use two different colors for the highlighting - red indicates the error message came from an absolute path (meaning it&#38;#39;s an installed library, not my code, and I&#38;#39;m less likely to change it) whereas green means it&#38;#39;s local code that I probably need to fix.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;term.png&#34; width=&#34;512&#34; height=&#34;281&#34; &gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Of course, combined with the previous tip, I can cmd-click on these links to open them in vscode.&lt;/p&gt;

&lt;h3 id=&#34;Complete-Control&#34;&gt;Complete Control&lt;/h3&gt;

&lt;p&gt;For the ultimate control you can have iTerm start a &#38;quot;coprocess&#38;quot;. This means that as well as rendring what it&#38;#39;s being sent it&#38;#39;ll also send a copy to an external running program (a &lt;i&gt;co-process&lt;/i&gt;.) That process can either simply monitor what&#38;#39;s being printed to the terminal, or it can send it&#38;#39;s own output which will be interpreted as the user having typed that at the terminal.&lt;/p&gt;

&lt;p&gt;Coprocessses can be started manually, or by a user key combination, or even by a trigger recognizing some text that indicates a program you want to control automatically from you&lt;/p&gt;

&lt;h3 id=&#34;Syncing-our-lib-directory&#34;&gt;Syncing our &lt;code&gt;lib&lt;/code&gt; directory&lt;/h3&gt;

&lt;p&gt;One cool trick I use coprocesses for is transferring my &lt;code&gt;lib&lt;/code&gt; directory of custom modules to any machine I&#38;#39;m connected to. Rather than worrying about if that machine can access the internet, if I can put the fils in my home directory somewhere insecure or not, or if I can connect directly to the machine I&#38;#39;m logged into to transfer files - I have a different solution.&lt;/p&gt;

&lt;p&gt;I just type them in again on each machine.&lt;/p&gt;

&lt;p&gt;Or rather, the co-process does this for me. Here&#38;#39;s an the example coprocess script to transfer the lib directory.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    #!/bin/sh

    echo &#38;#39;stty_orig=`stty -g`&#38;#39;
    echo &#38;#39;stty -echo&#38;#39;
    echo &#38;#39;clear&#38;#39;
    echo &#38;#39;echo &#38;quot;Please wait, transfering&#38;quot;&#38;#39;
    echo &#38;quot;cd ~&#38;quot;;
    echo &#38;quot;base64 -d | tar -xzf -&#38;quot;
    cd ~
    tar -czf - -C ~ lib | base64 -b 72
    perl -e &#38;#39;print &#38;quot;\x{04}&#38;quot;&#38;#39;
    echo &#38;#39;clear&#38;#39;
    echo &#38;#39;echo &#38;quot;All done!&#38;quot;&#38;#39;
    echo &#38;#39;stty $stty_orig&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you squint hard enough you can actually work out what this is doing. It types a command on the remote machine to base64 decode anything that the user types and send it to tar to spit out to the filesystem. It then runs a command locally to tarball up the lib dir and print out the base64 encoding - effectively typing it over the wire to the command we just set up. It finally sends a ctrl-d to let the other end we&#38;#39;re done before resetting the terminal.&lt;/p&gt;

&lt;h3 id=&#34;Conclusion&#34;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;I&#38;#39;ve barely scratchd the surface of what iTerm2 can do here, but I hope I&#38;#39;ve convinced you to give yourself th gift of an optimized environment this year!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-20T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Memories of Past Lives</title><link href="http://perladvent.org/2019/2019-12-19.html"/><id>http://perladvent.org/2019/2019-12-19.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Sometimes, Christmas can be the most stressful time of year. Even at the North Pole, it&#38;#39;s not all gumdrops and candycanes.&lt;/p&gt;

&lt;p&gt;Take, for example, one very red faced and obviously upset elf. Jolly Pudding was muttering under his breath at his computer after his code had shown yet another error message and ground to a halt.&lt;/p&gt;

&lt;p&gt;&#38;quot;And breath!&#38;quot; the Wise Old Elf consoled, &#38;quot;And tell me what the matter is.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Well&#38;quot;, Jolly explained, &#38;quot;I&#38;#39;m writing this quick one off script to produce a report on the toy produced this year. The trouble is that the code keeps failing after about half an hour or so. It&#38;#39;s &lt;i&gt;really&lt;/i&gt; annoying to debug.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;So you need it to run faster so you can debug it quicker?&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes. That&#38;#39;s exactly it! Right now I have sit here, twiddling my thumbs for half an hour each time. What&#38;#39;s worse is by the time it fails again I&#38;#39;ve totally lost the train of thought.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;What you need to do is cache the results of what you &lt;i&gt;did&lt;/i&gt; get to run okay between runs.&#38;quot;, whe Wise Old Elf explained sagely, &#38;quot;Then you&#38;#39;d be able to get to the point where the code breaks instantly&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh, I don&#38;#39;t know...that seems like a lot of work for a one off script.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;I&#38;#39;ve got the perfect module for you: Memoize&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Memozie! You can&#38;#39;t be serious!&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;A-Blast-from-the-past&#34;&gt;A Blast from the past.&lt;/h3&gt;

&lt;p&gt;Memoize was originally &lt;a href=&#34;http://perladvent.pm.org/2000/18/&#34;&gt;featured&lt;/a&gt; in the first ever Perl Advent calendar entry twenty years ago, back before there even were articles on each module.&lt;/p&gt;

&lt;p&gt;Memoize can be used to cache the results of a function call so if it&#38;#39;s called again with the same arguments the cached version is returned. The classic example is the fibonacci sequence.&lt;/p&gt;

&lt;p&gt;Without memoize:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($n)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Running this takes a while to execute:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl fib.pl
    102334155

    real    1m6.912s
    user    1m6.872s
    sys     0m0.032s&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the sam code with Memoize:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Memoize&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($n)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;memoize&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;fib&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Runs much, much faster&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl fib.pl
    102334155

    real    0m0.055s
    user    0m0.009s
    sys     0m0.009s&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Persisting-between-program-runs&#34;&gt;Persisting between program runs&lt;/h3&gt;

&lt;p&gt;This is all very well, but Memoize uses an &lt;i&gt;in memory&lt;/i&gt; cache. Jolly&#38;#39;s problem is that each time he runs his code his program has to re-do all the work up until the point that it crashes. Using memoize like above won&#38;#39;t speed up his code at all as every time the script runs it starts with a fresh empty cache.&lt;/p&gt;

&lt;p&gt;What we need to do is what the Wise Old Elf suggested - persist our results between program runs.&lt;/p&gt;

&lt;p&gt;Well, Memoize can do that:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;divide&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($n,$d)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # make this take artificially long for the purposes of&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the demo&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;sleep&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$d&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DB_File&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;tie&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DB_File&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/tmp/memoize&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;O_RDWR&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;O_CREAT&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;octal&#34;&gt;0666&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;memoize&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;divide&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;SCALAR_CACHE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HASH&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;-10&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;100 divided by $_ is &#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;divide&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the first time we run this it takes a while:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $time perl div.pl 
    100 divided by -10 is -10
    100 divided by -9 is -11.1111111111111
    100 divided by -8 is -12.5
    100 divided by -7 is -14.2857142857143
    100 divided by -6 is -16.6666666666667
    100 divided by -5 is -20
    100 divided by -4 is -25
    100 divided by -3 is -33.3333333333333
    100 divided by -2 is -50
    100 divided by -1 is -100
    Illegal division by zero at div.pl line 15.

    real    0m11.051s
    user    0m0.031s
    sys     0m0.004s&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But the next time is much much quicker:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl div.pl 
    100 divided by -10 is -10
    100 divided by -9 is -11.1111111111111
    100 divided by -8 is -12.5
    100 divided by -7 is -14.2857142857143
    100 divided by -6 is -16.6666666666667
    100 divided by -5 is -20
    100 divided by -4 is -25
    100 divided by -3 is -33.3333333333333
    100 divided by -2 is -50
    100 divided by -1 is -100
    Illegal division by zero at div.pl line 15.

    real    0m1.028s
    user    0m0.016s
    sys     0m0.012s&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whooo! instant death!&lt;/p&gt;

&lt;h3 id=&#34;Methods&#34;&gt;Methods&lt;/h3&gt;

&lt;p&gt;What if the thing we need to cache is a method call?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Fetcher&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;url&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;fetch&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Failed!\n&#38;quot;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;success&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Memoize&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DB_File&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;tie&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DB_File&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/tmp/memoize&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;O_RDWR&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;O_CREAT&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;octal&#34;&gt;0666&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;memoize&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;SCALAR_CACHE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HASH&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each time we run our Santa detecting script:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Fetcher&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;http://www.bbc.co.uk/news/&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;http://www.cnn.com/&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;http://news.ycombinator.com/&lt;br /&gt;)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$f&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Fetcher&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;SANTA IS HAPPENING&#38;quot;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$f&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;fetch&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/santa/i&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It takes the about same time:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl -I. santa.pl 

    real    0m1.254s
    user    0m0.090s
    sys     0m0.026s

    $ time perl -I. santa.pl 

    real    0m2.921s
    user    0m0.087s
    sys     0m0.028s

    $ time perl -I. santa.pl 

    real    0m1.203s
    user    0m0.085s
    sys     0m0.024s&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The problem is that Memoize sees each instance of &lt;code&gt;$f&lt;/code&gt; as being different between each run - it can&#38;#39;t tell that an &lt;code&gt;$f&lt;/code&gt; constructed with the same url will produce the same result. We can fix this with a &lt;i&gt;normalizer&lt;/i&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;memoize&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;SCALAR_CACHE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HASH&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;NORMALIZER&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($f,@)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$f&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&#38;#39;s the job of the normalizer to take the arguments and return a suitable cache key. Here we&#38;#39;re just using the URL that &lt;code&gt;$f&lt;/code&gt; has, as that&#38;#39;s the only thing that matters. Caching now works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl -I. santa.pl 

    real    0m3.503s
    user    0m0.125s
    sys     0m0.022s
    
    $ time perl -I. santa.pl 

    real    0m0.068s
    user    0m0.063s
    sys     0m0.005s&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;In-Conclusion&#34;&gt;In Conclusion&lt;/h3&gt;

&lt;p&gt;So it turns out what we&#38;#39;d been thinking purely as a way of speeding up our code in limited circumstances is even more valuable as a tool to create temporary code in development to help us stop going insane. There&#38;#39;s life in the old module yet!&lt;/p&gt;

&lt;h3 id=&#34;P.S.-You-can-Leave-it-in-the-production-code&#34;&gt;P.S. You can Leave it in the production code&lt;/h3&gt;

&lt;p&gt;What if your one off script becomes something you run once a day or so? We all know that happens more often than we&#38;#39;d like.&lt;/p&gt;

&lt;p&gt;The above code isn&#38;#39;t suitable for that - it&#38;#39;ll forever remember the news on the day the code was first run! (Or at least until the computer is restarted and &lt;code&gt;/tmp&lt;/code&gt; is cleared out). What we need is for our cache to expire after, say, an hour.&lt;/p&gt;

&lt;p&gt;Memoize, with the help of another core module, can do that too with layers of tied hashes:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;tie&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DB_File&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/tmp/memoize&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;O_CREAT&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;O_RDWR&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;octal&#34;&gt;0666&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;tie&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%forgetful&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Memoize::Expire&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LIFETIME&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;3600&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HASH&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;%cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span class=&#34;word&#34;&gt;memoize&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;SCALAR_CACHE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HASH&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;%forgetful&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;NORMALIZER&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($f,@)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$f&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2019-12-19T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Core Strength</title><link href="http://perladvent.org/2019/2019-12-18.html"/><id>http://perladvent.org/2019/2019-12-18.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;One of the things to consider in the brave new world of Perl Lambda functions we&#38;#39;ve been exploring together over the last few days, is dependance cost. Every individual web endpoint is effectively its own bundle of dependencies. If we&#38;#39;re not careful, the dependencies can add up quickly - for example, DateTime and its required dependencies alone adds 30MB+ of source code and support files. This is a problem when we&#38;#39;ve got a platform enforced hard limit of 50MB of decompressed files in our Lambda sourcecode zipfile - we either need to start splitting things out into layers, or slimming down.&lt;/p&gt;

&lt;p&gt;One thing we should not overlook is that Perl already comes with a collection of awesome modules for doing the core tings (hence the name - the core distribution.) We should use more of those when we can! But how do we know if chosen module is available?&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Module::CoreList&#34;&gt;Module::CoreList&lt;/a&gt; is a CPAN module designed to solve this problem. It keeps detailed information on what core modules were included with the main Perl distribution for each release.&lt;/p&gt;

&lt;p&gt;It&#38;#39;s accessible on the web from &lt;a href=&#34;http://corelist.rpee.be/&#34;&gt;http://corelist.rpee.be/&lt;/a&gt;, via the module interface, or most simply, via a command line tool.&lt;/p&gt;

&lt;h3 id=&#34;Streching-our-Core-Muscles&#34;&gt;Streching our Core Muscles&lt;/h3&gt;

&lt;p&gt;The command line tool has a bunch of really handy options - let&#38;#39;s take tour and see what we can work out.&lt;/p&gt;

&lt;p&gt;How about when a perl module was first released with perl?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ corelist List::Util
    Data for 2019-11-20
    List::Util was first released with perl v5.7.3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What was the date that went live?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ corelist -r 5.7.3
    Perl v5.7.3 was released on 2002-03-05&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;According to the List::Util&#38;#39;s documentation the &lt;code&gt;any&lt;/code&gt; function has been available since version 1.33. What version of perl was that released with?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ corelist -a List::Util
    Data for 2019-11-20
    List::Util was first released with perl v5.7.3
    v5.7.3     1.06_00   
    v5.8.0     1.07_00   
    v5.8.1     1.13      
    v5.8.2     1.13      
    v5.8.3     1.13      
    v5.8.4     1.13      
    v5.8.5     1.14      
    v5.8.6     1.14      
    v5.8.7     1.14      
    v5.8.8     1.18      
    v5.8.9     1.19      
    v5.9.0     1.13      
    v5.9.1     1.13      
    v5.9.2     1.14      
    v5.9.3     1.18      
    v5.9.4     1.18      
    v5.9.5     1.19      
    v5.10.0    1.19      
    v5.10.1    1.21      
    v5.11.0    1.21      
    v5.11.1    1.21      
    v5.11.2    1.22      
    v5.11.3    1.22      
    v5.11.4    1.22      
    v5.11.5    1.22      
    v5.12.0    1.22      
    v5.12.1    1.22      
    v5.12.2    1.22      
    v5.12.3    1.22      
    v5.12.4    1.22      
    v5.12.5    1.22      
    v5.13.0    1.22      
    v5.13.1    1.23      
    v5.13.2    1.23      
    v5.13.3    1.23      
    v5.13.4    1.23      
    v5.13.5    1.23      
    v5.13.6    1.23      
    v5.13.7    1.23      
    v5.13.8    1.23      
    v5.13.9    1.23      
    v5.13.10   1.23      
    v5.13.11   1.23      
    v5.14.0    1.23      
    v5.14.1    1.23      
    v5.14.2    1.23      
    v5.14.3    1.23      
    v5.14.4    1.23      
    v5.15.0    1.23      
    v5.15.1    1.23      
    v5.15.2    1.23      
    v5.15.3    1.23      
    v5.15.4    1.23      
    v5.15.5    1.23      
    v5.15.6    1.23      
    v5.15.7    1.23      
    v5.15.8    1.23      
    v5.15.9    1.23      
    v5.16.0    1.23      
    v5.16.1    1.25      
    v5.16.2    1.25      
    v5.16.3    1.25      
    v5.17.0    1.23      
    v5.17.1    1.25      
    v5.17.2    1.25      
    v5.17.3    1.25      
    v5.17.4    1.25      
    v5.17.5    1.25      
    v5.17.6    1.25      
    v5.17.7    1.26      
    v5.17.8    1.27      
    v5.17.9    1.27      
    v5.17.10   1.27      
    v5.17.11   1.27      
    v5.18.0    1.27      
    v5.18.1    1.27      
    v5.18.2    1.27      
    v5.18.3    1.27      
    v5.18.4    1.27      
    v5.19.0    1.27      
    v5.19.1    1.27      
    v5.19.2    1.27      
    v5.19.3    1.31      
    v5.19.4    1.32      
    v5.19.5    1.35      
    v5.19.6    1.35      
    v5.19.7    1.35      
    v5.19.8    1.36      
    v5.19.9    1.38      
    v5.19.10   1.38      
    v5.19.11   1.38      
    v5.20.0    1.38      
    v5.20.1    1.38      
    v5.20.2    1.38      
    v5.20.3    1.38      
    v5.21.0    1.38      
    v5.21.1    1.39      
    v5.21.2    1.39      
    v5.21.3    1.39      
    v5.21.4    1.41      
    v5.21.5    1.41      
    v5.21.6    1.41      
    v5.21.7    1.41      
    v5.21.8    1.41      
    v5.21.9    1.41      
    v5.21.10   1.41      
    v5.21.11   1.41      
    v5.22.0    1.41      
    v5.22.1    1.41      
    v5.22.2    1.41      
    v5.22.3    1.41      
    v5.22.4    1.41      
    v5.23.0    1.42_01   
    v5.23.1    1.42_01   
    v5.23.2    1.42_01   
    v5.23.3    1.42_01   
    v5.23.4    1.42_01   
    v5.23.5    1.42_01   
    v5.23.6    1.42_01   
    v5.23.7    1.42_01   
    v5.23.8    1.42_01   
    v5.23.9    1.42_02   
    v5.24.0    1.42_02   
    v5.24.1    1.42_02   
    v5.24.2    1.42_02   
    v5.24.3    1.42_02   
    v5.24.4    1.42_02   
    v5.25.0    1.42_02   
    v5.25.1    1.45_01   
    v5.25.2    1.45_01   
    v5.25.3    1.45_01   
    v5.25.4    1.45_01   
    v5.25.5    1.45_01   
    v5.25.6    1.46      
    v5.25.7    1.46_02   
    v5.25.8    1.46_02   
    v5.25.9    1.46_02   
    v5.25.10   1.46_02   
    v5.25.11   1.46_02   
    v5.25.12   1.46_02   
    v5.26.0    1.46_02   
    v5.26.1    1.46_02   
    v5.26.2    1.46_02   
    v5.26.3    1.46_02   
    v5.27.0    1.46_02   
    v5.27.1    1.46_02   
    v5.27.2    1.48      
    v5.27.3    1.48      
    v5.27.4    1.48      
    v5.27.5    1.49      
    v5.27.6    1.49      
    v5.27.7    1.49      
    v5.27.8    1.49      
    v5.27.9    1.49      
    v5.27.10   1.50      
    v5.27.11   1.50      
    v5.28.0    1.50      
    v5.28.1    1.50      
    v5.28.2    1.50      
    v5.29.0    1.50      
    v5.29.1    1.50      
    v5.29.2    1.50      
    v5.29.3    1.50      
    v5.29.4    1.50      
    v5.29.5    1.50      
    v5.29.6    1.50      
    v5.29.7    1.50      
    v5.29.8    1.50      
    v5.29.9    1.50      
    v5.29.10   1.50      
    v5.30.0    1.50      
    v5.30.1    1.50      
    v5.31.0    1.50      
    v5.31.1    1.50      
    v5.31.2    1.50      
    v5.31.3    1.50      
    v5.31.4    1.52      
    v5.31.5    1.52      
    v5.31.6    1.53      &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, more sucinctly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    corelist -a List::Util | perl -ne &#38;#39;/^\s+(\S+)\s+(\S+)/ &#38;amp;&#38;amp; $2 &#38;gt;= 1.33 &#38;amp;&#38;amp; print &#38;amp;&#38;amp; exit&#38;#39;
      v5.19.5    1.35      &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If I find a bug in the module do I have to install a new version of perl to get an update, or can I just download an updated version of the module from the CPAN? And where can I report that bug to?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    corelist -u List::Util
    Data for 2019-11-20
    List::Util was first released with perl v5.7.3
    upstream: cpan
    bug tracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Scalar-List-Utils&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(I can get a new version from the CPAN, and that&#38;#39;s the bug tracker URL)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    corelist -u PerlIO
    Data for 2019-11-20
    PerlIO was first released with perl v5.7.3
    upstream: undef
    bug tracker: unknown&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(PerlIO is shipped with perl, and I report bugs the same way I do with the core language itself, via &lt;a href=&#34;https://metacpan.org/module/perlbug&#34;&gt;perlbug&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;What do we see if we search for a module that doesn&#38;#39;t exist in core (even if we think it should be)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ corelist DateTime

    Data for 2019-11-20
    DateTime was not in CORE (or so I think)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This, of course, doesn&#38;#39;t mean that DateTime might not come preinstalled on your system (on macOS for example it&#38;#39;s distributed with the operating system.) Just it&#38;#39;s not in the main core distribution!&lt;/p&gt;

&lt;p&gt;What about something that was added to the core distribution but then removed again?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ corelist CGI
    Data for 2019-11-20
    CGI was first released with perl 5.004, deprecated (will be CPAN-only)
    in v5.19.7 and removed from v5.21.0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What about a core language level feature&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ corelist -f say
    Data for 2019-11-20
    feature &#38;quot;say&#38;quot; was first released with the perl v5.9.5 feature bundle&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(this means you can &lt;code&gt;use feature &#38;quot;say&#38;quot;&lt;/code&gt; on any perl after v5.9.5)&lt;/p&gt;

&lt;p&gt;You can even find out what were the module changes between Perl 5.28 and 5.30 for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ corelist --diff v5.28.0 v5.30.0
    App::Cpan                                 1.67      1.672
    Archive::Tar                              2.30       2.32
    Archive::Tar::Constant                    2.30       2.32
    Archive::Tar::File                        2.30       2.32
    B                                         1.74       1.76
    B::Concise                               1.003      1.004
    B::Debug                                  1.26   (absent)
    B::Deparse                                1.48       1.49
    B::Op_private                         5.028000   5.030000
    B::Terse                                  1.08       1.09
    CPAN                                      2.20       2.22
    CPAN::Distribution                        2.19       2.22
    CPAN::Mirrors                             2.12       2.21
    CPAN::Plugin                              0.96       0.97
    CPAN::Plugin::Specfile                    0.01       0.02
    CPAN::Shell                             5.5007     5.5008
    Compress::Raw::Bzip2                     2.074      2.084
    Compress::Raw::Zlib                      2.076      2.084
    Compress::Zlib                           2.074      2.084
    Config                                   5.028       5.03
    Config::Extensions                        0.02       0.03
    Config::Perl::V                           0.29       0.32
    Cwd                                       3.74       3.78
    DB_File                                  1.840      1.843
    Data::Dumper                             2.170      2.174
    Devel::PPPort                             3.40       3.52
    Devel::Peek                               1.27       1.28
    Digest::SHA                               6.01       6.02
    Encode                                    2.97       3.01
    Encode::Unicode                           2.17       2.18
    Errno                                     1.29       1.30
    ExtUtils::CBuilder                    0.280230   0.280231
    ExtUtils::CBuilder::Base              0.280230   0.280231
    ExtUtils::CBuilder::Platform::Unix    0.280230   0.280231
    ExtUtils::CBuilder::Platform::VMS     0.280230   0.280231
    ExtUtils::CBuilder::Platform::Windows   0.280230   0.280231
    ExtUtils::CBuilder::Platform::Windows::BCC   0.280230   0.280231
    ExtUtils::CBuilder::Platform::Windows::GCC   0.280230   0.280231
    ExtUtils::CBuilder::Platform::Windows::MSVC   0.280230   0.280231
    ExtUtils::CBuilder::Platform::aix     0.280230   0.280231
    ExtUtils::CBuilder::Platform::android   0.280230   0.280231
    ExtUtils::CBuilder::Platform::cygwin   0.280230   0.280231
    ExtUtils::CBuilder::Platform::darwin   0.280230   0.280231
    ExtUtils::CBuilder::Platform::dec_osf   0.280230   0.280231
    ExtUtils::CBuilder::Platform::os2     0.280230   0.280231
    ExtUtils::Manifest                        1.70       1.72
    ExtUtils::Miniperl                        1.08       1.09
    ExtUtils::ParseXS                         3.39       3.40
    ExtUtils::ParseXS::Constants              3.39       3.40
    ExtUtils::ParseXS::CountLines             3.39       3.40
    ExtUtils::ParseXS::Eval                   3.39       3.40
    ExtUtils::ParseXS::Utilities              3.39       3.40
    File::Copy                                2.33       2.34
    File::Find                                1.34       1.36
    File::Glob                                1.31       1.32
    File::GlobMapper                         1.000      1.001
    File::Path                                2.15       2.16
    File::Spec                                3.74       3.78
    File::Spec::AmigaOS                       3.74       3.78
    File::Spec::Cygwin                        3.74       3.78
    File::Spec::Epoc                          3.74       3.78
    File::Spec::Functions                     3.74       3.78
    File::Spec::Mac                           3.74       3.78
    File::Spec::OS2                           3.74       3.78
    File::Spec::Unix                          3.74       3.78
    File::Spec::VMS                           3.74       3.78
    File::Spec::Win32                         3.74       3.78
    File::Temp                              0.2304     0.2309
    Filter::Util::Call                        1.58       1.59
    GDBM_File                                 1.17       1.18
    HTTP::Tiny                               0.070      0.076
    I18N::Langinfo                            0.17       0.18
    IO                                        1.39       1.40
    IO::Compress::Adapter::Bzip2             2.074      2.084
    IO::Compress::Adapter::Deflate           2.074      2.084
    IO::Compress::Adapter::Identity          2.074      2.084
    IO::Compress::Base                       2.074      2.084
    IO::Compress::Base::Common               2.074      2.084
    IO::Compress::Bzip2                      2.074      2.084
    IO::Compress::Deflate                    2.074      2.084
    IO::Compress::Gzip                       2.074      2.084
    IO::Compress::Gzip::Constants            2.074      2.084
    IO::Compress::RawDeflate                 2.074      2.084
    IO::Compress::Zip                        2.074      2.084
    IO::Compress::Zip::Constants             2.074      2.084
    IO::Compress::Zlib::Constants            2.074      2.084
    IO::Compress::Zlib::Extra                2.074      2.084
    IO::Dir                                   1.39       1.40
    IO::File                                  1.39       1.40
    IO::Handle                                1.39       1.40
    IO::Pipe                                  1.39       1.40
    IO::Poll                                  1.39       1.40
    IO::Seekable                              1.39       1.40
    IO::Select                                1.39       1.40
    IO::Socket                                1.39       1.40
    IO::Socket::INET                          1.39       1.40
    IO::Socket::UNIX                          1.39       1.40
    IO::Uncompress::Adapter::Bunzip2         2.074      2.084
    IO::Uncompress::Adapter::Identity        2.074      2.084
    IO::Uncompress::Adapter::Inflate         2.074      2.084
    IO::Uncompress::AnyInflate               2.074      2.084
    IO::Uncompress::AnyUncompress            2.074      2.084
    IO::Uncompress::Base                     2.074      2.084
    IO::Uncompress::Bunzip2                  2.074      2.084
    IO::Uncompress::Gunzip                   2.074      2.084
    IO::Uncompress::Inflate                  2.074      2.084
    IO::Uncompress::RawInflate               2.074      2.084
    IO::Uncompress::Unzip                    2.074      2.084
    IPC::Cmd                                  1.00       1.02
    JSON::PP                               2.97001       4.02
    JSON::PP::Boolean                      2.97001       4.02
    Locale::Codes                             3.56   (absent)
    Locale::Codes::Constants                  3.56   (absent)
    Locale::Codes::Country                    3.56   (absent)
    Locale::Codes::Country_Codes              3.56   (absent)
    Locale::Codes::Country_Retired            3.56   (absent)
    Locale::Codes::Currency                   3.56   (absent)
    Locale::Codes::Currency_Codes             3.56   (absent)
    Locale::Codes::Currency_Retired           3.56   (absent)
    Locale::Codes::LangExt                    3.56   (absent)
    Locale::Codes::LangExt_Codes              3.56   (absent)
    Locale::Codes::LangExt_Retired            3.56   (absent)
    Locale::Codes::LangFam                    3.56   (absent)
    Locale::Codes::LangFam_Codes              3.56   (absent)
    Locale::Codes::LangFam_Retired            3.56   (absent)
    Locale::Codes::LangVar                    3.56   (absent)
    Locale::Codes::LangVar_Codes              3.56   (absent)
    Locale::Codes::LangVar_Retired            3.56   (absent)
    Locale::Codes::Language                   3.56   (absent)
    Locale::Codes::Language_Codes             3.56   (absent)
    Locale::Codes::Language_Retired           3.56   (absent)
    Locale::Codes::Script                     3.56   (absent)
    Locale::Codes::Script_Codes               3.56   (absent)
    Locale::Codes::Script_Retired             3.56   (absent)
    Locale::Country                           3.56   (absent)
    Locale::Currency                          3.56   (absent)
    Locale::Language                          3.56   (absent)
    Locale::Script                            3.56   (absent)
    Math::BigFloat                        1.999811   1.999816
    Math::BigFloat::Trace                     0.49       0.51
    Math::BigInt                          1.999811   1.999816
    Math::BigInt::Calc                    1.999811   1.999816
    Math::BigInt::CalcEmu                 1.999811   (absent)
    Math::BigInt::FastCalc                  0.5006     0.5008
    Math::BigInt::Lib                     1.999811   1.999816
    Math::BigInt::Trace                       0.49       0.51
    Math::BigRat                            0.2613     0.2614
    Module::CoreList                    5.20180622 5.20190522
    Module::CoreList::Utils             5.20180622 5.20190522
    Module::Load                              0.32       0.34
    Module::Metadata                      1.000033   1.000036
    NDBM_File                                 1.14       1.15
    Net::Ping                                 2.62       2.71
    ODBM_File                                 1.15       1.16
    POSIX                                     1.84       1.88
    PerlIO::encoding                          0.26       0.27
    PerlIO::scalar                            0.29       0.30
    Pod::Man                                  4.10       4.11
    Pod::ParseLink                            4.10       4.11
    Pod::Text                                 4.10       4.11
    Pod::Text::Color                          4.10       4.11
    Pod::Text::Overstrike                     4.10       4.11
    Pod::Text::Termcap                        4.10       4.11
    SDBM_File                                 1.14       1.15
    Storable                                  3.08       3.15
    Storable::Limit                        (undef)   (absent)
    Test2                                 1.302133   1.302162
    Test2::API                            1.302133   1.302162
    Test2::API::Breakage                  1.302133   1.302162
    Test2::API::Context                   1.302133   1.302162
    Test2::API::Instance                  1.302133   1.302162
    Test2::API::Stack                     1.302133   1.302162
    Test2::Event                          1.302133   1.302162
    Test2::Event::Bail                    1.302133   1.302162
    Test2::Event::Diag                    1.302133   1.302162
    Test2::Event::Encoding                1.302133   1.302162
    Test2::Event::Exception               1.302133   1.302162
    Test2::Event::Fail                    1.302133   1.302162
    Test2::Event::Generic                 1.302133   1.302162
    Test2::Event::Note                    1.302133   1.302162
    Test2::Event::Ok                      1.302133   1.302162
    Test2::Event::Pass                    1.302133   1.302162
    Test2::Event::Plan                    1.302133   1.302162
    Test2::Event::Skip                    1.302133   1.302162
    Test2::Event::Subtest                 1.302133   1.302162
    Test2::Event::TAP::Version            1.302133   1.302162
    Test2::Event::V2                      1.302133   1.302162
    Test2::Event::Waiting                 1.302133   1.302162
    Test2::EventFacet                     1.302133   1.302162
    Test2::EventFacet::About              1.302133   1.302162
    Test2::EventFacet::Amnesty            1.302133   1.302162
    Test2::EventFacet::Assert             1.302133   1.302162
    Test2::EventFacet::Control            1.302133   1.302162
    Test2::EventFacet::Error              1.302133   1.302162
    Test2::EventFacet::Hub                1.302133   1.302162
    Test2::EventFacet::Info               1.302133   1.302162
    Test2::EventFacet::Info::Table        (absent)    (undef)
    Test2::EventFacet::Meta               1.302133   1.302162
    Test2::EventFacet::Parent             1.302133   1.302162
    Test2::EventFacet::Plan               1.302133   1.302162
    Test2::EventFacet::Render             1.302133   1.302162
    Test2::EventFacet::Trace              1.302133   1.302162
    Test2::Formatter                      1.302133   1.302162
    Test2::Formatter::TAP                 1.302133   1.302162
    Test2::Hub                            1.302133   1.302162
    Test2::Hub::Interceptor               1.302133   1.302162
    Test2::Hub::Interceptor::Terminator   1.302133   1.302162
    Test2::Hub::Subtest                   1.302133   1.302162
    Test2::IPC                            1.302133   1.302162
    Test2::IPC::Driver                    1.302133   1.302162
    Test2::IPC::Driver::Files             1.302133   1.302162
    Test2::Tools::Tiny                    1.302133   1.302162
    Test2::Util                           1.302133   1.302162
    Test2::Util::ExternalMeta             1.302133   1.302162
    Test2::Util::Facets2Legacy            1.302133   1.302162
    Test2::Util::HashBase                 1.302133   1.302162
    Test2::Util::Trace                    1.302133   1.302162
    Test::Builder                         1.302133   1.302162
    Test::Builder::Formatter              1.302133   1.302162
    Test::Builder::Module                 1.302133   1.302162
    Test::Builder::Tester                 1.302133   1.302162
    Test::Builder::Tester::Color          1.302133   1.302162
    Test::Builder::TodoDiag               1.302133   1.302162
    Test::More                            1.302133   1.302162
    Test::Simple                          1.302133   1.302162
    Test::Tester                          1.302133   1.302162
    Test::Tester::Capture                 1.302133   1.302162
    Test::Tester::CaptureRunner           1.302133   1.302162
    Test::Tester::Delegate                1.302133   1.302162
    Test::use::ok                         1.302133   1.302162
    Thread::Queue                             3.12       3.13
    Time::HiRes                             1.9759     1.9760
    Time::Local                               1.25       1.28
    Time::Piece                             1.3204       1.33
    Time::Seconds                           1.3204       1.33
    Unicode                                 10.0.0     12.1.0
    Unicode::Collate                          1.25       1.27
    Unicode::Collate::CJK::Big5               1.25       1.27
    Unicode::Collate::CJK::GB2312             1.25       1.27
    Unicode::Collate::CJK::JISX0208           1.25       1.27
    Unicode::Collate::CJK::Korean             1.25       1.27
    Unicode::Collate::CJK::Pinyin             1.25       1.27
    Unicode::Collate::CJK::Stroke             1.25       1.27
    Unicode::Collate::CJK::Zhuyin             1.25       1.27
    Unicode::Collate::Locale                  1.25       1.27
    Unicode::UCD                              0.70       0.72
    User::grent                               1.02       1.03
    XS::APItest                               0.98       1.00
    XS::Typemap                               0.16       0.17
    arybase                                   0.15   (absent)
    bigint                                    0.49       0.51
    bignum                                    0.49       0.51
    bigrat                                    0.49       0.51
    bytes                                     1.06       1.07
    deprecate                                 0.03       0.04
    experimental                             0.019      0.020
    feature                                   1.52       1.54
    lib                                       0.64       0.65
    ok                                    1.302133   1.302162
    parent                                   0.236      0.237
    perlfaq                               5.021011 5.20190126
    re                                        0.36       0.37
    sigtrap                                   1.08       1.09
    threads::shared                           1.58       1.60
    utf8                                      1.21       1.22
    vars                                      1.04       1.05
    version                                 0.9923     0.9924
    version::regex                          0.9923     0.9924
    warnings                                  1.42       1.44&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Beyond-a-Core-Workout&#34;&gt;Beyond a Core Workout&lt;/h3&gt;

&lt;p&gt;One thing to consider however is that core modules aren&#38;#39;t your own source of modules on the enviroment.&lt;/p&gt;

&lt;p&gt;Consider running a Lambda function and using &lt;a href=&#34;https://metacpan.org/module/Try::Tiny&#34;&gt;Try::Tiny&lt;/a&gt;. Not a core module:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ corelist Try::Tiny
    Data for 2019-11-20
    Try::Tiny was not in CORE (or so I think)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But you can still use it in an AWS::Lambda powered function without having to supply it yourself in the zipfile! How is this possible? Well, it&#38;#39;s because AWS::Lambda &lt;i&gt;itself&lt;/i&gt; uses the module, and therefore it&#38;#39;s in the default layer that your Lambda function uses.&lt;/p&gt;

&lt;p&gt;What you need to do is look at the dependance graph for AWS::Lambda so you can see not only the direct dependencies of AWS::Lambda but its dependencies&#38;#39; dependencies and so on. You can probably use all of these too!&lt;/p&gt;

&lt;p&gt;You can literally see a graph for each module&#38;#39;s dependancies by visiting &lt;a href=&#34;https://cpandeps.grinnz.com/&#34;&gt;https://cpandeps.grinnz.com/&lt;/a&gt;. For example, here&#38;#39;s &lt;a href=&#34;https://cpandeps.grinnz.com/?dist=AWS-Lambda&#34;&gt;the graph&lt;/a&gt; for AWS::Lambda.&lt;/p&gt;

&lt;p&gt;Similarly, we noted that DateTime is available on macOS Catalina. Therefore, any runtime dependencies it requires must also be installed, correct? Well, it&#38;#39;s not quite that easy. You&#38;#39;d expect the excellent Params::ValidationCompiler to be available as it&#38;#39;s a requirement of the DateTime 1.51 module that&#38;#39;s on the CPAN. But macOS only bundles version 1.06, which doesn&#38;#39;t require Params::ValidationCompiler, so it&#38;#39;s not there. You have to sweat the details!&lt;/p&gt;

&lt;h3 id=&#34;Keep-Working-It&#34;&gt;Keep Working It&lt;/h3&gt;

&lt;p&gt;Of course, if you need something from the CPAN, you should go ahead and grab it. But, sometimes it&#38;#39;s good to run slim...&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-18T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Taking the Sleigh to a CloudFront</title><link href="http://perladvent.org/2019/2019-12-17.html"/><id>http://perladvent.org/2019/2019-12-17.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;The Wise Old Elf was very impressed how Sugarplum Snoozysnaps had &lt;a href=&#34;http://perladvent.org/2019/2019-12-15.html&#34;&gt;saved Christmas&lt;/a&gt; this year by moving some code to AWS Lambda at the last moment. So impressed that he wanted her to investigate how it could be used to do more.&lt;/p&gt;

&lt;p&gt;Right now the elves were running dedicated servers for a few internal Mojolicious apps. Would it be possible to port these to run entirely on AWS Lambda?&lt;/p&gt;

&lt;h3 id=&#34;Bundling-Mojolicious&#34;&gt;Bundling Mojolicious&lt;/h3&gt;

&lt;p&gt;First, let&#38;#39;s download Mojolicious to a local directory with &lt;a href=&#34;https://metacpan.org/module/cpanm&#34;&gt;cpanm&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $shell cpanm -L extra Mojolicious
    --&#38;gt; Working on Mojolicious
    Fetching http://www.cpan.org/authors/id/S/SR/SRI/Mojolicious-8.27.tar.gz ... OK
    Configuring Mojolicious-8.27 ... OK
    Building and testing Mojolicious-8.27 ... OK
    Successfully installed Mojolicious-8.27
    1 distribution installed&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This creates a local copy of Mojolicious in our file system like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ find extra/lib/perl5
    extra/lib/perl5
    extra/lib/perl5/Mojo.pm
    extra/lib/perl5/Test
    extra/lib/perl5/Test/Mojo.pm
    extra/lib/perl5/ojo.pm
    extra/lib/perl5/Mojolicious.pm&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sugarplum can now ask AWS::Lambda::Quick to upload this for her with the rest of her code by specifying &lt;code&gt;extra&lt;/code&gt; as an argument to &lt;code&gt;extra_files&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl5&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;        &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;mojo&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_files&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;extra&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;timeout&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once that&#38;#39;s done she can modify her script to load modules from the lib dir within the extra dir. Note the use of the &lt;code&gt;LAMBDA_TASK_ROOT&lt;/code&gt; environment variable to help us locate those files:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# Where we put the local Mojo&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;lib&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$ENV{&#39;LAMBDA_TASK_ROOT&#39;}/extra/lib/perl5&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since Mojolicious is a pure Perl distribution just uploading the installed files works regardless of different system architecture or perl versions between Sugarplum&#38;#39;s local machine and the virtual machine AWS Lambda is running under.&lt;/p&gt;

&lt;h3 id=&#34;Connecting-Lambda-to-Mojolicious&#34;&gt;Connecting Lambda to Mojolicious&lt;/h3&gt;

&lt;p&gt;Sugarplum needs to plumb the various bits together now, to get Mojolicious to somehow understand the hashref passed into the handler function and get it to spit out the hashref that the handler is expected to return.&lt;/p&gt;

&lt;p&gt;The first stage is to get AWS::Lambda to talk to any standard web server that can communicate via the PSGI function&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::PSGI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;text/plain&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;lt;html&#38;gt;&#38;lt;body&#38;gt;Hello, World @ &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;&#38;lt;/body&#38;gt;&#38;lt;/html&#38;gt;&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::PSGI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;wrap&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We already know how to get Mojolicious to produce a PSGI application. Sugarplum can use that instead of the hard coded application above:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;psgi&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::PSGI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;wrap&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ index.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;Hello, World @ &#38;lt;%= scalar time %&#38;gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And works! Well, at least Mojolicious executes and produces output of some sort...&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;mojoerr.jpg&#34; width=&#34;457&#34; height=315 alt=&#34;mojo error&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Uh, but what happened to her page? Why isn&#38;#39;t it producing her hello world application?&lt;/p&gt;

&lt;p&gt;Well, we remember we declared a route on &lt;code&gt;/&lt;/code&gt; but look at that debug output that&#38;#39;s not what the on the server is: It&#38;#39;s running on &lt;code&gt;/mojo&lt;/code&gt; for the base of &lt;code&gt;/quick&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We&#38;#39;ll fix that, but before we do, let&#38;#39;s consider how Sugarplum is going to host this as a top level domain.&lt;/p&gt;

&lt;h3 id=&#34;Configuring-Cloudfront&#34;&gt;Configuring Cloudfront&lt;/h3&gt;

&lt;p&gt;Cloudfront is AWS&#38;#39;s CDN solution that can sit infront of origin servers, S3 buckets, and - most usefully to Ms Snoozysnaps - the API Gateway running her Lambda function.&lt;/p&gt;

&lt;p&gt;For today&#38;#39;s exercise Sugarplum is just going to use one of Amazon&#38;#39;s own subdomains. When Sugarplum releases the live version she&#38;#39;s going to setup a CNAME for domain name she owns - but for development testing she doesn&#38;#39;t want to go to the trouble of verifying all that with Amazon.&lt;/p&gt;

&lt;p&gt;Sugarplum needs to load up the &lt;a href=&#34;https://console.aws.amazon.com/cloudfront/home#create-distribution&#34;&gt;web interface&lt;/a&gt; to setup a new Cloudfront distribution and click on &#38;quot;Web&#38;quot; to see&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;cloudfront.jpg&#34; width=&#34;530&#34; height=494 alt=&#34;cloudfront web ui&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;ul&gt; &lt;li&gt;Origin Domain Name should be set to the Lambda gateway domain name (in this case &#39;52p3rf890b.execute-api.us-east-1.amazonaws.com&#39;)&lt;/li&gt; &lt;li&gt;Origin Path should be set to &lt;code&gt;/quick/mojo&lt;/code&gt;&lt;/li&gt; &lt;li&gt;Origin Protocol Policy (which will show after you set Origin Domain Name) should be set to &lt;code&gt;HTTPS Only&lt;/code&gt; (as our API gateway doesn&#39;t support HTTP)&lt;/li&gt; &lt;li&gt;Object Caching should be set to &lt;code&gt;Customize&lt;/code&gt;, and all the TTLs should be set to &lt;code&gt;0&lt;/code&gt; to disable caching. Sugarplum Snoozysnaps will want to eventually to have her Mojo app produce custom caching headers to avoid each and every request running through Lambda, but this is good for development.&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;p&gt;Finally we Sugarplum can click on the &lt;code&gt;Create Distribution&lt;/code&gt; at the very bottom of the page. She&#38;#39;s probably got time to go get a cup of fresh eggnog while AWS churns away getting all that set up and allowing domain names to propagate.&lt;/p&gt;

&lt;p&gt;When the interface finally tells us it&#38;#39;s ready, we can load it up...and still see errors. At least this time we can tell &lt;i&gt;exactly&lt;/i&gt; what is broken.&lt;/p&gt;

&lt;h3 id=&#34;Making-the-Paths-Work&#34;&gt;Making the Paths Work&lt;/h3&gt;

&lt;p&gt;Sugarplum now knows what she needs to do to modify the paths.&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;The base URL needs to have /quick stripped off of it&lt;/li&gt; &lt;li&gt;The url itelf needs to have /mojo stripped off&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;p&gt;It&#38;#39;s important to do both of these so that URLs that Mojolicious creates linking to named routes (i.e. with the &lt;code&gt;url_for&lt;/code&gt; or &lt;code&gt;link_to&lt;/code&gt; helpers) end up linking to the right place.&lt;/p&gt;

&lt;p&gt;Modification of the URLs can be managed by adding a hook that does the modifications before dispatch&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hook&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;before_dispatch&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # see https://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Rewriting&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# for details on what exactly we&#39;re doing here&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # remove the &#39;/quick&#39; from the base url&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;trailing_slash&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # remove the &#39;/mojo&#39; from the main url&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;leading_slash&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;A-live-test&#34;&gt;A live test&lt;/h3&gt;

&lt;p&gt;Just to make sure this is working let&#38;#39;s add a few routes that will (a) link to each other via route names (b) change content randomly each page load&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;        &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;mojo&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_files&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;extra&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;timeout&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;lib&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$ENV{&#39;LAMBDA_TASK_ROOT&#39;}/extra/lib/perl5&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::PSGI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;psgi&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::PSGI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;wrap&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi_app&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hook&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;before_dispatch&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;trailing_slash&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;leading_slash&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;#   path         template    route name&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt;       &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;main-page&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/random&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;random&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;merry-christmas-page&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ index.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;lt;p&#38;gt;Hello, World&#38;lt;/p&#38;gt;&lt;br /&gt;&#38;lt;p&#38;gt;&#38;lt;%= link_to &#39;Say Merry Christmas&#39; =&#38;gt; &#39;merry-christmas-page&#39; %&#38;gt;&#38;lt;/p&#38;gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;br /&gt;&lt;br /&gt;@@ random.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;% my @options = (&lt;br /&gt;%     &#38;quot;Merry Christmas&#38;quot;,&lt;br /&gt;%     &#38;quot;Happy Holidays&#38;quot;,&lt;br /&gt;%     &#38;quot;Bon Noel&#38;quot;,&lt;br /&gt;%     &#38;quot;Feliz Navidad&#38;quot;,&lt;br /&gt;% );&lt;br /&gt;%= $options[ rand @options ];   # render a random @option&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we&#38;#39;ve uploaded that we can &lt;a href=&#34;https://duvw0nvf3um1j.cloudfront.net/&#34;&gt;finally test it in the browser&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-17T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Paws to Plan What Todo</title><link href="http://perladvent.org/2019/2019-12-16.html"/><id>http://perladvent.org/2019/2019-12-16.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Christmas is a busy time of the year. So much to do! And who can keep track of it all?&lt;/p&gt;

&lt;p&gt;Why, I can, with the help of OmniGroup&#38;#39;s &lt;a href=&#34;https://www.omnigroup.com/omnifocus/&#34;&gt;OmniFocus&lt;/a&gt; app. It&#38;#39;s an excellent all singing-all dancing GTD machine. Fully scriptable (both on macOS and iOS) and with &lt;i&gt;Focus&lt;/i&gt; views allowing you to filter your tasks by context, tag, or any other dimension, it really is a programmer&#38;#39;s todo list app.&lt;/p&gt;

&lt;p&gt;However, the one feature that OmniFocus is missing is a way to add things to its inbox of new tasks via a REST interface. Sure, you can &lt;i&gt;email&lt;/i&gt; things to a private email address on the OmniGroup servers so next time OmniFocus syncs it&#38;#39;ll import them into its encrypted database, but email &lt;i&gt;sucks&lt;/i&gt; as an import mechanism. SMTP is a fiddly little protocol, requiring non-core libraries, authentication, and considerable thought into avoiding anti-spam technologies. A HTTP endpoint can be accessed from anywhere - from Perl, from curl, or even from JavaScript in a browser bookmarklet.&lt;/p&gt;

&lt;p&gt;What I need is some way to bridge between an HTTP request to sending an email into my todo list.&lt;/p&gt;

&lt;p&gt;After reading &lt;a href=&#34;http://perladvent.org/2019/2019-12-15.html&#34;&gt;yesterday&#38;#39;s Perl Advent Calendar&lt;/a&gt; we know we can use &lt;a href=&#34;https://metacpan.org/module/AWS::Lambda::Quick&#34;&gt;AWS::Lambda::Quick&lt;/a&gt; to very quickly throw together a web accessible HTTP API. Maybe we can use something like that here?&lt;/p&gt;

&lt;h3 id=&#34;Permissions&#34;&gt;Permissions&lt;/h3&gt;

&lt;p&gt;In order to send email our Lambda services is going to have do more than just do some calculations and spit out a web page. Amazon offer an extensive range of web services APIs that allow Lambda to do anything from simply persisting data to a database to using Hyperledger Frabric for Blockchain shenanigans. Sometimes it seems like they&#38;#39;ve got an API for everything and the kitchen sink (actually they &lt;a href=&#34;https://www.amazon.com/Pull-Down-Activated-Technology-Stainless-9159TV-AR-DST/dp/B07CWY6M9R&#34;&gt;really do&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In order to send mail from our Lambda function we&#38;#39;re going to need tp make use of &lt;a href=&#34;https://aws.amazon.com/ses/&#34;&gt;Amazon Web Services&#38;#39;s Simple Email Services&lt;/a&gt; (AWS SES.) But, before we can make use of that we&#38;#39;re going to have to grant our Lambda function permission to actually call it!&lt;/p&gt;

&lt;p&gt;As part of the setup that AWS::Lambda::Quick does, it creates a new AWS IAM Role that gives it permission to do things. This role, called &lt;code&gt;perl-aws-lambda-quick&lt;/code&gt;, is initially configured to attach the &lt;code&gt;AWSLambdaRole&lt;/code&gt; and &lt;code&gt;CloudWatchLogsFullAccess&lt;/code&gt; policies so the Lambda function can be executed and so logs can be written. It&#38;#39;s through modifying this role we&#38;#39;re going to get SES access.&lt;/p&gt;

&lt;p&gt;First we write a new policy document that describes access to SES. In a production environment we&#38;#39;d probably want to limit the addresses that people can send emails from or to, but for our purposes today a broad policy that grants &lt;code&gt;ses:SendEmail&lt;/code&gt; and &lt;code&gt;ses:SendRawEmail&lt;/code&gt; for anywhere is fine.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Version&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;2012-10-17&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Statement&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synSpecial&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Action&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synSpecial&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synConstant&#34;&gt;ses:SendEmail&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synConstant&#34;&gt;ses:SendRawEmail&lt;/span&gt;&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Resource&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;*&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Effect&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Allow&lt;/span&gt;&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we can use the &lt;code&gt;aws&lt;/code&gt; command line interface to create a new policy from that document (which we&#38;#39;ve saved in &lt;code&gt;/tmp/allow-all-ses.json&lt;/code&gt;)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ aws iam create-policy \
             --policy-name allow-all-ses \
             --policy-document file:///tmp/allow-all-ses.json&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That command returns a bunch of data:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Policy&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;PolicyName&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;allow-all-ses&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;PermissionsBoundaryUsageCount&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;CreateDate&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;2019-12-15T22:56:49Z&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;AttachmentCount&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;IsAttachable&lt;/span&gt;&#38;quot;: &lt;span class=&#34;synConstant&#34;&gt;true&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;PolicyId&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;ANPA25GBIG3Z2LOSXPAAH&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;DefaultVersionId&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;v1&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Path&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;/&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;Arn&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;arn:aws:iam::749877081843:policy/allow-all-ses&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;UpdateDate&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;2019-12-15T22:56:49Z&lt;/span&gt;&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Most important in there is the ARN of the newly created policy. We can now attach that to our existing role.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ aws iam attach-role-policy \
             --policy-arn arn:aws:iam::749877081843:policy/allow-all-ses \
             --role-name perl-aws-lambda-quick&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hooray! Our Lambda functions now has permission to send email in general. However, we can&#38;#39;t still can&#38;#39;t send any old email just yet - because we haven&#38;#39;t verified our email addresses.&lt;/p&gt;

&lt;p&gt;You have to prove to Amazon that you own any email address you&#38;#39;re sending email from. This process is as simple as using the CLI to get Amazon to send you an email with a link you can click on:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ aws ses verify-email-identity --email-address mark@twoshortplanks.com&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&#38;#39;ll also need to move SES out of sandbox mode if you don&#38;#39;t want to have to verify email addresses you send to as well. This is a complicated process where you have to explain what I&#38;#39;m doing sending email to real humans. For now, I can just verify my destination address too so I&#38;#39;ll be able to send to that address without further ado:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ aws ses verify-email-identity --email-address sent-to-of@twoshortplanks.com&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Writing-The-Perl-Code&#34;&gt;Writing The Perl Code&lt;/h3&gt;

&lt;p&gt;In order to call the AWS API we&#38;#39;re going to need to make use of &lt;a href=&#34;https://metacpan.org/module/Paws&#34;&gt;Paws&lt;/a&gt; (the hilariously named Perl AWS interface.) Paws is an interface to all of AWS&#38;#39;s current APIs - but for today we&#38;#39;re just going to be making use of the &lt;a href=&#34;https://metacpan.org/module/Paws::SES::SendEmail&#34;&gt;Paws::SES::SendEmail&lt;/a&gt; workflow.&lt;/p&gt;

&lt;p&gt;In order to use Paws we need to install the modules somewhere where our Lambda script can make use of them. One option is to use the &lt;code&gt;extra_files&lt;/code&gt; option to AWS::Lambda::Quick to bundle up all the files along with our source code.&lt;/p&gt;

&lt;p&gt;A simpler option is to make use of Lambda &lt;i&gt;layers&lt;/i&gt;. These are common reusable zipfiles that layer their contents on top of the Lambda filesystem.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;send-to-omnifocus&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_layers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$layer_arn&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;timeout&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(We&#38;#39;ve also bumped out the timeout now to ten seconds to give our function more time to do stuff.)&lt;/p&gt;

&lt;p&gt;One of the best things about layers is that they can be shared between accounts. In fact, all AWS::Lambda::Quick &lt;i&gt;always&lt;/i&gt; uses the standard Perl Lambda prebuilt layer to provide the AWS::Lambda code that drives the handler. Here we&#38;#39;re just adding a second shared layer ontop of that (AWS Lambda currently allows you to have up to five layers and your per-function source code.)&lt;/p&gt;

&lt;p&gt;Since we want to use Paws we don&#38;#39;t even have to build our own layer - we can use one of the standard ones. Instead of passing in the ARN, we can just pass in the AWS::Lambda::Quick identifier for a layer it knows about and it&#38;#39;ll use the ARN for the layer in your region.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;send-to-omnifocus&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_layers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;paws&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;timeout&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, we can get to writing the actual meat of the function:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;send-to-omnifocus&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_layers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;paws&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;timeout&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;JSON::PP&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( encode_json )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Paws&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this is my own personal address that forwards to my top secret&lt;br /&gt;# OmniGroup maildrop address.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$EMAIL_ADDRESS&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;send-to-omnifocus@twoshortplanks.com&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$param&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;queryStringParameters&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$text&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$param&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_error&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Missing text&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$note&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;literal&#34;&gt;q{}&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get access to AWS&#39;s Simple Email Service&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ses&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Paws&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;service&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;SES&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;region&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;us-east-1&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # and use it to send an email&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$ses&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;SendEmail&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Destination&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;ToAddresses&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$EMAIL_ADDRESS&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Message&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Subject&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Charset&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;UTF-8&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Data&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;        &lt;span class=&#34;comment&#34;&gt;# todo text goes in the subject&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Body&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Text&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Charset&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;UTF-8&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# todo details go in the body&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Data&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$note&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Source&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;mark@twoshortplanks.com&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;statusCode&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;application/json&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;{&#38;quot;ok&#38;quot;:true}&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_error&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$status&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;statusCode&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$status&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;application/json&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;encode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;JSON::PP::false&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And now we can execute it&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ perl send-to-omnifocus.pl
    https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/send-to-omnifocus&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And start adding todo entires:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ curl &#38;#39;https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/send-to-omnifocus?text=Write+More+Perl+Advent+Articles&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now...I just have to &lt;i&gt;do&lt;/i&gt; those things.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-16T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Suddenly Serverless</title><link href="http://perladvent.org/2019/2019-12-15.html"/><id>http://perladvent.org/2019/2019-12-15.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;It was five minutes to midnight on Christmas Eve. And Sugarplum Snoozysnaps was going to have a heart attack.&lt;/p&gt;

&lt;p&gt;All these months of planning. And it&#38;#39;d all come crashing down thirty seconds ago. Literally. They never should have made the server racks out of candy canes. Too tempting for Yukon Cornelius&#38;#39; sweet tooth.&lt;/p&gt;

&lt;p&gt;Now in five minutes the elves on the shelves needed to know the exact moment to fly home so they wouldn&#38;#39;t impact Santa coming the other way and the website that they were meant to check was down.&lt;/p&gt;

&lt;p&gt;&#38;quot;In the next five minutes I&#38;#39;ve got to write a whole new web service. I&#38;#39;ve got to deploy it live. And it&#38;#39;s got to scale up to millions of hits.&#38;quot;&lt;/p&gt;

&lt;p&gt;Don&#38;#39;t Panic Sugarplum! You&#38;#39;re the most experienced Elf that Santa has. You can find a way to do the impossible again!&lt;/p&gt;

&lt;h3 id=&#34;AWS-Lambda&#34;&gt;AWS Lambda&lt;/h3&gt;

&lt;p&gt;AWS Lambda is one of the new breed of &lt;i&gt;serverless&lt;/i&gt; infrastructure offerings. Serverless doesn&#38;#39;t mean that there aren&#38;#39;t web servers running the code behind the scenes, but rather the programmer doesn&#38;#39;t have to worry about them - their concerns only go as far as writing individual logic that creates the pages. Someone else handles the rest.&lt;/p&gt;

&lt;p&gt;This means that Sugarplum doesn&#38;#39;t have to worry about setting up new hardware in the next three hundred seconds she has to spare. It even means that she doesn&#38;#39;t have to worry about configuring a pack of virtual machines or even a bunch of Kubernetes pods. All she has to worry about is writing the function - Amazon will take care of the compute for running them and scaling what&#38;#39;s needed so the elves on the shelves don&#38;#39;t overload anything. The north pole will get the first million hits for free, then they&#38;#39;ll be billed a small amount for each hit.&lt;/p&gt;

&lt;p&gt;So, what does a Lambda function look like in Perl? Why it&#38;#39;s nothing more than a script that contains a &lt;code&gt;handler&lt;/code&gt; function and ends with a true value.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;queryStringParameters&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;who&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Santa&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;statusCode&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;text/plain&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, $name&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Deploying with Lambda is quick - it takes just a few seconds to push up some code and enable it live. Can Sugarplum get it done in time?&lt;/p&gt;

&lt;h3 id=&#34;Do-It-Quickly&#34;&gt;Do It &lt;i&gt;Quickly&lt;/i&gt;&lt;/h3&gt;

&lt;p&gt;Okay, first things first. Sugarplum has to install the AWS tooling on her machine. This&#38;#39;ll take her about thirty seconds. She only has to copy and paste three lines from the AWS webpages to her command line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ curl &#38;quot;https://s3.amazonaws.com/aws-cli/awscli-bundle.zip&#38;quot; -o &#38;quot;awscli-bundle.zip&#38;quot;
    shell$ unzip awscli-bundle.zip
    shell$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Luckily for Sugarplum the north pole has already set up an Amazon account for use with AWS, and has funding source associated with it (less lucky for the Wise Old Elf whose credit card is about to take a pounding in the next few minutes.)&lt;/p&gt;

&lt;p&gt;She can spend about a minute &lt;a href=&#34;https://aws.amazon.com/blogs/security/how-to-find-update-access-keys-password-mfa-aws-management-console&#34;&gt;finding the AWS credentials for her account&lt;/a&gt; which she can then use in conjunction with the &lt;code&gt;aws configure&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ aws configure
    AWS Access Key ID [********************]:
    AWS Secret Access Key [********************]:
    Default region name [us-east-1]:
    Default output format [None]:&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;She now has access to the awesome power of AWS. She&#38;#39;s got three minutes and thirty seconds to get everything live!&lt;/p&gt;

&lt;h3 id=&#34;The-Actual-Coding&#34;&gt;The Actual Coding&lt;/h3&gt;

&lt;p&gt;Three minutes later she&#38;#39;s got the most basic of web pages ready to go:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;lib&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( lib )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;NorthPole::ElfOnTheShelfDispatch&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( time_to_go )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$template&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;HTML&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;head&#38;gt;&#38;lt;title&#38;gt;Elf on the Shelf Dispatch Instructions&#38;lt;/title&#38;gt;&#38;lt;/head&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;h1&#38;gt;INSTRUCTION&#38;lt;/h1&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;p&#38;gt;It is absolutely imperative at this time you INSTRUCTION&#38;lt;/p&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;HTML&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;handler&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;queryStringParameters&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$instruction&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time_to_go&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;LEAVE NOW&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;STAY PUT&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$html&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$template&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$template&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;substitute&#34;&gt;s/INSTRUCTION/$instruction/g&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;statusCode&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;text/html&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$html&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thank goodness she was able to re-use the NorthPole::ElfOnTheShelfDispatch module that used to run on those destroyed servers. Using a combination of the time of day and the elf&#38;#39;s family name (the clan the Elf was in determined where they&#38;#39;d been deployed around the world) it was able to work out what the elf should do at the moment they refreshed the page.&lt;/p&gt;

&lt;p&gt;Now all Sugarplum has to do was work out how to deploy this code to AWS. Should be straight forward, right...it&#38;#39;s just a few API calls.&lt;/p&gt;

&lt;h3 id=&#34;Complexity&#34;&gt;Complexity&lt;/h3&gt;

&lt;p&gt;The great thing about AWS is that it&#38;#39;s a super powerful set of infrastructure that&#38;#39;s capable of doing almost anything you can ask of it. The problem is, you have to work out how to ask for exactly what you want.&lt;/p&gt;

&lt;p&gt;To configure the AWS Lambda function to respond to a HTTP URL you need to take the following steps:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;Create a zip file containing all your code and supporting code.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Create (or update) an AWS Lambda function with this zip file, and configure Lambda to use the latest Perl layers to automatically bring in the necessary AWS::Lamba support code.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Define a Role with the permissions to run your code.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Create a REST API with AWS Gateway API.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Configure a resource for that REST API for this script.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Set up a method and put method response for that resource.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Manage an integration and integration response for that resource.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Nothing too complicated...I&#38;#39;m &lt;i&gt;sure&lt;/i&gt; Sugarplum can get all that done in the next thirty seconds.&lt;/p&gt;

&lt;h3 id=&#34;CPAN-to-the-Rescue&#34;&gt;CPAN to the Rescue!&lt;/h3&gt;

&lt;p&gt;When you need to setup a Lambda function in a hurry there&#38;#39;s &lt;a href=&#34;https://metacpan.org/module/AWS::Lambda::Quick&#34;&gt;AWS::Lambda::Quick&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All you need to do is put a single use statement at the top of your code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;elf-on-the-shelf-go&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, rather, since we need to also upload the &lt;code&gt;lib&lt;/code&gt; directory:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;AWS::Lambda::Quick&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;elf-on-the-shelf-go&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;extra_files&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;lib&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then run the script.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ perl elf-on-the-shelf-go.pl
    https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Look, it&#38;#39;s printed out a URL where it&#38;#39;s already live!&lt;/p&gt;

&lt;p&gt;AWS::Lambda::Quick has handled all the interactions with AWS for us, uploading a copy of the script (slightly modified so it no longer depends on AWS::Lambda::Quick&#38;gt; and configuring AWS to serve it for us on the URL it printed out. That didn&#38;#39;t even take 30 seconds!&lt;/p&gt;

&lt;p&gt;If we want, we can even see what it&#38;#39;s up to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    shell$ export AWS_LAMBDA_QUICK_DEBUG=1
    shell$ perl elf-on-the-shelf-go.pl
    updating function code
    function code updated
    updating function configuration
    searching for existing role
    found existing role
    function configuration updated
    searching for existing rest api
    found existing existing rest api
    searching of existing resource
    found exiting resource
    checking for existing method
    found existing method
    checking for existing method response
    found existing method response
    checking for existing integration
    found existing integration
    checking for existing integration response
    found existing integration response
    https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All that&#38;#39;s left for us to do is test it:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go?who=Candy+Sweettooth&#34;&gt;https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go?who=Candy+Sweettooth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go?who=Zippy+Flyfast&#34;&gt;https://52p3rf890b.execute-api.us-east-1.amazonaws.com/quick/elf-on-the-shelf-go?who=Zippy+Flyfast&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well done Sugarplum, Christmas is once again truly saved!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-15T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Animated GIFs</title><link href="http://perladvent.org/2019/2019-12-14.html"/><id>http://perladvent.org/2019/2019-12-14.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Ah, animated GIFs. Truly the best thing on the Internet since cat videos!&lt;/p&gt;

&lt;p&gt;How do people make those? I&#38;#39;ve no idea how other people do it, but I make &#38;#39;em with Perl.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;out.gif&#34; width=&#34;500&#34; height=&#34;442&#34; alt=&#34;Animated GIF of Santa with the words to A Visit from St. Nicholas animated on it&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/GD&#34;&gt;GD&lt;/a&gt; can create animated GIFs piecemeal. The simple steps involved are:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;Print out the result of &lt;code&gt;gifanimbegin&lt;/code&gt; to start the gif &lt;li&gt;Create a new GD image. For each image print out the result of calling &lt;code&gt;gifanimadd&lt;/code&gt;, making sure to pass in the previous image you output so GD only has to write out the &lt;i&gt;changed&lt;/i&gt; pixels. &lt;li&gt;Finally print out the result of &lt;code&gt;gifanimend&lt;/code&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;p&gt;And that&#38;#39;s all there really is to it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GD::Image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# constants&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_FACE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/System/Library/Fonts/Supplemental/Bodoni 72 Smallcaps Book.ttf&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_SIZE&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;comment&#34;&gt;# in pt&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$BLACK&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;comment&#34;&gt;# color in the GIF89a color map&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$WHITE&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# color in the GIF89a color map&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# write this out to a file&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$out&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;gt;:raw&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;/tmp/out.gif&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# load the background image&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$image&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;background_image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# start an animated gif&lt;br /&gt;#   first argument means to use the global GIF89a color map&lt;br /&gt;#   second argument means to turn on looping&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$out&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;gifanimbegin&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# send the background as the first frame of the animated gif&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$out&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;gifanimadd&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# process each line at a time&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$prev_frame&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;DATA&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\s+/&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # for each of the words that we&#39;re going to highlight&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# we need to write a new frame&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$word_index&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;arrayindex&#34;&gt;$#words&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # create the GD image for that frame&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;draw_frame&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;words&lt;/span&gt;      &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;word_index&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$word_index&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;line&lt;/span&gt;       &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$line&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # add the frame.  Because we&#39;re enabling compression and&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# passing in the previous frame only the pixels that are&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# changed per frame are actually saved - not the entire&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# image of santa each time&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$out&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;gifanimadd&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;                 &lt;span class=&#34;comment&#34;&gt;# dummy&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;                 &lt;span class=&#34;comment&#34;&gt;# dummy,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;                 &lt;span class=&#34;comment&#34;&gt;# dummy,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;240&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# two and a half seconds to read a line&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;                 &lt;span class=&#34;comment&#34;&gt;# enabling comparing to previous frame&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$prev_frame&lt;/span&gt;        &lt;span class=&#34;comment&#34;&gt;# previous frame to compare to&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # remember this frame for the next time round&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;symbol&#34;&gt;$prev_frame&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# finally write out the end of the animated gif&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$out&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;gifanimend&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;background_image&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GD::Image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;/tmp/santa.jpg&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;draw_frame&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%args&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$new_image&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;background_image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;words&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # &#38;quot;draw&#38;quot; the whole string as a class method, which doesn&#39;t&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# actually draw at all, but lets us know how wide the whole&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# string is so we can center it&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$width&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;GD::Image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stringFT&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$BLACK&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$FONT_FACE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$FONT_SIZE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;))[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;250&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$width&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # work out the three strings (left of / before hightlight,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the middle / hightlight, and then right of / after highlight)&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$left_of&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;join&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$_ &#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;splice&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;word_index&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$middle&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$right_of&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;join&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@words&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # draw each string.  stringFT returns the bounds as an array.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# Index 4 of this array is the x in the top right corner, i.e.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# what we want to assing to $x so we start drawing there again.&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$new_image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stringFT&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$BLACK&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_FACE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_SIZE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;425&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$left_of&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;))[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$new_image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stringFT&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$WHITE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_FACE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_SIZE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;425&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$middle&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;))[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$new_image&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stringFT&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$BLACK&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_FACE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FONT_SIZE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$x&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;number&#34;&gt;425&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$right_of&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$new_image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;Twas the night before Christmas, when all through the house&lt;br /&gt;Not a creature was stirring, not even a mouse;&lt;br /&gt;The stockings were hung by the chimney with care,&lt;br /&gt;In hopes that St. Nicholas soon would be there;&lt;br /&gt;The children were nestled all snug in their beds,&lt;br /&gt;While visions of sugar-plums danced in their heads;&lt;br /&gt;And mamma in her kerchief, and I in my cap,&lt;br /&gt;Had just settled our brains for a long winter&#39;s nap&lt;br /&gt;When out on the lawn there rose such a clatter,&lt;br /&gt;I sprang from my bed to see what was the matter,&lt;br /&gt;Away to the window I flew like a flash,&lt;br /&gt;Tore open the shutters and threw up the sash.&lt;br /&gt;The moon, on the breast of the new-fallen snow,&lt;br /&gt;Gave a lustre of mid-day to objects below;&lt;br /&gt;When, what to my wondering eyes should appear,&lt;br /&gt;But a miniature sleigh, and eight tiny rein-deer,&lt;br /&gt;With a little old driver, so lively and quick,&lt;br /&gt;I knew in a moment it must be St. Nick.&lt;br /&gt;More rapid than eagles his coursers they came,&lt;br /&gt;And he whistled, and shouted, and called them by name;&lt;br /&gt;&#38;quot;Now, Dasher! now, Dancer! now, Prancer and Vixen!&lt;br /&gt;On! Comet, on! Cupid, on! Dunder and Blitzen&#8212;&lt;br /&gt;To the top of the porch, to the top of the wall!&lt;br /&gt;Now, dash away, dash away, dash away all!&#38;quot;&lt;br /&gt;As dry leaves that before the wild hurricane fly,&lt;br /&gt;When they meet with an obstacle, mount to the sky,&lt;br /&gt;So, up to the house-top the coursers they flew,&lt;br /&gt;With a sleigh full of toys&#8212;and St. Nicholas too.&lt;br /&gt;And then in a twinkling I heard on the roof,&lt;br /&gt;The prancing and pawing of each little hoof.&lt;br /&gt;As I drew in my head, and was turning around,&lt;br /&gt;Down the chimney St. Nicholas came with a bound.&lt;br /&gt;He was dressed all in fur from his head to his foot,&lt;br /&gt;And his clothes were all tarnished with ashes and soot;&lt;br /&gt;A bundle of toys he had flung on his back,&lt;br /&gt;And he looked like a peddler just opening his pack;&lt;br /&gt;His eyes how they twinkled! his dimples how merry!&lt;br /&gt;His cheeks were like roses, his nose like a cherry;&lt;br /&gt;His droll little mouth was drawn up like a bow,&lt;br /&gt;And the beard on his chin was as white as the snow;&lt;br /&gt;The stump of a pipe he held tight in his teeth,&lt;br /&gt;And the smoke, it encircled his head like a wreath.&lt;br /&gt;He had a broad face, and a little round belly&lt;br /&gt;That shook when he laughed, like a bowl full of jelly.&lt;br /&gt;He was chubby and plump&#8212;a right jolly old elf;&lt;br /&gt;And I laughed when I saw him in spite of myself.&lt;br /&gt;A wink of his eye, and a twist of his head,&lt;br /&gt;Soon gave me to know I had nothing to dread.&lt;br /&gt;He spoke not a word, but went straight to his work,&lt;br /&gt;And filled all the stockings; then turned with a jerk,&lt;br /&gt;And laying his finger aside of his nose,&lt;br /&gt;And giving a nod, up the chimney he rose.&lt;br /&gt;He sprang to his sleigh, to his team gave a whistle,&lt;br /&gt;And away they all flew like the down of a thistle;&lt;br /&gt;But I heard him exclaim, ere he drove out of sight,&lt;br /&gt;&#38;quot;Merry Christmas to all, and to all a good night!&#38;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Maybe for my next gif I&#38;#39;ll include the entire works of Shakespeare...&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-14T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Draft Solution</title><link href="http://perladvent.org/2019/2019-12-13.html"/><id>http://perladvent.org/2019/2019-12-13.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Productivity&#34;&gt;Productivity&lt;/h3&gt;

&lt;p&gt;Nougat Jinglebubbles was an expert in Productivity (with a capital P.) That meant that he could spend hours and hours of each day optomizing his computer setup to ensure that he&#38;#39;d shaved a few seconds off of his workflow rather than working on debugging the new Christmas letter OCR software like he was supposed to be doing.&lt;/p&gt;

&lt;p&gt;Today he was looking at the latest version of Drafts Pro for his macOS laptop.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;drafts.jpg&#34; widht=&#34;814&#34; height=&#34;600&#34; alt=&#34;Drafts&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Drafts is an application that allows you to capture text on your iPhone, iPad, Apple Watch or Mac and have that content sync between the devices.&lt;/p&gt;

&lt;p&gt;What&#38;#39;s more, on iOS Drafts allows you to define actions to process that text, to easily ship it off to an email client, or create a calendar entry, or append it to a list in dropbox, or send it to your task management software. Or, if you were feeling really adventurous you could setup actions that executed JavaScript code to do pretty much wanted you wanted.&lt;/p&gt;

&lt;p&gt;Tap, tap, productive! (Eventually, after many hours of debugging.)&lt;/p&gt;

&lt;p&gt;And now with this week&#38;#39;s &lt;a href=&#34;https://www.macstories.net/reviews/drafts-for-mac-its-action-time/&#34;&gt;new release&lt;/a&gt; enabling feature parity with the iOS releases, you can even set up processing actions on your Mac. And of course running on the Mac without the restrictions of iOS you&#38;#39;ll be able to do whatever you want - run Perl scripts, process files on the local file system, everything!&lt;/p&gt;

&lt;p&gt;Oh No. No, wait you can&#38;#39;t.&lt;/p&gt;

&lt;p&gt;The problem is that JavaScript still runs inside the same type of sandbox on macOS. There is no way to shell out to Perl and get some real programming done.&lt;/p&gt;

&lt;p&gt;Or is there?&lt;/p&gt;

&lt;h3 id=&#34;And-now-for-something-somewhat-unrelated&#34;&gt;And now for something somewhat unrelated.&lt;/h3&gt;

&lt;p&gt;Wouldn&#38;#39;t it be nice to have a web server always running on your Mac. We could quickly visit a local web page and get some graphs on disk usage, or visit a page that used AppleScript to pull out info from our calendar, or scraped some news from the web to show us or...the list goes on.&lt;/p&gt;

&lt;p&gt;The trouble is that our local laptops only have so much memory. And if we run twenty or so web servers all the time, sitting idle waiting for requests, that memory quickly runs out.&lt;/p&gt;

&lt;p&gt;Worse, if we&#38;#39;ve got twenty web servers running we have to find some way to manage all of that. To restart them all when we make code changes. To ensure that they&#38;#39;re running all the time. That sounds like a real pain in the jingle bells.&lt;/p&gt;

&lt;h3 id=&#34;A-different-kind-of-serverless&#34;&gt;A different kind of serverless&lt;/h3&gt;

&lt;p&gt;How can we run a web server on our local machine without running a web server? What we need is a super-server - a server that can listen on any port we&#38;#39;d like to pretend there&#38;#39;s a web server running and fire up some program on demand whenever someone connects to it.&lt;/p&gt;

&lt;p&gt;On Linux that kind of functionality it provided by inetd (or xinetd or any of the other itterations.) On macOS that functionality is just one of the things the all powerful &lt;code&gt;launchd&lt;/code&gt; daemon can do.&lt;/p&gt;

&lt;p&gt;launchd is enterprise level software - you can tell by the way you have to configure it using XML. Here&#38;#39;s an example &lt;i&gt;plist&lt;/i&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;?&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xml version&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;1.0&#38;quot;&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; encoding&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;UTF-8&#38;quot;&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;?&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;!&lt;/span&gt;&lt;span class=&#34;synStatement&#34;&gt;DOCTYPE&lt;/span&gt; plist &lt;span class=&#34;synStatement&#34;&gt;PUBLIC&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;-//Apple//DTD PLIST 1.0//EN&#38;quot;&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;plist &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;version&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;1.0&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;Label&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;string&#38;gt;&lt;/span&gt;org.perladvent.example&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/string&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;!-- this is the program we want to run.  We can&#39;t specify&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;        a script here with a shebang line, it has to be an&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;        actual executable, so we specify our perl and pass&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;        the name of the script as an argument --&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;ProgramArguments&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;array&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;string&#38;gt;&lt;/span&gt;/usr/bin/perl&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/string&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;string&#38;gt;&lt;/span&gt;/Users/Nougat/servers/example.pl&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/string&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/array&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;!-- we don&#39;t want to be loaded right away, we want to loaded&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;        on demand when someone tries to connect to the port --&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;OnDemand&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;true/&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;!-- here&#39;s where we&#39;re listening - on port 54321 --&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;Sockets&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;Listeners&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;array&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;SockFamily&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;string&#38;gt;&lt;/span&gt;IPv4&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/string&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;SockServiceName&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;string&#38;gt;&lt;/span&gt;54321&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/string&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/array&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;!-- finally we want to configure launchd to emulate&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;         inetd in &#39;nowait&#39; mode.  This&#39;ll mean that launchd&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;         will handle all the port stuff for us.  It&#39;ll&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;         execute our program when someone connects and pass data&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;         sent to the port to the program via STDIN and anything&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;         output to STDOUT will be sent back across the port --&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;inetdCompatibility&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;key&#38;gt;&lt;/span&gt;Wait&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/key&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;false/&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/dict&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/plist&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As we want the code to run as the user we&#38;#39;re logged in as we need to create this file inside &lt;code&gt;~/Library/LaunchAgents&lt;/code&gt;. It should be named &lt;code&gt;org.perladvent.example.plist&lt;/code&gt;, as that&#38;#39;s what corresponds to the &lt;code&gt;label&lt;/code&gt; key in the plist.&lt;/p&gt;

&lt;p&gt;Once the file has been created we need to tell launchd to load it&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   $ launchctl load ~/Library/LaunchAgents/org.perladvent.example.plist&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No output from this command is good news!&lt;/p&gt;

&lt;h3 id=&#34;Writing-the-server-script&#34;&gt;Writing the &#38;#39;server&#38;#39; script&lt;/h3&gt;

&lt;p&gt;We now need to write a program that takes input on STDIN and produces output on STDOUT. Easy, right?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# disable STDOUT buffering&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$|&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Chatbot::Eliza&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$bot&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Chatbot::Eliza&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;STDIN&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;-- &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$bot&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;transform&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now connect to our server with telnet and talk to the bot over the local loopback interface:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ telnet 127.0.0.1 54321
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is &#38;#39;^]&#38;#39;.
    Hello
    -- Hi. What seems to be your problem?
    I&#38;#39;ve run out of tape to wrap the presents
    -- That&#38;#39;s quite interesting.
    Santa will be mad.
    -- Does talking about this bother you?
    A little.
    -- I&#38;#39;m not sure I understand you fully.
    No-one does.
    -- Why &#38;#39;no&#38;#39;?
    Everything is no!
    -- Are you saying no just to be negative?
    ^]
    telnet&#38;gt; quit
    Connection closed.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Okay, so we can write a simple &#38;#39;server&#38;#39; that sends data back and forth. How about handling web pages?&lt;/p&gt;

&lt;h3 id=&#34;PSGI&#34;&gt;PSGI&lt;/h3&gt;

&lt;p&gt;The PSGI standard is a simple protocol that allows web servers and web frameworks to have a common standard to communicate with each other. In theory any framework that is PSGI compatible can be used with any PSGI compatible webserver and vice versa.&lt;/p&gt;

&lt;p&gt;Here&#38;#39;s what the world&#38;#39;s simplest PSGI compatible web app looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$env&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Content-Type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;text/plain&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello person using $env-&#38;gt;{HTTP_USER_AGENT}&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Like all PSGI apps it&#38;#39;s just a subroutine that takes a hash reference and returns a bunch of array refs. Nothing more complicated than that.&lt;/p&gt;

&lt;p&gt;What we&#38;#39;re going to do is write a shim so that when launchd executes our script it&#38;#39;ll be able to use this - or any other PSGI compatible app - to generate the output.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# Process via PSGI.  &lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Request&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Response&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Message::PSGI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Only bother gathering the headers, ignore the body of the request&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;STDIN&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/^\r\n$/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;HTTP/1.1 &#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;from_psgi&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$psgi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Request&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;to_psgi&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;as_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wow, that was pretty straight forward. Does it work?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ curl -v 127.0.0.1:54321
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to 127.0.0.1 (127.0.0.1) port 54321 (#0)
    &#38;gt; GET / HTTP/1.1
    &#38;gt; Host: 127.0.0.1:54321
    &#38;gt; User-Agent: curl/7.64.1
    &#38;gt; Accept: */*
    &#38;gt;
    &#38;lt; HTTP/1.1 200 OK
    &#38;lt; Content-Type: text/plain
    * no chunk, no close, no size. Assume close to signal end
    &#38;lt;
    Hello person using curl/7.64.1
    * Closing connection 0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Awesome&lt;/p&gt;

&lt;h3 id=&#34;Using-a-more-advanced-framework&#34;&gt;Using a more advanced framework&lt;/h3&gt;

&lt;p&gt;Okay, so now we can plug in any framework we want, like Mojolicious:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Request&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Response&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Message::PSGI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;param&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$output&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;reverse&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$output&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# create a PSGI app from the Mojolicious app&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;error&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$psgi&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;psgi&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Process via PSGI.  Only bother with GET requests&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;STDIN&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/^\r\n$/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;HTTP/1.1 &#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;from_psgi&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$psgi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Request&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;to_psgi&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;as_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So there&#38;#39;s a really simple example of a server that can manipulate any text that&#38;#39;s passed into it.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ curl 127.0.0.1:54321?text=Example
    elpmaxE&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I wonder if Nougat would find this helpful?&lt;/p&gt;

&lt;h3 id=&#34;Back-to-the-Draft&#34;&gt;Back to the Draft&lt;/h3&gt;

&lt;p&gt;One of the things that Drafts can from within JavaScript is make HTTP requests. Including, say, to a server we have running on demand on localhost.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;var&lt;/span&gt; response = HTTP.create().request(&lt;span class=&#34;synIdentifier&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;url&#38;quot;&lt;/span&gt;: &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://127.0.0.1:54321&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;method&#38;quot;&lt;/span&gt;: &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;GET&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;parameters&#38;quot;&lt;/span&gt;: &lt;span class=&#34;synIdentifier&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;text&#38;quot;&lt;/span&gt; : draft.content &lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;)&lt;br /&gt;draft.content = response.responseText&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So Nougat needs to take the following steps:&lt;/p&gt;

&lt;p&gt;&lt;ol&gt; &lt;li&gt;Configure a Launchd plist to run a Perl script whenever something connects to a port on localhost&lt;/li&gt; &lt;li&gt;Load the plist. This only needs to be done once - it&#39;ll happen automatically on restart&lt;/li&gt; &lt;li&gt;Write a script that speaks PSGI at that location. If you use Mojolicious or another framework you can have it do different things on different paths!&lt;/li&gt; &lt;li&gt;Configure an action in Drafts that uses a JavaScript step to call this URL &lt;/ol&gt;

&lt;/p&gt;



&lt;p&gt;And with that, he can execute Perl actions to his heart&#38;#39;s content!&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;drafts-before.jpg&#34; widht=&#34;814&#34; height=&#34;600&#34; alt=&#34;Drafts: Before Transformation&#34;&gt;&lt;/center&gt; &lt;center&gt;&lt;img src=&#34;drafts-after.jpg&#34; widht=&#34;814&#34; height=&#34;600&#34; alt=&#34;Drafts: After TRansformation&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;(Maybe he should get back to work though!)&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-13T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Now With Added Interactivity</title><link href="http://perladvent.org/2019/2019-12-12.html"/><id>http://perladvent.org/2019/2019-12-12.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Yesterday the Wise Old Elf was &lt;a href=&#34;http://perladvent.org/2019/2019-12-10.html&#34;&gt;showing&lt;/a&gt; a hacky way to quickly throw together a GUI with a touch of HTML, JavaScript and a smattering of Perl code. It was really quick to create a GUI using that - but is there an even quicker, and easier, way?&lt;/p&gt;

&lt;h3 id=&#34;Introducing-GUIDeFATE&#34;&gt;Introducing GUIDeFATE&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/GUIDeFATE&#34;&gt;GUIDeFATE&lt;/a&gt; is a Perl GUI &#38;#39;framework&#38;#39; (and I use that word in the loosest possible sense) that&#38;#39;s primary goal is to allow you to quickly throw together a user interface in the least possible time. You sacrifice the precise control that you might have with a traditional framework in order to get something working in minutes.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s rewrite the first dialog example from yesterday. First, as always, we show the finished result: Running&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./guidefate-dialog.pl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pops up a native looking dialog box:&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;dialogguidefate.jpg&#34; alt=&#34;A dialog with text field and OK and Cancel buttons&#34; width=&#39;629&#39; height=&#39;249&#39;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Pressing the OK button closes the window and prints our familiar greeting:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./guidefate-dialog.pl
    Merry Christmas, Wise Old Elf&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I bet that was complicated to code, right? Nope, couldn&#38;#39;t be easier - we didn&#38;#39;t even have to really &lt;i&gt;code&lt;/i&gt; the GUI bit at all:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    #!/usr/bin/perl

    use 5.024;
    use warnings;

    use GUIDeFATE;

    my $gui = GUIDeFATE-&#38;gt;new(&#38;lt;&#38;lt;&#38;#39;DIALOG&#38;#39;);
    +---------------------------------------+
    |T Merry Christmas Wisher               |
    +M--------------------------------------+
    |  Who?                                 |
    |  [                                  ] |
    |  {   OK   }{ Cancel }                 |
    +---------------------------------------+
    DIALOG

    my $frame = $gui-&#38;gt;getFrame;

    # ok
    sub btn2 {
        my $who = $frame-&#38;gt;getValue(&#38;#39;textctrl1&#38;#39;);
        say &#38;quot;Merry Christmas, $who&#38;quot;;
        $frame-&#38;gt;quit
    }

    # cancel
    sub btn3 { $frame-&#38;gt;quit }

    $gui-&#38;gt;MainLoop;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rather than specifying the controls with some sort of abstract windowing toolkit, or laying them out with HTML and CSS as happened yesterday, GUIDeFATE takes a different approach: You simply draw out the user interface using ASCII-art. This doesn&#38;#39;t give the same level of control that you might normally want from your GUI toolkit, but what it does allow you to do is quickly create an interface in seconds.&lt;/p&gt;

&lt;p&gt;There&#38;#39;s also no explicit mapping of controls to Perl. Controls are named after the type of thing they are. The first control is called &#38;#39;textctrl1&#38;#39; because it&#38;#39;s a text control and the first control. The second control, a button, is &#38;#39;btn2&#38;#39;, the third is &#38;#39;btn3&#38;#39;. Pressing buttons call the subroutines named after the control. It&#38;#39;s all very simple - but you don&#38;#39;t have to even work that out. The quickest way to work out what a button is called is just to press it and read the error message.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./guidefate-dialog.pl
    Undefined subroutine &#38;amp;main::btn3 called at ./guidefate-dialog.pl line 35.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The quickest way to work out what the name of a text field is is to just print out the contents of all the text fields and take it from there:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;show_all_values&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;keys&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$control&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$_: &#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$control&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;GetValue&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$control&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;amp;&#38;amp;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$control&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;can&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;GetValue&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;But-I-liked-the-Browser&#34;&gt;But I liked the Browser!&lt;/h3&gt;

&lt;p&gt;In the above example GUIDeFATE was using the cross platform Wx graphics toolkit library. This means that the same code &lt;i&gt;should&lt;/i&gt; run Linux, macOS, and even Windows. But Wx can be a little tricky to install if you don&#38;#39;t have precompiled binaries to hand (I&#38;#39;ve been doing Perl for over twenty five years and it took me a few hours to wrestle it into submission using the latest versions of Perl and macOS.) The browser based solution we had yesterday didn&#38;#39;t have that same level of complex dependencies.&lt;/p&gt;

&lt;p&gt;Luckily, GUIDeFATE isn&#38;#39;t tied to any one underlying toolkit. It actually support &lt;i&gt;seven&lt;/i&gt; different backends - Wx, Tk, Gtk, Qt, Win32, HTML and Websocket. Switching to &#38;#39;Tk&#38;#39; is as simple as passing an extra argument to the constructor&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$gui&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GUIDeFATE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;DIALOG&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;tk&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;DIALOG&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or even ask it to just render in the browser:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$gui&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GUIDeFATE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;DIALOG&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;web&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;DIALOG&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Like so:&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;guidefatews.jpg&#34; alt=&#34;A dialog with text field and OK and Cancel buttons in a web browser&#34; width=&#39;661&#39; height=&#39;384&#39;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Clicking OK prints out the message as before - no further code changes were necessary.&lt;/p&gt;

&lt;h3 id=&#34;A-More-Complex-Example&#34;&gt;A More Complex Example&lt;/h3&gt;

&lt;p&gt;For good measure here&#38;#39;s a straight port of the code I wrote yesterday for editing notes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GUIDeFATE&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Path::Tiny&lt;/span&gt;    &lt;span class=&#34;words&#34;&gt;qw( path )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::UtilsBy&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( min_by )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;File::Copy&lt;/span&gt;    &lt;span class=&#34;words&#34;&gt;qw( move )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$gui&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GUIDeFATE&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;DIALOG&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;+---------------------------------------+&lt;br /&gt;|T Note Processor                       |&lt;br /&gt;+M--------------------------------------+&lt;br /&gt;|[                                     ]|&lt;br /&gt;|+T------------------------------------+|&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;||                                     ||&lt;br /&gt;|+-------------------------------------+|&lt;br /&gt;|{ Archive }                    { Save }|&lt;br /&gt;+---------------------------------------+&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;DIALOG&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$gui&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getFrame&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# where the files are kept&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ENV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HOME&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;notes&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$archive_dir&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ENV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HOME&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;.archived-notes&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this is the current file we&#39;re working on&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # find the oldest file to work on &lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;min_by&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stat&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;mtime&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # update the form in the browser&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# title is the filename (without dir nor extension)&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;textctrl0&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;regexp&#34;&gt;qr/.txt/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;));&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;TextCtrl2&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;slurp_utf8&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# archive clicked&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;btn3&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;move&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$ENV{HOME}/.notes-archive&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# save clicked&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;btn4&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getValue&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;textctrl0&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getValue&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;TextCtrl2&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # write the note out, possibly in a new location&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;.txt&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spew_utf8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # if we renamed the note delete the old note&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;regexp&#34;&gt;qr/.txt/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;unlink&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$gui&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;MainLoop&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;While not as colorful as the HTML version, it presents a simple functional interface:&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;noteproc.jpg&#34; alt=&#34;Screenshot of editor instance&#34; widht=&#34;632&#34; height=&#34;478&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;We can easily flesh this out just a little more. For example, we can add confirmation for archiving:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# archive clicked&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;btn3&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$frame&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;showDialog&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;Sure?&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;Are you sure you want to archive?&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;OKC&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;!&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;){&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;move&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$ENV{HOME}/.notes-archive&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;confirm.jpg&#34; alt=&#34;confirmation dialog&#34; width=&#34;532&#34; height=&#34;263&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;GUIDeFATE has a bunch of handy things to make quick output really simple - file dialogs, menus, and a collection of other widgets that I haven&#38;#39;t shown here. I encourage you to have a play with it - it&#38;#39;ll only take a minute or two to write your first app!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-12T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Now With Added Interactivity</title><link href="http://perladvent.org/2019/2019-12-11.html"/><id>http://perladvent.org/2019/2019-12-11.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;The Wise Old Elf had locked himself in his office for several days. The other elves were starting to get worried.&lt;/p&gt;

&lt;p&gt;It had all started after helping Buster Stripytights with a &lt;a href=&#34;http://perladvent.org/2019/2019-12-09.html&#34;&gt;command line script that launched a browser based web app&lt;/a&gt;. Then one of the elves had heard him muttering about something that Fir Cidergift had been working on to use Mojo::AsyncAwait to &lt;a href=&#34;http://perladvent.org/2019/2019-12-10.html&#34;&gt;handle awating like JavaScript does&lt;/a&gt;. Apparently something about these two ideas had inspired the elf into a deep coding session.&lt;/p&gt;

&lt;p&gt;The furious typing noises emanating from behind the door stopped. Suddenly The Wise Old Elf burst out of his office waving his arms.&lt;/p&gt;

&lt;p&gt;&#38;quot;Eureka! Eureka!&#38;quot; he shouted (which as we all know is elf for &#38;quot;this bath water is too hot&#38;quot;) &#38;quot;Just take a look at this&#38;quot;.&lt;/p&gt;

&lt;p&gt;Crowding around the elves watched the Wise Old Elf type at the terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   $ ./merry.pl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No sooner had he hit return then a browser window popped up on the screen showing a dialog.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;dialog.jpg&#34; alt=&#34;dialog with input and Ok and Cancel buttons&#34; width=&#34;720&#34; height=&#34;384&#34;&gt;

&lt;/p&gt;



&lt;p&gt;The wise old elf pressed a &#38;quot;OK&#38;quot; and back at the terminal the Perl script printed out the text from the text field.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./merry.pl
    Merry Christmas, Wise Old Elf&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;That&#38;#39;s not the impressive bit&#38;quot;, the Wise Old Elf explained. &#38;quot;The impressive bit is how I got it to do that&#38;quot;. He opened the his editor and brought up the source code.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/user/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;QuickBrowserGUI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::AsyncAwait&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;callback&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ok_clicked&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;getFormTextFieldValue&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Merry Christmas, $name\n&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;close&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;callback&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cancel_clicked&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;close&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ content.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;h1&#38;gt;Who?&#38;lt;/h1&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;div class=&#38;quot;form-group&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;input  id=&#39;string&#39; class=&#38;quot;form-control&#38;quot; value=&#38;quot;Santa&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;/div&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;div class=&#38;quot;form-group&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;button id=&#39;ok&#39;     class=&#38;quot;btn btn-primary clickable&#38;quot;&#38;gt;OK&#38;lt;/button&#38;gt;&lt;br /&gt;&#38;lt;button id=&#39;cancel&#39; class=&#38;quot;btn btn-danger  clickable&#38;quot;&#38;gt;Cancel&#38;lt;/button&#38;gt;&lt;br /&gt;&#38;lt;/div&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;function getFormTextFieldValue() {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return $(&#39;#string&#39;).val();&lt;br /&gt;}&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Perl code is being triggered from JavaScript (in this case automatically when the buttons are pressed) and we can also see the Perl code calling JavaScript code and asynchronously awaiting for the return value to be returned to it.&lt;/p&gt;

&lt;h3 id=&#34;Under-the-Hood&#34;&gt;Under the Hood&lt;/h3&gt;

&lt;p&gt;Under the hood the Wise Old Elf had tied Perl and JavaScript together with a websocket. He&#38;#39;d dressed the whole thing up with a wrapper of Bootstrap to make it pretty. And he&#38;#39;d taken advantage of the promises and await syntax to produce code that had virtually hidden the complexity of having to await for Perl and JavaScript to talk over a socket.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;QuickBrowserGUI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Switch back to modifying the main package directly&lt;br /&gt;# This is normally a really bad idea, but considering how this is&lt;br /&gt;# meant to be used in one-off scripts and how much we&#39;d have to&lt;br /&gt;# export (everything) this is actually the cleanest way to do it.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Browser::Open&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( open_browser )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::JSON&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( encode_json decode_json )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Net::EmptyPort&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( empty_port )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$port&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;empty_port&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# these are the only two HTTP routes.  All other communication occurs&lt;br /&gt;# via the websocket&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # The address of the websocket we&#39;re going to connect on.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# Use &#38;quot;encode_json&#38;quot; out of habit (any data you need to render&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# in a JavaScript should be encoded to JSON with a &#38;lt;script&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# safe JSON encoding library like Mojo::JSON)&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;word&#34;&gt;websocket_address&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;encode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;ws://127.0.0.1:$port/perl&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/exit&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# global state&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# named code that will be executed whenever called from JavaScript&lt;br /&gt;# via the websocket.  Can be declared as &#39;callback foo =&#38;gt; sub { ... };&#39;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%callbacks&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;callback&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($$)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$callback&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$callbacks&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$callback&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# on ready handlers that are called (in order) when the JavaScript&lt;br /&gt;# document is ready and the websocket has been connected.  Can&lt;br /&gt;# be declared as &#39;on_ready sub { ... };&#39;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@on_ready&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;on_ready&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;(&#38;amp;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@on_ready&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Whenever we call JavaScript from Perl we store a promise here&lt;br /&gt;# which will be resolved once the JavaScript sends the results back&lt;br /&gt;# down the websocket to Perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%websocket_resolvable_promises&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this websocket is the guts of the communication between the&lt;br /&gt;# browser and the perl code&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$websocket_tx&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;websocket&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/perl&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # $websocket_tx is the transaction we can use to send data to the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# websocket.  Normally when writing a webserver we&#39;d have&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# to worry about one of these per websocket connection, but&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# as we control both the server and client here we know for&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# sure there&#39;s only ever one websocket connection&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$websocket_tx&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;tx&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;core&#34;&gt;undef&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;decode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$message&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # TYPE &#39;ready&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the JS is sending a ready message meaning that we need&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# to call all of the on_ready handlers in @on_ready&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ready&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@on_ready&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # TYPE &#39;callback&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the JS is sending a callback message (either directly because&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# someone executed &#39;perlCallback(&#38;quot;name&#38;quot;, arg, arg..)&#39; or implicitly&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# because someone clicked on a &#39;.clickable&#39; button.  Note&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# that these callbacks cannot return anything to JavaScript.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# They&#39;re called in a blocking fashion so they should not block,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# but obviously they can await with async/await as normal and&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# call JavaScript functions via &#39;call_javascript&#39; to pass&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# control back to the JS code.&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;callback&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;oayload&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$cb&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$callbacks&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$cb&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$args&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # type &#39;response&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# the JS is sending the result of a call from Perl to JS&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# this includes the id of the promise that must be resolved&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# with whatever the JavaScript function returned&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;response&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$promise_to_resolve&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$websocket_resolvable_promises&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$promise_to_resolve&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this code calls the JavaScript routine on the client in an&lt;br /&gt;# async manner via the websocket, serializing and deserializing the&lt;br /&gt;# data you pass via JSON.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($@)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@args&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;@_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # each call needs a unique id&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # send the request to call the JavaScript over the websocket&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$websocket_tx&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;json&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;call&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;payload&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@args&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # create a promise that our websocket handler will resolve when&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# it gets the result from JavaScript back over the websocket&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$promise&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::Promise&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$websocket_resolvable_promises&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$id&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$promise&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$promise&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# starting up the webserver on the correct port, opening the browser&lt;br /&gt;# and suppressing logs.  This is the same as described in&lt;br /&gt;# http://perladvent.org/2019/2019-12-09.html&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;warn&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;http://127.0.0.1:$port&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hook&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;before_server_start&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;open_browser&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;daemon&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;--listen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# also include these templates plus whatever is in the main script&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;BEGIN&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;renderer&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;classes&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;QuickBrowserGUI&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;QuickBrowserGUI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ index.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&lt;br /&gt;% # load jquery, bootstrap, etc.&lt;br /&gt;&#38;lt;link rel=&#38;quot;stylesheet&#38;quot; href=&#38;quot;https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css&#38;quot; integrity=&#38;quot;sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T&#38;quot; crossorigin=&#38;quot;anonymous&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;script src=&#38;quot;https://code.jquery.com/jquery-3.2.1.slim.min.js&#38;quot; integrity=&#38;quot;sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN&#38;quot; crossorigin=&#38;quot;anonymous&#38;quot;&#38;gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;script src=&#38;quot;https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js&#38;quot; integrity=&#38;quot;sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q&#38;quot; crossorigin=&#38;quot;anonymous&#38;quot;&#38;gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;script src=&#38;quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js&#38;quot; integrity=&#38;quot;sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl&#38;quot; crossorigin=&#38;quot;anonymous&#38;quot;&#38;gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;% # when the page is closed have the browser send a POST&lt;br /&gt;% # to /exit to tell Mojolicious to shut down&lt;br /&gt;window.addEventListener(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;unload&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;() =&#38;gt; navigator.sendBeacon(&#38;quot;/exit&#38;quot;),&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;false&lt;br /&gt;)&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;var ws = new WebSocket(&#38;lt;%== $websocket_address %&#38;gt;);&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// handle messages from Perl&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ws.onmessage = (msg) =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;var data = JSON.parse(msg.data)&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// perl called us, call the named function and send the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// results back over the websocket&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (data.type == &#39;call&#39;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;var result = window[data.payload.name](...data.payload.args)&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ws.send(JSON.stringify({&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;type&#39;    : &#39;response&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;id&#39;      : data.id,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;payload&#39; : result&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}));&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// trigger the on_ready events perl side when the connection&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// has been established&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ws.onopen = () =&#38;gt; ws.send(&#39;{&#38;quot;type&#38;quot;:&#38;quot;ready&#38;quot;}&#39;)&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;function perlCallback(name, ...args) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ws.send(JSON.stringify({&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;type&#39;    : &#39;callback&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;payload&#39; : {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;name&#39; : name, &lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;args&#39; : args&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}))&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&lt;br /&gt;% # include the per script html&lt;br /&gt;&#38;lt;main role=&#38;quot;main&#38;quot; class=&#38;quot;container&#38;quot;&#38;gt;&lt;br /&gt;%= include &#39;content&#39;&lt;br /&gt;&#38;lt;/main&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;$(() =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// anything &#39;clickable&#39; will call the &#39;id_clicked&#39; callback&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// in perl space when clicked (i.e. something with the id of&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// &#38;quot;foo&#38;quot; will call the &#38;quot;foo_clicked&#38;quot; callback)&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$(&#39;.clickable&#39;).on(&#39;click&#39;, function () {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;var name = this.id + &#39;_clicked&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;perlCallback(name)&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;})&lt;br /&gt;});&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Writing-Something-More-Powerful&#34;&gt;Writing Something More Powerful&lt;/h3&gt;

&lt;p&gt;I&#38;#39;ve got a directory full of plain text files containing all the notes I&#38;#39;ve been collecting for that last five years or so. By now a lot of these are out of date.&lt;/p&gt;

&lt;p&gt;What I need to do is write a GUI application that will allow me to view one note at a time, starting with the oldest first, and let me either trash the note or modify the title and body of the note to bring it up to date.&lt;/p&gt;

&lt;p&gt;Maybe I can use the Wise Old Elf&#38;#39;s tool to write that? What we want is something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;noteeditor.jpg&#34; alt=&#34;Note Editor&#34; width=&#34;500&#34; height=&#34;309&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Finding the oldest file in the notes directory is easy with &lt;a href=&#34;https://metacpan.org/module/List::UtilBy&#34;&gt;List::UtilBy&lt;/a&gt;. It returns the item from the passed list for which the code in the &lt;code&gt;{ ... }&lt;/code&gt; produced the smallest value.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;min_by&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stat&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;mtime&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we have that we can just pass the title and contents of the file through to JavaScript&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt;            &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;regexp&#34;&gt;qr/.txt/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents_of_file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;slurp_utf8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;updateNote&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents_of_file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where it can update the forms:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;function&lt;/span&gt; updateNote(title,contents) &lt;span class=&#34;synIdentifier&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$(&lt;span class=&#34;synConstant&#34;&gt;&#39;#title&#39;&lt;/span&gt;).val(title);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$(&lt;span class=&#34;synConstant&#34;&gt;&#39;#contents&#39;&lt;/span&gt;).val(contents);&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that&#38;#39;s about it for the smarts. The rest is just simply reacting to button presses and writing out / moving the files.&lt;/p&gt;

&lt;p&gt;Here&#38;#39;s the full script - the entire application is no more than sixty lines of actual code.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;QuickBrowserGUI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::AsyncAwait&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Path::Tiny&lt;/span&gt;    &lt;span class=&#34;words&#34;&gt;qw( path )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::UtilsBy&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( min_by )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;File::Copy&lt;/span&gt;    &lt;span class=&#34;words&#34;&gt;qw( move )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# where the files are kept&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ENV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HOME&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;notes&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$archive_dir&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ENV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HOME&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;.archived-notes&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this is the current file we&#39;re working on&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # find the oldest file to work on &lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;min_by&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;stat&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;mtime&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;children&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # update the form in the browser&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# title is the filename (without dir nor extension)&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;regexp&#34;&gt;qr/.txt/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents_of_file&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;slurp_utf8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;updateNote&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$title&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$contents_of_file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;callback&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;save_clicked&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$new_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call_javascript&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;noteData&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # write the note out, possibly in a new location&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$notes_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$new_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;.txt&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spew_utf8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$new_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # if we renamed the note delete the old note&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$new_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;regexp&#34;&gt;qr/.txt/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;unlink&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;callback&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;archive_clicked&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;move&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$ENV{HOME}/.notes-archive&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;on_ready&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;next_note&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ content.html.ep&lt;br /&gt;&lt;br /&gt;&#38;lt;h1&#38;gt;Edit Notes&#38;lt;/h1&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;div class=&#38;quot;form-group&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;input id=&#39;title&#39; class=&#38;quot;form-control&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;/div&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;div class=&#38;quot;form-group&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;textarea id=&#39;contents&#39; class=&#38;quot;form-control&#38;quot;&#38;gt;&#38;lt;/textarea&#38;gt;&lt;br /&gt;&#38;lt;/div&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;div class=&#38;quot;form-group&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;button id=&#39;save&#39;    class=&#38;quot;btn btn-primary clickable float-right&#38;quot;&#38;gt;Save&#38;lt;/button&#38;gt;&lt;br /&gt;&#38;lt;button id=&#39;archive&#39; class=&#38;quot;btn btn-danger  clickable&#38;quot;&#38;gt;Archive&#38;lt;/button&#38;gt;&lt;br /&gt;&#38;lt;/div&#38;gt;&lt;br /&gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;function noteData() {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;title&#39;     : $(&#39;#title&#39;).val(),&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;contents&#39;  : $(&#39;#contents&#39;).val(),&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;br /&gt;function updateNote(title,contents) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$(&#39;#title&#39;).val(title);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$(&#39;#contents&#39;).val(contents);&lt;br /&gt;}&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2019-12-11T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Awaiting Christmas</title><link href="http://perladvent.org/2019/2019-12-10.html"/><id>http://perladvent.org/2019/2019-12-10.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Fir Cidergift was working on a new project at the North Pole to monitor world events. If there anything - anything at all - that was going on in the world that could impact Christmas deliveries, the elves need to know about it right away.&lt;/p&gt;

&lt;h3 id=&#34;A-Blocking-Solution&#34;&gt;A Blocking Solution&lt;/h3&gt;

&lt;p&gt;Fir&#38;#39;s prototype was a simple single process web server that would go off and scrape news sites and render them on one page. The current version only scraped the top story on Slate, but it was a good start for a morning&#38;#39;s work. Not only had Fir got the scraping working, but he&#38;#39;d also made a stab at a simple caching mechanism that cached the rendered output for thirty seconds to prevent the troops of elves at the North Pole from overloading their internet connection as they refeshed every few seconds.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;update_cache&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the home page of Slate&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://slate.com/&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # grab the url for the big headline&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;  &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.story-card__headline&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;href&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the actual article&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # render our homepage to a static file where we can have&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# mojo serve as needed&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;home.html&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render_to_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;template&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headline&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__hed&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sub_header&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__dek&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # cache the main picture from the article into the &#39;public&#39; dir&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# where mojo will directly serve static files from&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.image__src&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;data-normal&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;image.jpg&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# if someone gets &#39;/&#39; just spit out the static home.html file,&lt;br /&gt;# updating it if need be first&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;update_cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;reply&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;home.html&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;&lt;br /&gt;@@ template.html.ep&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;lt;h1&#38;gt;&#38;lt;%= $headline %&#38;gt;&#38;lt;/h1&#38;gt;&lt;br /&gt;&#38;lt;p&#38;gt;&#38;lt;%= $sub_header %&#38;gt;&#38;lt;/p&#38;gt;&lt;br /&gt;&#38;lt;img src=&#38;quot;image.jpg&#38;quot;&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;It&#38;#39;s looking good&#38;quot;, the Wise Old Elf commented. &#38;quot;But every thirty seconds things are going to get bad for the users. The unlucky elf that hit the page is going to have to sit there while all the websites are scraped. Also, anyone else that comes along isn&#38;#39;t going to get a page either - our webserver is single threaded and the thread is busy waiting around for webpages to come back to it over the internet, not serving data.&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Callbacks&#34;&gt;Callbacks&lt;/h3&gt;

&lt;p&gt;What Fir needed to do was change his code so that the end user always immediately be delivered the cached version of the page. If the cache was out of date then Mojolicious should start to scrape a new version in the background ready for the next elf that came along while at the same time in the foreground serving the older version to the current elf immediately. No more waiting for impatient elves!&lt;/p&gt;

&lt;p&gt;Fir read the documentation for Mojolicious and discovered that &lt;a href=&#34;https://metacpan.org/module/Mojo::UserAgent&#34;&gt;Mojo::UserAgent&lt;/a&gt; doesn&#38;#39;t have to block and return the scraped web page, but instead you can pass it a callback that will be executed when it&#38;#39;s done fetching data. A quick rewrite later and the &lt;code&gt;update_cache&lt;/code&gt; function now looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;update_cache&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($c)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the home page of Slate&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://slate.com/&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($, $homepage)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # grab the url for the big headline&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;  &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.story-card__headline&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;href&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # get the actual article&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($, $article)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;            # render our homepage to a static file where we can have&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# mojo serve as needed&lt;br /&gt;&lt;/span&gt;            &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;home.html&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render_to_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;template&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headline&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__hed&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sub_header&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__dek&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;            # cache the main picture from the article into the &#39;public&#39; dir&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# where mojo will directly serve static files from&lt;br /&gt;&lt;/span&gt;            &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.image__src&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;data-normal&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($, $picture)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;image.jpg&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The trouble was that the code had got decidedly harder to follow. Each network request resulted in another callback being scheduled from the parent callback. By the time that the callback for downloading the graphic which was called from the callback for downloading the article which was called from the callback for downloading the homepage was called, things were getting pretty deep - and the amount of indenting was getting out of control.&lt;/p&gt;

&lt;h3 id=&#34;Promises&#34;&gt;Promises&lt;/h3&gt;

&lt;p&gt;It was time to take a different approach. Fir remembered an article on &lt;a href=&#34;https://metacpan.org/module/Mojo::Promise&#34;&gt;Mojo::Promise&lt;/a&gt; from &lt;a href=&#34;http://www.perladvent.org/2018/2018-12-19.html&#34;&gt;last year&#38;#39;s advent calendar&lt;/a&gt;. Could using promises help with the crazy indenting?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;update_cache&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($c)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the home page of Slate&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://slate.com/&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($homepage, @)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # grab the url for the big headline&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;  &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.story-card__headline&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;href&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # get the actual article&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($article, @)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # render our homepage to a static file where we can have&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# mojo serve as needed&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;home.html&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render_to_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;template&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headline&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__hed&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sub_header&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__dek&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # get the main picture&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.image__src&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;data-normal&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($picture, @)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;        # cache the main picture from the article into the &#39;public&#39; dir&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# where mojo will directly serve static files from&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;image.jpg&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now Fir had a pattern of the &lt;code&gt;then&lt;/code&gt; subroutines being called whenever the previous promise was resolved. No more indenting!&lt;/p&gt;

&lt;p&gt;Personally however, I don&#38;#39;t find this much more readable than the callback code. Explicit promises are powerful things when you&#38;#39;re passing them around your code, but when control is just flowing from the top of the page to the bottom as it is here they can add a lot of extra code that is distracting. It&#38;#39;s also adding a whole bunch of extra scopes which make it harder to access variables from the top of your code in the bottom and can easily make the code very torturous to write - the cure is worse than the disease.&lt;/p&gt;

&lt;h3 id=&#34;Async-and-Await&#34;&gt;Async and Await&lt;/h3&gt;

&lt;p&gt;What we need is Perl to be a little more like JavaScript. JavaScript has an await keyword that can tell the interpreter to stop and wait for a promise to be fulfilled before continuing (just like &lt;code&gt;$promise-&#38;gt;wait&lt;/code&gt; does) but &lt;b&gt;also&lt;/b&gt; allow other code to run at that point. This typically isn&#38;#39;t something we can add to a language without making changes to the interpret itself - after all you need the ability to hop out of the subroutine and then later jump right back into the middle of the subroutine where you left off.&lt;/p&gt;

&lt;p&gt;Luckily, Fir hadn&#38;#39;t just been reading the Perl Advent Calendar last year, he&#38;#39;d also read the Mojolicious Advent Calendar&#38;#39;s &lt;a href=&#34;https://mojolicious.io/blog/2018/12/24/async-await-the-mojo-way/&#34;&gt;entry&lt;/a&gt; on the &lt;a href=&#34;https://metacpan.org/module/Mojo::AsyncAwait&#34;&gt;Mojo::AsyncAwait&lt;/a&gt; module which does &#38;quot;XS and C-level shenanigans&#38;quot; in the Perl interpreter to allow the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords to suspend and resume execution.&lt;/p&gt;

&lt;p&gt;Now Fir could rewrite the &lt;code&gt;update_cache&lt;/code&gt; subroutine to run &lt;code&gt;async&lt;/code&gt;, and he could use the &lt;code&gt;await&lt;/code&gt; keyword to stop and await the promises (while executing other code while it waits!)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;update_cache&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($c)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$time_to_update&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;time&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the home page of Slate&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://slate.com/&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # grab the url for the big headline&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;  &lt;span class=&#34;symbol&#34;&gt;$homepage&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.story-card__headline&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parent&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;href&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$article&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # render our homepage to a static file where we can have&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# mojo serve as needed&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;home.html&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render_to_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;template&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;headline&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__hed&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sub_header&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.article__dek&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # get the main picture&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.image__src&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;data-normal&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_p&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$picture_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # cache the main picture from the article into the &#39;public&#39; dir&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# where mojo will directly serve static files from&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;image.jpg&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$picture&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code looks identical to the original blocking code that Fir started out with, apart from the introduction of the &lt;code&gt;async&lt;/code&gt; keyword to declare the &lt;code&gt;update_cache&lt;/code&gt; subroutine and the use of &lt;code&gt;await $c-&#38;gt;ua-&#38;gt;get_p(...)&lt;/code&gt; instead of &lt;code&gt;$c-&#38;gt;ua-&#38;gt;get(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The behavior however is completely different - the subroutine stops executing as soon as it hits that first await and switches back to the caller, delivering the cached page while network requests continue in the background.&lt;/p&gt;

&lt;p&gt;Nice. Now all he had to do was add scraping logic for another thirty or so websites and he&#38;#39;d have something awesome to show at standup tomorrow morning.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-10T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Command Line Browser Apps</title><link href="http://perladvent.org/2019/2019-12-09.html"/><id>http://perladvent.org/2019/2019-12-09.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/chart.xkcd@1.1/dist/chart.xkcd.min.js&#34;&gt;&lt;/script&gt; &lt;svg class=&#34;chart&#34;&gt;&lt;/svg&gt; &lt;script&gt; const chartElement = document.querySelector(&#39;.chart&#39;); const lineChart = new chartXkcd.Line( chartElement, { title: &#34;Thousands Of Toys Produced&#34;, xLabel: &#34;Week&#34;, data: {&#34;datasets&#34;:[{&#34;data&#34;:[&#34;36&#34;,&#34;43&#34;,&#34;65&#34;,&#34;77&#34;,&#34;115&#34;,&#34;129&#34;,&#34;143&#34;,&#34;157&#34;,&#34;182&#34;,&#34;196&#34;,&#34;225&#34;,&#34;245&#34;,&#34;258&#34;,&#34;270&#34;,&#34;302&#34;,&#34;327&#34;,&#34;329&#34;,&#34;349&#34;,&#34;369&#34;,&#34;392&#34;,&#34;417&#34;,&#34;427&#34;,&#34;443&#34;,&#34;472&#34;,&#34;499&#34;,&#34;511&#34;,&#34;527&#34;,&#34;543&#34;,&#34;562&#34;,&#34;578&#34;,&#34;607&#34;,&#34;627&#34;,&#34;642&#34;,&#34;660&#34;,&#34;677&#34;,&#34;703&#34;,&#34;719&#34;,&#34;743&#34;,&#34;752&#34;,&#34;780&#34;,&#34;788&#34;,&#34;809&#34;],&#34;label&#34;:&#34;Electronics&#34;},{&#34;data&#34;:[&#34;92&#34;,&#34;136&#34;,&#34;214&#34;,&#34;227&#34;,&#34;267&#34;,&#34;303&#34;,&#34;307&#34;,&#34;328&#34;,&#34;340&#34;,&#34;376&#34;,&#34;360&#34;,&#34;380&#34;,&#34;398&#34;,&#34;392&#34;,&#34;390&#34;,&#34;393&#34;,&#34;393&#34;,&#34;405&#34;,&#34;390&#34;,&#34;391&#34;,&#34;404&#34;,&#34;412&#34;,&#34;409&#34;,&#34;399&#34;,&#34;432&#34;,&#34;423&#34;,&#34;397&#34;,&#34;431&#34;,&#34;403&#34;,&#34;409&#34;,&#34;406&#34;,&#34;426&#34;,&#34;434&#34;,&#34;423&#34;,&#34;433&#34;,&#34;436&#34;,&#34;424&#34;,&#34;435&#34;,&#34;436&#34;,&#34;431&#34;,&#34;421&#34;,&#34;424&#34;],&#34;label&#34;:&#34;Classic&#34;},{&#34;data&#34;:[&#34;98&#34;,&#34;139&#34;,&#34;145&#34;,&#34;162&#34;,&#34;187&#34;,&#34;130&#34;,&#34;164&#34;,&#34;161&#34;,&#34;197&#34;,&#34;231&#34;,&#34;236&#34;,&#34;244&#34;,&#34;288&#34;,&#34;341&#34;,&#34;337&#34;,&#34;348&#34;,&#34;407&#34;,&#34;438&#34;,&#34;429&#34;,&#34;401&#34;,&#34;500&#34;,&#34;448&#34;,&#34;475&#34;,&#34;569&#34;,&#34;500&#34;,&#34;524&#34;,&#34;571&#34;,&#34;559&#34;,&#34;639&#34;,&#34;657&#34;,&#34;614&#34;,&#34;631&#34;,&#34;671&#34;,&#34;757&#34;,&#34;775&#34;,&#34;778&#34;,&#34;776&#34;,&#34;826&#34;,&#34;760&#34;,&#34;788&#34;,&#34;865&#34;,&#34;897&#34;],&#34;label&#34;:&#34;Transport&#34;},{&#34;data&#34;:[&#34;609&#34;,&#34;446&#34;,&#34;592&#34;,&#34;546&#34;,&#34;511&#34;,&#34;441&#34;,&#34;497&#34;,&#34;544&#34;,&#34;420&#34;,&#34;604&#34;,&#34;606&#34;,&#34;427&#34;,&#34;600&#34;,&#34;603&#34;,&#34;507&#34;,&#34;485&#34;,&#34;422&#34;,&#34;518&#34;,&#34;548&#34;,&#34;516&#34;,&#34;436&#34;,&#34;594&#34;,&#34;425&#34;,&#34;573&#34;,&#34;486&#34;,&#34;601&#34;,&#34;436&#34;,&#34;590&#34;,&#34;460&#34;,&#34;603&#34;,&#34;518&#34;,&#34;475&#34;,&#34;589&#34;,&#34;539&#34;,&#34;436&#34;,&#34;605&#34;,&#34;454&#34;,&#34;541&#34;,&#34;576&#34;,&#34;503&#34;,&#34;434&#34;,&#34;467&#34;],&#34;label&#34;:&#34;Soft&#34;},{&#34;data&#34;:[&#34;1073&#34;,&#34;1123&#34;,&#34;1145&#34;,&#34;1033&#34;,&#34;1175&#34;,&#34;912&#34;,&#34;918&#34;,&#34;1085&#34;,&#34;986&#34;,&#34;949&#34;,&#34;1009&#34;,&#34;925&#34;,&#34;951&#34;,&#34;1074&#34;,&#34;1007&#34;,&#34;1080&#34;,&#34;911&#34;,&#34;903&#34;,&#34;1089&#34;,&#34;1080&#34;,&#34;1003&#34;,&#34;1150&#34;,&#34;1111&#34;,&#34;936&#34;,&#34;951&#34;,&#34;1133&#34;,&#34;1131&#34;,&#34;1167&#34;,&#34;1088&#34;,&#34;1176&#34;,&#34;965&#34;,&#34;922&#34;,&#34;1151&#34;,&#34;1068&#34;,&#34;1078&#34;,&#34;1173&#34;,&#34;973&#34;,&#34;1044&#34;,&#34;1023&#34;,&#34;952&#34;,&#34;1116&#34;,&#34;965&#34;],&#34;label&#34;:&#34;Candy&#34;},{&#34;data&#34;:[&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;5&#34;,&#34;100&#34;,&#34;121&#34;,&#34;212&#34;,&#34;232&#34;,&#34;234&#34;,&#34;242&#34;,&#34;256&#34;,&#34;278&#34;,&#34;300&#34;,&#34;412&#34;,&#34;812&#34;,&#34;900&#34;,&#34;901&#34;,&#34;906&#34;,&#34;904&#34;,&#34;961&#34;,&#34;980&#34;,&#34;456&#34;,&#34;0&#34;,&#34;0&#34;,&#34;0&#34;,&#34;43&#34;,&#34;981&#34;,&#34;990&#34;,&#34;1023&#34;,&#34;1203&#34;,&#34;1501&#34;,&#34;1988&#34;,&#34;2012&#34;,&#34;2024&#34;,&#34;2054&#34;],&#34;label&#34;:&#34;Novelty&#34;}],&#34;labels&#34;:[&#34;1&#34;,&#34;2&#34;,&#34;3&#34;,&#34;4&#34;,&#34;5&#34;,&#34;6&#34;,&#34;7&#34;,&#34;8&#34;,&#34;9&#34;,&#34;10&#34;,&#34;11&#34;,&#34;12&#34;,&#34;13&#34;,&#34;14&#34;,&#34;15&#34;,&#34;16&#34;,&#34;17&#34;,&#34;18&#34;,&#34;19&#34;,&#34;20&#34;,&#34;21&#34;,&#34;22&#34;,&#34;23&#34;,&#34;24&#34;,&#34;25&#34;,&#34;26&#34;,&#34;27&#34;,&#34;28&#34;,&#34;29&#34;,&#34;30&#34;,&#34;31&#34;,&#34;32&#34;,&#34;33&#34;,&#34;34&#34;,&#34;35&#34;,&#34;36&#34;,&#34;37&#34;,&#34;38&#34;,&#34;39&#34;,&#34;40&#34;,&#34;41&#34;,&#34;42&#34;]}, options: { yTickCount: 3, legendPosition: chartXkcd.config.positionType.upLeft } } ); &lt;/script&gt;

&lt;/p&gt;



&lt;p&gt;The weeks leading up to Christmas is a stressful time for most elves at the North Pole. Not so for Buster Stripytights. No...&lt;i&gt;every&lt;/i&gt; week was stressful for him.&lt;/p&gt;

&lt;p&gt;You see, Buster&#38;#39;s job is nothing short of making sure that enough toys are produced throughout the year. Every week it&#38;#39;s his job to compute how production is going, if they were ahead or behind schedule, and if Santa was going to have enough toys to make every good child happy, or if it was going to be a disaster this year. It hasn&#38;#39;t happened yet, but it&#38;#39;s only thanks to the diligent work of Mr Stripytights that any issue has been detected and dealt with before it became a real problem.&lt;/p&gt;

&lt;p&gt;As you can imagine, this involves a lot of number crunching and What-If type predictions. Most importantly, it involves a lot of quick data visualization.&lt;/p&gt;

&lt;p&gt;A few years ago Buster had despaired about getting the kind of interactive output he needed from his Perl data crunching scripts. That was before the Wise Old Elf had stepped in.&lt;/p&gt;

&lt;h3 id=&#34;The-Plan&#34;&gt;The Plan&lt;/h3&gt;

&lt;p&gt;Some people like to wield the terminal. Others like to have an interactive web page to control things.&lt;/p&gt;

&lt;p&gt;However, the Wise Old Elf is...wise. He likes the best of both worlds: Being able to jump from the command line into the browser and then quickly back to the terminal again, and onto the next command.&lt;/p&gt;

&lt;p&gt;For the task Buster needed he suggested making a script to immediately display the output of crunching the data in a web browser.&lt;/p&gt;

&lt;p&gt;When the script was executed from the terminal the Perl script should:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;Do the initial processing of data&lt;/li&gt; &lt;li&gt;Finds a random unused port on localhost&lt;/li&gt; &lt;li&gt;Open a web server on that unused port&lt;/li&gt; &lt;li&gt;Open the browser to point at that port&lt;/li&gt; &lt;li&gt;Finally when the browser window is closed, quit the script&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;p&gt;Simple! Let&#38;#39;s break down how to do that!&lt;/p&gt;

&lt;h3 id=&#34;The-Start-of-our-Web-Application&#34;&gt;The Start of our Web Application&lt;/h3&gt;

&lt;p&gt;Using a web server to pass the output to the browser has several advantages over writing out temporary files. We don&#38;#39;t have to figure out a way to periodically clear up those files. We can pause execution of subsequent commands in the terminal till the browser window is closed. We can control all files the browser has to load. And we have the full potential to pass information back and forward to Perl as the user interacts with the contents of the browser window.&lt;/p&gt;

&lt;p&gt;The start of our interactive tool is a &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; web app (using the &lt;a href=&#34;https://metacpan.org/module/Mojolicious::Lite&#34;&gt;Mojolicious::Lite&lt;/a&gt; framework).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;our&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$message&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Merry Christmas from the script!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;daemon&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;@@ index.html.ep&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;lt;%= $main::message %&#38;gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Running the script shows it starts up a server on the&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./script.pl
    [2019-11-23 10:20:42.21211] [19502] [info] Listening at &#38;quot;http://*:3000&#38;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We defined just one route (&lt;code&gt;/&lt;/code&gt;) that renders the &lt;code&gt;index&lt;/code&gt; template in the &lt;code&gt;DATA&lt;/code&gt; section, which will dynamically include data from our Perl code.&lt;/p&gt;

&lt;h3 id=&#34;Picking-a-Random-Port&#34;&gt;Picking a Random Port&lt;/h3&gt;

&lt;p&gt;At the moment our server will always open on the standard Mojolicious development port, port 3000. But Buster needs to be able to display more than one graph on the screen at any one time! Each of these servers are going to have to pick their own port to run on.&lt;/p&gt;

&lt;p&gt;This is actually quite simple with the help of the &lt;a href=&#34;https://metacpan.org/module/Net::EmptyPort&#34;&gt;Net::EmptyPort&lt;/a&gt; module which can find a random port that nothing else is using for us:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Net::EmptyPort&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( empty_port )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://127.0.0.1:&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;empty_port&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;daemon&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;--listen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Opening-The-Page-Automatically&#34;&gt;Opening The Page Automatically&lt;/h3&gt;

&lt;p&gt;To make this more like an &#38;quot;app&#38;quot; we don&#38;#39;t want Buster to have to copy any paste the URL from the terminal into his browser after the program starts up. We want the web page just to immediately open up as soon as the script is ready to display something to the user.&lt;/p&gt;

&lt;p&gt;This can be achieved by hooking the &lt;code&gt;before_server_start&lt;/code&gt; Mojolicious event. We&#38;#39;ll have it call the &lt;code&gt;open_browser&lt;/code&gt; function from the ever-so-handy &lt;a href=&#34;https://metacpan.org/module/Browser::Open&#34;&gt;Browser::Open&lt;/a&gt; module as soon as Mojolcious is ready to serve web pages.&lt;/p&gt;

&lt;p&gt;While we&#38;#39;re at it we don&#38;#39;t need Mojolicious to print URLs or debug information out to the console anymore, so we suppress all of that:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Net::EmptyPort&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( empty_port )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Browser::Open&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( open_browser )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# stop logging out when we visit URLs&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;warn&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# open the browser as soon as we start&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://127.0.0.1:&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;empty_port&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hook&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;before_server_start&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# don&#39;t display &#38;quot;Server available...&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;open_browser&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;daemon&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;--listen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Quitting-Time&#34;&gt;Quitting Time&lt;/h3&gt;

&lt;p&gt;To make this like a real application we don&#38;#39;t want Buster to have to remember to head back to the terminal and ctrl-C the command line program when he&#38;#39;s done with the graph. There must be some way to automatically quit the script as soon as the browser window closes!&lt;/p&gt;

&lt;p&gt;We can hook the &lt;code&gt;unload&lt;/code&gt; event in JavaScript fairly easily:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;window&lt;/span&gt;.addEventListener( &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;unload&#38;quot;&lt;/span&gt;, ... );&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But what should we have that do? Ideally we&#38;#39;d like to send a HTTP request back to the browser, but doing this via AJAX is unreliable: The page might complete closing before the asynchronous web request gets far enough along. Never fear! We&#38;#39;re running a faily modern web browser, so we can make use of &lt;i&gt;beacons&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;The Beacon API is a fire-and-forget non-blocking way to tell the browser to make a HTTP POST request &lt;i&gt;as soon as it is able to&lt;/i&gt; and we don&#38;#39;t care about the result. This means we can make a simple call like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;navigator.sendBeacon(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;/exit&#38;quot;&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the browser will continue to make the call to our backend even after its closed the window with our webpage in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# when our beacon notifies us, just quit right away&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/exit&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;@@ index.html.ep&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;// when the page is closed have the browser send a POST&lt;br /&gt;// to /exit to tell Mojolicious to shut down&lt;br /&gt;window.addEventListener(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;unload&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;() =&#38;gt; navigator.sendBeacon(&#38;quot;/exit&#38;quot;),&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;false&lt;br /&gt;);&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;lt;%= $main::message %&#38;gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Putting-It-All-Together&#34;&gt;Putting It All Together&lt;/h3&gt;

&lt;p&gt;Now we can put all of this together into building a script that dynamically shows any CSV as a graph:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojolicious::Lite&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Net::EmptyPort&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( empty_port )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Browser::Open&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( open_browser )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Text::CSV_XS&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;##########################################################&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$filename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$csv&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Text::CSV_XS&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;binary&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;auto_diag&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;lt;:encoding(utf8)&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$filename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$filename: $!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$titles&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$csv&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getline&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$xlabel&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$titles&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;labels&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[];&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;datasets&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;label&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$titles&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$row&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$csv&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;getline&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;labels&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$row&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;datasets&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}[&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$row&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;scalar&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$row&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;-1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$filename: $!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;##########################################################&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$c&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;index&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;title&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$filename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;substitute&#34;&gt;s/[.]csv$//r&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;xlabel&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$xlabel&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;cols&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/exit&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;level&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;warn&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://127.0.0.1:&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;empty_port&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hook&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;before_server_start&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$server&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;silent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;open_browser&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;daemon&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;--listen&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$base_url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;@@ index.html.ep&lt;br /&gt;% # This pages includes output directly in the  &#38;lt;script&#38;gt;...&#38;lt;/script&#38;gt;&lt;br /&gt;% # tags.  Note that this is only safe because:&lt;br /&gt;% &lt;br /&gt;% #   (a) I&#39;m using to_json which outputs characters not bytes so&lt;br /&gt;% #       when Mojolicious does the final byte encoding everything will&lt;br /&gt;% #        work out and not be double encoded, and&lt;br /&gt;% &lt;br /&gt;% #   (b) Mojo::JSON (unlike many other JSON libraries) *also* escapes&lt;br /&gt;% #       any &#39;/&#39; as &#39;\/&#39; meaning that a rogue &#39;&#38;lt;/script&#38;gt;&#39; in the data&lt;br /&gt;% #       won&#39;t terminate the script tags and present a possible&lt;br /&gt;% #       JavaScript injection attack&lt;br /&gt;% &lt;br /&gt;% use Mojo::JSON qw( to_json );&lt;br /&gt;&#38;lt;html&#38;gt;&lt;br /&gt;&#38;lt;script&#38;gt;&lt;br /&gt;// when the page is closed have the browser send a POST&lt;br /&gt;// to /exit to tell Mojolicious to shut down&lt;br /&gt;window.addEventListener(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;unload&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;() =&#38;gt; navigator.sendBeacon(&#38;quot;/exit&#38;quot;),&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;false&lt;br /&gt;);&lt;br /&gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;script src=&#38;quot;https://cdn.jsdelivr.net/npm/chart.xkcd@1.1/dist/chart.xkcd.min.js&#38;quot;&#38;gt;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;body&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;svg class=&#38;quot;chart&#38;quot;&#38;gt;&#38;lt;/svg&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;script&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;const chartElement = document.querySelector(&#39;.chart&#39;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;const lineChart = new chartXkcd.Line(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;chartElement,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;title: &#38;lt;%== to_json( $title ) %&#38;gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;xLabel: &#38;lt;%== to_json( $xlabel ) %&#38;gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;data: &#38;lt;%== to_json( $cols ) %&#38;gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;options: {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;legendPosition: chartXkcd.config.positionType.upLeft&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;lt;/script&#38;gt;&lt;br /&gt;&#38;lt;/body&#38;gt;&lt;br /&gt;&#38;lt;/html&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course, we could go further than all of this, making a completely interactive application with Perl and JavaScript passing information back and forward to each other...but that&#38;#39;s a project for another day.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-09T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Christmas Movie Time!</title><link href="http://perladvent.org/2019/2019-12-08.html"/><id>http://perladvent.org/2019/2019-12-08.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Have you ever wondered what the best Christmas Movie of all time is?&lt;/p&gt;

&lt;p&gt;Spoiler Alert: It&#38;#39;s &#38;quot;It&#38;#39;s a Wonderful Life&#38;quot;.&lt;/p&gt;

&lt;center&gt;&lt;iframe src=&#34;https://player.vimeo.com/video/280022699&#34; width=&#34;640&#34; height=&#34;480&#34; frameborder=&#34;0&#34; allow=&#34;fullscreen&#34; allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;

&lt;p&gt;Of course, that&#38;#39;s not just my opinion: I can prove it...with Perl.&lt;/p&gt;

&lt;h3 id=&#34;Christmas-Movies&#34;&gt;Christmas Movies&lt;/h3&gt;

&lt;p&gt;The first thing we need to prove this comprehensively is a collection of Christmas movies.&lt;/p&gt;

&lt;p&gt;Did you know that Wikipedia has a &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_Christmas_films&#34;&gt;list of Christmas movies&lt;/a&gt; on it? Neither did I, but I shouldn&#38;#39;t be surprised; It has pretty much everything and the &lt;a href=&#34;https://en.wikipedia.org/wiki/Kitchen_sink&#34;&gt;kitchen sink&lt;/a&gt; on it. Let&#38;#39;s scrape that list and put it in a database.&lt;/p&gt;

&lt;p&gt;First, we create need a table:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;CREATE&lt;/span&gt; &lt;span class=&#34;synSpecial&#34;&gt;TABLE&lt;/span&gt; wikipedia_films (&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;name TEXT,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;year &lt;span class=&#34;synType&#34;&gt;INTEGER&lt;/span&gt;&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can use the on-disk daemonless SQLite SQL database to do this. Most systems ship with the &lt;code&gt;sqlite3&lt;/code&gt; command line tool that&#38;#39;ll write the database from the SQL for us:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ sqlite3 /tmp/db &#38;lt; wikipedia_films.sql&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we need a program that can scrape the web page and populate the database. Easy-peasy with Mojo::UserAgent, which we&#38;#39;ve covered extensively in past advent calendars:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::UserAgent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;dbi:SQLite:dbname=/tmp/db&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ua&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::UserAgent&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$res&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ua&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;https://en.wikipedia.org/wiki/List_of_Christmas_films&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;is_error&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;begin_work&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$res&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;dom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;.wikitable tbody tr&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;each&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;td:first-child * a&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$year&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;td:nth-child(2)&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;STDERR&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Inserting $name ($year)&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;SQL&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$year&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;        INSERT INTO wikipedia_films&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;(name, year) VALUES (?,?)&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;SQL&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we&#38;#39;ve got a big old list in our database...but what&#38;#39;s the best of these movies?&lt;/p&gt;

&lt;h2 id=&#34;Downloading-The-IMDb&#34;&gt;Downloading The IMDb&lt;/h2&gt;

&lt;p&gt;If we&#38;#39;re being &lt;i&gt;scientific&lt;/i&gt; about this we shouldn&#38;#39;t just use &lt;i&gt;our&lt;/i&gt; opinion. We should use the wisdom of crowds: The Internet Movie Database rating for the film.&lt;/p&gt;

&lt;p&gt;So that&#38;#39;s straight forward: First we just download the entire of the IMDb...wait, you didn&#38;#39;t know we could do that? Sure! The IMDb publishes a bunch of tab separated compressed files of their core data every day.&lt;/p&gt;

&lt;p&gt;If we use lwp-mirror to download the files we can mirror the large files to disk.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   $ lwp-mirror https://datasets.imdbws.com/title.basics.tsv.gz title.basics.tsv.gz
   $ lwp-mirror https://datasets.imdbws.com/title.ratings.tsv.gz title.ratings.tsv.gz&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Because we&#38;#39;re using lwp-mirror we can safely re-run the downloads as often as we want - a new version will only be downloaded when the contents changes.&lt;/p&gt;

&lt;h2 id=&#34;Importing-into-Our-Database&#34;&gt;Importing into Our Database&lt;/h2&gt;

&lt;p&gt;Okay, we next need to put all of that data into our database. Let&#38;#39;s create a new table:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;CREATE&lt;/span&gt; &lt;span class=&#34;synSpecial&#34;&gt;TABLE&lt;/span&gt; imdb_films (&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;title TEXT,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;name TEXT,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;year &lt;span class=&#34;synType&#34;&gt;INTEGER&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;rating &lt;span class=&#34;synType&#34;&gt;FLOAT&lt;/span&gt;&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;CREATE&lt;/span&gt; &lt;span class=&#34;synSpecial&#34;&gt;INDEX&lt;/span&gt; imdb_films_title_idx&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;ON&lt;/span&gt; imdb_films(title);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the database:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ sqlite3 /tmp/db &#38;lt; imdb_films.sql&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And populate it with films:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::AllUtils&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( zip )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::UserAgent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;PerlIO::gzip&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Term::ProgressBar&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;dbi:SQLite:dbname=/tmp/db&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# how big is this file?&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;backtick&#34;&gt;`gunzip -c title.basics.tsv.gz | wc -l`&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# the file is gzipped, so use the gzip layer&lt;br /&gt;# to transparently decompress it as we read it&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;lt;:gzip&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;title.basics.tsv.gz&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Can&#39;t open file: $!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# read the first line in that contains the headings&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;readline&#34;&gt;&#38;lt;$fh&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@headings&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\t/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# prepare the insert statement&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sth&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;SQL&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;    INSERT INTO imdb_films&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;( title, name, year )&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;VALUES&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;( ?, ?, ?)&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;SQL&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# process each row of the file&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$progress&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Term::ProgressBar&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$total&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;ETA&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;linear&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;begin_work&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;$fh&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@row_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\t/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # zip returns an element from each of the arrays &lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# in turn i.e. key, value, key, value, key, value...&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;zip&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@headings&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@row_data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # ignore anything that isn&#39;t a movie or, ahem, isn&#39;t&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# in the true christmas spirit&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;titleType&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;movie&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;isAdult&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # insert just some of the fields from the data&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# note ratings aren&#39;t in here yet - next script!&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$sth&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;tconst&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;originalTitle&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;startYear&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$progress&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$.&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And do almost same thing with the ratings for the films:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;List::AllUtils&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( zip )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::UserAgent&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;PerlIO::gzip&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Term::ProgressBar&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DBI&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;dbi:SQLite:dbname=/tmp/db&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# how big is this file?&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$total&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;backtick&#34;&gt;`gunzip -c title.ratings.tsv.gz | wc -l`&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# the file is gzipped, so use the gzip layer&lt;br /&gt;# to transparently decompress it as we read it&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;lt;:gzip&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;title.ratings.tsv.gz&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Can&#39;t open file: $!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# read the first line in that contains the headings&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;readline&#34;&gt;&#38;lt;$fh&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@headings&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\t/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# prepare the insert statement&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sth&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;&#39;SQL&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;    UPDATE imdb_films&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;SET rating = ?&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;WHERE title = ?&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;SQL&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# process each row of the file&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$progress&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Term::ProgressBar&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$total&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;ETA&lt;/span&gt;   &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;linear&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;begin_work&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;readline&#34;&gt;&#38;lt;$fh&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@row_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;split&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\t/&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # zip returns an element from each of the arrays &lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# in turn i.e. key, value, key, value, key, value...&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;zip&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@headings&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@row_data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$sth&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;averageRating&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;tconst&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$progress&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$.&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$dbh&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;The-Moment-of-Truth&#34;&gt;The Moment of Truth&lt;/h3&gt;

&lt;p&gt;Finally, we can categorically prove what&#38;#39;s the best Christmas Film&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ sqlite3 /tmp/db
    sqlite&#38;gt; SELECT name
       ...&#38;gt; FROM wikipedia_films
       ...&#38;gt; JOIN imdb_films
       ...&#38;gt; USING (name, year)
       ...&#38;gt; ORDER BY rating DESC
       ...&#38;gt; LIMIT 1;
    It&#38;#39;s a Wonderful Life&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2019-12-08T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title type="html">Show Me Mo&#38;#39;</title><link href="http://perladvent.org/2019/2019-12-07.html"/><id>http://perladvent.org/2019/2019-12-07.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Conifer Sweetbaker, Software Programmer Elf second class grumbled. Despite what the Wise Old Elf insisted about maintaining the quality of the code they used at the North Pole, Conifer had to admit it to himself: He hated doing code reviews. And there was no code that he hated reviewing more than his best friend Currant Northwick&#38;#39;s spaghetti code.&lt;/p&gt;

&lt;p&gt;The Elf&#38;#39;s code was a mess of Perl that looks like it had been written before the first dot com boom. There were mostly no objects and Conifer had already caught more than one case where there&#38;#39;d been a typo in the hash keys (if those had just been objects and method calls Currant would have got a proper error message.) Occasionally Currant did deign to use an object - or at least that&#38;#39;s what Conifer thought he was doing by the smattering of &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;bless&lt;/code&gt; that peppered his code, but worryingly, there was a lack of accessor methods and direct access into the blessed hash all over the shop.&lt;/p&gt;

&lt;p&gt;&#38;quot;Why, for Santa&#38;#39;s sake, WHY? It&#38;#39;s as if you&#38;#39;d never heard of Moose&#38;quot; he cajoled his buddy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Moose&#34;&gt;Moose&lt;/a&gt; was of course the object framework of choice at the North Pole. The powerful postmodern object system not only provides a awesome abstraction for defining object classes and accessor methods beyond the primitive &lt;code&gt;bless&lt;/code&gt; keywords, but also provides a rich meta object protocol the envy of many lesser languages.&lt;/p&gt;

&lt;p&gt;&#38;quot;Too slow startup time&#38;quot;, Conifer complained. &#38;quot;And I couldn&#38;#39;t get it installed on the old development platform over in the southern workshop - they don&#38;#39;t have a C compiler for installing software like that&#38;quot;.&lt;/p&gt;

&lt;p&gt;Conifer didn&#38;#39;t want to argue with his friend. When Moose programs start up they do take a little longer than basic Perl code that doesn&#38;#39;t maintain a meta object protocol. And yes, you need a working compiler to install Moose&#38;#39;s dependencies (or the ability to apt-get install it - Moose is widely distributed in the ten years or so it&#38;#39;s been the defacto standard in Perl.) Conifer wasn&#38;#39;t sure which system Currant was talking about (all buildings surrounding the North Pole are to the south) but he realized he just didn&#38;#39;t want to argue.&lt;/p&gt;

&lt;p&gt;&#38;quot;How about Moo? That&#38;#39;s quick.&#38;quot;, Conifer tried a conciliatory suggestion.&lt;/p&gt;

&lt;p&gt;&#38;quot;Still slower than coding it by hand!&#38;quot;, Currant quipped, &#38;quot;Still needs an external dependency! And still too much typing.&#38;quot;&lt;/p&gt;

&lt;p&gt;There was no satisfying some elves. Moo is set of pure Perl classes that offered a subset of the Moose syntax that&#38;#39;ll do very nicely if for any number of reasons you can&#38;#39;t use Moose. But Conifer really wanted something as portable, quick and with as little typing as writing the broken frameworkless code he was trying to maintain now.&lt;/p&gt;

&lt;p&gt;There&#38;#39;s really no arguing with some Elves.&lt;/p&gt;

&lt;p&gt;&#38;quot;I know what you want,&#38;quot; Conifer laughted, &#38;quot;You want Mo, Baby!&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Mo&#34;&gt;Mo&lt;/h3&gt;

&lt;p&gt;If Moo is a cut down version of Moose, then &lt;a href=&#34;https://metacpan.org/module/Mo&#34;&gt;Mo&lt;/a&gt; is a super trimmed to the bone and then slightly further implementation of something not too dissimilar to Moo in as little code as humanly possible.&lt;/p&gt;

&lt;p&gt;Mo is like a super-terse version of Moose suitable for prototyping and quick one off scripts. For example, if you need to get a bunch of super heros together very, very quickly:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;codename&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Team&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;members&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;assemble&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39; assemble!&#39;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;members&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;@*&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$avengers&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Team&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;members&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Bruce Banner&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;The Hulk&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Clint Barton&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Hawkeye&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Natasha Romanoff&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Black Widow&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Steve Rogers&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Captain America&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Tony Stark&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Ironman&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$avengers&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;assemble&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Brevity&#34;&gt;Brevity&lt;/h3&gt;

&lt;p&gt;Just like Moose and Moo, Mo creates the constructor for your object class for you and allows you to define accessor methods with the handy &lt;code&gt;has&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;However, whereas Moose and Moo require at least an &lt;code&gt;is&lt;/code&gt; directive like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moose&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;rw&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;rw&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Mo allows you to skip all the boilerplate if you want.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;codename&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That doesn&#38;#39;t mean Mo isn&#38;#39;t able to handle complex things...it just does it with a lot less code.&lt;/p&gt;

&lt;p&gt;Consider this Moose code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Archer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moose&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Person&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;weapon&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;rw&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#39;s bow&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;arrows&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;normal&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;trick&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;explosive&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This can we written a lot more succinctly in Mo&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Archer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Person&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;weapon&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;codename&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#39;s bow&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;arrows&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ro&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;normal&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;trick&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;explosive&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Three key things make this code quicker to write:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;Everything is lazy by default. This means that the accessor values aren&#38;#39;t populated until they&#38;#39;re accessed the first time. To force something to be non-lazy with Mo, you explicitly have to use &lt;code&gt;&#38;lt;lazy =&lt;/code&gt; 0&#38;gt;&#38;gt;.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;The default keyword is completely optional. If Mo sees a subroutine reference it assumes it&#38;#39;s the default subroutine.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Array references and hash references are assumed to be templates that need to be shallow copied. So &lt;code&gt;[1,2,3]&lt;/code&gt; is the same as &lt;code&gt;lazy =&#38;gt; 1, default =&#38;gt; sub { [1,2,3] }&lt;/code&gt; in Moose. (Deep hash / arrays still require more traditional syntax however!)&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3 id=&#34;Speed&#34;&gt;Speed&lt;/h3&gt;

&lt;p&gt;Okay, so it&#38;#39;s quicker to write...but what about quicker to run, and more importantly, quicker to start up.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s see what the timings are for our above script by benchmarking it on one of Microsoft&#38;#39;s Standard Linux 4 core 8GB instances. I&#38;#39;ve prepared three versions of the script for testing, each using either Moose, Moo or Mo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ time perl moose.pl &#38;gt;/dev/null
    real    0m0.183s
    user    0m0.163s
    sys     0m0.020s
    
    $ time perl moo.pl &#38;gt;/dev/null
    real    0m0.036s
    user    0m0.020s
    sys     0m0.016s
    
    $ time perl mo.pl &#38;gt;/dev/null
    real    0m0.006s
    user    0m0.006s
    sys     0m0.000s&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can see that just as Moo is faster than Moose an order of magnitude, Mo is fast by an order of magnitude again. To put this in some real world scenario, the Mo code is so fast that we could execute it every single frame of a 60Hz television video playback. The Moose version barely gets the five frames a second of the worst possible animated gif.&lt;/p&gt;

&lt;h3 id=&#34;Memory&#34;&gt;Memory&lt;/h3&gt;

&lt;p&gt;Speed isn&#38;#39;t everything. What about memory usage?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ /usr/bin/time -f &#38;#39;%MK&#38;#39; perl moose.pl &#38;gt;/dev/null
    20200K
    $ /usr/bin/time -f &#38;#39;%MK&#38;#39; perl moo.pl &#38;gt;/dev/null
    8372K
    $ /usr/bin/time -f &#38;#39;%MK&#38;#39; perl mo.pl &#38;gt;/dev/null
    4668K&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whoa, that&#38;#39;s a big difference. We should also consider that the baseline Perl interpreter takes up the majority of the memory:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    /usr/bin/time -f &#38;#39;%MK&#38;#39; perl -e 1 &#38;gt;/dev/null
    4144K&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Portability&#34;&gt;Portability&lt;/h3&gt;

&lt;p&gt;In order to install Moose and its dependencies you need a working compiler. Moo is much easier to install - it&#38;#39;s a pure Perl solution, though still has several classes and its own distribution on the CPAN that you&#38;#39;ll need to run any Moo code. Mo? Mo is designed to be bundled with your application.&lt;/p&gt;

&lt;p&gt;It&#38;#39;s common to see a custom versions of Mo bundled with other modules within their distribution. Instead of using the CPAN version of Mo:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Spline::Reticulator&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;They rely on their own version that the include in the distribution itself&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Spline::Reticulator::Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The version of Mo that is distribution is often a cut down selection of the Mo source code providing just what the project needs and no more. Mo ships with a tool &lt;code&gt;Mo::Inline&lt;/code&gt; that makes producing these modules trivial.&lt;/p&gt;

&lt;p&gt;First, write a dummy package shim explaining what parts of Mo you&#38;#39;d like included&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Spline::Reticulator::Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# use Mo qw&#39;build builder default import&#39;;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then run the &lt;code&gt;mo-inline&lt;/code&gt; script to expand the code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  $ mo-inline .
  Mo Inlined ./lib/Spline/Reticulator/Mo.pm&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you were to poke inside the class now you&#38;#39;d see it contains a single line optomised version of Mo, just like one of the &lt;a href=&#34;https://fastapi.metacpan.org/source/ANPARKER/MikroTik-Client-v0.520/lib/MikroTik/Client/Mo.pm&#34;&gt;many examples on the CPAN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It&#38;#39;s even possible to completely inline Mo inside a script by including the inlined code directly in the script:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;My::Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;BEGIN&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$INC&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;My/Mo.pm&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;__FILE__&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;no&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$M&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;__PACKAGE__&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;::&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$M&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;Objec&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;...&#38;lt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;snip&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;My::Mo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;codename&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By wrapping the inline code inside a &lt;code&gt;BEGIN { ... }&lt;/code&gt; (to make sure that the code is executed before the code below it is parsed) and by introducing &lt;code&gt;$INC{&#38;#39;My/Mo.pm&#38;#39;}=__FILE__&lt;/code&gt; (which prevents perl from actually searching on disk for a file called &lt;code&gt;My/Mo.pm&lt;/code&gt;) we can create a &lt;code&gt;My::Mo&lt;/code&gt; class we can use in the script whenever we want to enable Mo.&lt;/p&gt;

&lt;h3 id=&#34;A-Victory-FSVO-Victory&#34;&gt;A Victory, FSVO Victory...&lt;/h3&gt;

&lt;p&gt;&#38;quot;Okay...Okay!&#38;quot;, Currant Northwick begrudgingly agreed, &#38;quot;I&#38;#39;ll rewrite this code with Mo. But you do realize you&#38;#39;re going to have to code review it again from scratch, right?&#38;quot;&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-07T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>The Weather Outside Is Frightful</title><link href="http://perladvent.org/2019/2019-12-06.html"/><id>http://perladvent.org/2019/2019-12-06.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Bad-Weather&#34;&gt;Bad Weather&lt;/h3&gt;

&lt;p&gt;Being at the North Pole snow is something that you have to get used to. There&#38;#39;s no snow days, you don&#38;#39;t get to call in if the weather is bad, and there&#38;#39;s no putting off Christmas no matter how much of the white stuff falls.&lt;/p&gt;

&lt;p&gt;Instead, you have to be prepared.&lt;/p&gt;

&lt;p&gt;It&#38;#39;s really important that the elves know when there&#38;#39;s a snowstorm coming, and they employ a whole team&lt;/p&gt;

&lt;p&gt;But what about us mere mortals? Shouldn&#38;#39;t we be prepared too in case, say, it snows so much at 6am on Christmas morning a car containing radioactive isotopes flips in a snowstorm, crashes into the telegraph pole outside your house, rips down all the electrical cabling that provides power to your house and then bursts into flames (don&#38;#39;t worry, that&#38;#39;s only ever happened to me &lt;a href=&#34;https://www.berkshireeagle.com/stories/richmond-crash-delays-delivery-of-cancer-treatment-materials,527955&#34;&gt;once&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Well, we can&#38;#39;t afford to have a team of elves working night and day to alert us of bad weather. But we can have Perl do it for us.&lt;/p&gt;

&lt;h3 id=&#34;Dark-Sky&#34;&gt;Dark Sky&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://darksky.net/&#34;&gt;DarkSky&lt;/a&gt; is a very handy website for gathering weather predictions offering doppler radar maps, predictive forecasting for up to a week in advance, per minute rainfall for the next hour in easy to read graphical form, and a compelling time-machine feature for seeing both historical and predicted future weather.&lt;/p&gt;

&lt;p&gt;More importantly for our purposes, it offers a &lt;a href=&#34;https://darksky.net/dev&#34;&gt;comprehensive API&lt;/a&gt; with a generous free tier that we can use to poll for weather updates throughout the day.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DarkSky::API&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DarkSky::API&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;key&lt;/span&gt;       &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;8e983a4b1eca4ebf9385f413f8ffa668&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # this is New York City, NY, USA&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;word&#34;&gt;longitude&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;-74.0060&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;latitude&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;40.7128&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$weather&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;currently&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$weather-&#38;gt;{icon}: $weather-&#38;gt;{summary}&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Where-are-we&#34;&gt;Where are we?&lt;/h3&gt;

&lt;p&gt;So far we&#38;#39;ve been passing fixed coordinates to the Dark Sky API. What we&#38;#39;d rather do is actually give weather reports from where we&#38;#39;re actually located!&lt;/p&gt;

&lt;p&gt;The first step is to find out what our external IP is. This isn&#38;#39;t the local IP address that&#38;#39;s been assigned to the computer we&#38;#39;re using (which is more than likely something from the private IP address space assigned by your router) but the real, globally addressable, IP address that our ISP assigned to our router.&lt;/p&gt;

&lt;p&gt;The easiest way to do this is to contact an external web site and ask it what IP address it received the request from. There&#38;#39;s several JSON API services that will happily do this for us.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;JSON::PP&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( decode_json )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip_response&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://api.myip.com&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Problem fetching IP&#38;quot;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip_response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;success&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# the response contains JSON of the form&lt;br /&gt;#   { &#38;quot;ip&#38;quot; : &#38;quot;50.116.23.211&#38;quot; ... }&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;decode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip_response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;content&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we have the IP address we need to translate that into latitude and longitude. MaxMind offer a free downloadable database of IP addresses to approximate locations that we can use to do this (they also offer more accurate databases for a fee which we can switch to if we want more accurate weather reports.)&lt;/p&gt;

&lt;p&gt;MaxMind publish some handy Perl modules on the CPAN that you can use to access these databases without much work:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GeoIP2::Database::Reader&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# follow the instructions at&lt;br /&gt;# https://dev.maxmind.com/geoip/geoip2/geolite2/&lt;br /&gt;# to be able to download the database&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$reader&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GeoIP2::Database::Reader&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;file&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;GeoLite2-City.mmdb&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;locales&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;en&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$city&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$reader&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;city&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ip&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$location&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$city&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;location&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# these are only approximate, but good enough&lt;br /&gt;# for our weather prediction&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Latitude:  &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$location&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;latitude&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Longitude: &#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$location&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;longitude&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now feed those coordinates into the DarkSky API to get the weather where we are:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;DarkSky::API&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;key&lt;/span&gt;       &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;8e983a4b1eca4ebf9385f413f8ffa668&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;longitude&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$location&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;longitude&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;latitude&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$location&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;latitude&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$weather&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;currently&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$weather-&#38;gt;{icon}: $weather-&#38;gt;{summary}&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Look-to-the-Future-Now&#34;&gt;Look to the Future Now&lt;/h3&gt;

&lt;p&gt;If we&#38;#39;re going to be forewarned about weather we need to examine the response from DarkSky a little more closely.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Dumper&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Dumper&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hourly&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gives us quite a lot of data:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$VAR1&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;data&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;ozone&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;327.9&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;visibility&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;4.759&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;summary&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Overcast&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;humidity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.91&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;uvIndex&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;icon&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cloudy&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windGust&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1.46&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;dewPoint&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;31.52&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;time&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1574139600&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;apparentTemperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;33.92&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;cloudCover&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipProbability&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windSpeed&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1.46&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;temperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;33.92&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windBearing&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;234&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;pressure&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1004.9&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipIntensity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;time&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1574143200&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;humidity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.91&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;uvIndex&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;icon&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;cloudy&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windGust&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;4.77&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;dewPoint&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;31.27&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;summary&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Overcast&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;ozone&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;328.3&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;visibility&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;3.397&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;temperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;33.66&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windBearing&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;275&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;pressure&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1005.3&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipIntensity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windSpeed&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;4.77&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipProbability&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;apparentTemperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;29.23&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;cloudCover&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;ozone&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;336.1&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;visibility&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;2.837&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;summary&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Light Snow&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;uvIndex&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windGust&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.16&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;icon&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;snow&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;dewPoint&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;30.49&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;humidity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.89&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;time&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1574164800&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipAccumulation&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.4816&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;apparentTemperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;33.36&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;cloudCover&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipProbability&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipType&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;snow&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windSpeed&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.16&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;windBearing&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;257&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;temperature&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;33.36&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;precipIntensity&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;0.0507&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;pressure&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1006.1&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;    &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see we&#38;#39;ve got lots of options for deciding if DarkSky thinks it&#38;#39;s going to snow: The &lt;code&gt;precipAccumulation&lt;/code&gt; (how much build up there&#38;#39;s going to be), the &lt;code&gt;precipProbability&lt;/code&gt; (how likely we&#38;#39;re going to get water falling from the sky) and &lt;code&gt;precipType&lt;/code&gt; (snow? sleet? rain?) But that&#38;#39;s probably overthinking the entire problem; If DarkSky thinks it&#38;#39;s going to snow that hour, it&#38;#39;ll pick the snow &lt;code&gt;icon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Generating a summary is therefore straight forward&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@hourly&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$forecast&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hourly&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;@*&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hour&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@hourly&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;scalar&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;localtime&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$hour&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;literal&#34;&gt;q{ }&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$hour&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;icon&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which today gives us the dire warning that there&#38;#39;s snow coming soon...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Tue Nov 19 11:00:00 2019 cloudy
    Tue Nov 19 12:00:00 2019 rain
    Tue Nov 19 13:00:00 2019 rain
    Tue Nov 19 14:00:00 2019 rain
    Tue Nov 19 15:00:00 2019 rain
    Tue Nov 19 16:00:00 2019 sleet
    Tue Nov 19 17:00:00 2019 snow
    Tue Nov 19 18:00:00 2019 snow
    Tue Nov 19 19:00:00 2019 snow
    Tue Nov 19 20:00:00 2019 cloudy
    Tue Nov 19 21:00:00 2019 cloudy
    Tue Nov 19 22:00:00 2019 cloudy
    ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&#38;#39;ll install this on a cron job on the Raspberry Pi that I have sitting hidden in the mass of cables by my desk. I&#38;#39;ll have to get it to give me a warning if the snow is coming somehow:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    my @hourly = $forecast-&#38;gt;hourly-&#38;gt;{data}-&#38;gt;@*;
    splice @hourly, 12;  # reduce @hourly to first 12 hours

    use List::AllUtils qw( any );
    if (any { $_-&#38;gt;{icon} eq &#38;#39;snow&#38;#39; } @hours) {
        # turn my lights blue!
        # see http://perladvent.org/2019/2019-12-05        
        use LightFactory;
        $_-&#38;gt;set_color(&#38;#39;blue&#38;#39;) for LightFactory-&#38;gt;new-&#38;gt;items;
    }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Anyway...I&#38;#39;d better go and make sure there&#38;#39;s gas for the snowblower.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-06T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Christmas Lights</title><link href="http://perladvent.org/2019/2019-12-05.html"/><id>http://perladvent.org/2019/2019-12-05.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;center&gt;&lt;img src=&#34;lights.gif&#34; width-&#34;300&#34; height=&#34;300&#34; alt=&#34;Hue Lights In Christmas Mode&#34; style=&#34;border: 5px solid black&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;h3 id=&#34;Smart-but-Dumb&#34;&gt;Smart, but Dumb&lt;/h3&gt;

&lt;p&gt;The lighting system in my house is &lt;i&gt;smart&lt;/i&gt;. I can ask Alexa to turn lights on and off, or I can press buttons mounted on my wall to select preselected scenes of lights. Lights come on automatically when it gets dark, and turn off automatically at last thing at night (since, you know, my kids are always leaving their closet lights on.)&lt;/p&gt;

&lt;p&gt;But the lights in my house are also &lt;i&gt;dumb&lt;/i&gt;. They don&#38;#39;t turn on when I wake up in the morning, they don&#38;#39;t change their appearance due to whatever&#38;#39;s going on in the world. They don&#38;#39;t even know to get my attention when I get that all important email!&lt;/p&gt;

&lt;p&gt;Right now they look the same all year round. Heck, do they even know it&#38;#39;s Christmas time at all?&lt;/p&gt;

&lt;p&gt;I can fix all of this though...with Perl.&lt;/p&gt;

&lt;h3 id=&#34;Phillips-Hue-Home-Bridge&#34;&gt;Phillips Hue Home Bridge&lt;/h3&gt;

&lt;p&gt;My lights use the peer-to-peer zigbee wireless protocol to relay control messages to each other. Command and control is handled by a Phillips Hue Home Bridge, a small &#38;#39;puck&#38;#39; shaped device with an ethernet socket in the back that can monitor the zigbee sensors and send out zigbee commands.&lt;/p&gt;

&lt;p&gt;The normal way to control the Hue bridge is with the GUI of the Hue app from your smartphone. But anything the smart phone can do you can also do through the Hub&#38;#39;s HTTP REST API...and with that, from Perl.&lt;/p&gt;

&lt;p&gt;The first step is to find out what the local internal IP address of the Hue Bridge is. If it can talk to the internet the Hue Bridge registers the current internal API it has with the central Phillips servers. You can query them with a simple HTTP JSON API call:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   $ curl https://www.meethue.com/api/nupnp
   [{&#38;quot;id&#38;quot;:&#38;quot;001788fffead6e94&#38;quot;,&#38;quot;internalipaddress&#38;quot;:&#38;quot;192.168.1.2&#38;quot;}]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sweet. The only other thing we need to do is get any API key we can use to talk to the bridge. This can be achieved by POSTing some JSON to the local Hue Bridge.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ curl -X POST -d &#38;#39;{&#38;quot;devicetype&#38;quot;:&#38;quot;perl-interface&#38;quot;}&#38;#39; http://192.168.1.2/api
    [{&#38;quot;error&#38;quot;:{&#38;quot;type&#38;quot;:101,&#38;quot;address&#38;quot;:&#38;quot;&#38;quot;,&#38;quot;description&#38;quot;:&#38;quot;link button not pressed&#38;quot;}}]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wait, that didn&#38;#39;t give us an API key...oh, right, it now wants us to press the button on the top of the hub to prove we&#38;#39;re authorized to get a new API key. Let&#38;#39;s press it, then try that again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    curl -X POST -d &#38;#39;{&#38;quot;devicetype&#38;quot;:&#38;quot;perl-interface&#38;quot;}&#38;#39; http://192.168.1.2/api
    [{&#38;quot;success&#38;quot;:{&#38;quot;username&#38;quot;:&#38;quot;xyznl1BwQryLMOhJ3uNPUxnR7eQIwwqrkd5Kt0dd&#38;quot;}}]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Great, that &lt;i&gt;username&lt;/i&gt; is our key. With it we can start making API calls:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    curl http://192.168.1.2/api/xyznl1BwQryLMOhJ3uNPUxnR7eQIwwqrkd5Kt0dd/config | json_pp
    {
        &#38;quot;netmask&#38;quot; : &#38;quot;255.255.255.0&#38;quot;,
        &#38;quot;portalstate&#38;quot; : {
            &#38;quot;signedon&#38;quot; : true,
            &#38;quot;incoming&#38;quot; : false,
            &#38;quot;outgoing&#38;quot; : true,
            &#38;quot;communication&#38;quot; : &#38;quot;disconnected&#38;quot;
        },
        &#38;quot;apiversion&#38;quot; : &#38;quot;1.35.0&#38;quot;,
        &#38;quot;backup&#38;quot; : {
            &#38;quot;status&#38;quot; : &#38;quot;idle&#38;quot;,
            &#38;quot;errorcode&#38;quot; : 0
        },
        &#38;quot;portalservices&#38;quot; : true,
        ...&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Building-a-Bridge-Class&#34;&gt;Building a Bridge Class&lt;/h3&gt;

&lt;p&gt;Messing around with the &lt;code&gt;curl&lt;/code&gt; command is going to get tiresome fast, so it&#38;#39;s time to break out our Perl code.&lt;/p&gt;

&lt;p&gt;First let&#38;#39;s start by writing a Bridge class that can handle the basics of talking HTTP to our Hue Bridge.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Bridge&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;JSON::PP&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( encode_json decode_json )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;http_tiny&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTTP::Tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ENV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;HUE_KEY&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ip_address&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;http_tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;https://www.meethue.com/api/nupnp&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Failed!&#39;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;success&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;decode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;internalipaddress&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# get the data from the URL fragment, and return the parsed JSON (if any)&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fragment&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fragment&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$tiny&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;http_tiny&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Failed!&#39;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;success&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;decode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$response&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;undef&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# turn the URL fragment into a full API URL&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_url&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fragment&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ip_address&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/api/&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;key&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fragment&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let&#38;#39;s use a simple script to see it in action&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Bridge&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Dumper&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$bridge&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Dumper&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;/lights/1&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This prints out &lt;i&gt;so much information&lt;/i&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$VAR1&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;name&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Left office&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;type&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Dimmable light&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;modelid&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;LWB014&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;manufacturername&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Philips&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;productid&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Philips-LWB014-1-A19DLv4&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;state&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;reachable&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;mode&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;homeautomation&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;bri&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;254&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;alert&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;lselect&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;on&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;uniqueid&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;00:17:88:01:03:c0:d5:d1-0b&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;swversion&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;1.46.13_r26312&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;swconfigid&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;69806BE9&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;capabilities&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;control&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;maxlumen&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;840&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;mindimlevel&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2000&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;certified&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;streaming&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;renderer&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;proxy&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;swupdate&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;state&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;noupdates&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;lastinstall&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;2019-05-06T18:53:05&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;function&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;functional&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;archetype&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;classicbulb&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;direction&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;omnidirectional&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;startup&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;configured&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;mode&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;safety&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;productname&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Hue white lamp&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Building-Out-the-Abstraction&#34;&gt;Building Out the Abstraction&lt;/h3&gt;

&lt;p&gt;Those are quite big data structures! What we need to do is work on an abstraction layer or two that will make working with them easier.&lt;/p&gt;

&lt;p&gt;First let&#38;#39;s implement a generic &lt;code&gt;Item&lt;/code&gt; role for all items. This&#38;#39;ll handle the nitty gritty of calling the API to fetch data whenever we want something:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    package Role::Item;

    use Moo::Role;
    use experimental &#38;#39;signatures&#38;#39;;

    use Bridge;

    # we need an &#38;#39;endpoint&#38;#39; method that&#38;#39;ll give us the
    # part of the URL we&#38;#39;re constructing
    requires &#38;#39;endpoint&#38;#39;;

    has bridge =&#38;gt; (
        is =&#38;gt; &#38;#39;lazy&#38;#39;,
        default =&#38;gt; sub { Bridge-&#38;gt;new() },
    );

    has id =&#38;gt; ( is =&#38;gt; &#38;#39;ro&#38;#39; );

    # this is where the data from the server is lazy-loaded via
    # the REST API
    has _data =&#38;gt; (
        is =&#38;gt; &#38;#39;ro&#38;#39;,
        clearer =&#38;gt; &#38;#39;flush_cache&#38;#39;,
        lazy =&#38;gt; 1,
        default =&#38;gt; sub ($self) {
            return $self-&#38;gt;bridge-&#38;gt;get($self-&#38;gt;endpoint.&#38;#39;/&#38;#39;.$self-&#38;gt;id);
        },
    );

    # all things in the Hue API (lights, sensors, rules, etc) have names
    sub name ($self) { $self-&#38;gt;_data-&#38;gt;{name} }

    1;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And we can implement that in a basic Light class:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Light&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Role::Item&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/lights&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# a few basic facts about the light from the data structure&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;on&lt;/span&gt;         &lt;span class=&#34;prototype&#34;&gt;($self)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;brightness&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($self)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;bri&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which means we can easily write a script to find the status of a given light:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Light&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$light&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Light&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$light&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39; light is &#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$light&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;on&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;on&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;off&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So if we happen to know the ID of the light we want to talk to, we can now find out if it&#38;#39;s on or not:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ ./status 1
    Left office is on&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;More-than-one-at-a-time&#34;&gt;More than one at a time?&lt;/h3&gt;

&lt;p&gt;Of course, I don&#38;#39;t tend to memorize the IDs of all the light bulbs in my house. It&#38;#39;d be much nicer to use the friendly name that we&#38;#39;ve assigned to the bulb. It&#38;#39;d also be great to pull down all the information for all the bulbs in one go rather than making an individual API call for each.&lt;/p&gt;

&lt;p&gt;The Hue Bridge supports this by performing a simple GET request without the id as part of the URL:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    curl http://192.168.1.2/api/xyZnl1BwQryLMNhJ3uNxUxnl7eQIwwqrkd5Kt0dd/lights
    {
    &#38;quot;2&#38;quot; : { ... },
    &#38;quot;4&#38;quot; : { ... },
    &#38;quot;15&#38;quot; : { ... },
    &#38;quot;6&#38;quot; : { ... },
    ...
    }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each of the keys in this top level object is the id of the light, and each of the values is the exact same JSON data structure that we would have received had we made an individual HTTP call per id.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s create a &lt;i&gt;Factory&lt;/i&gt; class that can use this API call to make multiple Light objects at once.&lt;/p&gt;

&lt;p&gt;First up, the generic Role again:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Role::Factory&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moo::Role&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Bridge&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# we need to know both the URL endpoint *and* the&lt;br /&gt;# name of the class we&#39;re constructing&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;requires&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;endpoint&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;item_class&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bridge&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;ro&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;clearer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;flush_cache&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;lazy&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;items&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_data_to_items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_data&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_data_to_items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;item_class&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;bridge&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;_data&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;keys&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note how we&#38;#39;re passing the &lt;code&gt;_data&lt;/code&gt; into the Light classes. Since we&#38;#39;re directly populating those attributes the objects never have to lazily call their &lt;code&gt;default&lt;/code&gt; callbacks and don&#38;#39;t have to make individual HTTP calls to populate themselves. Neat.&lt;/p&gt;

&lt;p&gt;The LightFactory class is tiny:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Moo&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;experimental&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;signatures&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Light&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Role::Factory&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/lights&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;item_class&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Light&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now trivially write a script to show the status of all lights in the house:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$lf&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$lf&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39; light is &#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;on&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;on&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;off&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which prints out some useful information:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Bottom Studio downlight light is on
    Center Front Room light is on
    Elder&#38;#39;s Overhead 1 light is on
    Elder&#38;#39;s Overhead 2 light is on
    Elder&#38;#39;s&#38;rsquo;s Closet light is off
    Left Front Room light is on
    Left Studio Rooflight light is on
    Left office light is on
    Library by Computer light is on
    Main door left light is on
    Main door right light is on
    Middle downlight studio light is on
    Right office light is on
    Right studio Rooflight light is on
    South light is off
    Top Downlight Studio light is on
    Upstairs Studio light is on
    West light is on
    Younger&#38;#39;s Closet light is off
    Younger&#38;#39;s overhead 1 light is on
    Younger&#38;#39;s overhead 2 light is on&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hmm. I need to turn some lights off.&lt;/p&gt;

&lt;h3 id=&#34;Turning-the-lights-off&#34;&gt;Turning the lights off&lt;/h3&gt;

&lt;p&gt;Reading information about lights is all very well, but we want more control than that.&lt;/p&gt;

&lt;p&gt;If we look at our light data structure we can see the &lt;code&gt;state&lt;/code&gt; key are some values we&#38;#39;d like to change:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$VAR1&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;state&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;reachable&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;mode&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;homeautomation&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;bri&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;254&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;alert&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;lselect&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;on&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$o&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON::PP::Boolean&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Specifically we&#38;#39;d like to change the &lt;code&gt;on&lt;/code&gt; value to false.&lt;/p&gt;

&lt;p&gt;Making changes with the Hue Bridge is easy - you just have to PUT data instead of GETing it.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    curl -X PUT \
         -d &#38;#39;{&#38;quot;on&#38;quot;:false} \
         http://192.168.1.2/api/xyznl1BwQryLMOhJ3uNPUxnR7eQIwwqrkd5Kt0dd/lights/1/state&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#38;#39;s two notable things about this curl command. Firstly, note the &lt;code&gt;/state&lt;/code&gt; at the end of the URL, meaning we&#38;#39;re changing the &lt;code&gt;state&lt;/code&gt; key &lt;i&gt;within&lt;/i&gt; the &lt;code&gt;lights/1&lt;/code&gt; object. Secondly, note that we&#38;#39;re not specifying all the keys in the JSON we&#38;#39;re putting to this URL - any key we don&#38;#39;t mention will be unchanged.&lt;/p&gt;

&lt;p&gt;So, we want to do this from Perl space. Let&#38;#39;s add a &lt;code&gt;put&lt;/code&gt; method to our Bridge class:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;put&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;($self, $fragment, $data)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_url&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fragment&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$tiny&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;http_tiny&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$tiny&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;PUT&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$url&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;content&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;encode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$data&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then to the Lights class add a &lt;code&gt;_set_state&lt;/code&gt; method that will take a hashref of new state options&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_set_state&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;( $self, $new_state )&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;bridge&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;put&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;join&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;endpoint&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;state&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$new_state&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can write some quick wrappers to turn lights on and off easily:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    sub turn ($self, $new) {
        my $value = defined($new) &#38;amp;&#38;amp; $new &#38;amp;&#38;amp; $new !~ /^off$/i ?
            JSON::PP::true : JSON::PP::false;
        $self-&#38;gt;_set_state({ on =&#38;gt; $value } );
    }
    sub turn_on($self)  { $self-&#38;gt;turn(&#38;#39;on&#38;#39;)  };
    sub turn_off($self) { $self-&#38;gt;turn(&#38;#39;off&#38;#39;) };&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let&#38;#39;s save some electricity!&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;turn_off&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new-items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Christmas-Lights&#34;&gt;Christmas Lights&lt;/h3&gt;

&lt;p&gt;On and off is all very good, but these lights are more capible than that. They can be set to arbitary brightness levels, and the more advanced ones can be set to any color.&lt;/p&gt;

&lt;p&gt;Since it&#38;#39;s Christmas, let&#38;#39;s use this ability to make some christmas lights.&lt;/p&gt;

&lt;p&gt;In order to set the color of the lights we&#38;#39;re going to need to work out the hue, saturation, and lightness rating for the colors we want to use. To do this we&#38;#39;re going to use the &lt;a href=&#34;https://metacpan.org/module/Convert::Color&#34;&gt;Convert::Color&lt;/a&gt; module from the CPAN, specifically by creating a &lt;a href=&#34;https://metacpan.org/module/Convert::Color::VGA&#34;&gt;Convert::Color::VGA&lt;/a&gt; instance from the name of the color we want to use and then converting it into an &lt;a href=&#34;https://metacpan.org/module/Convert::Color::HSL&#34;&gt;Convert::Color::HSL&lt;/a&gt; instance.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s wrap that up in a handy method inside the Light class:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;prototype&#34;&gt;($self, $color_name)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hsl&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Convert::Color::VGA&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$color_name&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;convert_to&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;hsl&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;_set_state&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;({&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;hue&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hsl&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hue&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;360&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;65535&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sat&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hsl&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;saturation&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;254&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;bri&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hsl&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;lightness&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;254&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;})&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can finally write a simple script to get the lights to change in a Christmassy way:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%all_lights&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;map&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$_&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LightFactory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# I really should have done a more consistent job of naming these&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@lights&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@all_lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;Bottom Studio downlight&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;Middle downlight studio&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;Top Downlight Studio&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;red&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;green&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;red&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sleep&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;green&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;red&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$lights&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_color&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;green&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sleep&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2019-12-05T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Going for Perl</title><link href="http://perladvent.org/2019/2019-12-04.html"/><id>http://perladvent.org/2019/2019-12-04.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Go-On&#34;&gt;Go On&lt;/h3&gt;

&lt;p&gt;Many people thought that Wise Old Elf, being, well, old, wouldn&#38;#39;t be much for these brand new programming languages. But, they forget... the Wise Old Elf isn&#38;#39;t just Old, he is Wise too. So he knew that you have to use the best tool for the job.&lt;/p&gt;

&lt;p&gt;More recently the Wise Old Elf had been running a small team of elves that had been experimenting with writing some of their more speed critical code in Go. Previously at the North Pole they&#38;#39;d resorted to using C code - often called via Inline::C - when their beloved Perl just wasn&#38;#39;t fast enough. But they&#38;#39;d had a lot of problems with that - dealing with memory management, having to often delve into the dark arts of XS coding, so the Wise Old Elf had started his experiment with something a little more &lt;i&gt;modern&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;And thus the Wise Old Elf suddenly found himself with a bunch of Go code that he would love the main North Pole codebase to be able to make use of without having to rewrite any of their battle-tested Perl code in Go. What he really needed was a way to call the new Go code from within Perl.&lt;/p&gt;

&lt;h3 id=&#34;Shared-Library-Time&#34;&gt;Shared Library Time&lt;/h3&gt;

&lt;p&gt;The Wise Old Elf decided he should have an experiment of his own. So he set himself a challenge: Write some trivial Go code and get it executed from within Perl.&lt;/p&gt;

&lt;p&gt;Go is perfectly capable of producing a shared library. Here&#38;#39;s a &#38;quot;Hello, World&#38;quot; type example for this time of the year.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;package main&lt;br /&gt;&lt;br /&gt;import &#38;quot;fmt&#38;quot;&lt;br /&gt;&lt;br /&gt;func main() {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;WishMerryChristmas()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;func WishMerryChristmas() {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;fmt.Println(&#38;quot;We wish you a Merry Christmas&#38;quot;);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To make this into a shared library we need to make a few scant changes.&lt;/p&gt;

&lt;p&gt;&lt;li&gt;Add an &lt;code&gt;import &#34;C&#34;&lt;/code&gt; statement&lt;/li&gt; &lt;li&gt;Empty out the &lt;code&gt;main()&lt;/code&gt; func&lt;/li&gt; &lt;li&gt;Add &lt;code&gt;//export&lt;/code&gt; decorators on what we want to export&lt;/li&gt;

&lt;/p&gt;



&lt;p&gt;Even with these changes the code looks mostly the same:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;package main&lt;br /&gt;&lt;br /&gt;import &#38;quot;C&#38;quot;&lt;br /&gt;&lt;br /&gt;import &#38;quot;fmt&#38;quot;&lt;br /&gt;&lt;br /&gt;func main() {}&lt;br /&gt;&lt;br /&gt;//export WishMerryChristmas&lt;br /&gt;func WishMerryChristmas() {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;fmt.Println(&#38;quot;We wish you a Merry Christmas&#38;quot;);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now compile this on the command line&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ go build -o merrychristmas.so -buildmode=c-shared
    $ ls merrychristmas*
    -rw-r--r-- 1 wiseold wiseold 1.3K Nov 19 00:27 merrychristmas.h
    -rw-r--r-- 1 wiseold wiseold 2.0M Nov 19 00:27 merrychristmas.so&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We&#38;#39;ve got two new files, the header file (the &lt;code&gt;merrychristmas.h&lt;/code&gt;) and the shared object file (the &lt;code&gt;merrychristmas.so&lt;/code&gt; file.) We can safely discard the header file since we&#38;#39;re not going to use the traditional C linking route here - we&#38;#39;re going to use one of Perl&#38;#39;s excellent Foreign Function Interface libraries to access the shared object instead.&lt;/p&gt;

&lt;p&gt;As an aside you&#38;#39;ll notice that &lt;code&gt;merrychristmas.so&lt;/code&gt; is huge for a library that has a single function that prints a simple string in it. That&#38;#39;s because it&#38;#39;s not just that - it also contains all of the Go runtime and packages also! We&#38;#39;re able to distribute that shared object along with our Perl code without any other Go scaffolding or support files.&lt;/p&gt;

&lt;h3 id=&#34;Bring-On-The-Platypus&#34;&gt;Bring On The Platypus&lt;/h3&gt;

&lt;p&gt;So, now we have a shared object from our Go code, how can we access it from Perl? With &lt;a href=&#34;https://metacpan.org/module/FFI::Platypus&#34;&gt;FFI::Platypus&lt;/a&gt; of course!&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;strict&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# load our chosen FFI interface module&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;FFI::Platypus&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# configure it to talk to our shared object library&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;FFI::Platypus&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;api&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;lib&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;./merrychristmas.so&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# bind the exported &#38;quot;WishMerryChristmas&#38;quot; to the&lt;br /&gt;# &#38;quot;WishMerryChristmas&#38;quot; function in Perl space, and&lt;br /&gt;# declare that it takes no args and returns nothing&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[]);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# call it!&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And does it work?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl merry.pl 
    We Wish You a Merry Christmas&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&#38;#39;s a Christmas miracle!&lt;/p&gt;

&lt;h3 id=&#34;In-Out-Shake-It-All-About&#34;&gt;In, Out, Shake It All About&lt;/h3&gt;

&lt;p&gt;Okay, so how about something more complicated. Let&#38;#39;s modify the go function to print out the message a number of times:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;//export WishMerryChristmas&lt;br /&gt;func WishMerryChristmas(n int) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for i := 0; i &#38;lt; n; i++ {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;fmt.Println(&#38;quot;We wish you a Merry Christmas&#38;quot;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now in our Perl script we can modify it to specify that we can pass in an argument:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;MerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;long&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;And a Happy New Year\n&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl merry.pl
    We wish you a Merry Christmas
    We wish you a Merry Christmas
    We wish you a Merry Christmas
    And a Happy New Year&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But hang on, why did we specify &lt;code&gt;long&lt;/code&gt; when we declared &lt;code&gt;WhichMerryChristmas&lt;/code&gt; to take an &lt;code&gt;int&lt;/code&gt;? That&#38;#39;s because we&#38;#39;re not specifying the &lt;b&gt;Go&lt;/b&gt; type to FFI::Platypus, but the &lt;code&gt;C&lt;/code&gt; type. And a Go &lt;code&gt;int&lt;/code&gt; is represented by a C &lt;code&gt;long&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can make this a whole lot clearer if we teach FFI::Platypus about a &lt;i&gt;type alias&lt;/i&gt; for Go ints:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# declare a type alias&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;go_int&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# then make use of it idiomatically when we attach&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;MerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;go_int&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This mismatch between C types and Go types becomes even clearer if we change our function to take a string:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;//export WishMerryChristmas&lt;br /&gt;func WishMerryChristmas(who string) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;fmt.Printf(&#38;quot;We wish you a Merry Christmas, %s\n&#38;quot;, who);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then naively call it from Perl:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# this code is wrong...&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;string&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Santa&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We get junk out:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl merry.pl 
    We wish you a Merry Christmas, SantapV&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&#38;#39;s because we&#38;#39;re calling the Go code with a C string (i.e. a null terminated array of bytes) rather than the Go string structure it expects which should look somewhat like this in C space:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synType&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;synType&#34;&gt;struct&lt;/span&gt;{&lt;span class=&#34;synType&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;synType&#34;&gt;char&lt;/span&gt; *p; go_int len;} go_str;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;A-Record-Solution&#34;&gt;A Record Solution&lt;/h3&gt;

&lt;p&gt;There&#38;#39;s several approaches we can take to help cross the divide. The simplest solution is to define in Perl space a wrapper for the struct we need to pass in&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GoString&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;FFI::Platypus::Record&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# this is the same as&lt;br /&gt;# typedef struct{const char *p; go_int len;} go_str;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;record_layout_1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;string rw&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;p&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;long&#39;&lt;/span&gt;      &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;len&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;And&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;suitable&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;argument&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;declaration&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;we&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;can&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;call&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Perl&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;#!perl&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;record(GoString)&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$stirng&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Santa&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$go_string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;GoString&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;len&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$go_string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We finally get the output we wanted all along:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl merry.pl 
    We wish you a Merry Christmas, Santa&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can further simplify this by passing the &lt;code&gt;attach&lt;/code&gt; method a &lt;i&gt;wrapper function&lt;/i&gt; that does the conversion for us:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;symbol&#34;&gt;$ffi&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;attach&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;record(GoString)&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;void&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$real_function&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;shift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$real_function&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;GoString&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;len&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$string&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;});&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;WishMerryChristmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Santa&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So a Merry Christmas to you too - from multiple programming languages!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-04T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Two Factor Elfication</title><link href="http://perladvent.org/2019/2019-12-03.html"/><id>http://perladvent.org/2019/2019-12-03.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Day three.&lt;/p&gt;

&lt;p&gt;Day three of piles of presents where there shouldn&#38;#39;t be.&lt;/p&gt;

&lt;p&gt;Day three of the hackers breaking into the North Pole systems.&lt;/p&gt;

&lt;p&gt;Enough was enough. Yes, they&#38;#39;d compromised Pepper&#38;#39;s account for the third day running, but Butters Jollycane was completely done with trying to get his hapless colleague to protect his password - it was a lost cause. Today was going to be the last day that anyone getting access to an Elf&#38;#39;s password was going to allow them access to all of the North Pole systems. Jollycane was going to configure their system for Two-Factor Authentication.&lt;/p&gt;

&lt;h3 id=&#34;FA&#34;&gt;2FA&lt;/h3&gt;

&lt;p&gt;Two Factor Authentication simply means that you have a second &lt;i&gt;factor&lt;/i&gt; that you use for authentication. Typical factors include things that you know (passwords, security questions,) things that you have (dongles or apps that create unguessable time-based sequences) or things that you are (biometrics.)&lt;/p&gt;

&lt;p&gt;Jollycane wasn&#38;#39;t able to add finger print or retinal scanners for the elves, so he was going to have to rely on the elves having something they knew (their password) and something they had.&lt;/p&gt;

&lt;h3 id=&#34;By-My-OATH-They-Wont-Get-In&#34;&gt;By My OATH They Won&#38;#39;t Get In&lt;/h3&gt;

&lt;p&gt;Jollycane needed a solution that he could implement in the next few hours. No time to ship devices to all the elves. He&#38;#39;d have them make use of their company issued cell phones for now.&lt;/p&gt;

&lt;p&gt;Google Authenticator is one of many smart-phone apps that implement the One Time Password algorithms as defined by the open &lt;a href=&#34;http://www.openauthentication.org&#34;&gt;OATH specification&lt;/a&gt; in order to act as a second factor. In short, every thirty seconds the app displays a new six digit code which the user has to enter when logging in in addition to the password.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;toph.gif&#34; width=&#34;600&#34; height=&#34;349&#34; alt=&#34;toph&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;There&#38;#39;s a shared secret known only to the website and the authenticator app. Both the app and the server run the same algorithm based upon the current time and the shared secret. If the code the user copies from their phone matches the server&#38;#39;s expected value then authentication occurs.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Authen::OATH&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Convert::Base32&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( decode_base32 )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$oath&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Authen::OATH&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$secret&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;mySecret&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$otp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$oath&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;totp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;  &lt;span class=&#34;word&#34;&gt;decode_base32&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$secret&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$the_code_the_user_entered&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$otp&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;ACCESS GRANTED&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;HACKER ALERT WHOOP WHOOP&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Passing-Secrets&#34;&gt;Passing Secrets&lt;/h3&gt;

&lt;p&gt;Jollycane needed a way to allow his elves to configure their authenticator apps. The most common way to do this is to create a QR code (a &#38;quot;2d barcode&#38;quot;) that uses the &lt;code&gt;otpauth&lt;/code&gt; url scheme. Google Authenticator allows users to &#38;quot;scan&#38;quot; this QR code with their phone&#38;#39;s camera.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;scanqr.gif&#34; width=&#34;320&#34; height=&#34;569&#34; alt=&#34;qr scan&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Crating these in Perl is actually quite straight forward once you know the format the URL should be in&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$qrcode&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;HTML::QRCode&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$html&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$qrcode&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;plot&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;otpauth://totp/&#39;&lt;/span&gt;                        &lt;span class=&#34;comment&#34;&gt;# URI standard&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;pfrostyflakes@northpole.example.com&#39;&lt;/span&gt;  &lt;span class=&#34;comment&#34;&gt;# label&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;?secret=&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;decode_base32&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$secret&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;comment&#34;&gt;# secret&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;amp;issuer=North+Pole&#39;&lt;/span&gt;                   &lt;span class=&#34;comment&#34;&gt;# issuer&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This outputs a bunch of HTML that when rended by a browser looks like:&lt;/p&gt;

&lt;p&gt;&lt;center&gt; &lt;table style=&#34;margin:0;padding:0;border-width:0;border-spacing:0;&#34;&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;tr style=&#34;border:0;margin:0;padding:0;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: black;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;td style=&#34;border:0;margin:0;padding:0;width:3px;height:3px;background-color: white;&#34;&gt;&lt;/tr&gt;&lt;/table&gt; &lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Hopefully that would be good enough to &lt;i&gt;finally&lt;/i&gt; keep these kids out!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-03T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>Have Elf Been Pwned?</title><link href="http://perladvent.org/2019/2019-12-02.html"/><id>http://perladvent.org/2019/2019-12-02.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Mess-with-the-Best&#34;&gt;Mess with the Best...&lt;/h3&gt;

&lt;p&gt;Butters Jollycane silently cursed. The piles of presents from yesterday were back. Each of the hackers had, if anything, larger piles of gifts waiting to go out than they&#38;#39;d had the day before - even though they were meant to be on the naughty list.&lt;/p&gt;

&lt;p&gt;This could only mean one thing: Somehow that Dade Murphy kid had got back into the system despite Jollycane&#38;#39;s efforts yesterday to make sure everyone was now using sufficiently strong passwords hasn&#38;#39;t been enough.&lt;/p&gt;

&lt;p&gt;He checked the logs. Yep, they&#38;#39;d got in using Pepper&#38;#39;s account again.&lt;/p&gt;

&lt;h3 id=&#34;Get-Hacked-Like-The-Rest&#34;&gt;...Get Hacked Like The Rest&lt;/h3&gt;

&lt;p&gt;&#38;quot;I don&#38;#39;t understand it&#38;quot;, Pepper explained, &#38;quot;that zxcvbn doohicky you made yesterday said the password was fine&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Okay, okay...let&#38;#39;s see the new Post-it note.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh I didn&#38;#39;t write it down this time&#38;quot;, Pepper beamed, &#38;quot;I can remember it! After all it&#38;#39;s one of the passwords I&#38;#39;ve been using for years!&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Wait, you use the same password on other sites?&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh yes. Very secure it is. I&#38;#39;ve never had a problem with it...I use it for all my accounts&#38;quot;&lt;/p&gt;

&lt;p&gt;Jollycane inwardly sighed.&lt;/p&gt;

&lt;h3 id=&#34;Have-I-Been-Pwned&#34;&gt;Have I Been Pwned?&lt;/h3&gt;

&lt;p&gt;Credential reuse on websites is a real problem. No matter how secure you make your website, if an elf reuses a username or password that they&#38;#39;ve used on another website and that website gets hacked, it can spell doom and gloom for your security. People who are most definitely on Santa&#38;#39;s naughty list regularly distribute lists of these stolen credentials. All Dade Murphy had to do was download a copy of these lists and see if the same compromised username and password that Pepper had used on Elf Single Mingles would allow him to login to Santa&#38;#39;s workshop using Pepper&#38;#39;s account.&lt;/p&gt;

&lt;p&gt;Jollycane quickly pulled up the &lt;a href=&#34;https://haveibeenpwned.com/&#34;&gt;Have I Been Pwned?&lt;/a&gt; website and tapped in Pepper&#38;#39;s account details.&lt;/p&gt;

&lt;p&gt;&lt;center&gt;&lt;img src=&#34;hibp.jpg&#34; width=&#34;630&#34; height=&#34;951&#34; alt=&#34;HIBP Website&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Have I Been Pwned keeps a copy of all of the well known lists of stolen credentials and allows people to check if their credentials have been exposed (if you haven&#38;#39;t done so recently you might want to check out &lt;i&gt;your&lt;/i&gt; email address on there right now!) Looking at the screen Jollycane could see numerous places where Pepper&#38;#39;s account had been broken into on other sites.&lt;/p&gt;

&lt;h3 id=&#34;Password-Reuse-Avoidance&#34;&gt;Password Reuse Avoidance?&lt;/h3&gt;

&lt;p&gt;Jollycane would send out an all workshop email instructing everyone to not reuse their passwords on any other websites. But how could he enforce that?&lt;/p&gt;

&lt;p&gt;Believe it or not, Have I Been Pwned? has an API for that, or rather they have an API that&#38;#39;ll let you work out if a password has been included anywhere in any of the well known published password breaches.&lt;/p&gt;

&lt;p&gt;Using the API is slightly more complicated than a normal REST API but &lt;a href=&#34;https://metacpan.org/module/WebService::HIBP&#34;&gt;WebService::HIBP&lt;/a&gt; abstracts away the details:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$hibp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;WebService::HIBP&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;santa&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;Merry Christmas&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;All I want For Christmas is Yoooooooooou&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$password was breached &#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$hibp&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot; times&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which shows us just how bad the elves choices are:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    santa was breached 10898 times
    Merry Christmas was breached 7 times
    All I want For Christmas is Yoooooooooou was breached 0 times&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But wait, how does it do that without sending HIBP the passwords to check? It obviously doesn&#38;#39;t download the terabytes of exposed data each time it checks.&lt;/p&gt;

&lt;p&gt;In a word: Hashing. The module creates a one-way hash of the password and then sends just the first few characters of the hash to HIBP via the API (you wouldn&#38;#39;t want to send the whole hash because that means HIBP could theoretically brute force the password.) HIBP returns a list of hashes of all compromised passwords that hashes also start with those same characters. The module can then check if the hash of the original password is contained in that list, and if it is, then blammo - the password was compromised.&lt;/p&gt;

&lt;h3 id=&#34;Another-Day&#34;&gt;Another Day&lt;/h3&gt;

&lt;p&gt;With Website::HIBP Jollycane had plugged another hole. But he was worried. He thought he was done yesterday...but he had a sneaking suspicion that he wasn&#38;#39;t quite done with his battle with these particular brats.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-02T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>J1ngle? No...zxcvbn</title><link href="http://perladvent.org/2019/2019-12-01.html"/><id>http://perladvent.org/2019/2019-12-01.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Zero-Cool-Situation&#34;&gt;Zero Cool Situation&lt;/h3&gt;

&lt;p&gt;Butters Jollycane stared up in amazement at the towering piles of gifts before him. Never before has he seen such large piles of presents for so few people. There was one pile for &#38;quot;Dade Murphey&#38;quot;, another for &#38;quot;Kate Libby&#38;quot; and another for someone with the dubious name of &#38;quot;Cereal Killer&#38;quot;.&lt;/p&gt;

&lt;p&gt;What&#38;#39;s worse, up until a week ago these people had been scheduled for a coal delivery, after a prank they&#38;#39;d been involved in with taking over some automated office lights in a tower block to display dubious messages had got them assigned to the naughty list. So why had Pepper Frostyflakes over in accounting used his login to assign them such lavish gifts at 3am this morning?&lt;/p&gt;

&lt;h3 id=&#34;Hack-The-Planet&#34;&gt;Hack The Planet!&lt;/h3&gt;

&lt;p&gt;&#38;quot;I was asleep in my bed at three am this morning!&#38;quot;, Pepper exclaimed, &#38;quot;Do I look like an Elf on the Shelf, up all night long? I need my beauty sleep&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;And your password is secure?&#38;quot;, Jollycane asked.&lt;/p&gt;

&lt;p&gt;&#38;quot;It&#38;#39;s locked away right here, where it always is&#38;quot;, Pepper replied as he unlocked his desk draw and pulled out a dog-eared post-it note. &#38;quot;I wrote it down and locked it away because of the silly password rules you tech Elfs put in place - you know, with the numbers and uppercase and lowercase and weird punctuation you guys always want.&#38;quot;&lt;/p&gt;

&lt;p&gt;Pepper flipped over the post it note which had just one word written on it: &lt;code&gt;J1ngle?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Jollycane&#38;#39;s jaw dropped. It was obvious that not only had they been hacked by some young miscreants, but they&#38;#39;d done it just by guessing Pepper&#38;#39;s super simple password.&lt;/p&gt;

&lt;h3 id=&#34;Never-fear-zxcvbn-is-here&#34;&gt;Never fear, zxcvbn is here&lt;/h3&gt;

&lt;p&gt;Jollycane thought for a minute if he should give Pepper the same lecture he&#38;#39;d given the last six elves that had had their password guessed by mischievous agents. It&#38;#39;s not enough to switch an &lt;code&gt;1&lt;/code&gt; for an &lt;code&gt;i&lt;/code&gt; or uppercase the first letter. It&#38;#39;s just a simple matter of programming to try those basic transformations too. But Pepper had followed the policy...maybe it was time for a change.&lt;/p&gt;

&lt;p&gt;What Jollycane needed was some way to &lt;i&gt;rate&lt;/i&gt; the passwords users were using. Luckily, Dropbox has been working on this problem and published their &lt;a href=&#34;https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/wheeler&#34;&gt;zxcvbn&lt;/a&gt; algorithm that can do just that. And there&#38;#39;s a Perl implementation on the CPAN, &lt;a href=&#34;https://metacpan.org/module/Data::Password::zxcvbn&#34;&gt;Data::Password::zxcvbn&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/perl&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;5.024&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;warnings&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Password::zxcvbn&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( password_strength)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;password_strength&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$ARGV&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The result is a hashref that contains a lot of useful information about the strength of the password. At the most basic we have the score:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@passwords&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;password&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;p@ssword&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;J1ngle?&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;horse staple battery generator&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;B2aFkgEhZvFstE9&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@passwords&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;password_strength&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$result-&#38;gt;{score} $password&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This goes from &lt;code&gt;0&lt;/code&gt; (never use) to &lt;code&gt;4&lt;/code&gt; (as secure as the algorithm can figure).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    0 password
    0 p@ssword
    1 J1ngle?
    4 horse staple battery generator
    4 B2aFkgEhZvFstE9&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What does that mean in a practical sense? Well, since Santa&#38;#39;s workshop hasn&#38;#39;t implemented any rate limiting on their website logins (ooops, they should really do that) the hackers can probably make, say, oh, ten requests a second to try and log in. How long would it take for each of the passwords to be broken?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Time::Duration&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( duration )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@passwords&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;password_strength&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$duration&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;duration&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;crack_times_seconds&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;online_no_throttling_10_per_second&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;$password would be cracked $duration&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    password would be cracked just now
    p@ssword would be cracked just now
    J1ngle? would be cracked 9 hours and 33 minutes
    horse staple battery generator would be cracked 2475117468353 years and 229 days
    B2aFkgEhZvFstE9 would be cracked 3170979 years and 72 days&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Okay, that really brings it home the difference in password security! Jollycane should implement a rule that no password with a score less than four can be used.&lt;/p&gt;

&lt;p&gt;But how can he do that? We&#38;#39;ve obviously established that simple rules that users (and hackers!) can easily follow don&#38;#39;t help matters. What users need is some interactive feedback about why the passwords they&#38;#39;re trying to use aren&#38;#39;t secure, and what they should do to improve them. &lt;code&gt;password_strength&lt;/code&gt; provides help with that too:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@passwords&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;password_strength&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;score&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;# $password&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$feedback&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$result&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;feedback&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;WARNING: $feedback-&#38;gt;{warning}&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$feedback&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;warning&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;* $_&#38;quot;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$feedback&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;suggestions&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;cast&#34;&gt;@*&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gives us output we can show the end user:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    # password
    WARNING: This is a top-10 common password
    * Add another word or two. Uncommon words are better.

    # p@ssword
    WARNING: This is similar to a commonly used password
    * Predictable substitutions like &#38;#39;@&#38;#39; instead of &#38;#39;a&#38;#39; don&#38;#39;t help very much
    * Add another word or two. Uncommon words are better.

    # J1ngle?
    * Capitalization doesn&#38;#39;t help very much
    * Predictable substitutions like &#38;#39;@&#38;#39; instead of &#38;#39;a&#38;#39; don&#38;#39;t help very much
    * Add another word or two. Uncommon words are better.&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Rollout&#34;&gt;Rollout&lt;/h3&gt;

&lt;p&gt;Jollycane rolled out the new policy and got all his users to change their passwords. Hopefully that would be enough to keep out those undeserving hooligans....at least until tomorrow....&lt;/p&gt;

&lt;/div&gt;</summary><updated>2019-12-01T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry></feed>