<?xml version="1.0" encoding="us-ascii"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Perl Advent Calendar 2024</title><id>https://perladvent.org/2024/</id><link href="https://perladvent.org/2024/atom.xml" rel="self"/><updated>2026-02-18T19:25:48Z</updated><author><name>PerlAdvent Org</name></author><generator uri="https://metacpan.org/pod/XML::Atom::SimpleFeed" version="0.905">XML::Atom::SimpleFeed</generator><entry><title>Merry Christmas, bless us, everyone</title><link href="https://perladvent.org/2024/2024-12-24.html"/><id>https://perladvent.org/2024/2024-12-24.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;center&gt;&lt;img src=&#34;santacoding.png&#34; width=&#34;512&#34; height=&#34;512&#34;&gt;&lt;/center&gt;

&lt;/p&gt;



&lt;p&gt;Merry Christmas, one and all.&lt;/p&gt;

&lt;p&gt;A quarter of a century (and, obviously, twenty five days) ago I created the very first Perl Advent Calendar entry. This Christmas, I&#38;#39;d like to take a minute to talk about all the wonderful presents this has given us over the years.&lt;/p&gt;

&lt;p&gt;Over this time the world has changed, and Perl&#38;#39;s role in the world with it. Twenty five years ago was the dawn of the dot-com boom, where we were concerned with the battle between Perl and Python, and the new upstart of Java. Which would be the dominant language? Twenty five years later we can see the fallacy of this debate - all of the languages won. We live in a vastly more complicated world where the Internet is now always on, in our pockets and on our wrists, controlling everything from our light switches to how we interact with our governments. And powering this is a plurality of programming languages, each with its own advantages and disadvantages, quirks and foibles, allowing people to use the best language for the task at hand - Java, or Python, or our beloved Perl - or any number of interesting and exciting language brethren new and old.&lt;/p&gt;

&lt;p&gt;With the development of the cloud and cloud technologies the old battle for the server is immaterial. Where we once used to fret over if Perl or Python was installed on &#38;quot;the server&#38;quot; and available for us (and which version!), we now have the ability to easily obtain whatever environment we want to use whichever language we want. Where we used to have to hand install software on the operating system this was supplanted by package systems, then by fleet management systems like ansible and puppet that could reproducibly install and manage any language we wanted across a whole fleet of machines, and then by virtual machine technologies like Amazon&#38;#39;s AMI system that can be used to quickly create a &#38;quot;throwaway&#38;quot; machine with whatever machine image we want installed on it.&lt;/p&gt;

&lt;p&gt;We now live in the Docker (container) world where each script can have its own version of Perl, with exactly the dependencies it needs installed just for it.&lt;/p&gt;

&lt;h3 id=&#34;Perl-5&#34;&gt;Perl 5&lt;/h3&gt;

&lt;p&gt;This changing environment has influenced Perl, allowing it to flourish in a way that we never would have imagined twenty five years ago. By making installing software easier we freed ourselves from the concept of the &#38;quot;core&#38;quot; Perl language extending what we could use out into reliably having whatever bits of CPAN we want at hand. This brought the philosophy of &#38;quot;CPAN is the language&#38;quot; to the forefront.&lt;/p&gt;

&lt;p&gt;Much of the work on Perl in the last twenty five years has been adapting the core interpreter so that it&#38;#39;s possible to modify it more and more by simply installing a module. Where we once begrudgingly relied on source code modifications to blindly manipulate the raw text of the code in order to add brand new language features we now exist in a world of pluggable syntax where such changes can be made in collaboration with perl&#38;#39;s own parser giving us (almost) infinite adaptability.&lt;/p&gt;

&lt;p&gt;The history of Perl has moved from higher level abstractions being built in large swaths of Perl code to code being made available on the CPAN that can integrate ever closer with the interpreter itself. Now finally in the last few years we&#38;#39;ve seen some of these experiments being turned into best practices that are built into the language itself.&lt;/p&gt;

&lt;p&gt;When I first learned Perl in 1998, I started with the &#38;quot;pink&#38;quot; version of Larry Wall&#38;#39;s &#38;quot;Programming Perl&#38;quot; book, which only covered Perl 4 and hence didn&#38;#39;t cover the topic of objects &lt;i&gt;at all&lt;/i&gt;. This resulted in some of the first Perl 5 code I wrote looking 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;triangle_area&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;$tri&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;symbol&#34;&gt;$tri&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;width&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;$tri&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;height&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;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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&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;width&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;triangle_area&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$shape&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;\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;My understanding of Perl changed pretty quickly when I started reading the man pages for the version of Perl I had installed and got my hands on the &#38;quot;blue&#38;quot; second edition of &#38;quot;Programming Perl&#38;quot;.&lt;/p&gt;

&lt;p&gt;Learning Perl 5&#38;#39;s object model meant the kind of Perl I would write would be more like this:&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;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;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$class&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;&#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;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;width&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;width&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;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;height&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;bless&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;$class&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;width&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;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;word&#34;&gt;width&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;height&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;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;word&#34;&gt;height&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;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;area&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;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;width&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;height&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;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;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;width&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;area&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;\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;This was so much better. We did&#38;#39;t have to name the &lt;code&gt;area&lt;/code&gt; method with a &lt;code&gt;triangle_&lt;/code&gt; prefix in order to disambiguate it from the any &lt;code&gt;rectangle_area&lt;/code&gt; or &lt;code&gt;circle_area&lt;/code&gt; function we might have in our code base.&lt;/p&gt;

&lt;p&gt;Then in 2006 along came &lt;a href=&#34;https://metacpan.org/module/Moose&#34;&gt;Moose&lt;/a&gt; which enabled a much more powerful way to write objects:&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;Triangle&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;br /&gt;&lt;span class=&#34;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;width&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;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;height&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;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;area&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;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;width&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;height&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;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;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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;width&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;area&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;\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;Later, in 2017, we had the first release of &lt;a href=&#34;https://metacpan.org/module/Mu&#34;&gt;Mu&lt;/a&gt; which allows us to write something quite a bit shorter:&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;Triangle&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;Mu&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ro&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;width&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ro&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;height&#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;area&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;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;width&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;height&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;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;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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;width&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;span class=&#34;word&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;area&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;\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;This is not only shorter, but it also has more error checking - if you don&#38;#39;t pass both &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; to &lt;code&gt;new&lt;/code&gt;, you&#38;#39;ll get an error.&lt;/p&gt;

&lt;p&gt;Last year we had the release of Perl 5.38. This brought into core a bunch of syntax (admirably still experimental) that we started as a community playing with in 2008 with &lt;a href=&#34;https://metacpan.org/module/MooseX::Declare&#34;&gt;MooseX::Declare&lt;/a&gt; - but implemented much better and safer. The code above can now be simplified even further:&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;version&#34;&gt;v5.38&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;feature&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;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;class&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;symbol&#34;&gt;$width&lt;/span&gt;  &lt;span class=&#34;operator&#34;&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;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;symbol&#34;&gt;$height&lt;/span&gt; &lt;span class=&#34;operator&#34;&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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;area&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;symbol&#34;&gt;$width&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$height&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Triangle&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;width&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;height&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$shape&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;area&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;The-Legacy-of-Perl&#34;&gt;The Legacy of Perl&lt;/h3&gt;

&lt;p&gt;Perl has changed throughout the years, but it&#38;#39;s always been Perl, with core ideas that have defined not only the &#38;quot;Perl way&#38;quot; of doing things for Perl, but elsewhere too!&lt;/p&gt;

&lt;p&gt;These ideas have infected the way we write code, not just in Perl, but in all modern programming languages. Amazing things that sprung up in Perl have become table stakes in other languages. The idea of having a central repository such as the CPAN that is the defacto place where you can find open source libraries that can be easily installed have become the norm - just look at JavaScript&#38;#39;s ever popular npm, Python&#38;#39;s PyPI, and Ruby&#38;#39;s RubyGems as examples. The concept of shipping these libraries with working tests like everything on the CPAN has been adopted everywhere, with CI/CD systems validating any serious software dependency on any commit.&lt;/p&gt;

&lt;p&gt;These aspects of Perl - along with countless other things - means that twenty five years on not only can I still enjoy writing Perl code in more powerful and fun ways than I ever could, but when I need to use another language better suited to a particular niche, a lot of the things I loved about Perl twenty five years ago are already there waiting for me.&lt;/p&gt;

&lt;h3 id=&#34;The-Advent-Calendar-Itself&#34;&gt;The Advent Calendar Itself&lt;/h3&gt;

&lt;p&gt;Speaking of things that other languages have adopted...&lt;/p&gt;

&lt;p&gt;The story of the Perl Advent Calendar starts, like all good stories, with a bad idea in the pub the night before. I don&#38;#39;t eat chocolate, so I was lamenting at a Perl Mongers social meeting that there were no good advent calendars for me - just the ones with a little picture behind the windows. I was saying that you should be able to get some other kind of reward. And then I thought how about some cool code?&lt;/p&gt;

&lt;p&gt;So the Perl Advent Calendar was born. Or it was the very next day, when I put the whole thing together during my lunch break. Back then I just named a module for the day and included the module&#38;#39;s POD as the advent calendar - next year I introduced a small description of why I&#38;#39;d picked it along with the POD, and the year after is when the full article format was introduced with links to search.cpan.org (and later metacpan) for the documentation.&lt;/p&gt;

&lt;p&gt;This turned out to be surprisingly popular, with the website being featured in NTK and on Slashdot (getting mentioned by Slashdot was a big deal in 1999).&lt;/p&gt;

&lt;p&gt;It wasn&#38;#39;t long before there were imitators for other programming languages. It became a tradition in the software world to have an Advent Calendar for your language of choice (or area of programming language - Perl has seen quite a few separate calendars for things like Perl 6, Catalyst, and even futures and promises). We now even have the Advent of Code which is a small advent calendar of programming puzzles that&#38;#39;s hugely popular (I, for my part, have never had time to do it since I was always spending time tweaking the Perl Advent Calendar at this time of year).&lt;/p&gt;

&lt;p&gt;Through the years the Advent Calendar became too much for me to manage on my own. I could no longer keep writing all twenty five articles myself, and so I started taking submissions. I eventually gave the calendar away for others to maintain, took it back again years later, and then finally gave it away again. Like any good project, it&#38;#39;s totally grown beyond me and has a life of its own.&lt;/p&gt;

&lt;p&gt;The crucial thing about the calendar - more important than any one article, any one year, or even any one programming language - is the idea that we can all enjoy learning about our favorite programming language. Things don&#38;#39;t have to be taught in boring instructions, but instead learning about something can be silly, daft, fun, and a treat! Why not have a story about how Santa&#38;#39;s elves are all Perl programmers faced with saving Christmas, and the only way to do it is to use this module? Why not have a tale about how we can control Christmas lights with Perl? Create Christmas memes? Many a clever person in the Perl community has talked about optimizing projects for fun - attracting volunteers by making projects enjoyable to be a part of - and the Perl Advent Calendar embraces this idea of rewarding everyone that reads it with joy. This popular format has been copied by other languages.&lt;/p&gt;

&lt;h3 id=&#34;A-Merry-Christmas-to-us-all-Bless-us-everyone&#34;&gt;A Merry Christmas to us all; Bless us, everyone&lt;/h3&gt;

&lt;p&gt;So, as I try to do every year, and on this twenty-fifth year of the Perl Advent Calendar, I wish you a Merry Christmas.&lt;/p&gt;

&lt;p&gt;Perl has this concept of &#38;quot;blessing&#38;quot; references to turn them into objects. One way to look at it is it&#38;#39;s Perl&#38;#39;s way of making something more than what it is - improving it, giving it powers and abilities it didn&#38;#39;t have before.&lt;/p&gt;

&lt;p&gt;So when I say &#38;quot;Bless us, everyone&#38;quot; I&#38;#39;d like you to think about how Perl has &#38;quot;bless&#38;quot;ed &lt;i&gt;everyone&lt;/i&gt;. Each programming language that has their own version of CPAN. Each programming language that has their own Advent Calendar. Everyone who&#38;#39;s written something daft just for the heck of it, and then shared it so people can learn. These are all things that Perl has helped bring to the world, and means that - even if you&#38;#39;ve never used Perl - you&#38;#39;re still a sort of Perl programmer. Because Perl is more than a language, a run-time - it&#38;#39;s about the people and ideas behind it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;merry&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$christmas&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;bless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$us&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Everyone&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2024-12-24T00:00:00Z</updated><category term="Perl"/><author><name>Mark Fowler</name></author></entry><entry><title>A New Logo for Perl</title><link href="https://perladvent.org/2024/2024-12-23.html"/><id>https://perladvent.org/2024/2024-12-23.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;logo.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;h3 id=&#34;The-Backstory-2020&#34;&gt;The Backstory (2020)&lt;/h3&gt;

&lt;p&gt;For many years, the question of what is or is not Perl&#38;#39;s logo has been quite murky. There is a fair amount of backstory and there is too much to dive into right here, but I will happily point you at Neil Bowers&#38;#39; tackling of the subject from 4 years ago: &lt;a href=&#34;https://neilb.org/2020/12/04/perl-and-camels.html&#34;&gt;https://neilb.org/2020/12/04/perl-and-camels.html&lt;/a&gt; It&#38;#39;s a quick, but informative read. I encourage you to take a peek.&lt;/p&gt;

&lt;p&gt;The TL;DR of it all is that Neil suggested:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;that Perl could adopt a new camel logo&#8212;separate from the one used on O&#39;Reilly&#39;s book cover&#8212;and use it as the official symbol for the Perl language.&lt;/li&gt; &lt;li&gt;the goal would be to create a logo that represents not just the language but also the community, one that feels friendly and welcoming.&lt;/li&gt; &lt;li&gt;He acknowledged that the &#34;camel association&#34; will always be tied to Perl, even if a new logo is chosen, but suggested this as a pragmatic approach&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;h3 id=&#34;The-Perl-Toolchain-Summit-2024&#34;&gt;The Perl Toolchain Summit (2024)&lt;/h3&gt;

&lt;p&gt;At the Perl Toolchain Summit in Lisbon this year, a small group of us had a chat about the logo (or non-logo) situation. The majority opinion was that someone should come up with a logo and present it for community use. Neil Bowers and I were nominated to take this on. Over the summer, Neil commissioned &lt;a href=&#34;https://www.zachroszczewski.com/&#34;&gt;Zach Roszczewski&lt;/a&gt; to come up with a new camel. Babs Veloso, Neil and I gave some initial feedback. After we were happy that we could proceed, we widened the circle of participants to a small, shadowy cabal, chatting on TPRF Slack. Among these were S&#38;eacute;bastien Feug&#38;egrave;re and Thibault Duponchelle, who had previous wrestled with the logo topic. Later, I participated in &lt;a href=&#34;https://blogs.perl.org/users/psc/2024/10/this-week-in-psc-163-2024-10-10.html&#34;&gt;a call with the PSC&lt;/a&gt;, where they gave some feedback on the logo. When I was at the London Perl and Raku Workshop this past October, S&#38;eacute;bastien, Thibault, Leo Lapworth and I got in a room together to discuss a path forward. We aimed to have a logo available for announcement as part of the Perl Advent Calendar, and here we are.&lt;/p&gt;

&lt;p&gt;Over the past week, we have polled various others for opinions on the logo and various versions of it. The result of all of this is the logo you see at the top of this article.&lt;/p&gt;

&lt;p&gt;Feedback on S&#38;eacute;bastien Feug&#38;egrave;re&#38;#39;s work was kindly given (in first name alphabetical order) by:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;Aristotle Pagaltzis&lt;/li&gt; &lt;li&gt;Bruno Meneguele&lt;/li&gt; &lt;li&gt;D Ruth Holloway&lt;/li&gt; &lt;li&gt;Dallas Hogan&lt;/li&gt; &lt;li&gt;Graham Knop&lt;/li&gt; &lt;li&gt;Kenta Kobayashi&lt;/li&gt; &lt;li&gt;Leo Lapworth&lt;/li&gt; &lt;li&gt;Makoto Nozaki&lt;/li&gt; &lt;li&gt;Marc Perry&lt;/li&gt; &lt;li&gt;Mohammad Anwar&lt;/li&gt; &lt;li&gt;Philippe Bruhat&lt;/li&gt; &lt;li&gt;Robert Spier&lt;/li&gt; &lt;li&gt;Thibault Duponchelle&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



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

&lt;p&gt;The license for this logo is CC-BY. That means that you are free to create derivative works based on the logo, with the only condition being attribution. We would love for this logo to be used widely, so we wanted to go with a permissive license.&lt;/p&gt;

&lt;h3 id=&#34;Who-Put-You-in-Charge&#34;&gt;Who Put You in Charge?&lt;/h3&gt;

&lt;p&gt;Nobody. In the absence of a benevolent dictator or a governing body for all of the Perl communities, nobody has a mandate to create a logo for the Perl language and all of the communities attached to it. Our only hope is that this logo is seen as a gift and that if you like it, you will use it.&lt;/p&gt;

&lt;p&gt;Traditionally the way that things work in our communities is that someone creates something in the hopes that it will be adopted by others. That may or may not happen, but the end result is entirely up to the various Perl communities. We hope that you like this logo. We hope that you use it. We are happy to work with you on using it, but we are not in charge of anything and it would be disingenuous to pretend it were otherwise.&lt;/p&gt;

&lt;h3 id=&#34;What-About-The-Perl-and-Raku-Foundation&#34;&gt;What About The Perl and Raku Foundation?&lt;/h3&gt;

&lt;p&gt;This logo is not affiliated with The Perl and Raku Foundation in any way. The foundation has its own logo (the onion), which can also be used under certain conditions. As far as I can tell, there is no appetite within the foundation to wade into the Perl logo debate. Having said that, an onion is not a camel, and I refer you to Neil&#38;#39;s blog post quoted above for an argument about why the logo really should just be a camel.&lt;/p&gt;

&lt;h3 id=&#34;Where-Can-I-Get-a-Copy-of-the-Logo&#34;&gt;Where Can I Get a Copy of the Logo?&lt;/h3&gt;

&lt;p&gt;The logo currently lives in the &lt;a href=&#34;https://github.com/metacpan/perl-assets&#34;&gt;metacpan/perl-assets repository&lt;/a&gt;. The repo is still a work in progress, but this is where you should be able to find static assets moving forward.&lt;/p&gt;

&lt;h3 id=&#34;Where-Can-I-Give-Feedback&#34;&gt;Where Can I Give Feedback?&lt;/h3&gt;

&lt;p&gt;Please &lt;a href=&#34;https://github.com/metacpan/perl-assets/issues&#34;&gt;open an issue&lt;/a&gt; if there&#38;#39;s something you&#38;#39;d like to discuss. Items worth discussing are:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;minor changes to the logo layout and look&lt;/li&gt; &lt;li&gt;general issues related to using the logo&lt;/li&gt; &lt;li&gt;offers of help&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;p&gt;Items not currently worth discussing:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt; &lt;li&gt;this should be something other than a camel&lt;/li&gt; &lt;li&gt;this requires a ground up redesign&lt;/li&gt; &lt;li&gt;I hate it&lt;/li&gt; &lt;/ul&gt;

&lt;/p&gt;



&lt;h3 id=&#34;Are-There-Other-Options&#34;&gt;Are There Other Options?&lt;/h3&gt;

&lt;p&gt;Yes, we would like you to have many different options when it comes to using the camel. I&#38;#39;m including some of them below.&lt;/p&gt;

&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;010.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;030.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;032.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;031.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;p&gt;&lt;div style=&#34;background-color: #003366; margin: 0 auto; text-align: center; max-width: 90%; padding: 20px;&#34;&gt; &lt;div style=&#34;max-width: 50%; margin: 0 auto;&#34;&gt; &lt;img src=&#34;033.png&#34; style=&#34;max-width: 100%;&#34;&gt; &lt;/div&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;h3 id=&#34;Who-Owns-this-Logo&#34;&gt;Who Owns this Logo?&lt;/h3&gt;

&lt;p&gt;Neil Bowers was the initial owner of the new camel. He has transferred the ownership of the original camel artwork to me. My preference would be to transfer ownership to MetaCPAN, but it&#38;#39;s not a legal entity, so I&#38;#39;m not entirely sure if that is possible. Since the license is CC-BY, I don&#38;#39;t actually know how much ownership matters, but for the time being, I am the camel&#38;#39;s steward.&lt;/p&gt;

&lt;h3 id=&#34;Whats-Next&#34;&gt;What&#38;#39;s Next?&lt;/h3&gt;

&lt;p&gt;We consider the logo to be production ready, but also a work in progress. We are in the process of approaching stakeholders and speaking to them about integrating the logo. In addition this, I expect the logo to be used in some way on both perl.com and metacpan.org.&lt;/p&gt;

&lt;p&gt;There are still some assets which need to be settled -- like a &lt;code&gt;favicon.ico&lt;/code&gt;, but if all goes well that should be taken care of in the very near future.&lt;/p&gt;

&lt;p&gt;It is my hope that the various Perl communities can standardize on this logo in the near term. This doesn&#38;#39;t have to be the forever logo for Perl, but just generally getting people on the same page would be an excellent start.&lt;/p&gt;

&lt;p&gt;If you think this logo is a good fit, please accept it as our gift to you. If it&#38;#39;s not a good fit for you, that&#38;#39;s ok too. TIMTOWTDI!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-23T00:00:00Z</updated><category term="Perl"/><author><name>Olaf Alders</name></author></entry><entry><title>use VERSION</title><link href="https://perladvent.org/2024/2024-12-22.html"/><id>https://perladvent.org/2024/2024-12-22.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;A-yearly-non-December-gift&#34;&gt;A yearly non-December gift&lt;/h3&gt;

&lt;p&gt;December is not the only season for gifts. Every year, sometime around the end of May, the Perl 5 Porters gift us with a new version of Perl. Or is it &lt;code&gt;perl&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;To quote &lt;a href=&#34;https://metacpan.org/module/perlfaq1#Whats-the-difference-between-perl-and-Perl&#34;&gt;&#38;quot;What&#38;#39;s the difference between &#38;quot;perl&#38;quot; and &#38;quot;Perl&#38;quot;?&#38;quot; in perlfaq1&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;

&lt;p&gt;&#38;quot;Perl&#38;quot; is the name of the language. Only the &#38;quot;P&#38;quot; is capitalized. The name of the interpreter (the program which runs the Perl script) is &#38;quot;perl&#38;quot; with a lowercase &#38;quot;p&#38;quot;.&lt;/p&gt;

&lt;p&gt;You may or may not choose to follow this usage. But never write &#38;quot;PERL&#38;quot;, because perl is not an acronym.&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;The version of &lt;code&gt;perl&lt;/code&gt; (the interpreter) is what you get when you type &lt;code&gt;perl -v&lt;/code&gt; on the command-line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl -v
    This is perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-gnu

    Copyright 1987-2024, Larry Wall

    Perl may be copied only under the terms of either the Artistic License or the
    GNU General Public License, which may be found in the Perl 5 source kit.

    Complete documentation for Perl, including FAQ lists, should be found on
    this system using &#38;quot;man perl&#38;quot; or &#38;quot;perldoc perl&#38;quot;.  If you have access to the
    Internet, point your browser at https://www.perl.org/, the Perl Home Page.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also run this (the interpreter version is available in two different formats, either as a floating-point number, or a v-string):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl -E &#38;#39;say for $], $^V&#38;#39;
    5.040000
    v5.40.0&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Whats-in-the-box-New-features&#34;&gt;What&#38;#39;s in the box? New features!&lt;/h3&gt;

&lt;p&gt;New versions of Perl come with bug fixes, speed improvements, deprecations and also new features. It&#38;#39;s still the same old Perl, that will continue to run your existing code. Perl version upgrades are so simple and drama-less they&#38;#39;re almost boring. (The Perl 5 Porters support the two most recent stable releases of Perl, which should be reason enough to upgrade your binary.)&lt;/p&gt;

&lt;p&gt;Long story short, Perl is extremely backwards compatible. This means that sensibly-written code from 20 years ago should still run under the next release of Perl. In other words, any &lt;code&gt;perl&lt;/code&gt; interpreter should understand code written against older versions of the Perl &lt;i&gt;language&lt;/i&gt; just fine. As I have personally experimented, this actually even applies to scripts targeting version 4 of the language!&lt;/p&gt;

&lt;p&gt;With such a strong commitment to backwards compatibility, how does one even introduce new features to the language? To quote from &lt;a href=&#34;https://metacpan.org/module/feature#DESCRIPTION&#34;&gt;&#38;quot;DESCRIPTION&#38;quot; in feature&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;

&lt;p&gt;It is usually impossible to add new syntax to Perl without breaking some existing programs. This pragma provides a way to minimize that risk.&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href=&#34;https://metacpan.org/module/feature&#34;&gt;feature&lt;/a&gt; module was introduced in Perl v5.10, to make it possible to introduce new features to the language without breaking existing scripts. A typical example would be the &lt;code&gt;say&lt;/code&gt; feature, which makes it possible to add the &lt;code&gt;say&lt;/code&gt; keyword to the language without breaking older scripts that might have defined a &lt;code&gt;sub say&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If one wanted to use the &lt;code&gt;say&lt;/code&gt; builtin, they would write:&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;feature&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;say&#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;Hello, world!&#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 of course, code that already has a &lt;code&gt;say&lt;/code&gt; subroutine defined would continue to work the same, as long as the feature is not enabled. This leaves time for the code&#38;#39;s author to look at their code and decide if they want to update it to take advantage of new features of the language.&lt;/p&gt;

&lt;p&gt;Note that enabling features always happens in the current lexical scope.&lt;/p&gt;

&lt;h4 id=&#34;The-gift-of-choice&#34;&gt;The gift of choice&lt;/h4&gt;

&lt;p&gt;Backwards-compatibility is preserved thanks to a compromise: people have to &lt;i&gt;opt in&lt;/i&gt; to the new features. The unfortunate side-effect is that Perl will continue to look the same (sometimes giving the feeling it&#38;#39;s stagnating) until you enable the new features!&lt;/p&gt;

&lt;p&gt;It should be noted, however, that not all Perl features are guarded by the &lt;code&gt;feature&lt;/code&gt; module. Syntax constructs that were syntax errors in previous versions of Perl can be introduced without a guard, and many were. The &lt;a href=&#34;https://metacpan.org/module/Syntax::Construct&#34;&gt;Syntax::Construct&lt;/a&gt; module has an exhaustive list of &#38;quot;syntactic constructs that are not implemented via the &lt;code&gt;feature&lt;/code&gt; pragma, but are still not compatible with older versions of Perl&#38;quot;.&lt;/p&gt;

&lt;p&gt;Perl v5.40 knows about 25 features. While they can be enabled or disabled (some features are used to disable undesirable constructs, like indirect object notation) one by one, there is a better way than 25 lines of boilerplate.&lt;/p&gt;

&lt;h3 id=&#34;use-VERSION&#34;&gt;&lt;code&gt;use VERSION&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;A special case of the &lt;code&gt;use&lt;/code&gt; builtin is &lt;code&gt;use VERSION&lt;/code&gt;. It performs several operations:&lt;/p&gt;

&lt;h4 id=&#34;Enforce-running-with-a-minimum-version-of-the-perl-binary&#34;&gt;Enforce running with a minimum version of the perl binary&lt;/h4&gt;

&lt;p&gt;The first thing that &lt;code&gt;use VERSION&lt;/code&gt; does is to declare which minimum version of &lt;code&gt;perl&lt;/code&gt; (the interpreter) you expect to run your code. If you demand a version later than that of the perl binary currently running your code, it&#38;#39;s going to die at compile time.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    $ perl -Mv5.38 -e1
    Perl v5.38.0 required--this is only v5.36.0, stopped.
    BEGIN failed--compilation aborted.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Although the version in &lt;code&gt;use VERSION&lt;/code&gt; can be written in either v-string style (&lt;code&gt;v5.36&lt;/code&gt;) or numeric style (&lt;code&gt;5.036&lt;/code&gt;), it is strongly recommended to use the former, as it&#38;#39;s more readable and matches with the way people talk about Perl versions. (Unless the code is expected to actually be run on a &lt;code&gt;perl&lt;/code&gt; older than v5.8, which was released in 2002).&lt;/p&gt;

&lt;h4 id=&#34;Load-the-corresponding-feature-bundle&#34;&gt;Load the corresponding feature bundle&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;feature&lt;/code&gt; module also defines &#38;quot;feature bundles&#38;quot;, which allows to load (or disable) multiple features together. (See &lt;a href=&#34;https://metacpan.org/module/feature#FEATURE-BUNDLES&#34;&gt;&#38;quot;FEATURE-BUNDLES&#38;quot; in feature&lt;/a&gt; for their definitions.)&lt;/p&gt;

&lt;p&gt;The special &lt;code&gt;use VERSION&lt;/code&gt; construct will implicitly load the corresponding feature bundle. For example:&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;version&#34;&gt;v5.10&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will implicitly load the corresponding feature bundle:&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;feature&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;:5.10&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will enable the &lt;code&gt;say&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;switch&lt;/code&gt; features. The parser will now understand the corresponding keywords, and behave accordingly. Error messages might differ a lot when a feature is enabled or not.&lt;/p&gt;

&lt;p&gt;Note that adding a subversion (e.g. &lt;code&gt;use v5.36.3&lt;/code&gt;) will have no effect on the bundles loaded (feature bundles are guaranteed to be the same for all sub-versions). It will of course have an effect on the interpreter version check described above.&lt;/p&gt;

&lt;p&gt;In addition to the implicit loading of features, &lt;code&gt;use v5.12&lt;/code&gt; or greater will enable &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;use v5.36&lt;/code&gt; will enable &lt;code&gt;warnings&lt;/code&gt; and &lt;code&gt;use v5.40&lt;/code&gt; will import &lt;a href=&#34;https://metacpan.org/module/builtin&#34;&gt;builtin&lt;/a&gt; functions (using a similar version bundle scheme: see &lt;a href=&#34;https://metacpan.org/module/builtin#Version-Bundles&#34;&gt;&#38;quot;Version-Bundles&#38;quot; in builtin&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;use VERSION&lt;/code&gt; is a lexical pragma, meaning that you could declare different version bundles for different parts of your code. For consistency, it&#38;#39;s really recommended that you pick one version, and stick with it for the entire file. In fact, a future release of &lt;code&gt;perl&lt;/code&gt; will disallow changing the version once one has been declared. So, really, don&#38;#39;t do that.&lt;/p&gt;

&lt;p&gt;The features included in bundles fall in two broad categories: new features, and deprecated features. Loading a bundle will perform the following two operations in a single line of code:&lt;/p&gt;

&lt;h5 id=&#34;Enable-modern-Perl-features&#34;&gt;Enable modern Perl features&lt;/h5&gt;

&lt;p&gt;Before features become part of the &#38;quot;official&#38;quot; language, they are often introduced as experiments. Experimental features are available only when requested via &lt;code&gt;use feature&lt;/code&gt;, and aren&#38;#39;t part of a bundle. In fact, they&#38;#39;ll even issue warnings when you use them. (See &lt;a href=&#34;https://metacpan.org/module/experimental&#34;&gt;experimental&lt;/a&gt; for more about this.)&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;signatures&lt;/code&gt; feature was introduced in &lt;code&gt;perl&lt;/code&gt; v5.20, and remained experimental until v5.34. That meant that, as long as the &lt;code&gt;perl&lt;/code&gt; binary was more recent than v5.20, one could use signatures in their code with &lt;code&gt;use feature &#38;#39;signatures&#38;#39;&lt;/code&gt;. That specific feature has been added to all bundles since &lt;code&gt;:5.36&lt;/code&gt;, which means that a single declaration (&lt;code&gt;use v5.36&lt;/code&gt;, or any later version) allows one to write Perl subroutines using signatures.&lt;/p&gt;

&lt;p&gt;Not all new features have to wait that long to become part of the language: &lt;code&gt;module_true&lt;/code&gt; was introduced in &lt;code&gt;perl&lt;/code&gt; v5.38, and was immediately added to the &lt;code&gt;:5.38&lt;/code&gt; feature bundle.&lt;/p&gt;

&lt;h5 id=&#34;Deprecate-discouraged-features&#34;&gt;Deprecate discouraged features&lt;/h5&gt;

&lt;p&gt;Bundles have also been used to disable features that have become discouraged. These are made part of the &lt;code&gt;:default&lt;/code&gt; feature bundle, and not included in later bundles.&lt;/p&gt;

&lt;p&gt;Doing it this way makes it possible to preserve the behaviour of ancient, unmaintained scripts and modules. Even if they load some feature bundle (via &lt;code&gt;use VERSION&lt;/code&gt;), any discouraged feature they might use will also be (retroactively) included in that bundle, preserving backward compatibitlity.&lt;/p&gt;

&lt;p&gt;This is how the features &lt;code&gt;indirect&lt;/code&gt;, &lt;code&gt;multidimensional&lt;/code&gt; and &lt;code&gt;bareword_filehandles&lt;/code&gt; came to be &#38;quot;removed&#38;quot; from later versions of Perl.&lt;/p&gt;

&lt;p&gt;This is the strategy the &lt;a href=&#34;https://metacpan.org/module/perlgov#The-Steering-Council&#34;&gt;Perl Steering Council&lt;/a&gt; has chosen to best stretch the language between the continuous introduction of new features and the preservation of backwards compatibility. For the record, many Perl 4 (which is functionally identical to Perl 3, from late 1989) scripts still run fine with &lt;code&gt;perl&lt;/code&gt; v5.40.&lt;/p&gt;

&lt;h3 id=&#34;Why-pick-a-Perl-version&#34;&gt;Why pick a Perl version?&lt;/h3&gt;

&lt;h4 id=&#34;Line-0-semantics&#34;&gt;Line 0 semantics&lt;/h4&gt;

&lt;p&gt;When &lt;code&gt;perl&lt;/code&gt; compiles Perl code, before it even reads the first byte of source code, it is in &#38;quot;Perl v5.8 compatibility mode&#38;quot;. The &lt;code&gt;:default&lt;/code&gt; bundle is implicitly loaded (it only contains features that are backward-compatible with traditional Perl).&lt;/p&gt;

&lt;p&gt;This means that the &#38;quot;sensibly-written code from 20 years ago&#38;quot; mentioned above is very likely to just run unmodified, and simply work.&lt;/p&gt;

&lt;p&gt;As explained above, unguarded syntactic features are available from line 0.&lt;/p&gt;

&lt;h4 id=&#34;Line-1-semantics&#34;&gt;Line 1 semantics&lt;/h4&gt;

&lt;p&gt;We&#38;#39;ve seen that &lt;code&gt;use VERSION&lt;/code&gt; automatically loads the corresponding feature bundle (and associated builtins). This single line of code enables all official features and disables all deprecated features for the given version of the Perl language.&lt;/p&gt;

&lt;p&gt;In other words, putting a &lt;code&gt;use VERSION&lt;/code&gt; line at the top (line 1) of a Perl script or module &lt;i&gt;declares&lt;/i&gt; which version of the &lt;i&gt;Perl language&lt;/i&gt; the code that follows is written under. And because the effect is lexical, a script can load other modules that declare they were written against a different version of the language, and everything works transparently.&lt;/p&gt;

&lt;p&gt;I strongly believe that &lt;code&gt;use VERSION&lt;/code&gt; should be the &lt;i&gt;first line&lt;/i&gt; of any Perl code.&lt;/p&gt;

&lt;h4 id=&#34;Declaring-a-baseline-of-the-Perl-language&#34;&gt;Declaring a baseline of the Perl language&lt;/h4&gt;

&lt;p&gt;For decades, the first recommendation made to Perl beginners and people asking for help on a Perl forum was to add &lt;code&gt;use strict; use warnings&lt;/code&gt; at the beginning of their code. This made Perl more helpful, as it would point to likely errors in programming (like undeclared variables or undefined values).&lt;/p&gt;

&lt;p&gt;Since Perl v5.36, these two statements are implicitly included via &lt;code&gt;use VERSION&lt;/code&gt;. It also enables the official features of that version of the language, and disables the deprecated ones.&lt;/p&gt;

&lt;p&gt;Therefore, &lt;code&gt;use VERSION&lt;/code&gt; helps define a good &lt;i&gt;baseline&lt;/i&gt; for the Perl language, so that the compiler can fully understand the code that follows. It&#38;#39;s of course possible to include experimental features, or re-enable deprecated features (the latter is really not recommended), to fine-tune the specific dialect of Perl in which the code is written.&lt;/p&gt;

&lt;p&gt;Future versions of &lt;code&gt;perl&lt;/code&gt; implicitly promise that they will understand the dialect of Perl declared by &lt;code&gt;use VERSION&lt;/code&gt;, and that they will run it to the best of their abilities.&lt;/p&gt;

&lt;h3 id=&#34;The-Once-and-Future-Perl&#34;&gt;The Once and Future Perl&lt;/h3&gt;

&lt;h4 id=&#34;The-new-use-strict-and-warnings&#34;&gt;The new &#38;quot;use strict and warnings&#38;quot;&lt;/h4&gt;

&lt;p&gt;Since the time of &#38;quot;use strict and warnings&#38;quot; as minimum requirements for decent Perl code, the language has evolved, and brought in more useful features. It also deprecated ancient features that were deemed bad ideas in a modern context (such as the indirect object notation).&lt;/p&gt;

&lt;p&gt;Writing &lt;code&gt;use VERSION&lt;/code&gt; as the first (active) line of any Perl code will &lt;i&gt;declare&lt;/i&gt; to the &lt;code&gt;perl&lt;/code&gt; interpreter (and to whoever is reading the code) which version of the Perl &lt;i&gt;language&lt;/i&gt; the code that follows is written in.&lt;/p&gt;

&lt;p&gt;So, just put a &lt;code&gt;use VERSION&lt;/code&gt; line at the top of all your Perl files.&lt;/p&gt;

&lt;p&gt;Whenever a new stable version is released (which happens every year towards the end of May), you should at least read the &lt;a href=&#34;https://metacpan.org/module/perldelta&#34;&gt;perldelta&lt;/a&gt; that accompanies it, and check if you see some new feature you think you&#38;#39;d want to use.&lt;/p&gt;

&lt;p&gt;There might exist some compatibility modules that you can use, to get a taste of those new features on a version of &lt;code&gt;perl&lt;/code&gt; that doesn&#38;#39;t support them natively. They might behave slightly differently, though.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;v5.36&lt;/code&gt; (released in May 2022) contains a very good mix of stable features (default &lt;code&gt;strict&lt;/code&gt; and &lt;code&gt;warnings&lt;/code&gt;, &lt;code&gt;signatures&lt;/code&gt;, &lt;code&gt;isa&lt;/code&gt;), as well as the removal of deprecated features (&lt;code&gt;indirect&lt;/code&gt;, &lt;code&gt;multidimensional&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Most Linux distributions released in 2024 include a version of Perl that will support &lt;code&gt;use v5.36&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&#34;Picking-which-version-of-Perl-to-code-in&#34;&gt;Picking which version of Perl to code in&lt;/h3&gt;

&lt;p&gt;The version of the Perl language you want to use will depend on the context in which the code is run. Private or proprietary code has different constraints than an Open Source project or library distributed on CPAN.&lt;/p&gt;

&lt;p&gt;Private or company code is only limited by the version of Perl used internally. It might be the stock &lt;code&gt;perl&lt;/code&gt; from the operating system selected by the organization. It might be a &lt;code&gt;perl&lt;/code&gt; compiled specifically for that purpose. Internal company or personal code can run on the bleeding edge!&lt;/p&gt;

&lt;p&gt;An Open Source project will usually be shipped with or installed on top of common operating systems, and will usually be tied to the version of &lt;code&gt;perl&lt;/code&gt; these operating systems package.&lt;/p&gt;

&lt;p&gt;The authors of modules distributed via CPAN distributions might want their code run on a broader selection of &lt;code&gt;perl&lt;/code&gt; versions.&lt;/p&gt;

&lt;p&gt;However, you&#38;#39;re unlikely to be able to start writing code with the latest version of the language very quickly:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;maybe you&#38;#39;re stuck with the version released by your OS vendor (but, do you know how easy it is to compile your own Perl?);&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;maybe you compile your own Perl, but it&#38;#39;s a core part of your infrastructure, and upgrading takes time; (although the Perl parts are likely to be the easiest ones, given Perl&#38;#39;s track record with backward and forward compatibility)&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;maybe you&#38;#39;re a CPAN author, and you still want to support older versions of Perl.&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;Updating-your-Perl-code-at-your-own-pace&#34;&gt;Updating your Perl code at your own pace&lt;/h4&gt;

&lt;p&gt;At work, across all of our tens of thousands of Perl modules, over 30 different &lt;code&gt;VERSION&lt;/code&gt; are declared with &lt;code&gt;use VERSION&lt;/code&gt;. From v5.1 (someone typoed 5.010 as 5.001) up to v5.36, going through v5.10, v5.18.2 (someone didn&#38;#39;t know the sub-version is ignored), v5.24, etc. Many files don&#38;#39;t have a &lt;code&gt;use VERSION&lt;/code&gt; line (using the Perl flavor of 2002, when our Perl code base was started). The word for this is &#38;quot;legacy&#38;quot;.&lt;/p&gt;

&lt;p&gt;Since the effect of &lt;code&gt;use VERSION&lt;/code&gt; is lexical, it&#38;#39;s possible to upgrade the version of the language your code uses &lt;i&gt;one file at a time&lt;/i&gt; (or even one scope at a time, but see &lt;a href=&#34;https://metacpan.org/module/perl5400delta#Restrictions-to-use-VERSION-declarations&#34;&gt;&#38;quot;Restrictions to use VERSION declarations&#38;quot; in perl5400delta&lt;/a&gt; for why you&#38;#39;ll probably want to stick with the whole file).&lt;/p&gt;

&lt;p&gt;I can confirm it&#38;#39;s really nice to be able to first upgrade the Perl binary without changing a single line of code, and &lt;i&gt;then&lt;/i&gt; choose which files to upgrade first.&lt;/p&gt;

&lt;p&gt;That transition can be difficult, though:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;v5.28 subtly changed the meaning of &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&#38;amp;&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;~&lt;/code&gt; (the &lt;code&gt;bitwise&lt;/code&gt; feature is enabled)&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;v5.36 won&#38;#39;t understand &lt;code&gt;$fido = new Camel &#38;quot;Amelia&#38;quot;&lt;/code&gt; (the &lt;code&gt;indirect&lt;/code&gt; feature is disabled)&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;v5.36 will complain about &lt;code&gt;sub foo ($$)&lt;/code&gt; (the &lt;code&gt;signatures&lt;/code&gt; feature is enabled, prototypes must be declared as an attribute)&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;v5.38 will complain about &lt;code&gt;open FH, $file&lt;/code&gt; (the &lt;code&gt;bareword_filehandles&lt;/code&gt; feature is disabled)&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Picking exactly which version of the Perl &lt;a href=&#34;https://metacpan.org/module/language&#34;&gt;language&lt;/a&gt; to use is the actual question one has to answer. And the answer will be different if one is a CPAN author, a hobbyist writing their own tools, an Open Source application developer or a developer for a company&#38;#39;s web application or internal tooling. The answer depends on several factors: one of them is the programmer&#38;#39;s desire to use recent Perl language features, and another is their expectations regarding the minimum version of the &lt;code&gt;perl&lt;/code&gt; &lt;i&gt;binary&lt;/i&gt; the code is expected to run on.&lt;/p&gt;

&lt;h4 id=&#34;Updating-your-Perl-code-faster&#34;&gt;Updating your Perl code faster&lt;/h4&gt;

&lt;p&gt;With tens of thousands of files to potentially update, I didn&#38;#39;t imagine for one second that I would do it manually. And even if my colleagues would also help, I knew that upgrading the &lt;code&gt;use VERSION&lt;/code&gt; line in their code would make some of them uneasy.&lt;/p&gt;

&lt;p&gt;So I wrote a module that would take Perl code, statically analyze it using &lt;a href=&#34;https://metacpan.org/module/PPI&#34;&gt;PPI&lt;/a&gt;, and bump the declared version to the requested one, while being extra careful about the issues detailed above (and others). Since that code contained no company secret, I was allowed to open source it.&lt;/p&gt;

&lt;p&gt;The code now lives on CPAN as &lt;a href=&#34;https://metacpan.org/module/Perl::Version::Bumper&#34;&gt;Perl::Version::Bumper&lt;/a&gt;. And has improved a lot since my last commit in the company repository. I&#38;#39;ve actually deleted the code since, and we now depend on the CPAN module.&lt;/p&gt;

&lt;p&gt;Here are a few examples of what it does, assuming we want to bump the example code to v5.40:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;simply bump the version number:&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;Hello, world!\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;becomes:&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;version&#34;&gt;v5.40&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;Hello, world!\n&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;remove compatibility modules that become unnecessary once the corresponding feature is enabled by the bundle:&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;Say::Compat&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;Hello, world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;becomes:&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;version&#34;&gt;v5.40&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;Hello, world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;remove warnings about experimental signatures, once they come out of experimental:&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;version&#34;&gt;v5.20&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;feature&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;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;single&#34;&gt;&#39;experimental::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;greeting&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$who&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;double&#34;&gt;&#38;quot;Hello, $who!&#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;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;greeting&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;becomes:&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;version&#34;&gt;v5.40&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;greeting&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$who&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;double&#34;&gt;&#38;quot;Hello, $who!&#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;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;greeting&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;rewrites prototypes when enabling the &lt;code&gt;signatures&lt;/code&gt; feature:&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;version&#34;&gt;v5.10&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;greeting&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;( $ )&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;sprintf&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, %s!&#38;quot;&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;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;greeting&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;becomes:&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;version&#34;&gt;v5.40&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;greeting&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;attribute&#34;&gt;prototype( $ )&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;sprintf&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, %s!&#38;quot;&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;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;greeting&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;world&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;disable features that might cause problems, and add a warning about them:&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;version&#34;&gt;v5.10&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;hello, world!&#38;quot;&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;span class=&#34;comment&#34;&gt;# flip the capital bit on the first letter&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;becomes:&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;version&#34;&gt;v5.40&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;# IMPORTANT: Please double-check the use of bitwise operators&lt;br /&gt;# before removing the `no feature &#39;bitwise&#39;;` line below.&lt;br /&gt;# See manual pages &#39;feature&#39; (section &#38;quot;The &#39;bitwise&#39; feature&#38;quot;)&lt;br /&gt;# and &#39;perlop&#39; (section &#38;quot;Bitwise String Operators&#38;quot;) for details.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;no&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;feature&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;bitwise&#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;hello, world!&#38;quot;&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;span class=&#34;comment&#34;&gt;# flip the capital bit on the first letter&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In safe mode, stop at the last version that compiles (v5.38 disabled the &lt;code&gt;bareword_filehandles&lt;/code&gt; feature, turning their use into a compile-time error):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;FH&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;greeting.txt&#39;&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;Can&#39;t open file: $!&#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;word&#34;&gt;FH&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, world!\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;becomes:&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;version&#34;&gt;v5.36&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;FH&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;greeting.txt&#39;&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;Can&#39;t open file: $!&#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;word&#34;&gt;FH&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Hello, world!\n&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the module is meant to upgrade older Perl code, I made sure it can run on &lt;code&gt;perl&lt;/code&gt; binaries as old as v5.10. And it can use &lt;code&gt;perl5.10.0&lt;/code&gt; to upgrade source code up to v5.40!&lt;/p&gt;

&lt;p&gt;The distribution contains a small command-line utility to process many files at a time: &lt;a href=&#34;https://metacpan.org/dist/Perl-Version-Bumper/view/bin/perl-version-bump&#34;&gt;perl-version-bump&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It runs in safe mode by default, meaning it will start at the version of the &lt;code&gt;perl&lt;/code&gt; used to run it, and decrease the target version number until the generated code compiles, or give up.&lt;/p&gt;

&lt;p&gt;If I can suggest some New Year Resolutions for 2025:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;decide which version of the Perl &lt;i&gt;language&lt;/i&gt; you want to code against in your various projects,&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;start consistently adding &lt;code&gt;use VERSION&lt;/code&gt; on line 1 of all your Perl files,&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and bump the existing versions where it makes sense!&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;</summary><updated>2024-12-22T00:00:00Z</updated><category term="Perl"/><author><name>Philippe Bruhat</name></author></entry><entry><title>That Time Perl+OpenMP Saved Christmas</title><link href="https://perladvent.org/2024/2024-12-21.html"/><id>https://perladvent.org/2024/2024-12-21.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;div style=&#34;text-align: center;&#34;&gt; &lt;img src=&#34;magi-camel-gifts-perl.jpg&#34;&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;p&gt;It was Christmas Eve, and Santa was facing a nightmare. The sleigh&#38;rsquo;s new GPS system, upgraded for the first time in centuries from following the Star of Bethlehem, was malfunctioning. As Santa checked the screen, the map was a chaotic mess &#38;mdash; locations were wrong, some coordinates did not make sense, and the sleigh was heading straight into the mountains instead of over the City of New Orleans!&lt;/p&gt;

&lt;p&gt;Santa&#38;#39;s CURRENT position read-out indicated he was over the Appalachians at the following coordinates:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  Sleigh Latitude  Sleigh Longitude  Sleigh Altitude (ft)
       38.0000&#38;deg; N        -81.500&#38;deg; W              300&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When he should be nearly exactly at,&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  Sleigh Latitude  Sleigh Longitude  Sleigh Altitude (ft)
       29.9500&#38;deg; N        -90.070&#38;deg; W              300&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;We can&#38;#39;t afford this!&#38;quot; Santa shouted. &#38;quot;We&#38;rsquo;ve got billions of presents to deliver, and the system&#38;#39;s down. And I want some gumbo!&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Santa, we can fix it,&#38;quot; Jingles, the lead elf, said with a frantic smile. &#38;quot;We just need more computing power. If we use OpenMP, we can parallelize the calculations and solve this quickly.&#38;quot;&lt;/p&gt;

&lt;p&gt;Santa stared. &#38;ldquo;What are you talking about, Jingles? I&#38;#39;m a giant elf, not a Scottish engineer on a television show for nerds!&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;We&#38;rsquo;ll solve the GPS problem by recalculating the distances to each delivery location. The sleigh&#38;rsquo;s GPS relies on triangulating data from multiple satellites. Right now, it&#38;#39;s too slow to process the data for each location one by one. We can use OpenMP to divide the problem into smaller parts, calculate distances in parallel, and get this fixed fast!&#38;rdquo;&lt;/p&gt;

&lt;p&gt;The data format of the positional satellites consisted of their positions in latitude, longitude, with an altitude; so the computations are necessarily in 3D space, and could contain any number of lines:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  39.2497677581748 -66.1173923826129 29161.8658117126
  -39.9677413540029 -41.3796046007432 23577.9949741844
  82.2366387689737 153.417562140013 20022.1066827945
  -43.4383552881127 -44.5406041422343 28011.9118605715
  14.0767035175103 4.23608833137735 27766.7951824014
  40.8573795733001 162.321349651587 25625.9162363042
  -26.9656081428904 27.0935089406365 23681.3611776769
  70.1644045619636 6.85910122836034 26946.7435635683
  -64.5469805915126 -14.9091572762404 24893.2320145114
  80.875127931392 -109.736894500449 23367.1123572306
  5.62494420727084 -70.3599057022578 24677.4930437516
  -78.594647140356 -69.1886836681495 21775.1041983417
  -20.7093134304093 50.3824566178804 28396.5251214701
  -8.19130244183 28.3379349990834 24113.3081697615
  -62.1626942859846 -165.892484372947 27881.1415552865
  -39.1505435434735 -14.0167682855066 25391.598017652
  -14.701859640773 33.3797684668173 28958.4392020613
  22.7094543766397 28.9295184727116 29847.2350918171
  58.9987788505186 -87.6847921052664 29544.1317147911
  83.6874173858257 -149.058764882263 22417.9224396509
  -8.01336267852399 97.8876777856595 27879.4674084787
  -55.3867650906297 -107.353651427755 21389.9702111095
  -78.8588152992001 -155.558248147836 25430.0402744441
  9.88995820014878 -0.204367766261981 20832.5618074863
  -76.8565255868645 -14.4804333171123 27013.5287117141
  -0.16890065869049 -40.7974093702016 22440.2960018416
  8.56759320194605 14.0242190926548 24229.1350707098
  89.3116725410715 19.3710347706399 28181.9446348641
  ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The distance formula was computationally expensive, involving square roots and trigonometry. But by using OpenMP, the task could be split into multiple OS threads, each calculating the distance to one satellite at the same time. The results would then be aggregated, and the sleigh&#38;#39;s GPS would know exactly where to direct its heading.&lt;/p&gt;

&lt;p&gt;The code would parallelize the work by dividing the list of satellites among a number of &lt;i&gt;pthreads&lt;/i&gt; using OpenMP&#38;#39;s &lt;code&gt;#pragma omp for&lt;/code&gt; construct! &lt;code&gt;OpenMP::Simple&lt;/code&gt; handles the required &lt;code&gt;#include &#38;lt;omp.h&#38;gt;&lt;/code&gt; and makes it easy to query the environment for information, such as &lt;code&gt;OMP_NUM_THREADS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Jingles quickly typed up a solution in Perl, his favorite programming language. The solution was simple but critically offloaded the triangulation computations to &lt;code&gt;Inline::C&lt;/code&gt; code containing OpenMP directives, using the &lt;code&gt;OpenMP&lt;/code&gt; module on &lt;i&gt;CPAN&lt;/i&gt;.&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;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;OpenMP&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;Inline&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;C&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#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;with&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw/OpenMP::Simple/&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;$omp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;OpenMP&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;symbol&#34;&gt;$omp&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;omp_num_threads&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;OMP_NUM_THREADS&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;number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# accept number of threads via commandline&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Sleigh&#39;s CURRENT position (latitude, longitude, altitude)&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_lat&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;38.0000&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;$sleigh_lon&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;-81.500&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;$sleigh_alt&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;300.0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# in meters&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Satellite positions in tab-delimited format&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@satellites&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;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;./satellite-data.txt&#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&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;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;$line&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;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$line&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;push&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@satellites&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;split&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;match&#34;&gt;/[\s\t]+/&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$line&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;# Function to calculate distance from sleigh to each satellite using OpenMP::Simple,&lt;br /&gt;# a subclass of Inline::C!&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$distances&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;calculate_distances&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$sleigh_lat&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_lon&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_alt&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;symbol&#34;&gt;@satellites&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;# Print the calculated distances&lt;br /&gt;&lt;/span&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;$distance&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;$distances&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;double&#34;&gt;&#38;quot;Distance: $distance meters\n&#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;Santa watched as Jingles split up the computational load. Each satellite&#38;rsquo;s data was handled in parallel by different threads, and within seconds, the recalculations were done. The GPS latency issues were fixed. Jingles already had some thoughts of improvements he could make using OpenMP&#38;#39;s ability to target GPUs directly, but was quite happy with the current resolution.&lt;/p&gt;

&lt;p&gt;Just as the final calculations finished, a gentle voice spoke, &#38;ldquo;When Faith sanctifies morally neutral technology, Good things are possible at Internet scale.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;Santa turned to see the Holy Family - Baby Jesus in the arms of His Virgin Mother accompanied by His foster father, Joseph; by the sleigh, smiling serenely. &#38;ldquo;Thank you,&#38;rdquo; Santa whispered. &#38;ldquo;Merry Christmas.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;With the sleigh back on track, Santa soared into the night sky.&lt;/p&gt;

&lt;p&gt;When things settled down, Santa was able to look at Jingles&#38;#39; full code, and it was something to behold!&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;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;OpenMP&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;Inline&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;C&lt;/span&gt;    &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#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;with&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw/OpenMP::Simple/&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;$omp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;OpenMP&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;symbol&#34;&gt;$omp&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;omp_num_threads&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;span class=&#34;operator&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# accept number of threads via commandline&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Sleigh&#39;s CURRENT position (latitude, longitude, altitude)&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_lat&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;38.0000&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;$sleigh_lon&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;-81.500&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;$sleigh_alt&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;300.0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# in meters&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Satellite positions in tab-delimited format&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@satellites&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;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;./satellite-data.txt&#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;die&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;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;$line&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;&lt;span class=&#34;word&#34;&gt;chomp&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$line&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;push&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@satellites&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;split&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;match&#34;&gt;/[\s\t]+/&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$line&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;# Function to calculate distance from sleigh to each satellite using Inline::C&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$distances&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;calculate_distances&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$sleigh_lat&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_lon&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh_alt&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;symbol&#34;&gt;@satellites&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;# Print the calculated distances&lt;br /&gt;&lt;/span&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;$distance&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;$distances&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;double&#34;&gt;&#38;quot;Distance: $distance meters\n&#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;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;__C__&lt;br /&gt;&lt;br /&gt;#include &#38;lt;math.h&#38;gt;&lt;br /&gt;&lt;br /&gt;// Function to convert geographic coordinates to Cartesian (x, y, z)&lt;br /&gt;void geo_to_cartesian(double lat, double lon, double alt, double *x, double *y, double *z) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;double R = 6371000;  // Earth&#39;s radius in meters&lt;br /&gt;&#38;nbsp;&#38;nbsp;double lat_rad = lat * M_PI / 180.0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;double lon_rad = lon * M_PI / 180.0;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;*x = (R + alt) * cos(lat_rad) * cos(lon_rad);&lt;br /&gt;&#38;nbsp;&#38;nbsp;*y = (R + alt) * cos(lat_rad) * sin(lon_rad);&lt;br /&gt;&#38;nbsp;&#38;nbsp;*z = (R + alt) * sin(lat_rad);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Function to calculate Euclidean distance between two points (x1, y1, z1) and (x2, y2, z2)&lt;br /&gt;double calculate_distance(double x1, double y1, double z1, double x2, double y2, double z2) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2) + pow(z2 - z1, 2));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* C function parallelized with OpenMP */&lt;br /&gt;&lt;br /&gt;// Main function to calculate the distances from the sleigh to each satellite&lt;br /&gt;AV* calculate_distances(double sleigh_lat, double sleigh_lon, double sleigh_alt, AV *satellites) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int satellite_count = av_len(satellites) + 1;  // The number of satellites&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;AV* distances = newAV();  // Create a new Perl array (AV) to hold the distances&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double sleigh_x, sleigh_y, sleigh_z;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Convert sleigh&#39;s geographic coordinates to Cartesian coordinates&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;geo_to_cartesian(sleigh_lat, sleigh_lon, sleigh_alt, &#38;amp;sleigh_x, &#38;amp;sleigh_y, &#38;amp;sleigh_z);&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Fetch satellite data into local arrays&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double *sat_latitudes = malloc(satellite_count * sizeof(double));&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double *sat_longitudes = malloc(satellite_count * sizeof(double));&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double *sat_altitudes = malloc(satellite_count * sizeof(double));&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Populate satellite data into local arrays (from the Perl array)&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (int i = 0; i &#38;lt; satellite_count; i++) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;AV *satellite = (AV*) SvRV(*av_fetch(satellites, i, 0));  // Fetch the satellite at index i&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sat_latitudes[i] = ((double) SvNV(*av_fetch(satellite, 0, 0)));  // Latitude&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sat_longitudes[i] = ((double) SvNV(*av_fetch(satellite, 1, 0)));  // Longitude&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sat_altitudes[i] = ((double) SvNV(*av_fetch(satellite, 2, 0)));  // Altitude&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;// Declare a temporary array to hold distances for each thread&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double *_distances = malloc(satellite_count * sizeof(double));&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;PerlOMP_GETENV_BASIC // read common environmental variables, provided by OpenMP::Simple&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Start parallel region&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;#pragma omp parallel shared(_distances)&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Parallel for loop to compute distances in parallel&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;#pragma omp for&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (int i = 0; i &#38;lt; satellite_count; i++) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;_distances[i] = 0.0;  // Initialize&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;double sat_x, sat_y, sat_z;&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;// Convert satellite&#39;s geographic coordinates to Cartesian coordinates&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;geo_to_cartesian(sat_latitudes[i], sat_longitudes[i], sat_altitudes[i], &#38;amp;sat_x, &#38;amp;sat_y, &#38;amp;sat_z);&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;// Calculate the distance from the sleigh to this satellite&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;_distances[i] = calculate_distance(sleigh_x, sleigh_y, sleigh_z, sat_x, sat_y, sat_z);&lt;br /&gt;&#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;// Combine the results from all threads into the main distances array inside the parallel region&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (int i = 0; i &#38;lt; satellite_count; i++) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;av_push(distances, newSVnv(_distances[i]));&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;// Free the private distance arrays and satellite data arrays&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;free(_distances);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;free(sat_latitudes);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;free(sat_longitudes);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;free(sat_altitudes);&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;// Return the AV containing the distances to Perl&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return distances;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;__END__&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;See More:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/pod/OpenMP&#34;&gt;https://metacpan.org/pod/OpenMP&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/pod/OpenMP::Simple&#34;&gt;https://metacpan.org/pod/OpenMP::Simple&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/pod/OpenMP::Environment&#34;&gt;https://metacpan.org/pod/OpenMP::Environment&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;Everyone is welcome to join the Perl+OpenMP Project at &lt;a href=&#34;https://github.com/Perl-OpenMP/&#34;&gt;https://github.com/Perl-OpenMP/&lt;/a&gt;!&lt;/dt&gt;
&lt;dd&gt;

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

&lt;/div&gt;</summary><updated>2024-12-21T00:00:00Z</updated><category term="Perl"/><author><name>oodler</name></author></entry><entry><title type="html">&#38;quot;Investigating a new respiratory virus outbreak in Santa&#38;#39;s workshop&#38;quot;</title><link href="https://perladvent.org/2024/2024-12-20.html"/><id>https://perladvent.org/2024/2024-12-20.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Christmas eve was quickly approaching, and Santa was trying to decide whether to purchase masks for all of the reindeer again this year (he was aware that there were documented cases of SARS-CoV-2 infecting White-Tail Deer (O. virginianaus), and he knew that Reindeer (R. tarandus) were closely related), when the Chief Elf interrupted his train of thought to inform him that two of the elves in the toy workshop were in the infirmary with respiratory distress. Testing had revealed that they were both infected with Respiratory Syncitial Virus (&lt;a href=&#34;https://en.wikipedia.org/wiki/Respiratory_syncytial_virus&#34;&gt;RSV&lt;/a&gt;), specifically with the RSVA subtype.&lt;/p&gt;

&lt;p&gt;Santa asked &#38;quot;Do we know where they were exposed, or if one of them transmitted it to the other? We may need to require everyone at the North Pole to mask again until after Christmas eve . . . &#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;The diagnostic lab says the two viruses are similar but not identical. Also they are not confident of the lineage calls and the clade assignments because we are using a new set of &lt;a href=&#34;https://en.wikipedia.org/wiki/Polymerase_chain_reaction&#34;&gt;PCR&lt;/a&gt; primers designed in &lt;a href=&#34;https://virological.org/t/preliminary-results-from-two-novel-artic-style-amplicon-based-sequencing-approaches-for-rsv-a-and-rsv-b/918&#34;&gt;Edinburgh&lt;/a&gt;, which are based on a newer RSVA reference sequence (&lt;a href=&#34;https://en.wikipedia.org/wiki/Reference_genome&#34;&gt;RefSeq&lt;/a&gt;). The problem is that we are using the RSVA &lt;a href=&#34;https://en.wikipedia.org/wiki/Phylogenetic_tree&#34;&gt;phylogenetic tree&lt;/a&gt; available at &lt;a href=&#34;http://usher.bio&#34;&gt;usher.bio&lt;/a&gt; and that tree was built using the &lt;a href=&#34;https://en.wikipedia.org/wiki/National_Center_for_Biotechnology_Information&#34;&gt;NCBI&lt;/a&gt; RefSeq as the root. You can see where the problem arises . . . &#38;quot;&lt;/p&gt;

&lt;p&gt;Santa thought for a moment and said &#38;quot;Oh, blast! We aligned our fastq reads to the other RefSeq, and at the primer trimming step we are using the genomic coordinates from that different virus strain. So if we first align against the NCBI RefSeq genome (to get more accurate lineage calls and clade assignments) then the &lt;a href=&#34;https://github.com/andersen-lab/ivar&#34;&gt;primer trimming step&lt;/a&gt; will not work as intended. Huh! Hmmm, do you think we could align those two different RSVA RefSeqs to each other and then modify the coordinates in the primer bed file so it uses the NCBI RefSeq&#38;#39;s genomic coordinates instead?!?&#38;quot;&lt;/p&gt;

&lt;p&gt;The Chief Elf sketched this out on the iceboard and said, &#38;quot;I think this could work. I will ask our bioinformatic software developers to tackle the problem.&#38;quot;&lt;/p&gt;

&lt;p&gt;And so gentle reader that is how we arrived at a simple, but tedious task of converting the genomic coordinates in this bed file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  RS20000581      44      66      RSVA_1_LEFT     1       +
  RS20000581      434     464     RSVA_1_RIGHT    1       -
  RS20000581      359     385     RSVA_2_LEFT     2       +
  RS20000581      749     773     RSVA_2_RIGHT    2       -
  RS20000581      669     699     RSVA_3_LEFT     1       +
  RS20000581      1057    1083    RSVA_3_RIGHT    1       -
  RS20000581      990     1016    RSVA_4_LEFT     2       +
  RS20000581      1366    1389    RSVA_4_RIGHT    2       -&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So that it would contain the correct coordinates from a different RSVA subtype RefSeq. It quickly became apparent that this problem could be solved much more quickly using the Awesome Power of Perl, unfortunately there was no out-of-the-box solution available in the existing base of Perl code. But how to proceed?&lt;/p&gt;

&lt;h3 id=&#34;Using-the-Awesome-Power-of-Perl-in-Bioinformatics&#34;&gt;Using the Awesome Power of Perl in Bioinformatics&lt;/h3&gt;

&lt;p&gt;Each of the two genome files are simple text &lt;a href=&#34;https://en.wikipedia.org/wiki/FASTA_format&#34;&gt;files&lt;/a&gt; consisting of an alphabet of five letters (A, C, G, and T (occasionally an N)) (technically RSV, similar to SARS-CoV-2 is an RNA virus, but life is simpler if we use &#38;#39;T&#38;#39; instead of &#38;#39;U&#38;#39;), all we need is a so-called &#38;quot;Global Alignment&#38;quot; of the two full-length sequences (they are each just over 15,000 bases in length). What we really want to know is where all of the longest stretches of identical sequences are located in the viral chromosomes. Fortunately an algorithm to calculate this was published by &lt;a href=&#34;https://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm&#34;&gt;Needleman and Wunsch&lt;/a&gt; back in 1970; it is considered a classic example of dynamic programming (which builds up the answer without consuming vast amounts of memory). Furthermore, the National Center of Biotechnology Information (NCBI) maintains a &lt;a href=&#34;https://blast.ncbi.nlm.nih.gov/Blast.cgi?PAGE_TYPE=BlastSearch&#38;amp;PROG_DEF=blastn&#38;amp;BLAST_PROG_DEF=blastn&#38;amp;BLAST_SPEC=GlobalAln&#38;amp;LINK_LOC=BlastHomeLink&#34;&gt;server&lt;/a&gt; we can use to upload any two closely related nucleotide sequences.&lt;/p&gt;

&lt;p&gt;The output from the N-W Global Alignment looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  Query  2177   GTGTGATTAACTACAGTGTATTAGATTTGACAGCAGAAGAACTAGAGGCTATCAAACATC  2236
                |||||||||||||||||||| |||| ||||||||||||||||||||||||||||||||||
  Sbjct  2221   GTGTGATTAACTACAGTGTACTAGACTTGACAGCAGAAGAACTAGAGGCTATCAAACATC  2280

  Query  2237   AGCTTAATCCAAAAGATAATGATGTAGAGCTTTGAGTTAATAAAAAGGTGGGGCAAATAA  2296
                ||||||||||||||||||||||||||||||||||||||||||||||  ||||||||||||
  Sbjct  2281   AGCTTAATCCAAAAGATAATGATGTAGAGCTTTGAGTTAATAAAAAA-TGGGGCAAATAA  2339&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Where the the top sequence in each pair is a stretch of 60 bases from the Edinburgh RefSeq (Labeled &#38;#39;Query&#38;#39;) and the bottom sequence in each pair is the matching stretch from the NCBI RefSeq (Labeled &#38;#39;Sbjct&#38;#39;). The unix pipe symbols denote a perfect match between the sequences at that base.&lt;/p&gt;

&lt;p&gt;Notice that the numbering systems are slightly offset, AND that there is a gap introduced with a hyphen in-between the A at 2327, and the T at 2328 in the Sbjct, directly across from the G at position 2384 in the Query. That can be interpreted as a single nucleotide insertion in the Query genome. So creating the mapping table between the two coordinate systems is not trivial. The sequences only share 94 percent identity over about 15,200 nucleotides, which means they diverge 6 percent of the time.&lt;/p&gt;

&lt;p&gt;To simplify the construction of our lookup table we can use grep to filter the starting alignment into two different subsets,&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;grep ^Query nw_alignment.txt &#38;gt; just_rows_from_edinburgh_refseq.txt&lt;br /&gt;grep ^Sbjct nw_alignment.txt &#38;gt; just_rows_from_ncbi_refseq.txt&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;N.B. you will have to go in and manually remove the first extraneous Query line from the first file. These become the first two input files to our script, the third file is the primer bed file we are starting with.&lt;/p&gt;

&lt;h3 id=&#34;Our-Script-that-Creates-a-Lookup-Table-and-then-Swaps-Coordinates-in-a-bed-File&#34;&gt;Our Script that Creates a Lookup Table, and then Swaps Coordinates in a bed File&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/env perl&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$query_input&lt;/span&gt; &lt;span class=&#34;operator&#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;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$subject_input&lt;/span&gt; &lt;span class=&#34;operator&#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;1&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;$input_bed&lt;/span&gt; &lt;span class=&#34;operator&#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;2&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;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$FH1&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;&#38;lt;&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$query_input&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;Could not open $query_input for reading\n&#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;open&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$FH2&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;&#38;lt;&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$subject_input&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;Could not open $subject_input for reading\n&#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;@global_array&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;$global_array&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;=&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;query&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;sbjct&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;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 global counter serves as the index for the first data structure&lt;br /&gt;# that captures the information at each place in the aligned sequences&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$counter&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;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# Parse the rows of the first file&lt;br /&gt;&lt;/span&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;$FH1&#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;&lt;span class=&#34;comment&#34;&gt;    # Each of these filtered rows has the same structure, in addition to&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# A, C, G, and T, the N-W output can also contain hyphens (dashes), so&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# our regex has to include that in the string we are capturing&lt;br /&gt;&lt;/span&gt;    &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;symbol&#34;&gt;$seq&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;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;m/Query\s+\d+\s+([\w\-]+)\s+\d+/&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;    # Parse the 60 nucleotide string into individual characters&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@qchars&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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;match&#34;&gt;//&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$seq&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;foreach&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$q&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@qchars&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;symbol&#34;&gt;$counter&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;comment&#34;&gt;        # Load up the array of hashrefs with the symbols&lt;br /&gt;&lt;/span&gt;        &lt;span class=&#34;symbol&#34;&gt;$global_array&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$counter&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;query&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;$q&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;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FH1&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$counter&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;&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;$FH2&#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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$seq&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;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;m/Sbjct\s+\d+\s+([\w\-]+)\s+\d+/&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;@schars&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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;match&#34;&gt;//&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$seq&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;foreach&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$s&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@schars&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;symbol&#34;&gt;$counter&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;symbol&#34;&gt;$global_array&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$counter&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;sbjct&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;$s&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;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FH2&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;# Now we process the information in each element of the&lt;br /&gt;# @global_array, and store that in this global hash&lt;br /&gt;# the keys of this hash are genomic coordinates for the&lt;br /&gt;# Edinburgh RSVA RefSeq and the values are the genomic&lt;br /&gt;# coordinates of the NCBI RSVA RefSeq&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;%ncbi_coordinates_of&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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$qcounter&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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$scounter&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;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;# There is nothing useful in the [0] element&lt;br /&gt;&lt;/span&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;$i&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;arrayindex&#34;&gt;$#global_array&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;$key&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&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;&#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;$value&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&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;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # If the string from the N-W output contains a &#39;-&#39; then we&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# need to handle those differently.  We only increment the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# $qcounter when the value in &#39;query&#39; matches a letter&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;$global_array&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;query&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;match&#34;&gt;m/\w/&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;symbol&#34;&gt;$qcounter&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;symbol&#34;&gt;$key&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$qcounter&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;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$global_array&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;sbjct&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;match&#34;&gt;m/\w/&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;$scounter&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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$scounter&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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$ncbi_coordinates_of&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$key&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;$value&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;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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$scounter&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;next&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;# After all of that manipulation and data wrangling, the problem now becomes&lt;br /&gt;# very simple.  We read in the starting primer bed file and iterate over each&lt;br /&gt;# TSV record.  We use the information in columns 2 and 3 to query the&lt;br /&gt;# %ncbi_coordinates_of hash, and insert the coordinates we want from the hash&lt;br /&gt;# values.  The modified record is immediately printed to STDOUT&lt;br /&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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$FH3&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;&#38;lt;&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$input_bed&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;Could not open $input_bed for reading\n&#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;while&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;readline&#34;&gt;&#38;lt;$FH3&#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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@fields&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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;match&#34;&gt;/\t/&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;    # Change the sequence name in column 1&lt;br /&gt;&lt;/span&gt;    &lt;span class=&#34;symbol&#34;&gt;$fields&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;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;NC_038235.1&#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;    # It is formally possible that either the &#39;start&#39; or the &#39;end&#39; of the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# Query sequence is NOT in the %ncbi_coordinates_of hash, so check for this&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;unless&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;symbol&#34;&gt;$ncbi_coordinates_of&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fields&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;and&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;exists&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ncbi_coordinates_of&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fields&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;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;symbol&#34;&gt;$fields&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;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$fields&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;=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;undef&#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;print&lt;/span&gt; &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;double&#34;&gt;&#38;quot;\t&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@fields&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&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;&lt;span class=&#34;comment&#34;&gt;    # Sometimes the &#39;start&#39; and &#39;end&#39; coordinates from the Query sequence ARE keys in the&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# %ncbi_coordinates_of hash, but the corresponding values from the Sbjct sequence are undef&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;# so continue processing gracefully when this occurs&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$fields&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;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ncbi_coordinates_of&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fields&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;//=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;undef&#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;$fields&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;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$ncbi_coordinates_of&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$fields&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;//=&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;undef&#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;word&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;\t&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@fields&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;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$FH3&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;exit&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;When you run the script, save the output to file, and then use &lt;code&gt;grep undef&lt;/code&gt; to see if there were any coordinates which could NOT be successfully mapped. If there are any then you can use &lt;code&gt;grep -v undef&lt;/code&gt; to save a copy without those lines.&lt;/p&gt;

&lt;p&gt;What did we learn? After running this script to create the new primer bed file for the NCBI RSVA RefSeq, and then rerunning their viral sequencing pipeline to align the amplified reads to the NCBI RefSeq, the bioinformaticians working with the infirmary could see that the two isolates from the workshop elves were more closely related than they had previously thought, but since the viruses still had many mutations separating them, it seemed like these were two separate exposures (or introductions) of RSVA to the North Pole compound.&lt;/p&gt;

&lt;p&gt;You can see that for yourself in this image (which I generated using &lt;a href=&#34;https://nextstrain.org/&#34;&gt;Nextstrain&#38;#39;s&lt;/a&gt; excellent &lt;a href=&#34;https://auspice.us/&#34;&gt;auspice.us&lt;/a&gt; tool for visualizing phylogenetic trees):&lt;/p&gt;

&lt;img src=&#34;Elf_Trees_Before_vs_After.png&#34; /&gt;

&lt;p&gt;Image credit: Nextstrain.org (The &lt;a href=&#34;https://github.com/yatisht/usher&#34;&gt;UShER&lt;/a&gt; suite of tools for working with mutation annotated trees (MAT) has an option to generate JSON outputs pre-formatted for display at Nextstrain!)&lt;/p&gt;

&lt;p&gt;N.B.: In accordance with the laws of Canada, Denmark, and Russia, the clinical metadata and any other personal health information (PHI) for these samples has been de-identified.&lt;/p&gt;

&lt;p&gt;These are two versions of the RSVA Global phylogenentic tree, showing a subtree of the samples that the most highly related to the two sequences from Santa&#38;#39;s workshop. The trees are shown rotated 90 degrees, so the &#38;quot;root&#38;quot; of the tree (which is not shown), is on the LEFT. You can see the &#38;quot;branches&#38;quot; as horizontal lines; and at the tips of each branch is a &#38;quot;leaf&#38;quot; (or a node). The leaves represent the sequences from different individual viruses in the database. Each tip is labeled with the name of the sample, and the two samples from the North Pole are colored black. The Y-axis has no units, and is arbitrary. The data depicting similarity is contained in the pattern of the branches (which are technically internal nodes), and in the LENGTH of the branches. The X-axis conveys information about the number of mutations in one sample, which is the number of nucleotide sequence changes that it takes to &#38;quot;get back&#38;quot; to the root of the tree.&lt;/p&gt;

&lt;p&gt;Panel A. shows where the UShER program placed the two samples from the North Pole, using the Edinburgh RefSeq genome, whereas Panel B. shows where they were placed after the same samples were aligned to the NCBI RefSeq genome, and the new Primer bed file created with our script was used to remove (or trim) the primers from the end of the reads. You can see that the placement of the Elf_0001 sample is almost the same in the two trees, whereas the placement of the Elf_0002 sample has moved. It is now forking off at a different branch of the tree and is closer (and therefore more similar, or more related) to Elf_0001.&lt;/p&gt;

&lt;p&gt;In principal, the flow and the steps used here could work with any paired RefSeqs for a pathogenic virus where you have created DNA sequencing amplicons with PCR primers from one coordinate system, but you want to be able to use your regular pipelines to align the fastq reads to a different RefSeq.&lt;/p&gt;

&lt;p&gt;If you would like to test/replicate/hack the code above, here are links to the three input files I used:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;a href=&#34;RS20000581_RSVA_reference.fasta&#34;&gt;The RSVA Reference sequence used with the Edinburgh group&#39;s primers&lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href=&#34;NC_038235.1.fa&#34;&gt;The RSVA RefSeq downloaded from NCBI (GenBank)&lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href=&#34;RSVA.primer.bed&#34;&gt;The RSVA PCR Primer bed file downloaded from the Edinburgh GitHub repo&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have some paired-end fastq files from RSV (or another RNA virus) you can get an idea of the processing steps to use to generate consensus.fasta files &lt;a href=&#34;https://github.com/pathogen-genomics/cdph-rsv&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During the COVID19 Global Pandemic these consensus.fasta files were essential for constructing the phylogenetic trees used by Epidemiologists to identify outbreaks of new strains.&lt;/p&gt;

&lt;p&gt;THM: Best to wear masks at the North Pole until after DEC-24.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-20T00:00:00Z</updated><category term="Perl"/><author><name>Marc Perry</name></author></entry><entry><title>Half My Life with Perl</title><link href="https://perladvent.org/2024/2024-12-19.html"/><id>https://perladvent.org/2024/2024-12-19.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;I had never met Randal Schwartz before, but when I reached out to him about contributing to year 25 of the Perl Advent Calendar, he immediately agreed. This year I wanted to try some new things and Randal suggested giving a Perl-specific talk which he had given once before, but which was lacking in recording quality. This gave Randal a chance to re-record his talk and it gave the Perl communities a chance to watch in real time. We&#38;#39;ve never before had a video recording as a Perl Advent article, but I believe quite strongly that this is in the spirit of the Advent Calendar project, which is about giving something back. Also, when I mentioned it to Mark Fowler back in October, he didn&#38;#39;t object, so I&#38;#39;ll take that as a seal of approval.&lt;/p&gt;

&lt;p&gt;-- Olaf Alders&lt;/p&gt;

&lt;p&gt;Randal&#38;#39;s advance summary of this talk:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Having been there, at the beginning with Perl, I will recount the early
    days through the modern era (or as much as I can cover in the time
    provided). I&#38;rsquo;ll deliver first-hand experience of the creation of the Camel
    Book, the Llama book, and the way I invaded comp.unix.questions with Perl 2
    answers so often that people would post &#38;ldquo;no Perl please&#38;rdquo;. Oh, and my
    version of the story of the Schwartzian Transform.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you enjoyed the video below, you may also enjoy the next in the series, which will be presented by Dave Cross: &lt;a href=&#34;https://lu.ma/3b8ekn8y&#34;&gt;Still Munging Data with Perl&lt;/a&gt;. If you are at all interested, please register now, even if you cannot attend, as that will allow us to share the recording URL with you after the fact.&lt;/p&gt;

&lt;p&gt;Now, please enjoy Randal Schwartz&#38;#39;s &#38;quot;Half My Life with Perl&#38;quot;:&lt;/p&gt;

&lt;p&gt;&lt;div style=&#34;text-align: center;&#34;&gt; &lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/fffJnNTcLog?si=DfxCTe_AF2fRNU4e&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt; &lt;/div&gt;

&lt;/p&gt;



&lt;/div&gt;</summary><updated>2024-12-19T00:00:00Z</updated><category term="Perl"/><author><name>Randal Schwartz</name></author></entry><entry><title>Santa will be watching</title><link href="https://perladvent.org/2024/2024-12-18.html"/><id>https://perladvent.org/2024/2024-12-18.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Another busy Christmas period loomed, and the North Pole was tirelessly working to get the final preparations ready. Inside a brightly lit meeting room Santa cleared his throat.&lt;/p&gt;

&lt;p&gt;&#38;quot;This is all very interesting, very... *yawn* ...stimulating&#38;quot;, he said, picking at some cookie crumbs that fell from his beard to the table. &#38;quot;But how is that going to help us? How does ... absorbability help us deliver gifts?&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Observability&#38;quot;, corrected Ada Slashd&#38;oacute;ttir, the team&#38;#39;s senior elf.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yeah, that&#38;#39;s what I said&#38;quot;, said Santa. &#38;quot;What does it do for us?&#38;quot;&lt;/p&gt;

&lt;p&gt;As their platform had started to grow, the elves had struggled to keep track on where the bottlenecks were. And now that they had migrated parts of it to microservices and their platform was becoming more distributed, even the tools they were familiar were starting to become unwieldy.&lt;/p&gt;

&lt;p&gt;&#38;quot;We need to have a way to see from the outside how our system behaves internally, Santa&#38;quot;, said Ada. &#38;quot;That&#38;#39;s observability. So it doesn&#38;#39;t &lt;i&gt;directly&lt;/i&gt; help us send gifts, but it helps us keep things running smoothly, and &lt;i&gt;that&lt;/i&gt; helps us send gifts&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;Isn&#38;#39;t that what logs do?&#38;quot;, asked Duende Juniorsson, the team&#38;#39;s junior elf, who was trying to catch up. &#38;quot;We already have logs in our services. Can&#38;#39;t we use those?&#38;quot;&lt;/p&gt;

&lt;p&gt;Santa liked logs. Yule logs in particular.&lt;/p&gt;

&lt;p&gt;&#38;quot;We can, for sure&#38;quot;, said Gnomo Knullpointer, who had recently joined Santa&#38;#39;s workshop as a coding elf. &#38;quot;Logs are useful because they have been around forever, so they are well supported. But they have their limitations, specially now that a single request can touch several separate microservices before we are done with it. We&#38;#39;d have to correlate logs across services, which is not easy.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Correlating logs is difficult, but there are other kinds of telemetry we can use&#38;quot;, said Ada. &#38;quot;In particular, we can uses &lt;i&gt;traces&lt;/i&gt; to track a request across services. This is called &#38;#39;distributed tracing&#38;#39;. Let me show you how it works.&#38;quot;&lt;/p&gt;

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

&lt;p&gt;Ada then opened the code for the &lt;i&gt;Naught-or-Not&lt;/i&gt; service, a &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; application that checked whether a specific child has been naughty or nice:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# Naught-or-Not: the &#39;naughty or nice&#39; service&lt;br /&gt;&lt;/span&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;word&#34;&gt;-signatures&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::SQLite&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;helper&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;sql&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;state&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sql&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::SQLite&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;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;sql&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;migrations&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;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;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;from_string&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;EOF&#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;migrate&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;-- 1 up&lt;br /&gt;create table naughty (id integer unique, naughty bool);&lt;br /&gt;-- 1 down&lt;br /&gt;drop table naughty;&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;EOF&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;/is-naughty/:id&#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;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;$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;id&#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;$db&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;sql&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;db&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 have a value for this ID, return it&lt;br /&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&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$db&lt;/span&gt;&lt;br /&gt;&#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;select&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;naughty&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;naughty&#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;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;symbol&#34;&gt;$id&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;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;hashes&lt;/span&gt;&lt;br /&gt;&#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;first&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;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;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;span class=&#34;word&#34;&gt;naughty&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;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;word&#34;&gt;naughty&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;word&#34;&gt;if&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;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;    # Otherwise, generate one and store it before returning&lt;br /&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;$naughty&lt;/span&gt; &lt;span class=&#34;operator&#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;int&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;rand&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;comment&#34;&gt;# Randomised naughtiness!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$db&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;insert&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;naughty&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;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;span class=&#34;word&#34;&gt;naughty&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$naughty&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;&#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;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;span class=&#34;word&#34;&gt;naughty&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$naughty&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;&#38;quot;The first thing we&#38;#39;ll need to do&#38;quot;, said Ada, &#38;quot;is get this code to start generating telemetry data. Traces in particular.&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Wait a second&#38;quot;, said Gnomo. &#38;quot;If we start touching the code to generate this telemetry, we run the risk of introducing bugs. Not to mention that we have a lot of code, we&#38;#39;ll never be able to do it all by hand...&#38;quot;&lt;/p&gt;

&lt;p&gt;Santa was starting to sweat.&lt;/p&gt;

&lt;p&gt;&#38;quot;We won&#38;#39;t have to. We can use &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry&#34;&gt;OpenTelemetry&lt;/a&gt; to do what is called &#38;#39;zero code instrumentation&#38;#39;, which will get us most of the way there. The first thing we&#38;#39;ll need to do is load up the &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::SDK&#34;&gt;OpenTelemetry::SDK&lt;/a&gt; in our application. This will read the configuration from the environment and set things up internally so any telemetry we generate gets exported correctly. And since we are using &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; we can use &lt;a href=&#34;https://metacpan.org/module/Mojolicious::Plugin::OpenTelemetry&#34;&gt;Mojolicious::Plugin::OpenTelemetry&lt;/a&gt; to actually generate telemetry data from our routes, all without actually touching any of the controller 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;Mojolicious::Lite&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;-signatures&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;OpenTelemetry::SDK&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::SQLite&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;plugin&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;OpenTelemetry&#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;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;And that&#38;#39;s it?&#38;quot;, asked Duende a little disappointed. He was already looking forward to touching a lot of code.&lt;/p&gt;

&lt;p&gt;&#38;quot;Pretty much. The rest is just configuration. You can already see this in motion by running the code and telling it to export the traces to the console&#38;quot;, said Ada, feeling pedagogic.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ OTEL_TRACES_EXPORTER=console ./server get &#39;/is-naughty/123&#39;&lt;br /&gt;[2024-12-16 23:24:29.37268] [821150] [trace] [nLc70JabTxup] GET &#38;quot;/is-naughty/123&#38;quot;&lt;br /&gt;[2024-12-16 23:24:29.37288] [821150] [trace] [nLc70JabTxup] Routing to a callback&lt;br /&gt;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;attributes&#39; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;client.address&#39; =&#38;gt; &#39;127.0.0.1&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;client.port&#39; =&#38;gt; &#39;47534&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;http.request.method&#39; =&#38;gt; &#39;GET&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;http.response.status_code&#39; =&#38;gt; 200,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;http.route&#39; =&#38;gt; &#39;/is-naughty/:id&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;network.protocol.version&#39; =&#38;gt; &#39;1.1&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;server.address&#39; =&#38;gt; &#39;127.0.0.1&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;server.port&#39; =&#38;gt; &#39;36703&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;url.path&#39; =&#38;gt; &#39;/is-naughty/123&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;user_agent.original&#39; =&#38;gt; &#39;Mojolicious (Perl)&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;},&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;dropped_attributes&#39; =&#38;gt; 0,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;dropped_events&#39; =&#38;gt; 0,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;dropped_links&#39; =&#38;gt; 0,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;end_timestamp&#39; =&#38;gt; &#39;1734391469.37503&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;events&#39; =&#38;gt; [],&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;instrumentation_scope&#39; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;name&#39; =&#38;gt; &#39;server&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;version&#39; =&#38;gt; &#39;&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;},&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;kind&#39; =&#38;gt; 2,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;links&#39; =&#38;gt; [],&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;name&#39; =&#38;gt; &#39;GET /is-naughty/:id&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;parent_span_id&#39; =&#38;gt; &#39;0000000000000000&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;resource&#39; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.command&#39; =&#38;gt; &#39;./server&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.command_args&#39; =&#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;&#39;get&#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;/is-naughty/123&#39;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.executable.name&#39; =&#38;gt; &#39;perl&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.executable.path&#39; =&#38;gt; &#39;/home/user/.perl/perls/perl-5.40.0/bin/perl&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.pid&#39; =&#38;gt; 821150,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.runtime.name&#39; =&#38;gt; &#39;perl&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;process.runtime.version&#39; =&#38;gt; &#39;v5.40.0&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;telemetry.sdk.language&#39; =&#38;gt; &#39;perl&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;telemetry.sdk.name&#39; =&#38;gt; &#39;opentelemetry&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;telemetry.sdk.version&#39; =&#38;gt; &#39;0.024&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;},&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;span_id&#39; =&#38;gt; &#39;3823100b9ca26a91&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;start_timestamp&#39; =&#38;gt; &#39;1734391469.3732&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;status&#39; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;code&#39; =&#38;gt; 0,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;description&#39; =&#38;gt; &#39;&#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;},&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;trace_flags&#39; =&#38;gt; 1,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;trace_id&#39; =&#38;gt; &#39;795c45630d3615ba1ebf524661bf01ac&#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39;trace_state&#39; =&#38;gt; &#39;&#39;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A wall of text (pretty printed here for clarity) appeared in front of the elves. Santa seized the opportunity to look like he knew what was going on by looking at the text in silence while thinking about what to have for lunch.&lt;/p&gt;

&lt;p&gt;After a couple of seconds Gnomo and Duende had pieced it together. It was all there! Data about the request itself, how long it took, where it came from, and even details about what code had generated it... and all of that without having to touch any of the core logic!&lt;/p&gt;

&lt;p&gt;&#38;quot;This is very cool&#38;quot;, said Gnomo, who had been reading through &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::Guides::Quickstart&#34;&gt;OpenTelemetry::Guides::Quickstart&lt;/a&gt; and was already making sense of things. &#38;quot;And it seems we can get even more data if we load some of the instrumentation libraries that are already out there. This route uses a database, so we could use the &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::Instrumentation::DBI&#34;&gt;OpenTelemetry::Instrumentation::DBI&lt;/a&gt; to get &lt;a href=&#34;https://metacpan.org/module/DBI&#34;&gt;DBI&lt;/a&gt; to also generate telemetry&#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;Mojolicious::Lite&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;-signatures&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;OpenTelemetry::SDK&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;OpenTelemetry::Instrumentation&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DBI&#39;&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::SQLite&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;plugin&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;OpenTelemetry&#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;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now running the test command printed even more output, this time including traces for all the DB operations, including the ones that executed when the service initially came up and ran the database migrations, etc.&lt;/p&gt;

&lt;p&gt;&#38;quot;What about our other services? Not all of them are using &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt;&#38;quot;, said Duende, thinking that maybe &lt;i&gt;then&lt;/i&gt; he&#38;#39;d have the chance to touch some code.&lt;/p&gt;

&lt;h3 id=&#34;Integrating-with-Dancer2&#34;&gt;Integrating with Dancer2&lt;/h3&gt;

&lt;p&gt;Ada loaded up the code for the Gift Allocation service, which was implemented in &lt;a href=&#34;https://metacpan.org/module/Dancer2&#34;&gt;Dancer2&lt;/a&gt;. This service made a call to Naught-or-Not to check whether this child had been naughty, and allocated either a lump of coal or one of the gifts from the child&#38;#39;s wishlist.&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;Gift::Assignment&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;Dancer2&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;word&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;serializer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON&#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;$ua&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;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;post&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/gift/assign&#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;$id&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;body_parameters&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;id&#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;$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;span class=&#34;double&#34;&gt;&#38;quot;$ENV{NAUGHT_OR_NOT_HOST}/is-naughty/$id&#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;unless&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;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;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;status&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;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;status&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;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;$res&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;reason&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;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;$body&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;symbol&#34;&gt;$res&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;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;gift&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;coal&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#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;span class=&#34;word&#34;&gt;naughty&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;@wants&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;body_parameters&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_all&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;wants&#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;span class=&#34;word&#34;&gt;gift&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$wants&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@wants&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;&#38;quot;Like with the &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; service, integrating this with &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry&#34;&gt;OpenTelemetry&lt;/a&gt; requires no code changes&#38;quot;, said Ada. Duende&#38;#39;s heart sank. Was he going to get to touch &lt;i&gt;any&lt;/i&gt; code? &#38;quot;All we need to do is load the &lt;a href=&#34;https://metacpan.org/module/Dancer2::Plugin::OpenTelemetry&#34;&gt;Dancer2::Plugin::OpenTelemetry&lt;/a&gt; plugin&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;And like with the &lt;a href=&#34;https://metacpan.org/module/DBI&#34;&gt;DBI&lt;/a&gt; instrumentation, since this route is using an HTTP client, we can load &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::Instrumentation::HTTP::Tiny&#34;&gt;OpenTelemetry::Instrumentation::HTTP::Tiny&lt;/a&gt; to get telemetry for the requests it makes!&#38;quot;, chimed in Gnomo, who was neck-deep in &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::Guides::Instrumentations&#34;&gt;the documentation for instrumentation libraries&lt;/a&gt; and getting very excited.&lt;/p&gt;

&lt;p&gt;The import list 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;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Gift::Assignment&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;Dancer2&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;Dancer2::Plugin::OpenTelemetry&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;OpenTelemetry::Instrumentation&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;HTTP::Tiny&#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;set&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;serializer&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;JSON&#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;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;Hold on a second... what happened to the &lt;a href=&#34;https://metacpan.org/module/HTTP::Tiny&#34;&gt;HTTP::Tiny&lt;/a&gt; import?&#38;quot;, asked Duende, who was trying to follow along to find ways to contribute.&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh, well that&#38;#39;s done automatically when we load the instrumentation library, so we don&#38;#39;t need to write that twice&#38;quot;, said Ada&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;And what about the &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::SDK&#34;&gt;OpenTelemetry::SDK&lt;/a&gt; import? Don&#38;#39;t we need it here as well?&#38;quot;, continued Duende.&lt;/p&gt;

&lt;p&gt;&#38;quot;Ah, well spotted, Duende!&#38;quot;, replied Ada. &#38;quot;We do need it, but we only need to load it once. Since this service will have more controllers once it&#38;#39;s finished, loading it in each one would be a hassle, so it&#38;#39;s better to load it once at the entry point. In this case, since we are using &lt;a href=&#34;https://metacpan.org/module/Plack&#34;&gt;Plack&lt;/a&gt; to mount it, we can load that in the top-level &lt;a href=&#34;https://metacpan.org/module/PSGI&#34;&gt;PSGI&lt;/a&gt; file&#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/env 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;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;single&#34;&gt;&#39;lib&#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;Gift::Assignment&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;OpenTelemetry::SDK&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;Plack::Builder&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;builder&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;Gift::Assignment&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;psgi_app&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;Once again they tested it. They brought up the server configured to send traces to the terminal, and made a request:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ curl -X POST \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;-H &#39;Content-Type: application/json&#39; \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;-d &#39;{&#38;quot;id&#38;quot;:123, &#38;quot;wants&#38;quot;:[&#38;quot;teddy_bear&#38;quot;, &#38;quot;crochet&#38;quot;]}&#39; \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;http://localhost:5000/gift/assign&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As the traces appeared in the logs now for both services, Gnomo spotted something. &#38;quot;Hey look! Ignore everything in the traces except for those span and trace IDs&#38;quot;. Pretty printed for your convenience, this is what he was looking at:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    naught-or-not  | {
      &#38;#39;name&#38;#39; =&#38;gt; &#38;#39;SELECT &#38;quot;naughty&#38;quot; FROM &#38;quot;naughty&#38;quot; WHERE &#38;quot;id&#38;quot; = ?&#38;#39;,
      &#38;#39;parent_span_id&#38;#39; =&#38;gt; &#38;#39;88b066b24f372f39&#38;#39;,
      &#38;#39;span_id&#38;#39; =&#38;gt; &#38;#39;932cf74c171b705e&#38;#39;,
      &#38;#39;trace_id&#38;#39; =&#38;gt; &#38;#39;16073bf43d281a0693ec7d5a9f4860a4&#38;#39;,
    }
    naught-or-not  | {
      &#38;#39;name&#38;#39; =&#38;gt; &#38;#39;GET /is-naughty/:id&#38;#39;,
      &#38;#39;parent_span_id&#38;#39; =&#38;gt; &#38;#39;7bd198e5a6ddd5fd&#38;#39;,
      &#38;#39;span_id&#38;#39; =&#38;gt; &#38;#39;88b066b24f372f39&#38;#39;,
      &#38;#39;trace_id&#38;#39; =&#38;gt; &#38;#39;16073bf43d281a0693ec7d5a9f4860a4&#38;#39;,
    }
    gifts          | {
      &#38;#39;name&#38;#39; =&#38;gt; &#38;#39;GET&#38;#39;,
      &#38;#39;parent_span_id&#38;#39; =&#38;gt; &#38;#39;d9718758cefd39d4&#38;#39;,
      &#38;#39;span_id&#38;#39; =&#38;gt; &#38;#39;7bd198e5a6ddd5fd&#38;#39;,
      &#38;#39;trace_id&#38;#39; =&#38;gt; &#38;#39;16073bf43d281a0693ec7d5a9f4860a4&#38;#39;,
    }
    gifts          | {
      &#38;#39;name&#38;#39; =&#38;gt; &#38;#39;POST /gift/assign&#38;#39;,
      &#38;#39;parent_span_id&#38;#39; =&#38;gt; &#38;#39;0000000000000000&#38;#39;,
      &#38;#39;span_id&#38;#39; =&#38;gt; &#38;#39;d9718758cefd39d4&#38;#39;,
      &#38;#39;trace_id&#38;#39; =&#38;gt; &#38;#39;16073bf43d281a0693ec7d5a9f4860a4&#38;#39;,
    }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Santa wondered if this is what it felt like to see the woman in the red dress.&lt;/p&gt;

&lt;p&gt;&#38;quot;What are we looking at?&#38;quot;, said Duende.&lt;/p&gt;

&lt;p&gt;&#38;quot;They are all linked!&#38;quot;, replied Gnomo excitedly. &#38;quot;They have the same &lt;code&gt;trace_id&lt;/code&gt;, and the &lt;code&gt;parent_span_id&lt;/code&gt; in each one is the same as the &lt;code&gt;span_id&lt;/code&gt; of the one that came before it, which appears lower in the logs&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;Well, except for that one at the bottom. That one just has a bunch of zeroes as the &lt;code&gt;parent_span_id&lt;/code&gt;&#38;quot;, commented Duende.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes, that is called the &lt;i&gt;root span&lt;/i&gt;&#38;quot;, said Ada. &#38;quot;The fact that it has a null &lt;code&gt;parent_span_id&lt;/code&gt; tells us that this is the span that initiated the trace&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;We could write something that would aggregate these data and present them in a neater way&#38;quot;, said Gnomo. &#38;quot;Maybe something using a kind of flamegraph like the ones from &lt;a href=&#34;https://metacpan.org/module/Devel::NYTProf&#34;&gt;Devel::NYTProf&lt;/a&gt;, which are great&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;We don&#38;#39;t have to!&#38;quot;, said Ada. &#38;quot;&lt;a href=&#34;https://opentelemetry.io&#34;&gt;The OpenTelemetry project&lt;/a&gt; has already done that for us. We can use the &lt;a href=&#34;https://opentelemetry.io/docs/collector&#34;&gt;OpenTelemetry Collector&lt;/a&gt; to aggregate all sorts of telemetry data generated via &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry&#34;&gt;OpenTelemetry&lt;/a&gt; and then configure it to send that data to whatever external service or platform we want&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh, so it&#38;#39;s vendor agnostic too?&#38;quot;, said Gnomo. &#38;quot;That&#38;#39;s very good&#38;quot;.&lt;/p&gt;

&lt;p&gt;Santa&#38;#39;s eye twitched at the mention of &#38;quot;agnostic&#38;quot;. This was a touchy subject in the North Pole. He looked at Helga Hakrni&#38;uuml;s, the Public Relations elf in the corner of the room who quietly shook their head. Santa let his breath out and wiped his brow.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes, that&#38;#39;s one of OpenTelemetry&#38;#39;s key selling points&#38;quot;, said Ada. &#38;quot;Vendor agnostic on the consumer side, and platform agnostic on the producer side, so you can use it in services written not only in all sorts of frameworks like we just saw, but also in all sorts of languages. They even have &lt;a href=&#34;https://opentelemetry.io/docs/demo&#34;&gt;a demo application&lt;/a&gt; where you can see this in motion&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;Is this true for Perl as well?&#38;quot;, said Gnomo. &#38;quot;We can obviously use it in &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/Dancer2&#34;&gt;Dancer2&lt;/a&gt;, but what about other frameworks, like &lt;a href=&#34;https://metacpan.org/module/Catalyst&#34;&gt;Catalyst&lt;/a&gt;? I heard that&#38;#39;s what the Milk-and-Cookies team was using for their service. Or plain &lt;a href=&#34;https://metacpan.org/module/CGI&#34;&gt;CGI&lt;/a&gt;? We still have a bunch of those in the older parts of the code&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes!&#38;quot;, said Ada. &#38;quot;The Perl implementation is relatively new, so there&#38;#39;s still work to do, but we do have &lt;a href=&#34;https://metacpan.org/module/Plack::Middleware::OpenTelemetry&#34;&gt;Plack::Middleware::OpenTelemetry&lt;/a&gt; that we can use for any server that uses &lt;a href=&#34;https://metacpan.org/module/Plack&#34;&gt;Plack&lt;/a&gt;. The integration might not be as close, but it does work. And it&#38;#39;s only a matter of time before more instrumentation libraries get released&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;So how do we use this Collector?&#38;quot;, asked Duende.&lt;/p&gt;

&lt;p&gt;&#38;quot;I&#38;#39;m glad you asked&#38;quot;, replied Ada.&lt;/p&gt;

&lt;h3 id=&#34;From-the-terminal-to-the-browser&#34;&gt;From the terminal to the browser&lt;/h3&gt;

&lt;p&gt;&#38;quot;The &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry&#34;&gt;OpenTelemetry&lt;/a&gt; distribution on CPAN has an example collector stack that we can use to see how things go together&#38;quot;, continued Ada. &#38;quot;There is also &lt;a href=&#34;https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/examples/demo&#34;&gt;a similar example managed by the developers of the collector itself&lt;/a&gt;, and a more realistic example in the &lt;a href=&#34;https://opentelemetry.io/docs/demo&#34;&gt;OpenTelemetry demo&lt;/a&gt; I mentioned before. We can use the one from CPAN to illustrate things for now&#38;quot;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;git clone https://github.com/jjatria/perl-opentelemetry&lt;br /&gt;cd perl-opentelemetry/examples/collector&lt;br /&gt;docker compose up --build&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;And then we tell the services to talk to it?&#38;quot;, asked Gnomo.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes&#38;quot;, said Ada. &#38;quot;We need to set a couple of environment variables for each deployment so that they can talk to the collector. It&#38;#39;s probably easier to show this with a docker compose file&#38;quot;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;naught-or-not&lt;/span&gt;&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;synIdentifier&#34;&gt;build&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; mojo&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; naught-or-not&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;volumes&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;./mojo:/app&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;command&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;hypnotoad&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;./server&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;--foreground&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;environment&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;IO_ASYNC_LOOP&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Mojo&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_BSP_MAX_EXPORT_BATCH_SIZE&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; http://localhost&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; naught-or-not&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; otlp&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;network_mode&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; host&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;gifts&lt;/span&gt;&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;synIdentifier&#34;&gt;build&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; dancer&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;container_name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; gifts&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;volumes&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;./dancer:/app&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;command&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;plackup&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;--server&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;Net::Async::HTTP::Server&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;./server.psgi&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;environment&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;NAUGHT_OR_NOT_HOST&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; http://localhost:8080&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_BSP_MAX_EXPORT_BATCH_SIZE&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; http://localhost&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; gifts&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; otlp&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;network_mode&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; host&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;In both cases we need to set &lt;code&gt;OTEL_SERVICE_NAME&lt;/code&gt; to something meaningful so we can identify where the traces come from. We also need to set &lt;code&gt;OTEL_TRACES_EXPORTER&lt;/code&gt; to &lt;code&gt;otlp&lt;/code&gt;, which is the protocol used by the collector, and &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; to the host that we are sending the telemetry to. In this case, both of these are set to their default values, but it&#38;#39;s good for now to be explicit&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;What about that &lt;code&gt;OTEL_BSP_MAX_EXPORT_BATCH_SIZE&lt;/code&gt;?&#38;quot;, asked Duende. He had already learned the hard way that a batch of one was a little pointless.&lt;/p&gt;

&lt;p&gt;&#38;quot;Oh, that&#38;#39;s only for testing. In a realistic scenario we can increase that to limit the number of requests that we make to the collector&#38;quot;, said Ada.&lt;/p&gt;

&lt;p&gt;&#38;quot;So the communication with the collector happens off-band?&#38;quot;, asked Gnomo.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes. When exporting to the console we got each span as it was produced, but in a production environment you&#38;#39;d expect many times more spans to be produced, and it would be very expensive to export each one as it came. It&#38;#39;s more efficient to batch them and send them all together when enough of them are ready. The Perl &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry&#34;&gt;OpenTelemetry&lt;/a&gt; implementation uses a &lt;a href=&#34;https://metacpan.org/module/OpenTelemetry::SDK::Trace::Span::Processor::Batch&#34;&gt;batch span processor&lt;/a&gt; written on top of &lt;a href=&#34;https://metacpan.org/module/IO::Async&#34;&gt;IO::Async&lt;/a&gt; to do this&#38;quot;.&lt;/p&gt;

&lt;p&gt;Santa wondered if his elves lived in the same planet as he did. He was glad there were people in the room who knew about this so he didn&#38;#39;t have to.&lt;/p&gt;

&lt;p&gt;&#38;quot;Is that why we need the &lt;code&gt;IO_ASYNC_LOOP&lt;/code&gt; variable in the &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; environment?&#38;quot;, asked Gnomo.&lt;/p&gt;

&lt;p&gt;&#38;quot;Yes&#38;quot;, said Ada. &#38;quot;We need that because &lt;a href=&#34;https://metacpan.org/module/Mojolicious&#34;&gt;Mojolicious&lt;/a&gt; has its own event loop, so we need to tell &lt;a href=&#34;https://metacpan.org/module/IO::Async&#34;&gt;IO::Async&lt;/a&gt; to use it&#38;quot;.&lt;/p&gt;

&lt;p&gt;&#38;quot;I see. And that&#38;#39;s why in the &lt;a href=&#34;https://metacpan.org/module/Dancer2&#34;&gt;Dancer2&lt;/a&gt; environment we use &lt;a href=&#34;https://metacpan.org/module/Net::Async::HTTP::Server&#34;&gt;Net::Async::HTTP::Server&lt;/a&gt;, because &lt;a href=&#34;https://metacpan.org/module/Dancer2&#34;&gt;Dancer2&lt;/a&gt; doesn&#38;#39;t have an event loop, and this gives it one&#38;quot;, said Gnomo.&lt;/p&gt;

&lt;p&gt;&#38;quot;Exactly&#38;quot;, said Ada. &#38;quot;And now, if we run this so it talks to the collector stack, and make some queries, we can see the traces in our browser. That stack connects the collector to a &lt;a href=&#34;https://www.jaegertracing.io&#34;&gt;Jaeger&lt;/a&gt; instance, which we should be able to see at &lt;a href=&#34;http://localhost:16686&#34;&gt;http://localhost:16686&lt;/a&gt;&#38;quot;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;jaeger.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;&#38;quot;Incidentally&#38;quot;, said Ada, &#38;quot;I&#38;#39;ve put all the files for what we&#38;#39;ve been talking about in &lt;a href=&#34;https://github.com/jjatria/perl-advent-otel-example-2024&#34;&gt;a repository you can check out&lt;/a&gt; if you want to run things yourself&#38;quot;.&lt;/p&gt;

&lt;p&gt;Santa was happy to finally be able to see some pretty pictures. And as he looked at his elves, all excitedly looking around and exploring the data, he felt a warm sense of pride and joy to see them learning and growing and getting things done.&lt;/p&gt;

&lt;p&gt;With this out of the way, he could finally get back to what mattered most: having those milk and cookies.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-18T00:00:00Z</updated><category term="Perl"/><author><name>Jos&#233; Joaqu&#237;n Atria</name></author></entry><entry><title>Annotating Christmas Trees</title><link href="https://perladvent.org/2024/2024-12-17.html"/><id>https://perladvent.org/2024/2024-12-17.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Lesser-known-businesses&#34;&gt;Lesser-known businesses&lt;/h3&gt;

&lt;p&gt;Everybody knows that the North Pole&#38;trade; produces and delivers Christmas gifts, but it&#38;rsquo;s not the sole business they&#38;rsquo;re into. In fact, they&#38;rsquo;ve recently engaged in production of Christmas ornaments and Christmas cards, too.&lt;/p&gt;

&lt;p&gt;As is common, starting a new business brings new problems. The elves needed a way to design decorated Christmas trees to evaluate their ornament proposals and their combinations. They started by planting small trees and decorating them by hand, but quickly found out this approach didn&#38;rsquo;t scale as more and more (and bigger and bigger) trees were needed.&lt;/p&gt;

&lt;p&gt;At a C-level meeting, Santa listened to laments of the managing elf responsible for the ornaments and narrowed his eyes at another young elf at the other corner of the table.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;You had something for arranging trees, right?&#38;rdquo; asked Santa.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Yes, but&#38;hellip;&#38;rdquo; spluttered the elf.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Let&#38;rsquo;s meet after lunch and see how we can share the knowledge,&#38;rdquo; commanded Santa.&lt;/p&gt;

&lt;h3 id=&#34;There-s-trees-and-there-s-trees&#34;&gt;There&#38;rsquo;s trees and there&#38;rsquo;s trees&lt;/h3&gt;

&lt;p&gt;In the afternoon (by the way, it was already dark, since it was the North Pole and summer was over) Santa met with the COO and CCO (where the second letter stands for Ornaments or Cards, respectively).The CBO (Chief Baubles Officer) was missing as his department was merged with the Ornaments in the last workforce shaping to trim the fat.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Can you show us what your department uses to visualise trees?&#38;rdquo; asked Santa, turning to the CCO.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Our developers found this open source tool called &lt;a href=&#34;https://github.com/ufal/TrEd&#34;&gt;TrEd&lt;/a&gt;, which stands for &#38;lsquo;Tree Editor&#38;rsquo;,&#38;rdquo; replied the elf. &#38;ldquo;And they&#38;rsquo;re still discovering new features it has. You can do much more than view the trees: you can easily change their structure, add attributes to nodes and edges, add secondary relations that turn the trees into full graphs, and there are also tools for searching large treebanks.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;I hate the jargon,&#38;rdquo; muttered Santa and turned to the COO, &#38;ldquo;but I guess you&#38;rsquo;re following.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Actually, not really,&#38;rdquo; replied the COO, &#38;ldquo;we need to arrange the ornaments, but we don&#38;rsquo;t want to change the structure of the trees. How is such a thing needed to produce a Christmas card, anyway?&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;We hear similar questions often,&#38;rdquo; said the CCO keeping a stiff upper lip. &#38;ldquo;At the beginning, we only produced English Christmas cards, so we didn&#38;rsquo;t need anything like that. But several years ago we started printing the cards in other languages, too, and we needed a way to translate all the greetings and wishes. We started with elvish translators, but we found ourselves in your boots, so to say: the approach didn&#38;rsquo;t scale.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;We needed an automated process. We reached for statistical machine translation, but for that, we needed large aligned corpora in both the source and target languages.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Corpora?&#38;rdquo; asked Santa raising an eyebrow.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Large collections of texts. And we quickly found out aligning the individual words wasn&#38;rsquo;t enough, as the grammar in various languages can change the words in different roles. The sentence structure stays usually much more similar across languages than individual words and their order. That&#38;rsquo;s why we started annotating the trees.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Decorating,&#38;rdquo; said Santa and nodded to the COO hopefully.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;No, annotating,&#38;rdquo; explained the CCO. &#38;ldquo;I&#38;rsquo;m talking about trees in the graph-theory sense. We arrange the words in a sentence to a tree and annotate the relations between them with their syntactic roles: this word is a subject of this verb, that word is an adverbial of that word,&#38;rdquo; and he started to gesticulate wildly.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Wait, wait,&#38;rdquo; the COO interrupted him, &#38;ldquo;can you show us what you&#38;rsquo;re talking about? I still have no idea.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Ho ho ho,&#38;rdquo; said Santa, &#38;ldquo;a picture is worth a thousand words!&#38;rdquo;&lt;/p&gt;

&lt;h3 id=&#34;Diving-deeper&#34;&gt;Diving deeper&lt;/h3&gt;

&lt;p&gt;The CCO opened his ChristmasPad and typed something into a terminal. &#38;ldquo;See? This is Ukrainian, by the way.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;ukr.svg&#34;&gt;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;That&#38;rsquo;s impressive,&#38;rdquo; admitted Santa, &#38;ldquo;but I fear there&#38;rsquo;s some kind of confusion.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Let&#38;rsquo;s have a look at a simpler example in English,&#38;rdquo; replied the CCO and quickly typed on the keyboard. &#38;ldquo;The annotated sentence is &lt;i&gt;Is that Microwave that you gave Dan really expensive?&lt;/i&gt;&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;microwave.svg&#34;&gt;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;You can see the pronoun &lt;i&gt;that&lt;/i&gt; references the word &lt;i&gt;microwave&lt;/i&gt;, and the word &lt;i&gt;microwave&lt;/i&gt; is an object of the verb &lt;i&gt;give&lt;/i&gt; in a semantic sense which we can also capture.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;No, no,&#38;rdquo; tried Santa to stop him, &#38;ldquo;linguistics is not our concern.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;That&#38;rdquo;s great!&#38;rdquo; rejoiced the CCO. &#38;ldquo;I&#38;rsquo;ve always wondered whether TrEd can be used outside of linguistics. There already is one such use: The tree editor serves as a client to a search engine. You assemble a tree and the engine searches your tree data to find where the tree would fit. The trick is you can specify different relations than the ordinary parent&#38;ndash;child one.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Ho ho ho,&#38;rdquo; nodded Santa, &#38;ldquo;Christmas is a family time!&#38;rdquo;.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;I mean this,&#38;rdquo; explained the CCO and again showed them his screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;pmltq.svg&#34;&gt;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Normally, the parent would be at the top, but here, we&#38;rsquo;re using the reversed relation, so the query will search for all nominal subjects whose parent is &lt;b&gt;not&lt;/b&gt; a verb.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;How can something that&#38;rsquo;s not a verb have a subject?&#38;rdquo; wondered Santa.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Let me show you the English example with the microwave again. The Universal Dependencies style uses adjectives in copula constructions as parents of the subject and the auxiliary verb. The word &lt;i&gt;expensive&lt;/i&gt; is not a verb, but the &lt;i&gt;microwave&lt;/i&gt; is its nominal subject.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;I fear this whole thing is of no use for us,&#38;rdquo; sighed the COO. &#38;ldquo;What programming language is the tool written in?&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;Perl,&#38;rdquo; replied the CCO. &#38;ldquo;It uses &lt;a href=&#34;https://metacpan.org/module/Tk::Canvas&#34;&gt;Tk::Canvas&lt;/a&gt; to edit the trees, which makes it rather easy to extend if you need more features.&#38;rdquo;&lt;/p&gt;

&lt;p&gt;&#38;ldquo;At least something our team would understand. And the search engine is also written in Perl?&#38;rdquo; asked the COO again.&lt;/p&gt;

&lt;p&gt;&#38;ldquo;There are in fact two implementations,&#38;rdquo; replied the CCO. &#38;ldquo;One uses SQL on &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;Postgres&lt;/a&gt; to store and query the data, but it&#38;rsquo;s only suitable for data that don&#38;rsquo;t change, as updating the database is quite slow. The second implementation uses Perl and is great for querying frequently changing data. If the data are large, you need some kind of parallelism to compensate its less favourable speed, we run it over &lt;a href=&#34;https://github.com/SchedMD/slurm&#34;&gt;slurm&lt;/a&gt;. But you can also write your queries directly in Perl. This will show you exactly the same trees as the query I showed you before.&#38;rdquo; And he again used the terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;btred -N -T -e &#39;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;FPosition()&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if $this-&#38;gt;{deprel} eq &#38;quot;nsubj&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;amp;&#38;amp; $this-&#38;gt;parent-&#38;gt;{upostag} ne &#38;quot;VERB&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#39; data/*.conllu | tred -l-&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;ldquo;Also, if you need to process the data without all the power TrEd offers, you can just use &lt;a href=&#34;https://metacpan.org/module/Treex::PML&#34;&gt;Treex::PML&lt;/a&gt;, the library that TrEd is based on. It implements the Prague Markup Language used as TrEd&#38;rsquo;s native data format. The previous five-liner turns almost into a screenful,&#38;rdquo; and he opened Elven Mate at Creating Scripts (EMaCS) and started to type, interrupted several times by squinting into the documentation.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synPreProc&#34;&gt;#!/usr/bin/perl&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use warnings&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use strict&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use feature&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;qw{ say }&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$ud_path&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synPreProc&#34;&gt;BEGIN &lt;/span&gt;{ &lt;span class=&#34;synIdentifier&#34;&gt;$ud_path&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$ENV{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;UD_DIR&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; }&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use lib&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$ud_path&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;/libs&#38;quot;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Treex::PML &lt;span class=&#34;synConstant&#34;&gt;qw{ ImportBackends AddResourcePath }&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@backends&lt;/span&gt; = ImportBackends(&lt;span class=&#34;synConstant&#34;&gt;&#39;UD&#39;&lt;/span&gt;);&lt;br /&gt;AddResourcePath(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$ud_path&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;/resources&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$schema&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;Treex::PML::Factory&#39;&lt;/span&gt;-&#38;gt;createPMLSchema({&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;use_resources&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;filename&lt;/span&gt;      =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;ud_schema.xml&#38;quot;&lt;/span&gt;});&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$file&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;@ARGV&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$doc&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;Treex::PML::Factory&#39;&lt;/span&gt;-&#38;gt;createDocumentFromFile(&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;$file&lt;/span&gt;, {&lt;span class=&#34;synConstant&#34;&gt;backends&lt;/span&gt; =&#38;gt; \&lt;span class=&#34;synIdentifier&#34;&gt;@backends&lt;/span&gt;});&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$tree_no&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$tree&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$doc&lt;/span&gt;-&#38;gt;trees) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$node_no&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;1&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;synStatement&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$node&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$tree&lt;/span&gt;-&#38;gt;descendants) {&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;synStatement&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;##&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$tree_no&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$node_no&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&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;synStatement&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$node-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;deprel&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;nsubj&#38;quot;&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;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$node&lt;/span&gt;-&#38;gt;parent-&#38;gt;{upostag} &lt;span class=&#34;synStatement&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;VERB&#38;quot;&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;$node_no&lt;/span&gt;;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;++&lt;span class=&#34;synIdentifier&#34;&gt;$tree_no&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;No-happy-ending&#34;&gt;No happy ending?&lt;/h3&gt;

&lt;p&gt;&#38;ldquo;It&#38;rsquo;s interesting, but I don&#38;rsquo;t see how our department could benefit from it,&#38;rdquo; shrugged the COO.&lt;/p&gt;

&lt;p&gt;Santa seemed lost in thought. &#38;ldquo;Maybe your department can&#38;rsquo;t,&#38;rdquo; murmured he, &#38;ldquo;but we have many other departments that need solutions&#38;hellip;&#38;rdquo;&lt;/p&gt;

&lt;p&gt;He dismissed the meeting by pointing at the door and strode towards his office. Can &lt;b&gt;you&lt;/b&gt; think of a way how you could benefit from the tool?&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-17T00:00:00Z</updated><category term="Perl"/><author><name>E. Choroba</name></author></entry><entry><title>Merry Inline C(hristmas)</title><link href="https://perladvent.org/2024/2024-12-16.html"/><id>https://perladvent.org/2024/2024-12-16.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Dancing-through-the-Cnow&#34;&gt;Dancing through the Cnow&lt;/h3&gt;

&lt;p&gt;It is this time of the year, the time you have finally some time to catch up finishing the projects you were supposed to do the previous months. A blizzard of C code, like the arrows in the movie 300 will descend upon you if you do, but you will end up in the naughty list of researchers if you don&#38;#39;t; for who will analyze your data if YOU drop the ball? But fear not, because &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; will save the day and Perl will make the glaCiers melt away.&lt;/p&gt;

&lt;p&gt;For the last couple of years, I have been using &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; to leverage a large amount of C code related to biological sequence (text) analysis for my research work. Our group has been using portable sequencing technologies by &lt;a href=&#34;https://nanoporetech.com/products/sequence/flongle&#34;&gt;Oxford Nanopore&lt;/a&gt; to measure RNA molecules in real-time as markers of kidney disease progression. The problem we are facing is that understanding the data is not trivial, and many traditional bioinformatic workflows need considerable adaptation to work. Often, one has to combine exotic pieces of code that is available in C libraries into complex workflows that are non standard and require one to use the power of Perl to glue them together. Let&#38;#39;s C how &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; can help us with the mess.&lt;/p&gt;

&lt;h3 id=&#34;To-A-or-not-to-A&#34;&gt;To A or not to A?&lt;/h3&gt;

&lt;p&gt;The molecules I am interested at have a tail of A&#38;#39;s at the end of the sequence, e.g. something line ACTGCCATCAAGAAAAAAAAAAAAA , but the A&#38;#39;s are irrelevant to the analysis I want to do. Often there are errors in the text, so that the sequence is not perfect, e.g. one is looking at something like this ACTGCCATCAAGAAAAAAAAAAAAAAGTAACAAAA. The question is, how can I remove the A&#38;#39;s at the end of the sequence knowing that the error exists? There is a Python program &lt;a href=&#34;https://cutadapt.readthedocs.io/en/stable/&#34;&gt;cutadapt&lt;/a&gt;, for this task, but note why I don&#38;#39;t want to use it for my tasks:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;I will have to fire off another process, which is slow&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I will have to use the hard disk and pipes for IPC (slow)&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My downstream data analyses take place in Perl and C, so I will have to convert the data back and forth&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let&#38;#39;s C how we can filter the noisy A&#38;#39;s in Perl and then how we can add performance with &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&#34;Regex-for-noisy-As&#34;&gt;Regex for noisy A&#38;#39;s&lt;/h4&gt;

&lt;p&gt;The general idea here is to use a regex that puts an upper limit to the proportion of errors in the A tails of the sequence. For example something like this:&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;$polyA_min_25_pct_A&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;regexp&#34;&gt;qr/&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;( ## match a poly A tail which is&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;## delimited at its 5&#39; by *at least* one A&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;A{1,}&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;## followed by the tail proper which has a&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;(?:     ## minimum composition of 25% A, i.e.&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;## we are looking for snippets with&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;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;## up to 3 CTGs followed by at&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;## least one A&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;[CTG]{0,3}A{1,}&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;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;|     ## OR&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;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;## at least one A followed by&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;## up to 3 CTGs&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;A{1,}[CTG]{0,3}&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;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;)+    ## extend as much as possible&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;)\z/xp&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 then use it like this:&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$s&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;ACTGCCATCAAGAAAAAAAAAAAAAAGTAACAAAA&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$s&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;m/$$polyA_min_25_pct_A/&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;$best_index&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;magic&#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;when one runs the code above, the &lt;code&gt;$best_index&lt;/code&gt; will be 29, i.e. the regex filtered out everything after: ACTGCC&lt;/p&gt;

&lt;p&gt;The cutadapt algorithm does not use a regex, but a simple scoring system to decide when to stop adding letters to the inferred tail. In particular, the algorithm considers all possible suffixes in the sequence of interest, and after filtering those that have more than 20% non-A letters, returns the position of the suffix with the largest score as the beginning of the tail. The algorithm in Perl is shown below:&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;perl_cutadapt&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$s&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;&lt;span class=&#34;keyword&#34;&gt;my&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;word&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$s&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$best_index&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$n&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$best_score&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$score&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;&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;$i&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;reverse&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;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;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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$nuc&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;substr&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$i&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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$score&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$nuc&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;A&#39;&lt;/span&gt; &lt;span class=&#34;operator&#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;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;&#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;$score&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$best_score&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;&lt;span class=&#34;symbol&#34;&gt;$best_index&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$i&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;&lt;span class=&#34;symbol&#34;&gt;$best_score&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$score&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;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$best_index&lt;/span&gt; &lt;span class=&#34;operator&#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;symbol&#34;&gt;$best_index&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;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$best_score&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;lt;&lt;/span&gt; &lt;span class=&#34;float&#34;&gt;0.4&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;$best_index&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;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;&lt;span class=&#34;symbol&#34;&gt;$best_index&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$best_index&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;The A part is deemed to be 23 letters long and the non-A part is inferred to be ACTGCCATCAAG&lt;/p&gt;

&lt;h4 id=&#34;Inline::C-for-performance&#34;&gt;Inline::C for performance&lt;/h4&gt;

&lt;p&gt;The algorithm as implemented is not very fast, and for large or many sequences it can be very slow. But as we will C, we can use &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; to speed things up. The C code is shown below:&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;Inline&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;C&lt;/span&gt;         &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#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;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_cutadapt_in_C&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$s&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;span class=&#34;comment&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;__C__&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &#38;lt;stdlib.h&#38;gt;&lt;br /&gt;#include &#38;lt;string.h&#38;gt;&lt;br /&gt;#include&#38;lt;stdio.h&#38;gt;&lt;br /&gt;#include &#38;lt;math.h&#38;gt;&lt;br /&gt;&lt;br /&gt;int _cutadapt_in_C(char *s) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int n = strlen(s);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int best_index = n;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int best_score = 0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int score = 0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (int i = n - 1; i &#38;gt;= 0; i--) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;char nuc = s[i];&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (nuc == &#39;A&#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;score += 1;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;else {&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;score -= 2;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (score &#38;gt; best_score) {&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;best_index = i;&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;best_score = score;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;best_index = (best_score &#38;lt; -0.4 * (best_index + 1)) ? n : n - best_index;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return best_index;&lt;br /&gt;&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But how fast is fast? Let&#38;#39;s run a benchmark using different sequence lengths and fixing the length of the A tail to be 20% of the sequence length. The results (mean and standard deviation in microseconds over 2000 repetitions for each length) are shown below (the benchmarking code may be found in the /scripts directory of &lt;a href=&#34;https://metacpan.org/module/Bio::SeqAlignment::Examples::TailingPolyester&#34;&gt;Bio::SeqAlignment::Examples::TailingPolyester&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;| Algorithm    | Language | Target Sequence Length |                    |                   |                 |&lt;br /&gt;|--------------|----------|------------------------|--------------------|-------------------|-----------------|&lt;br /&gt;|              |          | 100                    | 1000               | 2000              | 10000           |&lt;br /&gt;|--------------|----------|------------------------|--------------------|-------------------|-----------------|&lt;br /&gt;| cutadapt     | Perl     | 16.0&#38;plusmn;2.5               | 150.0&#38;plusmn;11.1         | 310.0&#38;plusmn;21.0        | 1500.0&#38;plusmn;88.0     |&lt;br /&gt;| regex        | Perl     | 26.0&#38;plusmn;10.0              | 310.0&#38;plusmn;26.0         | 620.0&#38;plusmn;42.0        | 3200.0&#38;plusmn;140.0    |&lt;br /&gt;| cutadaptC    | Perl/C   | 0.6&#38;plusmn;1.0                | 3.1&#38;plusmn;1.1            | 6.0&#38;plusmn;1.4           | 28.0&#38;plusmn;4.3        |&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A nice 30-50x speedup for the C code over the Perl code. The savings are real, considering that a typical long RNA-seq experiment may have 10^6 - 10^7 reads, and each read may have a length of 1000 bases.&lt;/p&gt;

&lt;h3 id=&#34;Making-memories-this-C-hristmas&#34;&gt;Making memories this C(hristmas)&lt;/h3&gt;

&lt;p&gt;The C code is not very complex, but it is a good example of how one can use &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; to speed up tasks. But the module can help with more than that. For example, one can use it to interface with other foreign code, by making and managing shared memory regions. Consider an example, in which we hijack the &lt;code&gt;Newxz&lt;/code&gt; and &lt;code&gt;Safefree&lt;/code&gt; functions from the Perl API to allocate and free memory areans to make &lt;code&gt;$memory&lt;/code&gt;. Such a variable is effectively a pointer to a memory arena, and we can use it to store and retrieve data from it. Suppose that one had a library that took such an arena as input and filled it with data. Then the arena could dance with any other library that expected a pointer to a memory arena. The library could be written in C, or Assembly. For example, this is how one can sum lots and lots of random numbers using either C or Assembly, under the loving embrace of Perl working with &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt;:&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;Inline&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;C&lt;/span&gt;         &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#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;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Inline&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;C&lt;/span&gt;         &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;2_000_000&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;$memory&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;alloc_with_Newxz&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;generate_random_double_array&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&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;Benchmark&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(:all)&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;cmpthese&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;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;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;C&#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;sum_array_C&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&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;single&#34;&gt;&#39;ASM&#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;sum_array_doubles&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&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;single&#34;&gt;&#39;ASM_AVX&#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;sum_array_doubles_AVX_unaligned&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$number_of_doubles&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;&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;free_with_Safefree&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&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;Inline&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ASM&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;DATA&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;AS&lt;/span&gt;      &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;nasm&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;ASFLAGS&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;-f elf64&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;PROTO&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;sum_array_doubles&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;double(void *,size_t)&#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;sum_array_doubles_AVX_unaligned&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;double(void *,size_t)&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;separator&#34;&gt;__DATA__&lt;/span&gt;&lt;span class=&#34;comment&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;data&#34;&gt;__C__&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &#38;lt;stdlib.h&#38;gt;&lt;br /&gt;#include &#38;lt;string.h&#38;gt;&lt;br /&gt;#include&#38;lt;stdio.h&#38;gt;&lt;br /&gt;#include &#38;lt;math.h&#38;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int _cutadapt_in_C(char *s) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int n = strlen(s);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int best_index = n;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int best_score = 0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;int score = 0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (int i = n - 1; i &#38;gt;= 0; i--) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;char nuc = s[i];&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (nuc == &#39;A&#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;score += 1;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;else {&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;score -= 2;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (score &#38;gt; best_score) {&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;best_index = i;&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;best_score = score;&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;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;best_index = (best_score &#38;lt; -0.4 * (best_index + 1)) ? n : n - best_index;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return best_index;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#define IsSVValidPtr(sv)  do { \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (!SvOK((sv))) { \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;croak(&#38;quot;Pointer is not defined&#38;quot;); \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;} \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (!SvIOK((sv))) { \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;croak(&#38;quot;Pointer does not contain an integer&#38;quot;); \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;} \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;IV value = SvIV((sv)); \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;if (value &#38;lt;= 0) { \&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;croak(&#38;quot;Pointer is negative or zero&#38;quot;); \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;} \&lt;br /&gt;} while(0)&lt;br /&gt;&lt;br /&gt;#define SetTypedPtr(ptr,sv, type) type *ptr; \&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ptr = (type *) SvIV((sv))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void generate_random_double_array(SV *sv, size_t num_elements) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;IsSVValidPtr(sv);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;SetTypedPtr(array, sv, double);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (size_t i = 0; i &#38;lt; num_elements; ++i) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;array[i] = ((double)rand() / RAND_MAX) * 10.0 - 5.0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;double sum_array_C(SV *sv, size_t length) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;IsSVValidPtr(sv);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;double sum = 0.0;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;SetTypedPtr(array, sv, double);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;for (size_t i = 0; i &#38;lt; length; i++) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sum += array[i];&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return sum;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// get a buffer&lt;br /&gt;SV* alloc_with_Newxz(size_t length) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;char* array ;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Newxz(array, length, char);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;return newSVuv(PTR2UV(array));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void free_with_Safefree(size_t address) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;void* buffer = (void*)address;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Safefree(buffer);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;__ASM__&lt;br /&gt;NSE    equ 4 ; number of SIMD double elements per iteration&lt;br /&gt;DOUBLE equ 8 ; number of bytes per double&lt;br /&gt;; Use RIP-relative memory addressing&lt;br /&gt;default rel&lt;br /&gt;&lt;br /&gt;; Mark stack as non-executable for Binutils 2.39+&lt;br /&gt;section .note.GNU-stack noalloc noexec nowrite progbits&lt;br /&gt;&lt;br /&gt;SECTION .text&lt;br /&gt;&lt;br /&gt;global sum_array_doubles&lt;br /&gt;sum_array_doubles: ; based on Kusswurm listing 5-7c&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;; Initialize&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vxorpd xmm0, xmm0, xmm0 ; sum = 0.0&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sub rdi, DOUBLE              ; rdi = &#38;amp;array[-1]&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Loop1:&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;add rdi, DOUBLE&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vaddsd xmm0, xmm0, qword [rdi]&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sub rsi, 1&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;jnz Loop1&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ret&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;global sum_array_doubles_AVX_unaligned&lt;br /&gt;sum_array_doubles_AVX_unaligned: ; based on Kusswurm listing 9-4d&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vxorpd ymm0, ymm0, ymm0         ; sum = 0.0&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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;; i = 0 in the comments of this block&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;lea r10,[rdi - DOUBLE]          ; r10 = &#38;amp;array[i-1]&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;cmp rsi, NSE                    ; check if we have at least NSE elements&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;jb Remainder_AVX                ; if not, jump to remainder&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;lea r10, [rdi-NSE * DOUBLE]     ; r10 = &#38;amp;array[i-NSE]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Loop1_AVX:&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;add r10, DOUBLE * NSE        ; r10 = &#38;amp;array[i]&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vaddpd ymm0, ymm0, [r10]     ; sum += array[i]&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sub rsi, NSE                 ; decrement the counter&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;cmp rsi, NSE                 ; check if we have at least NSE elements&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;jae Loop1_AVX                ; if so, loop again&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;; Reduce packed sum using SIMD addition&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vextractf128 xmm1, ymm0, 1      ; extract the high 128 bits&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vaddpd xmm2, xmm1, xmm0         ; sum += high 128 bits&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vhaddpd xmm0, xmm2, xmm2        ; sum += low 128 bits&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;test rsi, rsi                   ; check if we have any elements left&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;jz End_AVX                      ; if not, jump to the end&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;add r10, DOUBLE * NSE  - DOUBLE ; r10 = &#38;amp;array[i-1]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;; Handle the remaining elements&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;Remainder_AVX:&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;add r10, DOUBLE&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;vaddsd xmm0, xmm0, qword [r10]&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;sub rsi, 1&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;jnz Remainder_AVX&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;End_AVX:&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;;vmovsd xmm0, xmm5&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;ret&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this example we make 2 million of doubles, fill them up with random numbers and then benchmark their sum them up in either C or Assembly. For the latter we can use your grandfather&#38;#39;s era Assembly or bring to the table a vectorized version that uses SIMD instructions (in this case AVX extensions). In my old Xeon, this is what I get:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;          Rate     ASM       C ASM_AVX
ASM      565/s      --     -0%    -68%
C        565/s      0%      --    -68%
ASM_AVX 1778/s    215%    215%      --&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In my applications, I use this trick to interface with vectorized, hand optimized Assembly code, when intrinsics fail to deliver performance in C. But even in such cases, the memory management is done by Perl through &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Give your self a present this C(hristmas) and learn how to use &lt;a href=&#34;https://metacpan.org/module/Inline::C&#34;&gt;Inline::C&lt;/a&gt; to speed things up. And if making memories seems too much, fear not, the &lt;a href=&#34;https://metacpan.org/module/Task::MemManager&#34;&gt;Task::MemManager&lt;/a&gt; module that I wrote up, will cut you some slaCk.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-16T00:00:00Z</updated><category term="Perl"/><author><name>Christos Argyropoulos</name></author></entry><entry><title>Perl, my child, is love in Github Actions</title><link href="https://perladvent.org/2024/2024-12-15.html"/><id>https://perladvent.org/2024/2024-12-15.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;img src=&#34;elf-ci.jpg&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Santa saw Christmas was approaching and there was so much stuff to do already. A lot of it had to do with quality assurance: were the newsletters added properly to the repository? Did those newsletter include whatever needed to be included in the first place? Were they properly formatted?&lt;/p&gt;

&lt;p&gt;As any self-respecting Christmas-coding shop, Santa used GitHub. And needed to set up the QA pipelines properly. And fast. With Perl. And what&#38;#39;s best to have stuff dome quickly? Like tinsel in Christmas, boilerplate is what takes you there.&lt;/p&gt;

&lt;h3 id=&#34;Lets-talk-a-bit-about-GitHub-Actions&#34;&gt;Let&#38;#39;s talk a bit about GitHub Actions&lt;/h3&gt;

&lt;p&gt;There are several kinds of GitHub actions. You can create them using a Docker container or JavaScript. But there&#38;#39;s a third kind called &lt;a href=&#34;https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action&#34;&gt;composite actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A composite action essentially is a combination of several steps that might include other actions or running scripts. You can basically include the whole action in a single file, like this.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Hello Perl&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Simplest Perl composite action&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;branding&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;icon&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;briefcase&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;blue&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;inputs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;action-input&lt;/span&gt;&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;synIdentifier&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;What it is about&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;required&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt; # or not&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;World&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;runs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;using&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;composite&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; print %ENV;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;shell&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; perl &lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Print input&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;env&lt;/span&gt;&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;synIdentifier&#34;&gt;TEMPLATE_INPUT&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ${{ inputs.action-input}}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; print $ENV{&#39;ACTION_INPUT&#39;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;shell&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; perl &lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a very basic &lt;code&gt;action.yml&lt;/code&gt; file, that placed in your main directory will simply print the environment variables to your GitHub actions visible log. Not a great deal, useful if you want to know the values of certain variables. But it can be used as first steps to any action, to debug it... and it uses Perl to do so.&lt;/p&gt;

&lt;p&gt;It&#38;#39;s not very widely known, but you can add a &lt;code&gt;shell&lt;/code&gt; key to any step in a GitHub action so that it interprets whatever is in the &lt;code&gt;run&lt;/code&gt; step; the &lt;code&gt;{0}&lt;/code&gt; will be substituted by the name of a (I guess) temporal file that contains the step. So this is kinda&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl possibly_temporary_file_that_contains_print_ENV.pl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(It might create a temporary file with a shebang and run it, TBH I don&#38;#39;t know)&lt;/p&gt;

&lt;p&gt;But see, we&#38;#39;re not using any kind of container or downloaded module or anything else. Just the very basic stuff that&#38;#39;s already there in the Ubuntu runner (and, as far as I can tell, in other runners too).&lt;/p&gt;

&lt;p&gt;You can go ahead and test it this way:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Run basic action&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;test&lt;/span&gt;&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;synIdentifier&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ubuntu-latest&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Run basic action&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Run basic test&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; JJ/perl-advent-2024-test-action-1@main&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(or whatever else you named it). It will simply print a wall of environment variable names and values, badly formatted. But the point is, it just works and it&#38;#39;s fast since it&#38;#39;s not using anything that&#38;#39;s not already in the Ubuntu runner.&lt;/p&gt;

&lt;p&gt;Who said badly formatted? Maybe we can do it better? Right-on, let&#38;#39;s use &lt;a href=&#34;https://metacpan.org/module/JSON::PP&#34;&gt;JSON::PP&lt;/a&gt;. Change the last step in &lt;code&gt;action.yml&lt;/code&gt; to:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; |&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;use JSON::PP;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;print JSON::PP-&#38;gt;new-&#38;gt;ascii-&#38;gt;pretty-&#38;gt;allow_nonref-&#38;gt;encode( \%ENV );&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;shell&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; perl &lt;span class=&#34;synSpecial&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is going to be a bit nicer. But what gives? We&#38;#39;re not using CPAN. Right, there are quite a bit of CPAN modules already installed there, just like this one. Since &lt;code&gt;perl&lt;/code&gt; is there, and &lt;code&gt;cpan&lt;/code&gt; too, you will have at least any module that goes with any of them (CGI is no longer there, so you will not be able to deploy a website while your action is running). &lt;a href=&#34;https://metacpan.org/module/JSON::PP&#34;&gt;JSON::PP&lt;/a&gt; is one of those core modules, so no big deal. And no big time: this takes all of 0 seconds to run (OK, not really 0, but that&#38;#39;s what&#38;#39;s reported. Probably takes a small fraction of a second). Why would it take longer? All you need to run the action is already there, set up for you to use.&lt;/p&gt;

&lt;p&gt;What else is in there? A probably incomplete list is &lt;a href=&#34;https://gist.github.com/JJ/edf3a39d68525439978da2a02763d42b&#34;&gt;here&lt;/a&gt; but I am not totally sure it&#38;#39;s up to date; in fact, I &lt;i&gt;know&lt;/i&gt; that &lt;a href=&#34;https://github.com/actions/runner-images/issues/10567&#34;&gt;LWP::Protocol::https has disappeared&lt;/a&gt;, so there is that. If I had to make broad categories of the modules we can find, there are the Debian configuration related modules, &lt;a href=&#34;https://metacpan.org/module/LWP&#34;&gt;LWP&lt;/a&gt; and auxiliaries, &lt;code&gt;git&lt;/code&gt; and HTML stuff, and odds and ends. There are enough goodies there that I would look first before installing something via cpan, which takes time, you need to set up a cache, and so on.&lt;/p&gt;

&lt;p&gt;The caveat? All that is pretty much undocumented, so anything you rely on (as the above mentioned &lt;code&gt;LWP::Protocol::https&lt;/code&gt; might disappear from one version of the runner to the next.&lt;/p&gt;

&lt;h3 id=&#34;But-theres-a-lot-of-boilerplate&#34;&gt;But there&#38;#39;s a lot of boilerplate&lt;/h3&gt;

&lt;p&gt;Right-on, there is. Files need to be created, values need to be filled, and doing it from scratch can be cumbersome and CoPilot is not there to help you. No sweat. Just use &lt;a href=&#34;https://github.com/JJ/perl-composite-action-template&#34;&gt;this template&lt;/a&gt; for an action. Instantiate it, and hit the ground running.&lt;/p&gt;

&lt;p&gt;In fact, there&#38;#39;s a bit more there: a &lt;code&gt;lib&lt;/code&gt; directory, a &lt;code&gt;cpanfile&lt;/code&gt; and some other things. We&#38;#39;ll get to that later. Meanwhile, let&#38;#39;s return our attention to old Santa, who wants all his newsletters properly formatted, that is, the first line must contain a Markdown header: &lt;code&gt;#&lt;/code&gt; followed by a space, and then a capital letter. A proper headline through and through.&lt;/p&gt;

&lt;p&gt;We can do a proper module here to test our stuff. In the spirit of Unix (and Perl), we can do very simple actions, that take very little time, and which we can combine different ways or just run independently (like for instance &lt;a href=&#34;https://github.com/JJ/github-action-check-version-in-readme-is-latest&#34;&gt;this very useful -and underappreciated- action that just checks that the example in the README has the same version as the latest published one&lt;/a&gt;). So let&#38;#39;s put the code that does the action in a module:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Markdowner&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use feature&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;signatures&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use parent&lt;/span&gt; Exporter;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;our&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@EXPORT_OK&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;qw(headerOK)&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;headerOK&lt;/span&gt;( $&lt;span class=&#34;synIdentifier&#34;&gt;fileContent &lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$fileContent&lt;/span&gt; =~ &lt;span class=&#34;synStatement&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;^&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\#\h+[A-Z]&lt;/span&gt;&lt;span class=&#34;synStatement&#34;&gt;/&lt;/span&gt;;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just a simple regex to check what we said. But code that is not tested is broken, so let add a test:&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 ../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;Markdowner&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(headerOK)&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;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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$str&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;double&#34;&gt;&#38;quot;# Yes&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;#    FOO&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;# Bar&#38;quot;&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;&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;headerOK&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$str&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;&#171;$str&#187; is OK&#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;for&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$badStr&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;double&#34;&gt;&#38;quot;#Yes&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;#    foo&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;#\nBar&#38;quot;&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;&lt;span class=&#34;word&#34;&gt;isnt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;headerOK&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$badStr&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;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;&#171;$badStr&#187; fails&#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;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You might wonder why I&#38;#39;m using &lt;a href=&#34;https://metacpan.org/module/Test::More&#34;&gt;Test::More&lt;/a&gt;, which is deprecated-ish in favor of Test2::Bundle::More, instead of the latter. Along with why I&#38;#39;m using a feature pragma instead of the more precise &lt;code&gt;use V5.36&lt;/code&gt;. Your questions will be answered in due time.&lt;/p&gt;

&lt;p&gt;Because right next we will, or course, be testing this via Actions:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Perl tests&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;push&lt;/span&gt;&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;synIdentifier&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;*&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;pull_request&lt;/span&gt;&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;synIdentifier&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;*&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;build-in-container&lt;/span&gt;&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;synIdentifier&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ubuntu-latest&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;strategy&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;fail-fast&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;false&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;matrix&lt;/span&gt;&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;synIdentifier&#34;&gt;version&lt;/span&gt;&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;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;5.32&#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;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;5.34&#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;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;5.30&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Test perl v${{ matrix.version }}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; actions/checkout@v4&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Regular tests with ${{ matrix.version }}&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;env&lt;/span&gt;&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;&lt;span class=&#34;synIdentifier&#34;&gt;PERL_VERSION&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ${{ matrix.version }}&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;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; prove --exec &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;perl -Mv$PERL_VERSION&#38;quot;&lt;/span&gt; -lv t/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The version that the runner uses is 5.34; for the time being the default Ubuntu runner is all it has. And we want to use that default runner. See here? No setup necessary; we just set up the matrix, check out, and run the tests! Actually, we are kinda cheating here. Since the only perl we do have in the runner is the 5.34 version, we need to simulate the other versions via a not very well known option of `prove` that allows us to tell it which version pragma is going to precede the running of the test. Since we don&#38;#39;t have any other version to override it, it will simply run 5.34 &lt;i&gt;as if&lt;/i&gt; it was running the version in the matrix. This test again clocks in at 0 seconds in GitHub actions, the only fraction of the action taking up a bit more being checkout. Again, fast as fast can be as long as you can put up with testing stuff with 5.34, which is no big deal, since actions are going to run in the action runner.&lt;/p&gt;

&lt;p&gt;Of course, you have to actually &lt;i&gt;do&lt;/i&gt; something with this for the action to work. So you can write something like this in a file called &lt;code&gt;action.src.pl&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;v5.34&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use feature&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;signatures&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use lib&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;qw(lib)&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Markdowner &lt;span class=&#34;synConstant&#34;&gt;qw(headerOK)&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;GitHub::Actions;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@directories&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;split&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot; &#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$ENV{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;DIRS&#39;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;start_group(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Markdown headers&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$dir&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;@directories&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@markdownFiles&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;glob&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$dir&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;/*.md&#38;quot;&lt;/span&gt;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$mdFile&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;@markdownFiles&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$mdfh&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&#38;lt;&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$mdFile&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$firstLine&lt;/span&gt; = &#38;lt;&lt;span class=&#34;synIdentifier&#34;&gt;$mdfh&lt;/span&gt;&#38;gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;close&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$mdfh&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;chop&lt;/span&gt;( &lt;span class=&#34;synIdentifier&#34;&gt;$firstLine&lt;/span&gt; );&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt; ( headerOK( &lt;span class=&#34;synIdentifier&#34;&gt;$firstLine&lt;/span&gt; ) ) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;debug &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&#194;&#171;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$firstLine&#194;&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#187; is proper markdown ho, ho, ho&#38;quot;&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;} &lt;span class=&#34;synStatement&#34;&gt;else&lt;/span&gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;error_on_file( &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Haar! &#194;&#171;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$firstLine&#194;&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#187; is not proper markdown&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$mdFile&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; );&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;br /&gt;end_group;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which takes as input a string with whitespace-separated directories, looks up markdown files (with the extension &lt;code&gt;.md&lt;/code&gt;), and checks if the header is OK. But before doing that, it issues a &lt;code&gt;start_group&lt;/code&gt; command from &lt;a href=&#34;https://metacpan.org/module/GitHub::Actions&#34;&gt;GitHub::Actions&lt;/a&gt;. This is a no-dependencies CPAN module by YT that perlizes GitHub actions commands so that you don&#38;#39;t have to worry about them; in this case, it makes all text to be enclosed in a &lt;i&gt;group&lt;/i&gt; so that the output is not too verbose; too many messages can occlude actual errors or warnings. Because it will also print if the first line of the markdown files is OK, and Santa can be happy about his newsletters and memos being properly formatted, or maybe not, in which he will turn into a pirate and issue an error indicating the name of the file (and the line and columns, but this is not important in this case).&lt;/p&gt;

&lt;p&gt;You can check how it goes, for instance, &lt;a href=&#34;https://github.com/JJ/perl-advent-2024-test-action-2/actions/runs/12098612575&#34;&gt;in this run&lt;/a&gt; (actually, while you&#38;#39;re at it, you can check the whole repository &lt;a href=&#34;https://github.com/JJ/perl-advent-2024-test-action-2&#34;&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Still not there, however. Now what we have is a CPAN module, didn&#38;#39;t you sell us on the motto &#38;quot;No setup required&#38;quot;? Right on, I did, which is why now you need to fatpack the whole thing into a single file &lt;code&gt;action.pl&lt;/code&gt;. This is what we will actually pack into the action-packed action. OK, maybe that&#38;#39;s an action too many. Anyway, we can now define the action metadata this way:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;inputs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;directories&lt;/span&gt;&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;synIdentifier&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Directories to look for files&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; .&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;runs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;using&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;composite&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; actions/checkout@v4&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ${GITHUB_ACTION_PATH}/action.pl&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;env&lt;/span&gt;&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;synIdentifier&#34;&gt;DIRS&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ${{ inputs.directories }}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;shell&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; bash&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It checks out the repository that holds the action, but it actually needs just the single file without needing to adjust library directories or anything like that. It just works. The specific step takes around 1 second, with is 100% more than it did when it took 0 seconds, but still. Not a great deal.&lt;/p&gt;

&lt;p&gt;With this, Santa was happy because he could set up GitHub Actions for his newsletter/letter/whatever I said above that used markdown repository. Be it in its own action or as part or another, this will be quite enough:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; checkout&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; actions/checkout@v4&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Run over this repo&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; repo/action-name@main&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;with&lt;/span&gt;&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;synIdentifier&#34;&gt;directories&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;. docs&#38;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Haar! There was a bad file in the batch! Without failing the whole step, which wouldn&#38;#39;t look good on the repo badges, GitHub reported &#38;quot;1 error&#38;quot; in &#38;quot;Annotations&#38;quot; for the workflow, and then&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;     Run over this repo: docs/this-is-bad.md#L1
     Haar! &#38;laquo;Just bad&#38;raquo; is not proper markdown&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;He called the responsible elf after running &lt;code&gt;git blame&lt;/code&gt; on the bad file, who said of course that &#38;quot;Ha ha I was just testing ha. That&#38;#39;s a mighty good workflow. Merry Christmas to you!&#38;quot;&lt;/p&gt;

&lt;p&gt;Santa smiled broadly and said &#38;quot;That it is. Perl, my child, is love in GitHub actions&#38;quot; Merry Christmas everyone!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-15T00:00:00Z</updated><category term="Perl"/><author><name>JJ Merelo</name></author></entry><entry><title>Using Valiant for Validations in Agendum: A Deep Dive into DBIx::Class Integration</title><link href="https://perladvent.org/2024/2024-12-14.html"/><id>https://perladvent.org/2024/2024-12-14.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;img src=&#34;valiant_logo.webp&#34; height=250 width=250 style=&#39;float:left;padding-right:1em&#39; /&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/jjn1056/Agendum&#34;&gt;Agendum&lt;/a&gt; is a single user task-tracking application built on Perl &lt;a href=&#34;https://metacpan.org/module/Catalyst&#34;&gt;Catalyst&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt;. I&#38;#39;m using it as a testbed to experiment with various new approaches to building web applications with this venerable framework as well as a platform to demo those experiments in articles and at conferences.&lt;/p&gt;

&lt;p&gt;One concern of any web framework is data validation. Perl has no lack of validation libraries, including some that have &lt;a href=&#34;https://metacpan.org/module/Catalyst&#34;&gt;Catalyst&lt;/a&gt; integrations. To this list of options I&#38;#39;ve added my own approach, &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; which I hope has enough distinguishing features to merit your interest.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; can be used in several ways, but in this article I&#38;#39;m going to focus on how I&#38;#39;ve integrated it with &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; in Agendum. I recognize many people prefer validation as a layer separate from the physical database model, but I&#38;#39;ve found that for many applications it&#38;#39;s useful to have the validation declaratively defined in the model layer. This allows for a single source of truth for the validation rules, and also allows for the validation to be automatically applied when the data changed on the database. This is especially useful in a web application where the data is often roundtripped between the database and the user for editing. If this offends your sense of architectural purity, there are ways to use &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; that are more separate from the database layer, but I&#38;#39;m not going to cover that in this article. You&#38;#39;ll have to look over the documentation yourself ;). In any case this approach has worked well for many other frameworks such as Ruby on Rails, so I think it&#38;#39;s worth considering for Perl as well.&lt;/p&gt;

&lt;p&gt;This article will not cover all the ins and outs of &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; but I will provide an overview of the features that are most relevant to this discussion. Hopefully this will provoke your interest enough to check out the &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; documentation and give it a try. If you are a &#38;#39;code first&#38;#39; kind of person, you can also just clone down &lt;a href=&#34;https://github.com/jjn1056/Agendum&#34;&gt;Agendum&lt;/a&gt;, look it over and start it up with &lt;code&gt;make up&lt;/code&gt; on any system with Docker installed.&lt;/p&gt;

&lt;h3 id=&#34;Valiant-Overview-in-a-nutshell&#34;&gt;Valiant Overview (in a nutshell)&lt;/h3&gt;

&lt;p&gt;This is a very brief overview of &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt;. For more details please see the &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; basically is a role that can be applied to any Perl class. It provides a declarative way to define validation rules for the class. These rules typically are applied to &lt;a href=&#34;https://metacpan.org/module/Moo&#34;&gt;Moo&lt;/a&gt; or &lt;a href=&#34;https://metacpan.org/module/Moose&#34;&gt;Moose&lt;/a&gt; style attributes but you can also declare model level validations when you have rules that apply to the object as a whole or to multiple attributes. The rules are defined in a simple DSL that is easy to read and write. Here&#38;#39;s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; MyApp::Model::User&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Moo;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Valiant::Validations;&lt;br /&gt;&lt;br /&gt;has &lt;span class=&#34;synConstant&#34;&gt;&#39;name&#39;&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;is&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;ro&#39;&lt;/span&gt;);&lt;br /&gt;has &lt;span class=&#34;synConstant&#34;&gt;&#39;email&#39;&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;is&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;ro&#39;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;validates &lt;span class=&#34;synConstant&#34;&gt;&#39;name&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;;&lt;br /&gt;validates &lt;span class=&#34;synConstant&#34;&gt;&#39;email&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;format&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;email&#39;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This adds two validation rules to the &lt;code&gt;User&lt;/code&gt; class. The first rule says that the &lt;code&gt;name&lt;/code&gt; attribute must be present (i.e. not &lt;code&gt;undef&lt;/code&gt; or an empty string). The second rule says that the &lt;code&gt;email&lt;/code&gt; attribute must be a valid email address. Let&#38;#39;s see how this works in practice:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$user&lt;/span&gt; = MyApp::Model::User-&#38;gt;new(&lt;span class=&#34;synConstant&#34;&gt;name&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;John&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;email&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;&#39;not_an_email&#39;&lt;/span&gt;);&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$user&lt;/span&gt;-&#38;gt;validate;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;%errors&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$user&lt;/span&gt;-&#38;gt;errors-&#38;gt;to_hash(&lt;span class=&#34;synConstant&#34;&gt;full_messages&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will return a hash:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;( &lt;span class=&#34;synConstant&#34;&gt;email&lt;/span&gt; =&#38;gt; [&lt;span class=&#34;synConstant&#34;&gt;&#39;Email is not an email address&#39;&lt;/span&gt;] )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#38;#39;s a lot of different ways to define validations, to customize error messages, and to retrieve the errors. You can also define custom validators, and you can define validations that are conditional on other attributes. You can also define validations that are conditional on the object state. All this is covered in the full &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; documentation but this gives you the basic idea.&lt;/p&gt;

&lt;h3 id=&#34;The-Demo-Database-Model&#34;&gt;The Demo Database Model&lt;/h3&gt;

&lt;p&gt;Here&#38;#39;s an overview of the database model for Agendum, which we&#38;#39;ll use as a demo for the rest of this article:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;            +-----------------+                +------------------+
            |      tasks      |                |     comments     |
            +-----------------+                +------------------+
            | *task_id*       |&#38;lt;------------+  | *comment_id*     |
            | title           |             |--| task_id (FK)     |
            | description     |                | content          |
            | due_date        |                | created_at       |
            | priority        |                +------------------+
            | status          |
            | created_at      |
            | updated_at      |
            +-----------------+
                   ^
                   |
                   |
            +-----------------+
            |   task_labels   |
            +-----------------+
            | *task_id* (FK)  |
            | *label_id* (FK) |
            +-----------------+
                   |
                   v
            +-----------------+
            |      labels     |
            +-----------------+
            | *label_id*      |
            | name (UNIQUE)   |
            +-----------------+&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The database model is pretty simple:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;Tasks&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Stores core information for each task, including the title, description, due_date, priority, and status. This is the main entity in the application.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Labels&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Contains information for categorizing tasks, such as &#38;#39;Work&#38;#39;, &#38;#39;Research&#38;#39;, or &#38;#39;Planning&#38;#39;. Tasks can be assigned zero or more labels.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Task-Labels&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;A many-to-many relationship table linking tasks and labels, enabling a task to have multiple labels and a label to apply to multiple tasks.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Comments&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Stores comments on tasks; basically a freeform note taking log of activity on a task. Each task can have zero or more comments.&lt;/p&gt;

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

&lt;p&gt;If you want full details you can check out the actual SQL used to create the database in the Agendum repository &lt;a href=&#34;https://github.com/jjn1056/Agendum/blob/main/sql/deploy/initial.sql&#34;&gt;https://github.com/jjn1056/Agendum/blob/main/sql/deploy/initial.sql&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Here&#38;#39;s the validation rules for the &lt;code&gt;Task&lt;/code&gt; class in Agendum:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;Task Title&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The task title must be present and between 2 and 48 characters.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Task Description&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The task description must be present and between 2 and 2000 characters.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Task Due Date&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The task due date must be present and a valid date that is greater than or equal to the current date. This validation is only enforced when the due date is created or actually changed in an update.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Task Priority&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The task priority must be present and be a number between 1 and 5&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;Task Status&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The task status must be present and be one of &#38;#39;pending&#38;#39;, &#38;#39;in_progress&#38;#39;, &#38;#39;on_hold&#38;#39;, &#38;#39;blocked&#38;#39;, &#38;#39;canceled&#38;#39;, or &#38;#39;completed&#38;#39;. A task cannot return to &#38;#39;Pending&#38;#39; once it&#38;#39;s begun. In addition, once a task is set to completed it cannot be changed to any other status. Lastly a task that is &#38;#39;on hold&#38;#39; or &#38;#39;blocked&#38;#39; can&#38;#39;t be set to completed directly.&lt;/p&gt;

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

&lt;p&gt;And the rules for the &lt;code&gt;Comment&lt;/code&gt; class:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;Content&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;The content must be present and between 2 and 2000 characters.&lt;/p&gt;

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

&lt;p&gt;There&#38;#39;s an element of arbitrariness to these rules, but I wanted something complex enough to be interesting but not so complex that it would be overwhelming. Feel free to tinker as you wish.&lt;/p&gt;

&lt;h3 id=&#34;Valiant-and-DBIx::Class&#34;&gt;Valiant and DBIx::Class&lt;/h3&gt;

&lt;p&gt;So how do we integrate &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; with &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt;? There are two &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; components that you need to add to your result sources and your resultsets, which integrate &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; with &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; and additionally provide extra features to make it work more like how you&#38;#39;d expect it. We also provide some value added features to &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; to make it easier to work with &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; in the context of a web application, such as full recursive update and create. You can check out the core documentation at your leisure (&lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant&#34;&gt;DBIx::Class::Valiant&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant::Result&#34;&gt;DBIx::Class::Valiant::Result&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant::ResultSet&#34;&gt;DBIx::Class::Valiant::ResultSet&lt;/a&gt;) , but I&#38;#39;ll provide a brief overview here using code from Agendum. First here&#38;#39;s the Schema class (if you are not super familiar with &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; you might want to check out the &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; documentation before continuing):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;DBIx::Class::Schema&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;load_components(&lt;span class=&#34;synConstant&#34;&gt;qw/&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::Schema::QuoteNames&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::Schema::DidYouMean&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::Schema::DateTime/&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;load_namespaces(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;default_resultset_class&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;DefaultRS&#38;quot;&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#38;#39;s nothing here directly related to &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; but I wanted to show you the schema class for completeness. Here&#38;#39;s the base result class:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema::Result&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;DBIx::Class&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;load_components(&lt;span class=&#34;synConstant&#34;&gt;qw/&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Valiant::Result&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Valiant::Result::HTML::FormFields&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  ResultClass::TrackColumns&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Core&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  InflateColumn::DateTime&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;/&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here you can see I&#38;#39;m installing the core &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant::Result&#34;&gt;DBIx::Class::Valiant::Result&lt;/a&gt; component, as well as some other components that are useful for web applications. The &lt;code&gt;Valiant::Result::HTML::FormFields&lt;/code&gt; component is useful for generating form fields in a web application, and the &lt;code&gt;ResultClass::TrackColumns&lt;/code&gt; component is useful for tracking changes to the database model. You will see later that we need that to make the status validations work.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;InflateColumn::DateTime&lt;/code&gt; component is useful for working with &lt;a href=&#34;https://metacpan.org/module/DateTime&#34;&gt;DateTime&lt;/a&gt; objects in the database model.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind is that &lt;code&gt;Valiant::Result&lt;/code&gt; needs to be the first component in the list. I&#38;#39;m working on figuring out if this can be relaxed, but for now it&#38;#39;s a requirement.&lt;/p&gt;

&lt;p&gt;Here&#38;#39;s the base resultset class:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema::ResultSet&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;DBIx::Class::ResultSet&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;load_components(&lt;span class=&#34;synConstant&#34;&gt;qw/&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Valiant::ResultSet&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::ResultSet::Shortcut&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::ResultSet::Me&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::ResultSet::SetOperations&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;  Helper::ResultSet::IgnoreWantarray&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synConstant&#34;&gt;/&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again I&#38;#39;m installing the core &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant::ResultSet&#34;&gt;DBIx::Class::Valiant::ResultSet&lt;/a&gt; component, as well as some other components that are useful for web applications. Our component again is first in the list.&lt;/p&gt;

&lt;p&gt;Lastly here&#38;#39;s the default resultset class, which was mentioned in the Schema class. This is the resultset DBIC uses when you don&#38;#39;t have a custom one for a given result source:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema::DefaultRS&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::ResultSet&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see there is not much going on here. I know a lot of people set the default resultset class to the base resultset but I&#38;#39;d rather separate them out. It&#38;#39;s a personal preference.&lt;/p&gt;

&lt;p&gt;Ok, so far there is nothing really interesting. The real magic happens in the result classes. Let&#38;#39;s do the &lt;code&gt;Task&lt;/code&gt; result class. I will introduce the code in sections with explanations but you can always see the full code in the Agendum repository.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema::Result::Task&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::Result&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;table(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;tasks&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;add_columns(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;task_id&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;integer&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_auto_increment&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;title&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;varchar&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;size&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;255&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;description&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;text&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;due_date&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;date&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;priority&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;integer&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;default_value&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;status&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;varchar&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;size&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;50&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;default_value&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;pending&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;track_storage&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;created_at&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;timestamptz&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;default_value&lt;/span&gt; =&#38;gt; \&lt;span class=&#34;synConstant&#34;&gt;&#39;NOW()&#39;&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;updated_at&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;timestamptz&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;default_value&lt;/span&gt; =&#38;gt; \&lt;span class=&#34;synConstant&#34;&gt;&#39;NOW()&#39;&lt;/span&gt; },&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;set_primary_key(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;task_id&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;has_many(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;comments&lt;/span&gt; =&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::Result::Comment&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;{ &lt;span class=&#34;synConstant&#34;&gt;&#39;foreign.task_id&#39;&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;self.task_id&#39;&lt;/span&gt; }&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;has_many(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;task_labels&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::Result::TaskLabel&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;{ &lt;span class=&#34;synConstant&#34;&gt;&#39;foreign.task_id&#39;&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;self.task_id&#39;&lt;/span&gt; }&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This first part is just the standard &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; stuff. We define the table, the columns, the primary key, and the relationships. BTW you can use &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Candy&#34;&gt;DBIx::Class::Candy&lt;/a&gt; with this if you like, &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; integrations support that and you can read about it in the docs. However you might have noticed the &lt;code&gt;track_storage&lt;/code&gt; flag on the status field. We need that for the status validations since we will be comparing the state in the database to the new proposed value. It&#38;#39;s provided by the &lt;code&gt;DBIx::ClassResultClass::TrackColumns&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;The next part is where we start adding the &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; stuff:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;__PACKAGE__-&#38;gt;accept_nested_for(&lt;span class=&#34;synConstant&#34;&gt;&#39;task_labels&#39;&lt;/span&gt;, {&lt;span class=&#34;synConstant&#34;&gt;allow_destroy&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;});&lt;br /&gt;__PACKAGE__-&#38;gt;accept_nested_for(&lt;span class=&#34;synConstant&#34;&gt;&#39;comments&#39;&lt;/span&gt;, {&lt;span class=&#34;synConstant&#34;&gt;allow_destroy&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since &lt;a href=&#34;https://metacpan.org/module/DBIx::Class::Valiant::Result&#34;&gt;DBIx::Class::Valiant::Result&lt;/a&gt; adds support for nested updates and creates (and optionally deletes) we need to tell the result class which relationships we want to support this for. In this case we want to support nested updates and creates for the &lt;code&gt;task_labels&lt;/code&gt; and &lt;code&gt;comments&lt;/code&gt; relationships. This is useful for web applications where you might want to update a task and its labels and comments all in one go. I force you to specify this support as a security feature. The next bit defines the validations:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;title&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;length&lt;/span&gt;=&#38;gt;[&lt;span class=&#34;synConstant&#34;&gt;2&lt;/span&gt;,&lt;span class=&#34;synConstant&#34;&gt;48&lt;/span&gt;]));&lt;br /&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;description&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;length&lt;/span&gt;=&#38;gt;[&lt;span class=&#34;synConstant&#34;&gt;2&lt;/span&gt;,&lt;span class=&#34;synConstant&#34;&gt;2000&lt;/span&gt;]));&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;priority&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;));&lt;br /&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;status&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Basic validations for required fields and minimum and maximum lengths. The next bit is a bit more complex:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;status&lt;/span&gt; =&#38;gt; (&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;inclusion&lt;/span&gt; =&#38;gt; \&lt;span class=&#34;synIdentifier&#34;&gt;&#38;amp;status_list&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;with&lt;/span&gt; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;method&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;valid_status&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;on&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;update&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;if&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;is_column_changed&#39;&lt;/span&gt;, &lt;span class=&#34;synComment&#34;&gt;# This method defined by DBIx::Class::Row&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;},&lt;br /&gt;&#38;nbsp;&#38;nbsp;)&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Status is an enum field, so we use the &lt;code&gt;inclusion&lt;/code&gt; validator to ensure the value is in the list of valid status values. You have a few ways to describe the list of valid values, but in this case I&#38;#39;m using a method:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;status_list &lt;/span&gt;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;) = &lt;span class=&#34;synStatement&#34;&gt;shift&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;qw(pending in_progress on_hold blocked canceled completed)&lt;/span&gt;;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This can be useful if the allowed list changes based on things like the user or the state of the object.&lt;/p&gt;

&lt;p&gt;I also specify that this validation only runs on update (not create) and only if the status field is changed. This is to prevent running the validation when the object is first created, since we won&#38;#39;t have any &#38;#39;old&#38;#39; values for the comparison. Additionally we only want to run this validation if the status field is changed since some of the validation logic depends on &#38;#39;old state versus new state&#38;#39;. This is a common pattern in web applications where the user submits the entire form and then you have to distinguish on the server side between fields that have changes and those that don&#38;#39;t. For many validations such as &#38;#39;presence&#38;#39; running the validation for each request has no downside other than additional overhead, but for the status field the rules are complex enough that we want to be more selective.&lt;/p&gt;

&lt;p&gt;I also define a custom validator for the status field via a method which is called when the status is updated and changed. Here&#38;#39;s that method:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;valid_status&lt;/span&gt;($&lt;span class=&#34;synIdentifier&#34;&gt;self&lt;/span&gt;, $&lt;span class=&#34;synIdentifier&#34;&gt;attribute_name&lt;/span&gt;, $&lt;span class=&#34;synIdentifier&#34;&gt;value&lt;/span&gt;, $&lt;span class=&#34;synIdentifier&#34;&gt;opt&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$old&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;get_column_storage(&lt;span class=&#34;synIdentifier&#34;&gt;$attribute_name&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# If the task is not pending, it can&#39;t return to pending (ie once a task is&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# started it can&#39;t be unstarted)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;pending&#39;&lt;/span&gt; &#38;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$old&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;pending&#39;&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;errors-&#38;gt;add(&lt;span class=&#34;synIdentifier&#34;&gt;$attribute_name&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;can&#39;t return to pending&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$opt&lt;/span&gt;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# If the task is completed, it can&#39;t change to any other status (ie once a task is&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# finished it can&#39;t be unfinished)&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$old&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;completed&#39;&lt;/span&gt; &#38;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;completed&#39;&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;errors-&#38;gt;add(&lt;span class=&#34;synIdentifier&#34;&gt;$attribute_name&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;task is already finished&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$opt&lt;/span&gt;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# If the task is on hold or blocked, it can&#39;t be moved to completed.  You need to&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# unhold or unblock it first.&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$old&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;on_hold&#39;&lt;/span&gt; &#38;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;completed&#39;&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;errors-&#38;gt;add(&lt;span class=&#34;synIdentifier&#34;&gt;$attribute_name&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;on hold can&#39;t change to completed&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$opt&lt;/span&gt;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$old&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;blocked&#39;&lt;/span&gt; &#38;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;completed&#39;&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;errors-&#38;gt;add(&lt;span class=&#34;synIdentifier&#34;&gt;$attribute_name&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;blocked can&#39;t change to completed&#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$opt&lt;/span&gt;);&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So in this method we enforce the more complex state transition rules for the status field.&lt;/p&gt;

&lt;p&gt;So when using &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; you have a lot of options for describing validation rules. These can be as complex or simple as you need. For the simple stuff we try to stick with the built in validators so we can be declarative about the rules but when you need power &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; makes it easy to drop into an arbitrarily complex method.&lt;/p&gt;

&lt;p&gt;The last bit is the due date validation:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;due_date&lt;/span&gt; =&#38;gt; (&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;date&lt;/span&gt; =&#38;gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;min_eq&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;{ &lt;span class=&#34;synStatement&#34;&gt;pop&lt;/span&gt;-&#38;gt;today },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;if&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;is_column_changed&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;)&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we use the &lt;code&gt;date&lt;/code&gt; validator to ensure the value is a valid date, and then we use the &lt;code&gt;min_eq&lt;/code&gt; option to ensure the date is greater than or equal to the current date. We only enforce this validation if the due date is changed. Otherwise we&#38;#39;d get validation errors whenever a task was late and the user tried to change a different field such as descriptions, or adding a comment.&lt;/p&gt;

&lt;p&gt;Finally the &lt;code&gt;Comment&lt;/code&gt; result class has a validation. It&#38;#39;s not much so I will just show all the code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; Agendum::Schema::Result::Comment&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Agendum::Syntax;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use base&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::Result&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;table(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;comments&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;add_columns(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;comment_id&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;integer&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_auto_increment&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;task_id&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;integer&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;content&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;text&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt; },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;created_at&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;data_type&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;timestamptz&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;is_nullable&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;retrieve_on_insert&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;default_value&lt;/span&gt; =&#38;gt; \&lt;span class=&#34;synConstant&#34;&gt;&#39;NOW()&#39;&lt;/span&gt; },&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;set_primary_key(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;comment_id&#38;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;belongs_to(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;task&lt;/span&gt; =&#38;gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema::Result::Task&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;{ &lt;span class=&#34;synConstant&#34;&gt;&#39;foreign.task_id&#39;&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;self.task_id&#39;&lt;/span&gt; }&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;# Here&#39;s the validation rule&lt;/span&gt;&lt;br /&gt;__PACKAGE__-&#38;gt;validates(&lt;span class=&#34;synConstant&#34;&gt;content&lt;/span&gt; =&#38;gt; (&lt;span class=&#34;synConstant&#34;&gt;presence&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;length&lt;/span&gt;=&#38;gt;[&lt;span class=&#34;synConstant&#34;&gt;2&lt;/span&gt;,&lt;span class=&#34;synConstant&#34;&gt;2000&lt;/span&gt;]));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So the content field is required and must be between 2 and 2000 characters.&lt;/p&gt;

&lt;h3 id=&#34;Using-the-Validations&#34;&gt;Using the Validations&lt;/h3&gt;

&lt;p&gt;In Agendum, these validations are run from the &lt;a href=&#34;https://metacpan.org/module/Catalyst&#34;&gt;Catalyst&lt;/a&gt; controllers which gather field info from a POST request. However if you are just writing a test script you can run the validations directly on the object. Here&#38;#39;s an example (it&#38;#39;s from &lt;code&gt;t/advent.t&lt;/code&gt; in the Agendum repository):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Test::Most;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Test::DBIx::Class &lt;span class=&#34;synConstant&#34;&gt;-schema_class&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;Agendum::Schema&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;ok &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$task&lt;/span&gt; = Schema-&#38;gt;resultset(&lt;span class=&#34;synConstant&#34;&gt;&#39;Task&#39;&lt;/span&gt;)-&#38;gt;create({&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;title&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;A Task&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;description&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;A description&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;due_date&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;2000-12-31&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;priority&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;status&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;pending&#39;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;comments&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;{ &lt;span class=&#34;synConstant&#34;&gt;content&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;A comment&#39;&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;synConstant&#34;&gt;content&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;A&#39;&lt;/span&gt; }, &lt;span class=&#34;synComment&#34;&gt;# This comment is too short&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;synConstant&#34;&gt;task_labels&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;{ &lt;span class=&#34;synConstant&#34;&gt;label&lt;/span&gt; =&#38;gt; { &lt;span class=&#34;synConstant&#34;&gt;name&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;not_a_label&#39;&lt;/span&gt; } },&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;],&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Devel::Dwarn;&lt;br /&gt;Dwarn +{ &lt;span class=&#34;synIdentifier&#34;&gt;$task&lt;/span&gt;-&#38;gt;errors-&#38;gt;to_hash(&lt;span class=&#34;synConstant&#34;&gt;full_messages&lt;/span&gt;=&#38;gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;) };&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This would output something like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;+{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;comments&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Comments Are Invalid&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;],&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;comments[1].content&#38;quot;&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Comments Content is too short (minimum is 2 characters)&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;],&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;task_labels&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Task Labels Are Invalid&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;],&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;task_labels[0].label&#38;quot;&lt;/span&gt; =&#38;gt; [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Task Labels Label Related Model &#39;Label&#39; Not Found&#38;quot;&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;],&lt;br /&gt;};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You might notice I didn&#38;#39;t need to call &lt;code&gt;validate&lt;/code&gt; on the object. That&#38;#39;s because the &lt;code&gt;create&lt;/code&gt; method calls &lt;code&gt;validate&lt;/code&gt; for you. Same thing for &lt;code&gt;update&lt;/code&gt;. If you prefer more control you can disable this behavior, but I find it useful for web applications. The database modification will be canceled if validations fail. However the DBIC result object will maintain the changed state, which makes it useful as a place to hold request information for use in a web template or HTML form. In fact the &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; distribution includes a component that can generate form fields for you based on the the model (&lt;a href=&#34;https://metacpan.org/module/Valiant::HTML::FormBuilder&#34;&gt;Valiant::HTML::FormBuilder&lt;/a&gt;) but covering that is beyond the scope of this article.&lt;/p&gt;

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

&lt;p&gt;I hope this article has given you a basic overview of how to use &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; with &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt;. I skipped on a few of the more advanced features, such as custom validators, delegating validation to other objects, internationalization, filtering, using the form builder and more. If you want to get a feel for some of those things you can play with the Agendum application and read more about them in the full &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;I&#38;#39;m always looking for feedback on &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; and the &lt;a href=&#34;https://metacpan.org/module/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; integration. If you have any thoughts or suggestions please feel free to reach out to me via email or on the &lt;a href=&#34;https://github.com/jjn1056/Valiant&#34;&gt;Valiant&lt;/a&gt; github repository. I&#38;#39;m also happy to take pull requests if you have a feature you&#38;#39;d like to add. For example, it&#38;#39;s very easy to add more built in validators, or enhance existing ones. I hope you find &lt;a href=&#34;https://metacpan.org/module/Valiant&#34;&gt;Valiant&lt;/a&gt; useful and I look forward to seeing what you build with it. If you are interested in how to use this to build web applications with &lt;a href=&#34;https://metacpan.org/module/Catalyst&#34;&gt;Catalyst&lt;/a&gt; I hope you&#38;#39;ll check out the Agendum repository and keep an eye out for future articles and talks, including one pending later this December 2024.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-14T00:00:00Z</updated><category term="Perl"/><author><name>John Napiorkowski</name></author></entry><entry><title>Production monitoring</title><link href="https://perladvent.org/2024/2024-12-13.html"/><id>https://perladvent.org/2024/2024-12-13.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Meeting-the-Quota&#34;&gt;Meeting the Quota&lt;/h3&gt;

&lt;p&gt;Santa has a big problem. The world&#38;#39;s population is growing, and there are over 2 billion children alive today. Long ago, he had already turned his cute little shop into a big production facility. The elves have been trained to run highly sophisticated production lines 24/7 all year round.&lt;/p&gt;

&lt;p&gt;But it&#38;#39;s a huge task. Literally every second counts. While Santa guards the exact numbers very closely (especially his naughty/nice list), we can make some estimates:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;Most children fall under the nice category. Since Santa wouldn&#38;#39;t know the final result until he starts his delivery run, let&#38;#39;s assume that he has to produce presents for everyone. Make that 2 billion children from ages zero to fourteen.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each child gets, on average, 2 presents&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The elves work every day, even when Santa is out for delivery.&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives us 365 days (leap years are quite relaxed, they give a whole extra day!). That gives the elves 525.600 minutes to work with.&lt;/p&gt;

&lt;p&gt;To reach the required 4 billion presents, the elves have to produce on average 7610,35 presents per minute. If any production line falls behind, the others have to pick up the pace, or children will have a bad christmas experience.&lt;/p&gt;

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

&lt;p&gt;The elves already implemented monthly, weekly and even daily reports in their warehouse management systems. But now they want production numbers every minute in order to be able to react more quickly to any problems.&lt;/p&gt;

&lt;p&gt;While they can run the other reports from their warehouse management system, getting the minute-by-minute data in real time proved a bit more tricky. So Santa decided to do some Perl coding to solve that problem. The production lines already use Perl in their control systems, so reporting the numbers should be easy to add to the codebase.&lt;/p&gt;

&lt;p&gt;After some googling, he found an article on PerlMonks about a module called &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; that could do (more-or-less) real-time communication between programs. So he decided to give it a try.&lt;/p&gt;

&lt;h3 id=&#34;Server-Configuration&#34;&gt;Server Configuration&lt;/h3&gt;

&lt;p&gt;Since this is a network application, it needs to be secured. A self-signed certificate will do nicely for testing (but Santa also set a reminder to learn about that LetsEncrypt thing after the Christmas season). For now, he just copied&#38;amp;pasted some awfully weird openssl command line to get the job done:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    yes &#38;#39;&#38;#39; | openssl req -new -newkey rsa:4096 -x509 -sha256 -days 36500 -nodes -out exampleserver.crt -keyout exampleserver.key&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To set up the server itself, Santa also created a simple XML configuration file (server.xml). For testing, Santa uses 127.0.0.1 to run tests, but will change that to the correct IP later (the exact network setup in Santas factory is proprietary information, though).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;clacks&#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;appname&#38;gt;&lt;/span&gt;Clacks Master&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/appname&#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;ip&#38;gt;&lt;/span&gt;127.0.0.1&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/ip&#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;port&#38;gt;&lt;/span&gt;49888&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/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;pingtimeout&#38;gt;&lt;/span&gt;600&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/pingtimeout&#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;interclackspingtimeout&#38;gt;&lt;/span&gt;60&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/interclackspingtimeout&#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;persistancefile&#38;gt;&lt;/span&gt;clackspersistance.dat&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/persistancefile&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;ssl&#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;cert&#38;gt;&lt;/span&gt;exampleserver.crt&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/cert&#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;exampleserver.key&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;/ssl&#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 main user that has all permissions and also Interclacks --&#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;username&#38;gt;&lt;/span&gt;santa&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/username&#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;password&#38;gt;&lt;/span&gt;WorkSmarterNotHarder&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/password&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;throttle&#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;maxsleep&#38;gt;&lt;/span&gt;5000&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/maxsleep&#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;step&#38;gt;&lt;/span&gt;1&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/step&#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;/throttle&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/clacks&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;The server code itself is as simple as it gets. All he needs to do is load &lt;a href=&#34;https://metacpan.org/module/Net::Clacks::Server&#34;&gt;Net::Clacks::Server&lt;/a&gt; and call its run method.&lt;/p&gt;

&lt;p&gt;But to make sure he can easily test local changes to &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; without having to &#38;quot;make install&#38;quot; it every time, he also added a bit of boilerplate code to support the &lt;code&gt;--debug&lt;/code&gt; command line flag:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synPreProc&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;v5.40&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use strict&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use warnings&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use diagnostics&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use utf8&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Carp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$isDebugging&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synPreProc&#34;&gt;BEGIN &lt;/span&gt;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synStatement&#34;&gt;defined&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$ARGV[&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;]&lt;/span&gt;) &#38;amp;&#38;amp; &lt;span class=&#34;synIdentifier&#34;&gt;$ARGV[&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;--debug&#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;synStatement&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Development INC activated&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#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;synStatement&#34;&gt;unshift&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@INC&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;/home/santa/src/Net-Clacks/lib&#38;quot;&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;&lt;span class=&#34;synIdentifier&#34;&gt;$isDebugging&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Net::Clacks::Server;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$configfile&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;shift&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@ARGV&lt;/span&gt;;&lt;br /&gt;croak(&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;No Config file parameter&#38;quot;&lt;/span&gt;) &lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(!&lt;span class=&#34;synStatement&#34;&gt;defined&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$configfile&lt;/span&gt;) || &lt;span class=&#34;synIdentifier&#34;&gt;$configfile&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;&#39;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$worker&lt;/span&gt; = Net::Clacks::Server-&#38;gt;new(&lt;span class=&#34;synIdentifier&#34;&gt;$isDebugging&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$configfile&lt;/span&gt;);&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$worker&lt;/span&gt;-&#38;gt;run;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Running-the-Server&#34;&gt;Running the Server&lt;/h3&gt;

&lt;p&gt;Now Santa can simply run the server with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl server.pl server.xml&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Design-Decisions&#34;&gt;Design Decisions&lt;/h3&gt;

&lt;p&gt;There are multiple ways to track events in &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt;. One way would be that every time a present is produced, the machines NOTIFY the LISTENer. But that would mean the listener would receive 1000&#38;#39;s of messages per minute and count them.&lt;/p&gt;

&lt;p&gt;But there&#38;#39;s a smarter way. Santa can just have the &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; server do all the counting by sending it INCREMENT command for a variable name. The variable will get autovivified on first use.&lt;/p&gt;

&lt;p&gt;To read the numbers minute-by-minute, Santa can just read the variable at the top of every minute. Of course, he can&#38;#39;t just set the variable back to zero, as he would be in a race condition with the increment commands from the production lines. Fortunately, while the increment and decrement commands default to &#38;quot;1&#38;quot;, they have an optional argument to use any arbitrary number.&lt;/p&gt;

&lt;p&gt;By reading out the current variable value, then sending this value as the decrement amount, Santa can avoid the race condition entirely. Well, technically, the value on the server side might not reach zero internally, but that doesn&#38;#39;t matter, the minute-by-minute numbers as seen from the client side will still be correct. Dealing with asynchronous events give Santa a headache.&lt;/p&gt;

&lt;h3 id=&#34;Central-Client&#34;&gt;Central Client&lt;/h3&gt;

&lt;p&gt;Clients need a lot less configuration than the server, so Santa doesn&#38;#39;t need a special configuration file. For now, he can just hardcode the login information.&lt;/p&gt;

&lt;p&gt;The first thing he does is code up the monitoring client (monitor.pl):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synPreProc&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;v5.40&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use strict&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use warnings&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use diagnostics&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use mro&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;c3&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Carp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Net::Clacks::Client;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Time::HiRes &lt;span class=&#34;synConstant&#34;&gt;qw(sleep)&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;santa&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$password&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;WorkSmarterNotHarder&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$applicationname&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;monitoring&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$is_caching&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$countername&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;FactoryOutput&#39;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt; = Net::Clacks::Client-&#38;gt;new(&lt;span class=&#34;synConstant&#34;&gt;&#39;127.0.0.1&#39;&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;49888&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$password&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$applicationname&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$is_caching&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;ping(); &lt;span class=&#34;synComment&#34;&gt;# We need to regularly send a ping() to the server to avoid disconnects&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;doNetwork(); &lt;span class=&#34;synComment&#34;&gt;# Do the actual network part, most commands just get buffered in a queue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$nextping&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;time&lt;/span&gt; + &lt;span class=&#34;synConstant&#34;&gt;60&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$timestamp&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;&#39;&lt;/span&gt;; &lt;span class=&#34;synComment&#34;&gt;# When program starts, report the current count (if any) immediately&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;while&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$nextping&lt;/span&gt; &#38;lt; &lt;span class=&#34;synStatement&#34;&gt;time&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;$client&lt;/span&gt;-&#38;gt;ping();&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;$nextping&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;time&lt;/span&gt; + &lt;span class=&#34;synConstant&#34;&gt;60&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;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;doNetwork();&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;while&lt;/span&gt;((&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$msg&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;getNext())) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$msg-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;disconnect&#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;&lt;span class=&#34;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;+++ Disconnected by server, reason given: &#39;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$msg-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&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;synStatement&#34;&gt;last&lt;/span&gt;;&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;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$newtime&lt;/span&gt; = getMinuteTimestamp();&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$newtime&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;ne&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$timestamp&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;synComment&#34;&gt;# Time changed&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;$timestamp&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$newtime&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;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$produced&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;retrieve(&lt;span class=&#34;synIdentifier&#34;&gt;$countername&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;synStatement&#34;&gt;if&lt;/span&gt;(!&lt;span class=&#34;synStatement&#34;&gt;defined&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$produced&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;$produced&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;; &lt;span class=&#34;synComment&#34;&gt;# Only happens on initial startup when the clacks variable is not initialized&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;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Initial startup!&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#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;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;$client&lt;/span&gt;-&#38;gt;decrement(&lt;span class=&#34;synIdentifier&#34;&gt;$countername&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$produced&lt;/span&gt;); &lt;span class=&#34;synComment&#34;&gt;# Decrement the count by the number we just received&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;$client&lt;/span&gt;-&#38;gt;doNetwork();&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$produced&lt;/span&gt;, &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\t&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&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;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$produced&lt;/span&gt; &#38;lt; &lt;span class=&#34;synConstant&#34;&gt;7610&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;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;ALERT!!!! PRODUCTION SHORTFALL!!!!!&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#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;synStatement&#34;&gt;else&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;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;OK!&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;sleep&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;0.1&lt;/span&gt;); &lt;span class=&#34;synComment&#34;&gt;# Let&#39;s run the loop 10 times a second, this should get us very close to the top of every minute without wasting too much CPU cycles&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;disconnect();&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;exit&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;0&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;getMinuteTimestamp &lt;/span&gt;{&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;# Quick-and-dirty hack, only changes at the start of every minute&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;$sec&lt;/span&gt;,&lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$hour&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$mday&lt;/span&gt;,&lt;span class=&#34;synIdentifier&#34;&gt;$mon&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$year&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$wday&lt;/span&gt;,&lt;span class=&#34;synIdentifier&#34;&gt;$yday&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$isdst&lt;/span&gt;) = &lt;span class=&#34;synStatement&#34;&gt;localtime&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;time&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;join&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;&#39;:&#39;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$year&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$mon&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$mday&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$hour&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt;);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Incrementing-the-Counter&#34;&gt;Incrementing the Counter&lt;/h3&gt;

&lt;p&gt;For incrementing the counter, Santa created the creatively named, object-oriented package &#38;quot;OutputCounter&#38;quot;. This can then be easily used in existing perl programs.&lt;/p&gt;

&lt;p&gt;Before creating a proper distribution on his local CPAN (&#38;quot;DarkPAN&#38;quot;) mirror, he just put this and a test program into a single file (factorymachine.pl):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synPreProc&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; OutputCounter&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;v5.40&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use strict&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use diagnostics&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use mro&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;c3&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Carp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Net::Clacks::Client;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;new&lt;/span&gt;($&lt;span class=&#34;synIdentifier&#34;&gt;proto&lt;/span&gt;, %&lt;span class=&#34;synIdentifier&#34;&gt;config&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$class&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;ref&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$proto&lt;/span&gt;) || &lt;span class=&#34;synIdentifier&#34;&gt;$proto&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;bless&lt;/span&gt; \&lt;span class=&#34;synIdentifier&#34;&gt;%config&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$class&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;%defaults&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;synConstant&#34;&gt;ip&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;127.0.0.1&#39;&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;synConstant&#34;&gt;port&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;49888&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;synConstant&#34;&gt;username&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;santa&#39;&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;synConstant&#34;&gt;password&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;WorkSmarterNotHarder&#39;&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;synConstant&#34;&gt;applicationname&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;NonbrandStackablePlasticBlocks&#39;&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;synConstant&#34;&gt;is_caching&lt;/span&gt; =&#38;gt; &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;&lt;span class=&#34;synConstant&#34;&gt;countername&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;FactoryOutput&#39;&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;synConstant&#34;&gt;nextping&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synStatement&#34;&gt;time&lt;/span&gt; + &lt;span class=&#34;synConstant&#34;&gt;60&lt;/span&gt;,&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;&lt;span class=&#34;synStatement&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$key&lt;/span&gt; (&lt;span class=&#34;synStatement&#34;&gt;keys&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;%defaults&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;synStatement&#34;&gt;if&lt;/span&gt;(!&lt;span class=&#34;synStatement&#34;&gt;defined&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{$key}&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;synComment&#34;&gt;# Use the default&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;$self-&#38;gt;{$key}&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$defaults{$key}&lt;/span&gt;;&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;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;_createClient();&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;_createClient&lt;/span&gt;($&lt;span class=&#34;synIdentifier&#34;&gt;self&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt; = Net::Clacks::Client-&#38;gt;new(&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;applicationname&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;is_caching&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;ping(); &lt;span class=&#34;synComment&#34;&gt;# We need to regularly send a ping() to the server to avoid disconnects&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;-&#38;gt;doNetwork(); &lt;span class=&#34;synComment&#34;&gt;# Do the actual network part, most commands just get buffered in a queue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$client&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;return&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;sub &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;increment&lt;/span&gt;($&lt;span class=&#34;synIdentifier&#34;&gt;self&lt;/span&gt;) {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;nextping&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &#38;lt; &lt;span class=&#34;synStatement&#34;&gt;time&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;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;-&#38;gt;ping();&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;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;nextping&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; = &lt;span class=&#34;synStatement&#34;&gt;time&lt;/span&gt; + &lt;span class=&#34;synConstant&#34;&gt;60&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;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;-&#38;gt;doNetwork();&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;while&lt;/span&gt;((&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$msg&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;-&#38;gt;getNext())) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;synIdentifier&#34;&gt;$msg-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;disconnect&#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;&lt;span class=&#34;synComment&#34;&gt;# Got a disconnect. Throw away current connection and create a new one&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;synStatement&#34;&gt;print&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Connection error&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&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;$self&lt;/span&gt;-&#38;gt;_createClient();&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;synStatement&#34;&gt;last&lt;/span&gt;;&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;&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;-&#38;gt;increment(&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;countername&lt;/span&gt;&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;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;-&#38;gt;doNetwork();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt; main&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;v5.40&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use strict&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use diagnostics&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use mro&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;c3&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Carp;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Time::HiRes &lt;span class=&#34;synConstant&#34;&gt;qw(sleep)&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synComment&#34;&gt;#use OutputCounter;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$counter&lt;/span&gt; = OutputCounter-&#38;gt;new(&lt;span class=&#34;synConstant&#34;&gt;applicationname&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;CandyCaneMaker&#39;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;while&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$counter&lt;/span&gt;-&#38;gt;increment();&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;sleep&lt;/span&gt;(&lt;span class=&#34;synConstant&#34;&gt;0.05&lt;/span&gt;); &lt;span class=&#34;synComment&#34;&gt;# Increment ~20 times a second&lt;/span&gt;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Final-Thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;

&lt;p&gt;This is a minimal example of what &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; can do. Yes, it takes quite a bit of boilerplate code to get up and running, but that&#38;#39;s true for a lot of network code. &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; is more designed for speed and reliability (including WHEN to spend those precious CPU cycles), than it is for simple coding. But, generally, the tradeoff can be a significant benefit. The author of &lt;a href=&#34;https://metacpan.org/module/Net::Clacks&#34;&gt;Net::Clacks&lt;/a&gt; uses it in many commercial, production critical systems.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-13T00:00:00Z</updated><category term="Perl"/><author><name>Rene Schickbauer</name></author></entry><entry><title>Stay focused and organised during the advent season</title><link href="https://perladvent.org/2024/2024-12-12.html"/><id>https://perladvent.org/2024/2024-12-12.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Toys-warehouse-and-drones&#34;&gt;Toys warehouse and drones&lt;/h3&gt;

&lt;p&gt;Christmas is approaching and the elves are getting excited and stressed preparing all the packages. In 2024, North Pole is equipped with a Toy Warehouse that replaced the ancient Toy Workshop. Toys are now ordered to a network of third party elves workshops.&lt;/p&gt;

&lt;p&gt;In the recent years, Santa simplified his deliveries by investing in a Reindeer Drones Delivery system, also known as &lt;i&gt;RDD&lt;/i&gt;. It makes the work much easier and reduced deliveries errors: did you ever wonder why some persons were unhappy with their christmas present?&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;stay-focused-and-organised.jpg&#34; alt=&#34;Toy Factory in the Santa&#39;s Toyland Unit during the Christmas Fantasy parade on Main Street, U.S.A. at Disneyland park.&#34;&gt; &lt;span&gt; &lt;em&gt;Toy Factory&lt;/em&gt;, by &lt;a href=&#34;https://flic.kr/p/Jj5aoy&#34;&gt;Steven Miller&lt;/a&gt; - &lt;a =href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC BY 2.0 license&lt;/a&gt; &lt;/span&gt;

&lt;/p&gt;



&lt;p&gt;North Pole elves use top notch automation and deliveries dispatch systems, but still, this is a very critical time with a non-negotiable deadline. The stress is at its peak. All sort of operational issues must be managed, what requires to communicate efficiently with the &lt;i&gt;DEBT&lt;/i&gt; (Devops Elves Backend Team).&lt;/p&gt;

&lt;h3 id=&#34;Agile-at-the-Toys-warehouse&#34;&gt;Agile at the Toys warehouse&lt;/h3&gt;

&lt;p&gt;Santa&#38;#39;s elves teams do daily standups. This is what &lt;a href=&#34;https://christmas-specials.fandom.com/wiki/Boss_Elf&#34;&gt;Elf Product Owner &lt;/a&gt; decided after it looked like it would improve the overall North Pole teams&#38;#39; mental health. Each elf have to speak up and announce:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;what they did in the last day&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what they plan to do today&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;announce any blocker so that other elves can support them&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It proved to encourage team communication and Elf Product Owner is able to inform other teams about any blockers that could put this sensitive work at risk.&lt;/p&gt;

&lt;h3 id=&#34;Someone-is-getting-extra-stressed&#34;&gt;Someone is getting extra-stressed&lt;/h3&gt;

&lt;p&gt;Now &lt;a href=&#34;https://christmas-specials.fandom.com/wiki/Hermey&#34;&gt;Hermey the Elf&lt;/a&gt; got more problems... Since Hermey was not too interested in toys making and that he seemed to have an interest in meticulous work, Elf Product Owner decided to realocate him to the RDD team led by Elf Sparkle. Getting stressed about the long December month was already quite a burden. But speakink every morning in front of other elves about what he accomplished, tell his feelings, and asking for help is definitely too much for Hermey. The poor thing couldn&#38;#39;t bear it and each day, he would babble a meaningless report. The stress of the social meeting makes it harder for Hermey because he is not the kind of person able to improvise a thirty-seconds talk.&lt;/p&gt;

&lt;h3 id=&#34;App::Standup::Diary-to-the-rescue&#34;&gt;App::Standup::Diary to the rescue&lt;/h3&gt;

&lt;p&gt;Sparkle, the team manager, noticed Hermey&#38;#39;s struggle. She decided to share a tool she developed and used since a couple of years. &lt;i&gt;&#38;quot;Take a look at App::Standup::Diary on the CPAN&#38;quot;&lt;/i&gt;, she said. &lt;i&gt;&#38;quot;It might help you organising your thoughts and communicate better.&#38;quot;&lt;/i&gt;. Hermey nodded and went to his terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    cpm install -g App::Standup::Diary&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;i&gt;&#38;quot;Check the chat, I have sent you a nice bash alias&#38;quot;&lt;/i&gt; Sparkle said:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    alias diary=&#38;quot;diary --data-dir ~/toy-warehouse/standup --project-name &#38;#39;rdd&#38;#39;&#38;quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you will have configured that, we&#38;#39;ll take a dive into the internals together!&lt;/p&gt;

&lt;h3 id=&#34;Daily-usage&#34;&gt;Daily usage&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--data-dir&lt;/code&gt; is where the stand-up notes are automatically stored and sorted by year and month. One file per day. &lt;code&gt;--project-name&lt;/code&gt; is used for each note file name, as well as the file title. The directory tree looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;tree /home/sparkle/toy-warehouse/standup&lt;br /&gt;/home/sparkle/toy-warehouse/standup&lt;br /&gt;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2021&lt;br /&gt;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2022&lt;br /&gt;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2023&lt;br /&gt;&#38;#x2514;&#38;#x2500;&#38;#x2500; 2024&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 01&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 02&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 03&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; etc.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x2514;&#38;#x2500;&#38;#x2500; 11&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2024-11-04_rdd.md&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2024-11-05_rdd.md&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2024-11-06_rdd.md&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; etc.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2024-11-28_rdd.md&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x251C;&#38;#x2500;&#38;#x2500; 2024-11-29_rdd.md&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#x2514;&#38;#x2500;&#38;#x2500; 2024-11-30_rdd.md&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is all what you need to know to use it as your daily stand-up notes app. Just run &lt;code&gt;diary&lt;/code&gt; every morning and get prepared for the next meeting. Maybe you want to know more about how it works, though?&lt;/p&gt;

&lt;h3 id=&#34;A-dive-into-App::Standup::Diary&#34;&gt;A dive into App::Standup::Diary&lt;/h3&gt;

&lt;p&gt;Let&#38;#39;s inspect what kind of marvels this command line tool contains. It use the following CPAN modules:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/module/Object::Pad&#34;&gt;Object::Pad&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;It&#38;#39;s used as a base OO toolkit for classes and roles. More on that later.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/module/Time::Piece&#34;&gt;Time::Piece&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Dates management.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/module/Mojo::Template&#34;&gt;Mojo::Template&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Reusable boilerplate to generate stand-up Markdown files.&lt;/p&gt;

&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&#34;https://metacpan.org/module/Path::Tiny&#34;&gt;Path::Tiny&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;

&lt;p&gt;Directories and files path management.&lt;/p&gt;

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

&lt;p&gt;The &lt;code&gt;diary&lt;/code&gt; command-line tool retrieve the user arguments, create the diary file object and write it on disk.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;GetOptions&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;data-dir:s&#38;quot;&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;$data_dir&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;double&#34;&gt;&#38;quot;project-name:s&#38;quot;&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;$project_name&lt;/span&gt;&lt;br /&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;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;double&#34;&gt;&#38;quot;Error in command line arguments\n&#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;$diary&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;App::Standup::Diary&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&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;data_dir&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data_dir&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;project_name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$project_name&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$diary&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;/code&gt;&lt;/pre&gt;

&lt;p&gt;The main &lt;code&gt;App::Standup::Diary&lt;/code&gt; file compose two roles (with the &lt;code&gt;:does()&lt;/code&gt; keyword) for dealing with the dates and project parts. Each role manage it&#38;#39;s own set of fields, e.g. &lt;code&gt;App::Standup::Role::Date&lt;/code&gt; provide the daily &lt;code&gt;$date&lt;/code&gt; as a &lt;code&gt;Time::Piece&lt;/code&gt; object so that it can be reused all over the place. The &lt;code&gt;App::Standup::Diary::Template&lt;/code&gt; class generates Markdown files:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;# &#38;lt;%= $project_name %&#38;gt; &#38;lt;%= $today %&#38;gt;&lt;br /&gt;&lt;br /&gt;- done&lt;br /&gt;- todo&lt;br /&gt;- blocking&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It doesn&#38;#39;t allow customisation at the moment but we would really like to add a &lt;code&gt;--template&lt;/code&gt; option so that the elves can provide a file type of their choice, or seasonnal ornaments for those who like ASCII art or emoji.&lt;/p&gt;

&lt;h3 id=&#34;Usage-of-Object::Pad&#34;&gt;Usage of Object::Pad&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/App::Standup::Diary&#34;&gt;App::Standup::Diary&lt;/a&gt; have been written with &lt;a href=&#34;https://metacpan.org/module/Object::Pad&#34;&gt;Object::Pad&lt;/a&gt; from scratch. It has not been converted from another OO toolkit, e.g. Moose. Its main class is equipped with the following fields:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;$daily_data_path&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$data_dir&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$template&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition two more field are provided by the &lt;a href=&#34;https://metacpan.org/module/App::Standup::Role::Date&#34;&gt;App::Standup::Role::Date&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/App::Standup::Role::Project&#34;&gt;App::Standup::Role::Project&lt;/a&gt; roles:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;$date&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$project_name&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It makes very easy to declare all the class fields at the top of the class. Various &lt;a href=&#34;https://metacpan.org/pod/Object::Pad#field&#34;&gt;field attributes&lt;/a&gt; are available to manipulate each fields capacites:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$daily_data_path&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;accessor&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$data_dir&lt;/span&gt;        &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;param&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;reader&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;field&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;word&#34;&gt;accessor&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;writer&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that &lt;code&gt;field&lt;/code&gt; replaced the &lt;code&gt;has&lt;/code&gt; keyword for attribute declaration in usual OO Perl toolkits. It&#38;#39;s still possible to create non-class variables with e.g. &lt;code&gt;my $santa&lt;/code&gt;, though, no worries. Now, let&#38;#39;s take a look at the main method:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;method&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;br /&gt;&lt;br /&gt;&#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_template&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Standup::Diary::Template&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;date&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;date&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;project_name&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;project_name&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#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;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;should_create_dir&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;$daily_data_path should be created&#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;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;create_directories_tree&lt;/span&gt;&lt;span class=&#34;structure&#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;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;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;init_daily_data_path&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;build_path&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;date&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ymd&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;structure&#34;&gt;)));&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file_path&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;$self&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;build_full_file_path&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&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;$file_path&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;$template&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;operator&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Diary entry created $file_path&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$file_path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;exists&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Entry already exist&#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;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first benefit is that we have automatic method signature (if using Perl &lt;code&gt;5.26&lt;/code&gt;+), and that &lt;code&gt;$self&lt;/code&gt; is provided without having to declare it. All class fields are available through &lt;code&gt;$self&lt;/code&gt;, including the ones that comes from roles. Since &lt;code&gt;$template&lt;/code&gt; got a &lt;code&gt;:writer&lt;/code&gt; attribute, we got a &lt;code&gt;set_template&lt;/code&gt; method to mutate it&#38;#39;s value.&lt;/p&gt;

&lt;p&gt;After testing the &lt;code&gt;diary&lt;/code&gt; software for a couple of years, Sparkle consider writing new code for the Reindeer Drone Delivery core software with &lt;a href=&#34;https://metacpan.org/module/Object::Pad&#34;&gt;Object::Pad&lt;/a&gt; (or more likely it&#38;#39;s core implementation) instead of the aging OO toolkits, as it prove to allow the writing of clearer code, with good separation of concerns and easily testable.&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;br /&gt;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Time::Piece&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;App::Standup::Diary&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;$d&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;App::Standup::Diary&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;data_dir&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;north-pole-warehouse&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;project_name&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;rdd&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;isa_ok&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$d&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Time::Piece&#39;&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;$d&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;br /&gt;&lt;span class=&#34;word&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-e&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$d&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;daily_data_path&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;The file have been created on disk&#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;$today&lt;/span&gt; &lt;span class=&#34;operator&#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;br /&gt;&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;symbol&#34;&gt;$year_month&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;$today&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;ymd&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;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;m/ \d{4} \/ \d{2} /gx&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;symbol&#34;&gt;$d&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;daily_data_path&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;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;data_dir&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;operator&#34;&gt;.&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$year_month&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Daily data path match today\&#39;s date&#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;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Happy-advent-season&#34;&gt;Happy advent season!&lt;/h3&gt;

&lt;p&gt;In the end Hermey finally got permission from Elf Product Owner to open a dentist&#38;#39;s office at the North Pole, with having him scheduled for an immediate appointment after noticing something bad with his teeth. Sparkle improved the tool with the rest of the team, what made them more productive and relaxed.&lt;/p&gt;

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

&lt;p&gt;The &lt;i&gt;Santa&#38;#39;s Helpers&#38;#39; Helpers&lt;/i&gt; article by D Ruth Holloway from the &lt;a href=&#34;https://perladvent.org/2023/2023-12-11.html&#34;&gt;11th day of the 2023 Advent Calendar&lt;/a&gt; was an inspiration to write this one.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-12T00:00:00Z</updated><category term="Perl"/><author><name>S&#233;bastien Feug&#232;re</name></author></entry><entry><title>Have fun with Map::Tube</title><link href="https://perladvent.org/2024/2024-12-11.html"/><id>https://perladvent.org/2024/2024-12-11.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;&lt;img src=&#34;to-trains.jpg&#34;&gt; &lt;p class=&#34;attribution&#34;&gt;&#34;&lt;a rel=&#34;noopener noreferrer&#34; href=&#34;https://www.flickr.com/photos/58433307@N08/53726096544&#34;&gt;Swiss Cottage Underground Station (Jubilee Line)&lt;/a&gt;&#34; by &lt;a rel=&#34;noopener noreferrer&#34; href=&#34;https://www.flickr.com/photos/58433307@N08&#34;&gt;hugh llewelyn&lt;/a&gt; is licensed under &lt;a rel=&#34;noopener noreferrer&#34; href=&#34;https://creativecommons.org/licenses/by-sa/2.0/?ref=openverse&#34;&gt;CC BY-SA 2.0 &lt;img src=&#34;https://mirrors.creativecommons.org/presskit/icons/cc.svg&#34; style=&#34;height: 1em; margin-right: 0.125em; display: inline;&#34; /&gt;&lt;img src=&#34;https://mirrors.creativecommons.org/presskit/icons/by.svg&#34; style=&#34;height: 1em; margin-right: 0.125em; display: inline;&#34; /&gt;&lt;img src=&#34;https://mirrors.creativecommons.org/presskit/icons/sa.svg&#34; style=&#34;height: 1em; margin-right: 0.125em; display: inline;&#34; /&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;/p&gt;



&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Map::Tube&#34;&gt;Map::Tube&lt;/a&gt; is one of my earliest submissions to &lt;b&gt;CPAN&lt;/b&gt; and it&#38;#39;s a project that&#38;#39;s very close to my heart. Over the years, I&#38;#39;ve had the joy of writing about it and I&#38;#39;ve spoken about it at many conferences. I consider it my baby though I share it with many amazing contributors who have helped make it sleeker and more powerful. Each day, I&#38;#39;m thankful for the contributions of those who&#38;#39;ve made this project better. Many thanks also to everyone who has used used the library to build maps for cities around the world. As of today, there are &lt;a href=&#34;https://metacpan.org/pod/Map::Tube#MAP-LEADER-BOARD&#34;&gt;43 maps&lt;/a&gt; created by &lt;b&gt;12 contributors&lt;/b&gt;.&lt;/p&gt;

&lt;h3 id=&#34;Inspiration-behind-the-map&#34;&gt;Inspiration behind the map&lt;/h3&gt;

&lt;p&gt;Our first window to open is about inspiration! The journey began with the idea to create a map for the &lt;b&gt;London Tube&lt;/b&gt;, the famous underground rail network. The first map created was &lt;a href=&#34;https://metacpan.org/module/Map::Tube::London&#34;&gt;Map::Tube::London&lt;/a&gt;. As work on &lt;b&gt;Map::Tube::London&lt;/b&gt; progressed, I realized there was an opportunity to build a framework so others could create map for their own cities. And so, &lt;a href=&#34;https://metacpan.org/module/Map::Tube&#34;&gt;Map::Tube&lt;/a&gt; was born. So now you know where the term &lt;b&gt;Tube&lt;/b&gt; came from!&lt;/p&gt;

&lt;h3 id=&#34;The-first-days-play:-Lets-get-started&#34;&gt;The first day&#38;#39;s play: Let&#38;#39;s get started&lt;/h3&gt;

&lt;p&gt;It&#38;#39;s time to open the first window and dive in! To get started, you&#38;#39;ll need to install the framework and at least one map. For today, let&#38;#39;s stick with the &lt;b&gt;London Tube&lt;/b&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ cpanm -v Map::Tube&lt;br /&gt;&lt;br /&gt;$ cpanm -v Map::Tube::London&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now it&#38;#39;s time to get your hands dirty!&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;version&#34;&gt;v5.40&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;Map::Tube::London&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;$map&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Map::Tube::London&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;say&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$map&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;get_shortest_route&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Baker Street&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Wembley Park&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Opening the window to the route finder, you should see something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;Baker Street (Bakerloo, Circle, Hammersmith &#38;amp; City, Jubilee, Metropolitan), Finchley Road (Jubilee, Metropolitan), Wembley Park (Jubilee, Metropolitan)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Nice and easy, right?&lt;/p&gt;

&lt;p&gt;If you&#38;#39;re feeling a bit lazy and don&#38;#39;t want to write the &lt;b&gt;4 lines&lt;/b&gt; perl, no worries, there&#38;#39;s a shortcut! The &lt;a href=&#34;https://metacpan.org/module/Map::Tube::CLI&#34;&gt;Map::Tube::CLI&lt;/a&gt; distribution comes with a command line utility called &lt;b&gt;map-tube&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s repeat today&#38;#39;s fun with just one line using the command line tool: &lt;b&gt;map-tube&lt;/b&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ cpanm -v Map::Tube::CLI&lt;br /&gt;&lt;br /&gt;$ map-tube --map London --start &#38;#39;Baker Street&#38;#39; --end &#38;#39;Wembley Park&#38;#39;&lt;br /&gt;Baker Street (Bakerloo, Circle, Hammersmith &#38;amp; City, Jubilee, Metropolitan), Finchley Road (Jubilee, Metropolitan), Wembley Park (Jubilee, Metropolitan)&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;So-whats-inside-the-map-Peek-behind-the-curtain&#34;&gt;So what&#38;#39;s inside the map? Peek behind the curtain&lt;/h3&gt;

&lt;p&gt;Curious about how it all works? Open the next window to peek inside! There&#38;#39;s a package called &lt;a href=&#34;https://metacpan.org/module/Map::Tube::Cookbook&#34;&gt;Map::Tube::Cookbook&lt;/a&gt;, which is part of the &lt;a href=&#34;https://metacpan.org/module/Map::Tube&#34;&gt;Map::Tube&lt;/a&gt; distribution that explains everything that you need to know to create a new map.&lt;/p&gt;

&lt;p&gt;I remember the first time I gave a presentation about &lt;b&gt;Map::Tube&lt;/b&gt; at &lt;b&gt;London Perl Workshop 2017&lt;/b&gt;. During the talk, I challenged the audience to create a map by the end of the workshop and win a prize. To my delight, we had &lt;a href=&#34;https://metacpan.org/module/Map::Tube::Athens&#34;&gt;Map::Tube::Athens&lt;/a&gt; by &lt;b&gt;Errietta Kostala&lt;/b&gt;.&lt;/p&gt;

&lt;h3 id=&#34;Can-I-create-a-map-image-Yes-you-can&#34;&gt;Can I create a map image? Yes you can!&lt;/h3&gt;

&lt;p&gt;Opening the next window, you&#38;#39;ll find the answer: Yes, you can generate a map images in &lt;b&gt;png&lt;/b&gt; format. Use the &lt;b&gt;map-tube&lt;/b&gt; command line tool to make it happen:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ cpanm -v Map::Tube::Plugin::Graph&lt;br /&gt;$ map-tube --map London --generate_map&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or if you only want a particular line, like the &lt;b&gt;Bakerloo Line&lt;/b&gt;, here&#38;#39;s how you do it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ map-tube --map London --line Bakerloo --generate_map&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will create a file in your current directory called &lt;b&gt;Bakerloo.png&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;Bakerloo.png&#34;&gt;

&lt;/p&gt;



&lt;h3 id=&#34;Build-your-own-map-server&#34;&gt;Build your own map server&lt;/h3&gt;

&lt;p&gt;Opening the next window reveals the magic behind creating a map server. With the help of &lt;a href=&#34;https://metacpan.org/module/Map::Tube::Server&#34;&gt;Map::Tube::Server&lt;/a&gt;, setting up a map server is easy. Here&#38;#39;s how to create your server with &lt;b&gt;map-server.psgi&lt;/b&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;use v5.40;&lt;br /&gt;use Map::Tube::Server;&lt;br /&gt;use Plack::Builder;&lt;br /&gt;&lt;br /&gt;builder { mount &#38;#39;/map-tube/v1&#38;#39; =&#38;gt; Map::Tube::Server-&#38;gt;to_app; };&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once you&#38;#39;ve set it up, start your map server like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ plackup map-server.psgi&lt;br /&gt;HTTP::Server::PSGI: Accepting connections at http://0:5000/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let&#38;#39;s make a call to the map server to repeat today&#38;#39;s search i.e. finding the route from &lt;b&gt;Baker Street&lt;/b&gt; to &lt;b&gt;Wembley Park&lt;/b&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ curl http://127.0.0.1:5000/map-tube/v1/shortest-route/london/baker%20street/wembley%20park&lt;br /&gt;[&#38;quot;Baker Street (Bakerloo, Circle, Hammersmith and City, Jubilee, Metropolitan)&#38;quot;,&#38;quot;Finchley Road (Jubilee, Metropolitan)&#38;quot;,&#38;quot;Wembley Park (Jubilee, Metropolitan)&#38;quot;]&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Access-the-public-API&#34;&gt;Access the public API&lt;/h3&gt;

&lt;p&gt;Behind the next window, you&#38;#39;ll find the public API! If you&#38;#39;ve have setup your map server, you can access the service through an API, using the distribution: &lt;a href=&#34;https://metacpan.org/module/Map::Tube::API&#34;&gt;Map::Tube::API&lt;/a&gt;. First set the &lt;b&gt;MAP_BASE_URL&lt;/b&gt; environment variable.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ export MAP_BASE_URL=http://127.0.0.1:5000&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let&#38;#39;s create a script, &lt;b&gt;map-api.pl&lt;/b&gt;, to make the API call:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;use v5.40;&lt;br /&gt;use Data::Dumper;&lt;br /&gt;use Map::Tube::API;&lt;br /&gt;&lt;br /&gt;my $api = Map::Tube::API-&#38;gt;new;&lt;br /&gt;say Dumper($api-&#38;gt;shortest_route({ map =&#38;gt; &#38;#39;london&#38;#39;, start =&#38;gt; &#38;#39;Baker Street&#38;#39;, end =&#38;gt; &#38;#39;Wembley Park&#38;#39; }));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&#38;#39;s time to make that API call:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ perl map-api.pl&lt;br /&gt;$VAR1 = [&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#39;Baker Street (Bakerloo, Circle, Hammersmith and City, Jubilee, Metropolitan)&#38;#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#39;Finchley Road (Jubilee, Metropolitan)&#38;#39;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;#39;Wembley Park (Jubilee, Metropolitan)&#38;#39;&lt;br /&gt;];&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;How-about-other-maps-Open-a-new-city&#34;&gt;How about other maps? Open a new city!&lt;/h3&gt;

&lt;p&gt;Ready for a new adventure? Open the next window to discover how to install other city maps. To install a map, like &lt;b&gt;Delhi&lt;/b&gt;, just do the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ cpanm -v Map::Tube::Delhi&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, if you&#38;#39;re feeling adventurous and want to explore all the maps, use the distribution &lt;a href=&#34;https://metacpan.org/module/Task::Map::Tube::Bundle&#34;&gt;Task::Map::Tube::Bundle&lt;/a&gt; to install them all at once.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;$ cpanm -v Task::Map::Tube::Bundle&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;</summary><updated>2024-12-11T00:00:00Z</updated><category term="Perl"/><author><name>Mohammad Sajid Anwar</name></author></entry><entry><title>Programming like a BEAST</title><link href="https://perladvent.org/2024/2024-12-10.html"/><id>https://perladvent.org/2024/2024-12-10.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h2 id=&#34;Enhance-Your-Development-Workflow-with-the-e-Module&#34;&gt;Enhance Your Development Workflow with the e Module&lt;/h2&gt;

&lt;p&gt;You know how certain aspects of development can be a real drag - slow and pretty boring? I&#38;rsquo;ve definitely been there. But here&#38;rsquo;s where the &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; module comes in and changes the game.&lt;/p&gt;

&lt;h2 id=&#34;Rapid-Prototyping&#34;&gt;Rapid Prototyping&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; module comes loaded in with many features that can aid in rapid prototyping as well as testing.&lt;/p&gt;

&lt;h3 id=&#34;Lets-Make-Selenium-Testing-Less-Painful-Again&#34;&gt;Let&#38;#39;s Make Selenium Testing Less Painful Again&lt;/h3&gt;

&lt;p&gt;One of its cool features is the &lt;code&gt;repl&lt;/code&gt; breakpoint command, which can seriously simplify debugging and testing processes across the board.&lt;/p&gt;

&lt;p&gt;It can also be used from the command line like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;perl&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;-Me&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-e&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;my $var = 111; repl&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With that simple one-liner, you trigger an interactive breakpoint which pauses the execution of the program.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;repl_startup.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;You can investigate data and try things out.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;repl_execute.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Once satisfied, you can quit the debugger and code flow would continue as usual.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;repl_exit.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;So, instead of dealing with all the usual hassle of setting &#38;quot;breakpoints&#38;quot; and debugging manually (yes, I taking about you &lt;code&gt;print&lt;/code&gt;), you can use &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; to just pop in a breakpoint whenever you need it. This means you can pause execution, check out what&#38;rsquo;s going on, and make adjustments on the fly.&lt;/p&gt;

&lt;p&gt;A prime example of where &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; shines is in Selenium testing, where it can significantly ease the process. Selenium tests can be notoriously slow and cumbersome, but the &lt;code&gt;repl&lt;/code&gt; breakpoint feature makes it less painful.&lt;/p&gt;

&lt;p&gt;However, it&#38;rsquo;s not just for Selenium, &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; can enhance various development tasks by making the whole debugging and testing experience accelerated, quickly done, and, dare I say, even a bit enjoyable.&lt;/p&gt;

&lt;h3 id=&#34;More-Tricks-up-its-Sleeve&#34;&gt;More Tricks up its Sleeve&lt;/h3&gt;

&lt;p&gt;Besides making a fancy breakpoint, &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; can a wide array of tricks to help one in common daily tasks.&lt;/p&gt;

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

&lt;p&gt;Have you ever added a print statement, only to realize that there is no output for some reason? (Assuming you have no syntax errors).&lt;/p&gt;

&lt;p&gt;Now, imagine working in a large, complex codebase where, buried deep within a function, &lt;code&gt;STDOUT&lt;/code&gt; or &lt;code&gt;STDERR&lt;/code&gt; is being closed.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;close&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;STDOUT&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;STDOUT&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;&#38;gt;&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;/dev/null&#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;You wont ever see this :(&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So you now need to find the place that closes &lt;code&gt;STDOUT&lt;/code&gt; to comment it out for debugging or figure out how to print it to the screen (maybe there is a &lt;code&gt;--verbose&lt;/code&gt; option).&lt;/p&gt;

&lt;h4 id=&#34;Say-Hello-to-the-Obnoxious-Say&#34;&gt;Say Hello to the Obnoxious Say&lt;/h4&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;e&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;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;You&#39;ll see this.\n.&#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;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;And this&#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;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;*STDOUT&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;close&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;*STDERR&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;print&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;But not this.\n&#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;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;YOU&#39;ll STILL SEE THIS!&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whether your output streams are open, closed, or redirected, the obnoxious say will always ensure the message gets displayed on your screen.&lt;/p&gt;

&lt;h3 id=&#34;Pod-Table-Of-Contents&#34;&gt;Pod Table Of Contents&lt;/h3&gt;

&lt;p&gt;Need a quick overview of a Perl module? Well, the &lt;code&gt;pod&lt;/code&gt; command-line tool (included with &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; is your friend.&lt;/p&gt;

&lt;p&gt;Using it you can get a quick overview of a module:&lt;/p&gt;

&lt;p&gt;- Methods names and summaries. - Module inheritance. - Jump into the module quickly (via Vim).&lt;/p&gt;

&lt;p&gt;At a glance I can see what methods are available for a module.&lt;/p&gt;

&lt;h4 id=&#34;For-Mojo::UserAgent&#34;&gt;For Mojo::UserAgent&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;pod_user_agent.png&#34;&gt;

&lt;/p&gt;



&lt;h4 id=&#34;For-Mojo::JSON&#34;&gt;For Mojo::JSON&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;pod_json.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Can also look at a specific method:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;pod_json_j.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Using the edit option you can also jump inside the module file to explore how it works:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;pod_json_j_edit.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;(In that case above, need to press &lt;code&gt;n&lt;/code&gt; again to find the actual definition of the method)&lt;/p&gt;

&lt;h3 id=&#34;Performance-Efficiency&#34;&gt;Performance Efficiency&lt;/h3&gt;

&lt;p&gt;Worried about performance? Well, stop it! &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; has been optimized to have a very tiny footprint to conserve space and resources. Also, it makes great use of Just In Time (JIT) module loading: you pay only for what you use. For instance, I use it on my smartphone with Termux, and it&#38;rsquo;s incredibly fast - even with the phone&#38;#39;s limited resources. The impact is practically negligible, and it runs smoothly without any noticeable delays.&lt;/p&gt;

&lt;h3 id=&#34;Interactive-Demos&#34;&gt;Interactive Demos&lt;/h3&gt;

&lt;p&gt;To see &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; in action, just install it with cpan(m):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;cpanm e&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then skim through the short doc.&lt;/p&gt;

&lt;p&gt;You can also try out this &lt;a href=&#34;https://perlbanjo.com/dbce20686b&#34;&gt;Perl Banjo&lt;/a&gt; interactive demo. You can run a few example snippets and experiment with them directly.&lt;/p&gt;

&lt;p&gt;Feel free to modify the snippets on the right to explore different features and see how &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; can enhance your own development workflow.&lt;/p&gt;

&lt;p&gt;(Unfortunately, at this time, not all features can be currently used in PerlBanjo.)&lt;/p&gt;

&lt;h3 id=&#34;Your-Turn&#34;&gt;Your Turn&lt;/h3&gt;

&lt;p&gt;With the power of &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; at your fingertips, it&#38;rsquo;s time to dive in and explore! Whether you&#38;rsquo;re easing the pain of Selenium testing, ensuring your output is always present with the obnoxious &lt;code&gt;say&lt;/code&gt;, or seamlessly navigating documentation using &lt;code&gt;pod&lt;/code&gt;, &lt;a href=&#34;https://metacpan.org/module/e&#34;&gt;e&lt;/a&gt; is here to transform your experience. Try it out and see how it fits into your workflow!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-10T00:00:00Z</updated><category term="Perl"/><author><name>tim.potapov+advent</name></author></entry><entry><title>A Time-Tested Powerhouse for Processing XML</title><link href="https://perladvent.org/2024/2024-12-09.html"/><id>https://perladvent.org/2024/2024-12-09.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;XML was the preferred communication language used by services in the early 2000s. During that time, governments were establishing their own e-government systems, while companies were developing their SOAP services. Then, BOOM! Services began to adopt JSON because it was a lightweight and efficient alternative to XML. However, even though JSON became the new standard, old services were still in use and being maintained.Rewriting a system from scratch is not easy, and it might not even be necessary. That might be why XML is still in use, or perhaps governments and companies simply adopt the &#38;quot;as far as it goes&#38;quot; mindset. Who knows! Let&#38;#39;s parse some XML and beat that services up!&lt;/p&gt;

&lt;p&gt;Here is where the sweetest and most beloved Perl library, &lt;a href=&#34;https://metacpan.org/module/XML::Twig&#34;&gt;XML::Twig&lt;/a&gt;, comes into play.&lt;/p&gt;

&lt;h3 id=&#34;Parsing-XML-File&#34;&gt;Parsing XML File&lt;/h3&gt;

&lt;p&gt;Suppose we provide a service to manage internet access for a hotel&#38;rsquo;s guests. The hotel&#38;#39;s property management system stores its guests in a file using XML. We need to read that file in order to check if the user is a guest of the hotel. In order to gain access to internet a user should type their id and wifi password correctly.&lt;/p&gt;

&lt;p&gt;The following is the XML format:&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;guestList&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;guest &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;room&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;201&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;wifiKey&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;ryan1234&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Michael&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;surname&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Scott&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;idType&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;P&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;id&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;CB5634431&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;gender&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;M&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;country&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;USA&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkIn&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;2024-11-29 14:03:23&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkOut&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&#38;lt;/guest&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;guest &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;room&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;202&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;wifiKey&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;X0K7F2ie!&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Dwight&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;surname&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Schrute&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;idType&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;P&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;id&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;AB3056430&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;gender&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;M&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;country&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;USA&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkIn&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;2024-11-29 14:03:23&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkOut&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&#38;lt;/guest&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;guest &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;room&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;305&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;wifiKey&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;SNbsnz&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;name&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Selim Serhat&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;surname&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;Celik&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;idType&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;TC&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;id&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;00011122233&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;gender&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;M&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;country&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;TR&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkIn&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;2024-11-29 14:03:23&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;checkOut&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&#38;lt;/guest&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synComment&#34;&gt;&#38;lt;!-- Other guest entries can follow here --&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/guestList&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This file is located at &lt;code&gt;hotelpms/guests.xml&lt;/code&gt;. To parse file we need to provide this file path to &lt;code&gt;parsefile&lt;/code&gt; function, the library also has a function named &lt;code&gt;parse&lt;/code&gt;. According to documentation if your job is with file you should use &lt;code&gt;parsefile&lt;/code&gt; but if you want to parse a string that contains whole XML document then you could use &lt;code&gt;parse&lt;/code&gt;.&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;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;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;diagnostics&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;XML::Twig&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;check_guest&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;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;core&#34;&gt;shift&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XML::Twig&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;symbol&#34;&gt;$twig&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;parsefile&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;/hotelpms/guests.xml&#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;span class=&#34;comment&#34;&gt;# handle error;&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;In order to access guest tags, we first need to obtain guestList, which is the root of this XML document. The &lt;code&gt;$twig-&#38;gt;root&lt;/code&gt; method returns the direct parent of all other elements. On the other hand, the &lt;code&gt;children&lt;/code&gt; method returns list of elements. The method can take an optional argument. If a string is passed to the method, XML elements that match the string will be fetched; otherwise, all elements of the current root will be fetched in document order. The returned list contains elements that are instances of the &lt;a href=&#34;https://metacpan.org/module/XML::Twig::Elt&#34;&gt;XML::Twig::Elt&lt;/a&gt; class. So, this means you can perform any operations that the &lt;a href=&#34;https://metacpan.org/module/XML::Twig::Elt&#34;&gt;XML::Twig::Elt&lt;/a&gt; class allows.&lt;/p&gt;

&lt;p&gt;As you can see, information we are looking for is the attributes of the XML element tag. To reach the name of a person we must access name attribute. &lt;a href=&#34;https://metacpan.org/module/XML::Twig::Elt&#34;&gt;XML::Twig::Elt&lt;/a&gt; has various functions on attributes. The &lt;code&gt;att&lt;/code&gt; method will do the job. To note that, because the XML we have is automatically generated by another system, so we haven&#38;#39;t checked if the element has the attribute. To check if an element has a specific attribute, you can call the &lt;code&gt;att_exist&lt;/code&gt; it will return true if the attribute exists for the element, false otherwise.&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;check_guest&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;  # previous lines above&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@guests&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;root&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;span class=&#34;single&#34;&gt;&#39;guest&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;  # $guests[0]-&#38;gt;att(&#39;name&#39;) will return Michael&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we put all the information we&#38;rsquo;ve discussed together, the function would look 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;check_guest&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;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;core&#34;&gt;shift&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$password&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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XML::Twig&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;symbol&#34;&gt;$twig&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;parsefile&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;/hotelpms/guests.xml&#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;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;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# handle error&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@guests&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;root&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;span class=&#34;single&#34;&gt;&#39;guest&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&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;$guest&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@guests&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;number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$guest&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;att&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;id&#39;&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;symbol&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;amp;&#38;amp;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$guest&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;att&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;wifiKey&#39;&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;symbol&#34;&gt;$password&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;));&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&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;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;Parsing-XML-Response&#34;&gt;Parsing XML Response&lt;/h3&gt;

&lt;p&gt;Suppose that there are servers somewhere in this universe. We don&#38;#39;t know exactly when the answer will be returned it changes depending on the data of our request, but we know the exact XML response format. It is said that this server provides answers about the Earth&#38;#39;s past based on what it observes. So, demanding information related to an much earlier date will take much longer to travel due to the speed of light and it is open anyone to send request. A researcher wants to find out why the stones at Gobeklitepe were buried. Researcher knows that when the answer reach researcher will not be alive. All conditions have been maintained for the message to return, and a system was designed to await all requests by some world organization. The language selected was Perl.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;working-on-december.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Here is expected response format:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;HTTP/1.1 200 OK&lt;br /&gt;Content-Type: text/xml; charset=utf-8&lt;br /&gt;Content-Length: length&lt;br /&gt;&lt;br /&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;synSpecial&#34;&gt;soap&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;Envelope &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xsi&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://www.w3.org/2001/XMLSchema-instance&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xsd&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://www.w3.org/2001/XMLSchema&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt; &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xmlns&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;soap&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://schemas.xmlsoap.org/soap/envelope/&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;soap&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;Body&#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;observeResult &lt;/span&gt;&lt;span class=&#34;synType&#34;&gt;xmlns&lt;/span&gt;=&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;http://universe.com/&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;query&#38;gt;&lt;/span&gt;string&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/query&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;observation&#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;year&#38;gt;&lt;/span&gt;int&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/year&#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;nation&#38;gt;&lt;/span&gt;string&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/nation&#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;purpose&#38;gt;&lt;/span&gt;string&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/purpose&#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;details&#38;gt;&lt;/span&gt;string&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/details&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/observation&#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;/observeResult&#38;gt;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;soap&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;Body&#38;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;&#38;lt;/&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;soap&lt;/span&gt;&lt;span class=&#34;synComment&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;Envelope&#38;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;first_child&lt;/code&gt; function in the XML::Twig module is used to retrieve the first child element of the current element in the XML document. &lt;a href=&#34;https://metacpan.org/module/XML::Twig::Elt&#34;&gt;XML::Twig::Elt&lt;/a&gt; class has &lt;code&gt;text&lt;/code&gt; method that extracts the content from inside tag.&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;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;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;diagnostics&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;XML::Twig&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;LWP::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;$ua&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;LWP::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;$req&lt;/span&gt; &lt;span class=&#34;operator&#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;new&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;POST&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;http://universe.com?query=gobeklitepe&#38;quot;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#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;content_type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;text/XML; charset=utf-8&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#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;header&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;SOAPAction&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;http://universe.com/observations&#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;$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;request&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$req&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;$xml_response&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&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;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_success&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;&lt;span class=&#34;symbol&#34;&gt;$xml_response&lt;/span&gt; &lt;span class=&#34;operator&#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;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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;XML::Twig&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;symbol&#34;&gt;$twig&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;$xml_response&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;die&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Unable to parse XML document&#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;$observation&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$twig&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;soap:Body&#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;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;observeResult&#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;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;observation&#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;# Reaching observation data one by one&lt;br /&gt;&lt;/span&gt;&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;symbol&#34;&gt;$observation&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;year&#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;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;$nation&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$observation&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;nation&#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;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;$purpose&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$observation&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;purpose&#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;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;$details&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$observation&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;first_child&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;details&#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;structure&#34;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Happy holidays!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-09T00:00:00Z</updated><category term="Perl"/><author><name>Emine Sule Celik</name></author></entry><entry><title type="html">Pixie the Elf Picks an Artist: Exploring Perl&#38;#39;s new class syntax</title><link href="https://perladvent.org/2024/2024-12-08.html"/><id>https://perladvent.org/2024/2024-12-08.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;The-Problem:-Repetitive-Artists&#34;&gt;The Problem: Repetitive Artists&lt;/h3&gt;

&lt;p&gt;&lt;img src=&#34;pixie-music.png&#34;&gt;

&lt;/p&gt;



&lt;p&gt;Pixie the Elf (whose parents had strange ideas about names), was well-known at the North Pole for her love of music. But she often found herself in a bit of a repetitive loop. It was just too easy to listen to the same artists over and over again. With 15 years of meticulously scrobbled data on &lt;a href=&#34;https://last.fm/&#34;&gt;Last.fm&lt;/a&gt;, she wondered if she could leverage this information to diversify her playlists. Determined to find a solution, Pixie delved into the Last.fm API and, after some coding, developed &lt;code&gt;pickanartist&lt;/code&gt;, a Perl program designed to recommend artists she hadn&#38;#39;t engaged with recently.&lt;/p&gt;

&lt;h3 id=&#34;Using-PickAnArtist&#34;&gt;Using PickAnArtist&lt;/h3&gt;

&lt;p&gt;To break free from her musical monotony, Pixie used PickAnArtist as follows:&lt;/p&gt;

&lt;ol&gt;

&lt;li&gt;&lt;p&gt;&lt;b&gt;Setup:&lt;/b&gt; She cloned the PickAnArtist repository from GitHub and ensured all necessary dependencies were installed.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Configuration:&lt;/b&gt; Pixie set her Last.fm username and defined thresholds for minimum and maximum play counts to filter artists.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Execution:&lt;/b&gt; By running the script with her username, the program fetched her listening data, filtered artists based on the specified play counts, and randomly selected an artist for her to revisit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    pickanartist --user=pixiethelf --min=500 --max=1000&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Outcome:&lt;/b&gt; The script displayed the name of an artist within the specified play count range, prompting Pixie to explore their music anew.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Belle &#38;amp; Sebastian (978 plays)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pixie found their latest album on ElfTunes and added it to her play queue.&lt;/p&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;How-pickanartist-works&#34;&gt;How &lt;code&gt;pickanartist&lt;/code&gt; works&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;pickanartist&lt;/code&gt; program comprises two main components:&lt;/p&gt;

&lt;ol&gt;

&lt;li&gt;&lt;p&gt;&lt;b&gt;The Command-Line Program:&lt;/b&gt; Handles user input and initializes the process.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;The Perl Class:&lt;/b&gt; Encapsulates the core functionality, utilizing Perl&#38;#39;s modern class syntax.&lt;/p&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&#34;The-command-line-program&#34;&gt;The command-line program&lt;/h3&gt;

&lt;p&gt;The command-line program doesn&#38;#39;t do anything complicated, it just creates an object of class App::LastFM::PickAnArtist (using values passed on the command line) and calls its &lt;code&gt;run()&lt;/code&gt; method.&lt;/p&gt;

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

&lt;p&gt;The core functionality is all in the class - called App::LastFM::PickAnArtist. Pixie had written many Perl classes in her time, but she decided to use this as an excuse to try out the new Perl class syntax that had been introduced in Perl 5.38 and that is documented in &lt;a href=&#34;https://perldoc.perl.org/perlclass&#34;&gt;perlclass&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Normally, you would tell the Perl compiler that you were using the new class syntax by adding &lt;code&gt;use feature &#38;#39;class&#38;#39;&lt;/code&gt; to your code. But Pixie had read about &lt;a href=&#34;https://metacpan.org/pod/Feature::Compat::Class&#34;&gt;Feature::Compat::Class&lt;/a&gt; which makes the syntax available on earlier versions of Perl (as far back as 5.14) and she wanted to use that in case one of her less up-to-date gnome colleagues wanted to use her code.&lt;/p&gt;

&lt;p&gt;The first nice thing about the new syntax was that you could replace the &lt;code&gt;package&lt;/code&gt; keyword with &lt;code&gt;class&lt;/code&gt; - to better define what the code was. So Pixie&#38;#39;s file started like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Feature::Compat::Class;&lt;br /&gt;&lt;br /&gt;class App::LastFM::PickAnArtist;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;She then added a feature she needed along with a useful module:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;use feature&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;say&#39;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&#34;synStatement&#34;&gt;use &lt;/span&gt;Net::LastFM;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The next new keyword was &lt;code&gt;field&lt;/code&gt;, which you use to define attributes of the class (like &lt;code&gt;has&lt;/code&gt; in Moo or Moose). But in the new syntax, you&#38;#39;re defining something more like a lexical (&lt;code&gt;my&lt;/code&gt;) variable that can be read by all methods inside the class. You can also add the &lt;code&gt;:param&lt;/code&gt; attribute to define the field as something that can be passed to the object constructor, along with an expression to define a default value for the field.&lt;/p&gt;

&lt;p&gt;Pixie&#38;#39;s three parameter fields looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt; :param = &lt;span class=&#34;synConstant&#34;&gt;&#39;pixietheelf&#39;&lt;/span&gt;;&lt;br /&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt; :param = &lt;span class=&#34;synConstant&#34;&gt;500&lt;/span&gt;;&lt;br /&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$max&lt;/span&gt; :param = &lt;span class=&#34;synConstant&#34;&gt;1000&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;She then added a few more fields that would be useful when running the code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$lastfm&lt;/span&gt; = Net::LastFM-&#38;gt;new(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;api_key&lt;/span&gt;    =&#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$ENV{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;LASTFM_API_KEY&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;api_secret&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$ENV{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;LASTFM_API_SECRET&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;,&lt;br /&gt;);&lt;br /&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$method&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;&#39;user.getTopArtists&#39;&lt;/span&gt;;&lt;br /&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;@artists&lt;/span&gt;;&lt;br /&gt;field &lt;span class=&#34;synIdentifier&#34;&gt;$artist&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looking back over the code, Pixie added a TODO comment reminding herself that she should really deal with the case when the environment variables weren&#38;#39;t set. She&#38;#39;d do that... one day.&lt;/p&gt;

&lt;p&gt;Next, Pixie came to the methods for her class. There were three main differences to the OO Perl she had written before. Firstly, she could define methods with a &lt;code&gt;method&lt;/code&gt; keyword. Secondly, each method has access to the class fields - and you can just treat them like variables. And, finally, each method automatically has access to a &lt;code&gt;$self&lt;/code&gt; variable - without having to read it from the arguments!&lt;/p&gt;

&lt;p&gt;The first method was simple:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;method run {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;getartists;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;pickartist;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;render;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pixie was following good programming design practice here. She broke the problem down into simpler steps. One to get the artists from the LastFM API, one to pick an artist to recommend and one to display the results to the user.&lt;/p&gt;

&lt;p&gt;The next method was the most complex:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;method getartists {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$data&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$page&lt;/span&gt; = &lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;do&lt;/span&gt; {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$data&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$lastfm&lt;/span&gt;-&#38;gt;request_signed(&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;method&lt;/span&gt; =&#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$method&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;user&lt;/span&gt;   =&#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synConstant&#34;&gt;page&lt;/span&gt;   =&#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$page&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;synStatement&#34;&gt;for&lt;/span&gt; (&lt;span class=&#34;synIdentifier&#34;&gt;@{$data-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;topartists&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;artist&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}}&lt;/span&gt;) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$_-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;playcount&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &#38;gt; &lt;span class=&#34;synIdentifier&#34;&gt;$max&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$_-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;playcount&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &#38;lt; &lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@artists&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#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;synIdentifier&#34;&gt;$page&lt;/span&gt;;&lt;br /&gt;&#38;nbsp;&#38;nbsp;} &lt;span class=&#34;synStatement&#34;&gt;until&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;$data-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;topartists&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;artist&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}[&lt;/span&gt;-&lt;span class=&#34;synConstant&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;]{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;playcount&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt; &#38;lt; &lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;No artists with between &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$min&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt; and &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$max&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt; plays for &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@artists&lt;/span&gt;;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is where Pixie makes the actual call to the LastFM API. Most of the complexity is caused by LastFM paging the data it gives you, so you might need to make multiple calls in order to get all the data you want.&lt;/p&gt;

&lt;p&gt;But at the end of this method, the &lt;code&gt;@artists&lt;/code&gt; field contains one element for each artist the user has listened to between the minimum and maximum requested number of times.&lt;/p&gt;

&lt;p&gt;The last two methods are so simple, that Pixie hesitated to even make them methods:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;method pickartist {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;$artist&lt;/span&gt; = &lt;span class=&#34;synIdentifier&#34;&gt;$artists[&lt;/span&gt; &lt;span class=&#34;synStatement&#34;&gt;rand&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;@artists&lt;/span&gt; &lt;span class=&#34;synIdentifier&#34;&gt;]&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;method render {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$artist-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt; (&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$artist-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;playcount&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;)&#38;quot;&lt;/span&gt;;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pixie&#38;#39;s code is available on GitHub - &lt;a href=&#34;https://github.com/davorg/pickanartist&#34;&gt;github.com/davorg/pickanartist&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;Embracing-Modern-Perl&#34;&gt;Embracing Modern Perl&lt;/h3&gt;

&lt;p&gt;Pixie is happy with the way the code worked out. She thinks that the new syntax makes her code far easier to read, maintain and extend. She particularly like the way that the fields looked and acted like ordinary variables. This made a lot of code far simpler. Under &#38;quot;classic&#38;quot; Perl class syntax (and even with Moo or Moose) you would access the value of an attribute by using its accessor with code like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;The username is &#38;quot;&lt;/span&gt;, &lt;span class=&#34;synIdentifier&#34;&gt;$self&lt;/span&gt;-&#38;gt;username;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, maybe you would directly access the hash reference with code like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;The username is &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$self-&#38;gt;{&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;username&lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the first case, you&#38;#39;re using a subroutine which you can&#38;#39;t easily use in a string. In the second case, you&#38;#39;re relying on the internal representation of the class which breaks encapsulation.&lt;/p&gt;

&lt;p&gt;But the new syntax simplifies that to:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synStatement&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#38;quot;The username is &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;$username&lt;/span&gt;&lt;span class=&#34;synConstant&#34;&gt;&#38;quot;&lt;/span&gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The field looks and acts just like a variable, so you can easily use it in a string and you don&#38;#39;t need to know anything about the internals of the class to access the value.&lt;/p&gt;

&lt;p&gt;This syntax is a work in progress. It was first introduced with Perl 5.38 in 2023 and Perl 5.40 added a few more features (for example, it will now auto-generate an accessor method for any field with the attribute &lt;code&gt;:reader&lt;/code&gt;) and it will, no doubt, gain more functionality when Perl 5.42 is released next year.&lt;/p&gt;

&lt;p&gt;That said, it isn&#38;#39;t complete. It doesn&#38;#39;t yet support roles and (for obvious reasons) it&#38;#39;s missing the huge ecosystem of plugins and extensions that Moo/se has accumulated over the last twenty years.&lt;/p&gt;

&lt;p&gt;But don&#38;#39;t let that stop you from trying it out. Pixie didn&#38;#39;t need to use all of the features it currently has - so it can already do a lot more than is shown in her code.&lt;/p&gt;

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

&lt;p&gt;Pixie&#38;rsquo;s story demonstrates how modern Perl features and powerful APIs like Last.fm can combine to solve real-world problems in elegant ways. Whether you&#38;rsquo;re a seasoned Perl hacker or just getting started, the perlclass syntax makes it easier than ever to write maintainable, feature-rich code.&lt;/p&gt;

&lt;p&gt;So, take a leaf out of Pixie&#38;rsquo;s book&#38;mdash;or in this case, her playlist&#38;mdash;and let Perl help you create something fun, practical, and uniquely yours.&lt;/p&gt;

&lt;p&gt;Happy coding, and may your playlists stay diverse and delightful!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-08T00:00:00Z</updated><category term="Perl"/><author><name>Dave Cross</name></author></entry><entry><title>Keeping the Elves Busy</title><link href="https://perladvent.org/2024/2024-12-07.html"/><id>https://perladvent.org/2024/2024-12-07.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Christmas-Crisis&#34;&gt;Christmas Crisis&lt;/h3&gt;

&lt;p&gt;Already well into advent, Santa&#38;#39;s elves were busy in the workshop, checking lists, wrapping presents, and preparing for the big night. That was the plan anyway. But as Santa popped his head around the door, he saw that most of the elves were standing around looking at their phones or playing games with Rudolph. Because the other reindeer wouldn&#38;#39;t.&lt;/p&gt;

&lt;p&gt;&#38;quot;What&#38;#39;s going on?&#38;quot; asked Santa. &#38;quot;Why isn&#38;#39;t everyone working?&#38;quot;&lt;/p&gt;

&lt;p&gt;&#38;quot;Well,&#38;quot; explained Holly, the project manager, &#38;quot;this legacy inventory software we&#38;#39;re using is single-threaded, so it can only handle one job at a time.&#38;quot; She explained that the elves took it in turns to pick up a new job, work on it and then mark it as completed. But for the rest of the time there was nothing for them to do.&lt;/p&gt;

&lt;p&gt;&#38;quot;We&#38;#39;ve asked our supplier to update the software,&#38;quot; she continued, &#38;quot;since we&#38;#39;ve got all these CPUs on our server just sitting there doing nothing. But they told us it would cost $44 billion, and who has that sort of money? At this rate we&#38;#39;ll never get the presents ready by Christmas Eve.&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Parallel::ForkManager-to-the-Rescue&#34;&gt;Parallel::ForkManager to the Rescue&lt;/h3&gt;

&lt;p&gt;Santa considered the problem. Holly was correct, the server had plenty of spare capacity. So Santa checked out the repository and fired up his editor. He came across this method:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;sub process_presents ($self) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;while (my $present = $self-&#38;gt;get_next_present) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$present-&#38;gt;process;&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&#38;quot;Aha!&#38;quot; thought Santa, &#38;quot;this is the bottleneck.&#38;quot; Just as Holly had explained, the software couldn&#38;#39;t process a new present until the previous one had been completed.&lt;/p&gt;

&lt;p&gt;So Santa installed &lt;a href=&#34;https://metacpan.org/module/Parallel::ForkManager&#34;&gt;Parallel::ForkManager&lt;/a&gt; and changed the method to this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;sub process_presents ($self) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;my $pm = Parallel::ForkManager-&#38;gt;new(6);&lt;br /&gt;&#38;nbsp;&#38;nbsp;while (my $present = $self-&#38;gt;get_next_present) {&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$pm-&#38;gt;start and next;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$present-&#38;gt;process;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;$pm-&#38;gt;finish;&lt;br /&gt;&#38;nbsp;&#38;nbsp;}&lt;br /&gt;&#38;nbsp;&#38;nbsp;$pm-&#38;gt;wait_all_children;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;The-Elves-Get-Back-to-Work&#34;&gt;The Elves Get Back to Work&lt;/h3&gt;

&lt;p&gt;A quick test showed that all six elves were now kept busy.&lt;/p&gt;

&lt;p&gt;Santa asked Mrs Claus to review the code. He explained that &lt;a href=&#34;https://metacpan.org/module/Parallel::ForkManager&#34;&gt;Parallel::ForkManager&lt;/a&gt; is a module that wraps perl&#38;#39;s native &lt;code&gt;fork&lt;/code&gt; function, allowing multiple processes to run in parallel. It is simple to use and has an API which adds additional features.&lt;/p&gt;

&lt;p&gt;With the new code, six presents could be processed at once - one for each elf. The line &lt;code&gt;my $pm = Parallel::ForkManager-&#38;gt;new(6);&lt;/code&gt; creates a new instance of the &lt;code&gt;Parallel::ForkManager&lt;/code&gt; class, allowing a maximum of six concurrent child processes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; method is called on the object to fork off a new child process. As with the core &lt;code&gt;fork&lt;/code&gt; function, the method returns the &lt;code&gt;pid&lt;/code&gt; of the child process to the parent and &lt;code&gt;0&lt;/code&gt; to the child. So adding &lt;code&gt;and next&lt;/code&gt; means that the parent process immediately starts the next iteration of the loop. If there are alreadxy six child processes running, the parent will wait for one to finish before starting a new one.&lt;/p&gt;

&lt;p&gt;The child process processes the present and then calls the &lt;code&gt;finish&lt;/code&gt; method to end the child process.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;wait_all_children&lt;/code&gt; method is called after the loop to wait for all child processes to finish before the method returns.&lt;/p&gt;

&lt;p&gt;Mrs Claus was impressed with the new code, but suggested that Santa added error handling, tests and documentation.&lt;/p&gt;

&lt;h3 id=&#34;Santas-Work-is-Done&#34;&gt;Santa&#38;#39;s Work is Done&lt;/h3&gt;

&lt;p&gt;With Santa&#38;#39;s changes in production the elves were back to work and the project was back on schedule. Christmas was saved!&lt;/p&gt;

&lt;p&gt;Holly read through the documentation for &lt;a href=&#34;https://metacpan.org/module/Parallel::ForkManager&#34;&gt;Parallel::ForkManager&lt;/a&gt; and noticed the API included methods such as &lt;code&gt;run_on_finish&lt;/code&gt; and &lt;code&gt;run_on_wait&lt;/code&gt;. She added new tickets to use these methods to provide metrics to power a bunch of really exciting dashboards.&lt;/p&gt;

&lt;p&gt;Everyone was happy. Except for Rudolph.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-07T00:00:00Z</updated><category term="Perl"/><author><name>Paul Johnson</name></author></entry><entry><title>Have Yourself an AI Christmas!</title><link href="https://perladvent.org/2024/2024-12-06.html"/><id>https://perladvent.org/2024/2024-12-06.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;Ah for the good old days, when every Christmas season my family would gather in front of the roaring hearth, our glue, glitter, paint brushes, pens and card stock all spread out as we lovingly created personal Christmas cards for our friends and family.&lt;/p&gt;

&lt;p&gt;But who has time for that now? Nowadays, if my family sends out cards at all, we buy them in bulk at the local stationery store.&lt;/p&gt;

&lt;p&gt;Santa would not approve.&lt;/p&gt;

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

&lt;p&gt;We&#38;#39;ve already surrendered our coding to Copilot, our research to ChatGPT, and have replaced our real-life friends with simulated personalities on Character.ai. So why not let AI do our Christmas greetings for us?&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/OpenAI::API&#34;&gt;OpenAI::API&lt;/a&gt; to the rescue! This handy-dandy Perl module exposes OpenAI&#38;#39;s API to Perl, and lets you do chat, text completion, and image generation from the command line. What we&#38;#39;re going to do is:&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;1. Get a description of the type of card to generate and (optionally) the name of its recipient.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;2. Call OpenAI&#38;#39;s chat API to turn the description into an image generation prompt.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;3. Call OpenAI&#38;#39;s image generation API to generate an image from the prompt and store it online.&lt;/dt&gt;
&lt;dd&gt;

&lt;/dd&gt;
&lt;dt&gt;4. Use &lt;a href=&#34;https://metacpan.org/module/Browser::Open&#34;&gt;Browser::Open&lt;/a&gt; to display the image in your favorite web browser.&lt;/dt&gt;
&lt;dd&gt;

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

&lt;p&gt;When you run the script it does this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;   &#38;gt; perl merry_xmas.pl &#38;quot;A humorous card for my Uncle Jim, who loves cats&#38;quot;
   The AI elves are hard at work (tippity tappity)...done!&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Your browser will then open up and display something like this:&lt;/p&gt;

&lt;img src=&#34;xmas-card-for-uncle-jim.png&#34; /&gt;

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

&lt;p&gt;For this script to work, you&#38;#39;ll need to install the &lt;a href=&#34;https://metacpan.org/module/Browser::Open&#34;&gt;Browser::Open&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/OpenAI::API&#34;&gt;OpenAI::API&lt;/a&gt; modules from CPAN. You&#38;#39;ll also need to register an OpenAI account, get an API key, and add the key to your environment using the name &lt;a href=&#34;https://metacpan.org/module/OPENAI_API_KEY&#34;&gt;OPENAI_API_KEY&lt;/a&gt;. Instructions for this can be found at &lt;a href=&#34;https://platform.openai.com/docs/quickstart&#34;&gt;https://platform.openai.com/docs/quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&#34;The-Main-Script&#34;&gt;The Main Script!&lt;/h3&gt;

&lt;p&gt;Here&#38;#39;s the main script. Save it as merry_xmas.pl:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#!/usr/bin/env perl&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;pragma&#34;&gt;lib&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;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;single&#34;&gt;&#39;open_browser&#39;&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;ChristmasCard&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;die&lt;/span&gt; &lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;END&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;unless&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;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;Usage: $0 &#38;quot;card description&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;Generate a Christmas card from a brief description.&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;If the description contains the recipient&#39;s name, the name will&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;be incorporated in the card&#39;s greeting.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;END&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;$description&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;@ARGV&#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;print&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;STDERR&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;The AI elves are hard at work (tippity tappity)...&#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;$url&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ChristmasCard&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_card&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$description&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;STDERR&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;...done!\n&#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;open_browser&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;/code&gt;&lt;/pre&gt;

&lt;p&gt;This script imports the &lt;code&gt;ChristmasCard.pm&lt;/code&gt; module which does the actual work. It then uses &lt;code&gt;@ARGV&lt;/code&gt; to read the description of the card from the command line and passes it to the &lt;code&gt;ChristmasCard-&#38;gt;make_card()&lt;/code&gt; method. This method returns a URL to the image and passes it to the &lt;a href=&#34;https://metacpan.org/module/Browser::Open&#34;&gt;Browser::Open&lt;/a&gt; &lt;code&gt;open_browser()&lt;/code&gt; function to display the image. You can then download the image, attach it to an email, etc.&lt;/p&gt;

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

&lt;p&gt;The second and last piece is the &lt;code&gt;ChristmasCard&lt;/code&gt; library. Save this file as &lt;code&gt;ChristmasCard.pm&lt;/code&gt; in a subdirectory named &lt;code&gt;lib&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;comment&#34;&gt;#########################################################3&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ChristmasCard::Request::ImageGenerator&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;Types::Standard&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(Str Enum)&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;strictures&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;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;namespace::clean&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;extends&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;OpenAI::API::Request::Image::Generation&#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;model&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;isa&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Str&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;word&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;quality&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;isa&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Enum&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;standard&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;hd&#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;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;#########################################################3&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ChristmasCard::Response::ImageGenerator&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;strictures&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;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;br /&gt;&lt;span class=&#34;word&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;OpenAI::API::Response::Image::Generation&#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;single&#34;&gt;&#39;revised_prompt&#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;required&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;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;comment&#34;&gt;#########################################################3&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ChristmasCard&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;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;OpenAI::API::Request::Chat&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;make_card&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;$class&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;$description&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;$prompt&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$class&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_prompt&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$description&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;$class&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_image&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$prompt&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;make_prompt&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;$class&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;$description&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;||&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;A nostalgic illustration typical of Christmases past.&#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;$prompt&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;heredoc&#34;&gt;&#38;lt;&#38;lt;END&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;heredoc_content&#34;&gt;Please generate a text prompt in 120 words or less that can be passed to&lt;br /&gt;the OpenAI image generator to generate the illustration for a Christmas card.&lt;br /&gt;If the name of the recipient is mentioned in the description, write out&lt;br /&gt;&#38;quot;Merry Christmas &#38;lt;recipient&#38;gt;&#38;quot;.&lt;br /&gt;&lt;br /&gt;The following text describes the image:&lt;br /&gt;&lt;br /&gt;$description&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;heredoc_terminator&#34;&gt;END&lt;br /&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;$req&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;OpenAI::API::Request::Chat&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;&lt;span class=&#34;word&#34;&gt;model&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;gpt-4o-mini&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;messages&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;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;role&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;system&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;content&#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;You are a helpful assistant.&#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;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;role&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;user&#38;quot;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;double&#34;&gt;&#38;quot;content&#38;quot;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$prompt&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;&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;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;$resp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#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;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;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$resp&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;choices&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;message&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;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;make_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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$class&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;$prompt&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;double&#34;&gt;&#38;quot;Usage: \$card-&#38;gt;image(\$prompt)&#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;$req&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;ChristmasCard::Request::ImageGenerator&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;&lt;span class=&#34;word&#34;&gt;model&lt;/span&gt;  &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;dall-e-3&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;prompt&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$prompt&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;quality&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;hd&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;n&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;&lt;span class=&#34;word&#34;&gt;response_format&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;url&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#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;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$resp&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#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;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;&lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$resp&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;number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;]{&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;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;Ignore the first few blocks of code at the top of the file for now, and take a look at the section that begins with &lt;code&gt;package ChristmasCard;&lt;/code&gt;. This package begins by importing &lt;a href=&#34;https://metacpan.org/module/OpenAI::API::Request::Chat&#34;&gt;OpenAI::API::Request::Chat&lt;/a&gt;, which is the programmatic interface to ChatGPT. It then defines the top-level method, &lt;code&gt;make_card()&lt;/code&gt;. This method takes the card description provided by the user, and passes it to a method called &lt;code&gt;make_prompt()&lt;/code&gt; which calls ChatGPT to take the description and turn it into a full image prompt suitable for generating a christmas card. For example, if the prompt is &#38;quot;A humorous card for my Uncle Jim, who loves cats&#38;quot;, the resulting prompt might be something along the lines of:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  A whimsical Christmas card design featuring a cozy living room
  scene. In the center, a fluffy cat wearing a Santa hat is playfully
  tangled in colorful Christmas lights. Surrounding the cat are
  cheerful holiday decorations, including a beautifully decorated
  tree, stockings hung by the fireplace, and a few playful kittens
  peeking out from under the tree. Snow gently falls outside the
  window, creating a warm, festive atmosphere. The text at the top
  reads, &#38;quot;Merry Christmas Uncle Jim!&#38;quot; in playful, bold letters. The
  overall vibe should be humorous and heartwarming, perfect for a
  cat-loving uncle.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This prompt is now passed to the &lt;code&gt;make_image()&lt;/code&gt; method, which generates the image and returns its URL.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s have a look at the &lt;code&gt;make_prompt()&lt;/code&gt; method. It adds a preface to the card description that tells ChatGPT what we&#38;#39;re aiming for. The resulting instruction passed to ChatGPT will look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  Please generate a less than 120 words text prompt that can be passed
  to the OpenAI image generator to generate the illustration for a
  Christmas card.  If the name of the recipient is mentioned in the
  description, write out &#38;quot;Merry Christmas &#38;lt;recipient&#38;gt;&#38;quot;.

  The following text describes the image:

  A humorous card for my Uncle Jim, who loves cats&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We now open a new chat connection using &lt;code&gt;OpenAI::API::Request::Chat-&#38;gt;new()&lt;/code&gt;, and specify the language model to use and the messages to send to it. In this case, we use the &lt;code&gt;gpt-4o-mini&lt;/code&gt; model, and pass the language model a context-setting message of &#38;quot;You are a helpful assistant&#38;quot; followed by the instruction we generated earlier. We then call the resulting object&#38;#39;s &lt;code&gt;send()&lt;/code&gt; method, and return the content of the language model&#38;#39;s response. (The response object is actually a nested set of hashes that contains a lot of metadata about the transaction in addition to the response itself.)&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s look at the &lt;code&gt;make_image()&lt;/code&gt; method, which generates the image. We take the image-generation prompt from the previous step, and pass it to &lt;code&gt;ChristmasCard::Request::ImageGenerator-&#38;gt;new()&lt;/code&gt;. In addition to the prompt, we pass arguments for the AI model to use (&lt;code&gt;dall-e-3&lt;/code&gt;), the quality of the image (&lt;code&gt;hd&lt;/code&gt; for high definition), the number of images to generate, and the &lt;code&gt;response_format&lt;/code&gt;. You can either request a URL or the actual image data. We choose the &lt;code&gt;url&lt;/code&gt; format because it is easier to deal with.&lt;/p&gt;

&lt;p&gt;We then call the returned request object&#38;#39;s &lt;code&gt;send()&lt;/code&gt; method to get a response, navigate to the URL nested deep inside the response object like a chocolate Santa in a stocking, and return it.&lt;/p&gt;

&lt;p&gt;Now we&#38;#39;ll talk about the two packages defined at the top of &lt;code&gt;ChristmasCard.pm&lt;/code&gt;, one named &lt;code&gt;ChristmasCard::Request::ImageGenerator&lt;/code&gt; and the other named &lt;code&gt;ChristmasCard::Response::ImageGenerator&lt;/code&gt;. The &lt;a href=&#34;https://metacpan.org/module/OpenAI::API&#34;&gt;OpenAI::API&lt;/a&gt; module is equipped with a class called &lt;a href=&#34;https://metacpan.org/module/OpenAI::API::Request::Image::Generation&#34;&gt;OpenAI::API::Request::Image::Generation&lt;/a&gt; which handles image generation requests. Unfortunately this module is a bit out of date and neither handles the newer &lt;code&gt;dall-e-3&lt;/code&gt; model nor supports the &lt;code&gt;hd&lt;/code&gt; format, both of which generate much nicer cards than the older versions.&lt;/p&gt;

&lt;p&gt;Fortunately the OpenAI module is well-designed and easy to extend. The &lt;code&gt;ChristmasCard::Request::ImageGenerator&lt;/code&gt; module, imported at the top of &lt;a href=&#34;https://metacpan.org/module/ChristmasCard.pm&#34;&gt;ChristmasCard.pm&lt;/a&gt;, extends &lt;a href=&#34;https://metacpan.org/module/OpenAI::API::Request::Image::Generation&#34;&gt;OpenAI::API::Request::Image::Generation&lt;/a&gt; to define two new generation parameters: &lt;code&gt;model&lt;/code&gt; and &lt;code&gt;quality&lt;/code&gt;. We use the Moose subclassing mechanism, which provides the &lt;code&gt;extends&lt;/code&gt; operator to generate a subclass of &lt;a href=&#34;https://metacpan.org/module/OpenAI::API::Request::Image::Generation&#34;&gt;OpenAI::API::Request::Image::Generation&lt;/a&gt;, and the &lt;code&gt;has&lt;/code&gt; operator to add two new typed accessors to the &lt;code&gt;ChristmasCard::Request::ImageGenerator&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/OpenAI::API&#34;&gt;OpenAI::API&lt;/a&gt; requires every request generator to have a corresponding response parser. We define &lt;code&gt;ChristmasCard::Response::ImageGenerator&lt;/code&gt; to be a subclass of &lt;a href=&#34;https://metacpan.org/module/OpenAI::API::Response::Image::Generation&#34;&gt;OpenAI::API::Response::Image::Generation&lt;/a&gt;, and add a new &lt;code&gt;revised_prompt&lt;/code&gt; accessor to it in order to accommodate a new field returned by the &lt;code&gt;dall-e-3&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;And there you have it! Now, if you&#38;#39;re ambitious -- or maybe just lazy -- you can create a text file of friend and family names and their personal idiosyncrasies. Read from the file, write a little loop around &lt;code&gt;ChristmasCard-&#38;gt;make_card()&lt;/code&gt;, and get all your Christmas cards done in two shakes of the Easter bunny&#38;#39;s tail.&lt;/p&gt;

&lt;p&gt;Time to hit the eggnog!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-06T00:00:00Z</updated><category term="Perl"/><author><name>lincoln.stein</name></author></entry><entry><title type="html">Santa&#38;#39;s Naughty and Nice Data Formats</title><link href="https://perladvent.org/2024/2024-12-05.html"/><id>https://perladvent.org/2024/2024-12-05.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h3 id=&#34;Santas-Naughty-and-Nice-Data-Formats&#34;&gt;Santa&#38;#39;s Naughty and Nice Data Formats&lt;/h3&gt;

&lt;p&gt;Santa faces some of the same technology issues that many of us have faced. After a succession of elves half-implemented a reindeer tracking system, his reindeer database is in a sad state, there&#38;#39;s nobody to fix it, and his naughty list has a special section for people who create dirty data.&lt;/p&gt;

&lt;p&gt;Now he needs to fix up his records so he can generate the reports his compliance elves keep hassling him about (a whole other thing&#38;mdash;don&#38;#39;t ask). That same compliance team also makes Santa use auditable source control, so his &lt;a href=&#34;https://github.com/briandfoy/santas-reindeers-rx-perl-advent-2024&#34;&gt;data and programs are in GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One file, &lt;i&gt;data/donner.json&lt;/i&gt;, started with this 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;quot;&lt;span class=&#34;synStatement&#34;&gt;Name&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Donner&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;aliases&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;quot;&lt;span class=&#34;synConstant&#34;&gt;Dunder&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Donder&lt;/span&gt;&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;start-date&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;1823-12-24&lt;/span&gt;&#38;quot;&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Another file, &lt;i&gt;data/rudolph.json&lt;/i&gt;, has similar data but with slightly different field names and a different date format. This one must have come from the new interns who had to guess what to do because there aren&#38;#39;t any docs:&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;quot;&lt;span class=&#34;synStatement&#34;&gt;name&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Rudolph&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;start_date&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;12/24/1939&lt;/span&gt;&#38;quot;&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These differences mean that some reindeers are left out of some of the reindeer games because their records are simply skipped.&lt;/p&gt;

&lt;h3 id=&#34;Enter-Data::Rx&#34;&gt;Enter Data::Rx&lt;/h3&gt;

&lt;p&gt;At first this seems like a simple problem of checking each hash to ensure it has the right set of keys. The same goes for its values. That could be its own Perl program. But, as the data structure gets more and more complicated, so does the code. Santa is an old-school, zero-conf, minimal code sorta guy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; provides a way to declare what a data structure should look like and what sort of values it should have. Santa knows it&#38;#39;s going to take a minute to clean up all of his files, so he&#38;#39;ll start with two things he knows. He wants the fields to be &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;start_date&lt;/code&gt;. He creates his Rx specification as a Perl data structure:&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;$record&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;&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;//rec&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;required&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;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;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;word&#34;&gt;start_date&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;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This says that there is a record type (think &#38;quot;hash&#38;quot;), that there are two required keys, and that the values for those keys are strings. This Perl data structure is the basis for the schema that &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; creates and which Santa then uses to validate a data structure:&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;word&#34;&gt;Data::Rx&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$rx&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Rx&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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$schema&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$rx&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_schema&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$record&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$schema&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;assert_valid&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;/code&gt;&lt;/pre&gt;

&lt;p&gt;Putting that together with the boring programming work gets Santa his starting program:&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;version&#34;&gt;v5.14&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::Rx&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::File&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;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;$record&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;&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;//rec&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;required&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;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;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;word&#34;&gt;start_date&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;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;&#38;nbsp;&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;$rx&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Rx&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;$schema&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$rx&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_schema&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$record&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;foreach&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;span class=&#34;word&#34;&gt;sort&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;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;Checking $file&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#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;eval&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::JSON::decode_json&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Mojo::File&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;$file&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;slurp&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;&lt;span class=&#34;keyword&#34;&gt;unless&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;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$error&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;=~&lt;/span&gt; &lt;span class=&#34;substitute&#34;&gt;s/\.^/\n/gmr&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;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;\tCould not read &#38;lt;$file&#38;gt;: $error&#38;quot;&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;next&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$schema&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;assert_valid&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;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$at&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;&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;word&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$at&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&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;$failure&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;$at&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;failures&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;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;\t$failure&#38;quot;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Santa runs his program on a couple of the files, knowing he&#38;#39;s going to get several errors. Just with the two files shown earlier, Santa finds that there are some misnamed fields (&#38;quot;unexpected entries&#38;quot;) and missing values for required entries:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;$ perl bin/validate data/rudolph.json data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/donner.json&lt;br /&gt;&#38;nbsp;&#38;nbsp;Failed //rec: found unexpected entries: Name aliases start-date (error: unexpected at $data)&lt;br /&gt;&#38;nbsp;&#38;nbsp;Failed //rec: no value given for required entry start_date (error: missing at $data)&lt;br /&gt;&#38;nbsp;&#38;nbsp;Failed //rec: no value given for required entry name (error: missing at $data)&lt;br /&gt;&#38;nbsp;Checking data/rudolph.json&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;He&#38;#39;ll fix these up in a moment, but he has to run out to the workshop to handle a slow down on the toy train assembly line. That gives you some time to investigate Rx.&lt;/p&gt;

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

&lt;p&gt;The &lt;a href=&#34;https://rx.codesimply.com&#34;&gt;Rx language&lt;/a&gt; allows us to easily specify basic structure as well as extend it for more complex types. The &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; module implements this for Perl, but the language can be implemented in anything (and just about is). We can write out our specification in just about anything too, but we&#38;#39;ll stick to Perl for now.&lt;/p&gt;

&lt;p&gt;In Perl, this starts with a hash with the key &lt;code&gt;type&lt;/code&gt; to specify what the first element should be. In this case, the type is &lt;code&gt;//rec&lt;/code&gt;, the Rx name for a hash (dictionary, map, JSON object, and so on):&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;$record&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;&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;//rec&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There are many other types, but at the top level you probably have a &lt;code&gt;//rec&lt;/code&gt;, &lt;code&gt;//arr&lt;/code&gt; (array), &lt;code&gt;//map&lt;/code&gt; (all values are the same type), or a &lt;code&gt;//seq&lt;/code&gt; (sequence).&lt;/p&gt;

&lt;p&gt;Next, we can specify the required keys, and specify the value that each of these keys takes. Each of the values is another Rx specification, and in this case, each of them is a string (&lt;code&gt;//str&lt;/code&gt;):&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;$record&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;&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;//rec&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;required&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;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;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;word&#34;&gt;start_date&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;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we have our schema, we tell &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; to create the Perl object we can use to validate the data:&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;$rx&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Rx&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;$schema&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$rx&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_schema&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$record&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To apply the schema to a Perl data structure, we call &lt;code&gt;assert_valid&lt;/code&gt;, which throws an exception if the validation fails:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;word&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$schema&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;assert_valid&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;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&#38;#39;s also a binary &lt;code&gt;check&lt;/code&gt;, but that only reports yes or no. That could be valuable in some cases, such as when we don&#38;#39;t want to see thousands of lines of errors in continuous integration.&lt;/p&gt;

&lt;p&gt;But now Santa has got the toy trains running again, so he&#38;#39;s back to working on his data problems.&lt;/p&gt;

&lt;h3 id=&#34;Fixing-the-data-errors&#34;&gt;Fixing the data errors&lt;/h3&gt;

&lt;p&gt;Santa fixes up the field names easily enough to get the new &lt;code&gt;data/donner.json&lt;/code&gt;:&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;quot;&lt;span class=&#34;synStatement&#34;&gt;aliases&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;quot;&lt;span class=&#34;synConstant&#34;&gt;Dunder&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Donder&lt;/span&gt;&#38;quot;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;name&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;Donner&lt;/span&gt;&#38;quot;,&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;quot;&lt;span class=&#34;synStatement&#34;&gt;start_date&lt;/span&gt;&#38;quot;: &#38;quot;&lt;span class=&#34;synConstant&#34;&gt;1823-12-24&lt;/span&gt;&#38;quot;&lt;br /&gt;&lt;span class=&#34;synSpecial&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Some of the errors, but he still has an error for the &lt;code&gt;aliases&lt;/code&gt; key:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;$ perl bin/validate data/rudolph.json data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/donner.json&lt;br /&gt;&#38;nbsp;&#38;nbsp;Failed //rec: found unexpected entries: Name aliases (error: unexpected at $data)&lt;br /&gt;&#38;nbsp;Checking data/rudolph.json&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To handle &lt;code&gt;aliases&lt;/code&gt;, Santa needs to specify an array. In Rx, an array has values that are all the same type (say, all strings). Santa extends his specification a little. He makes the &lt;code&gt;aliases&lt;/code&gt; field optional. It&#38;#39;s okay if Donner has aliases (more than one even), and it&#38;#39;s okay if Rudolph doesn&#38;#39;t have that field at all:&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;$record&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;&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;//rec&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;required&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;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;type&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;word&#34;&gt;start_date&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;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#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;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;optional&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;aliases&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;&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;//arr&#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;contents&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;//str&#39;&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;&#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;&lt;span class=&#34;structure&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now both files validate:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;$ perl bin/validate data/rudolph.json data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/rudolph.json&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;Custom-types&#34;&gt;Custom types&lt;/h3&gt;

&lt;p&gt;Santa still has a problem because the date formats in his two test records don&#38;#39;t match. The values are both strings, but that&#38;#39;s it. Donner has &lt;code&gt;1823-12-24&lt;/code&gt; but Rudolph has &lt;code&gt;12/24/1939&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Santa defines a new package that inherits from &lt;a href=&#34;https://metacpan.org/module/Data::Rx::CommonType::EasyNew&#34;&gt;Data::Rx::CommonType::EasyNew&lt;/a&gt;. This package defines &lt;code&gt;type_uri&lt;/code&gt;, which is the name for the new type, and &lt;code&gt;assert_valid&lt;/code&gt;, which is the Perl subroutine that calls &lt;code&gt;fail&lt;/code&gt; with the parts that &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; needs to report the error:&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;Reindeer::YYYYMMDD&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;pragma&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;Data::Rx::CommonType::EasyNew&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;type_uri&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;{&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;tag:example.com,EXAMPLE:rx/reindeer-date&#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;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;assert_valid&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;my&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;$value&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;&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;unless&lt;/span&gt; &lt;span class=&#34;core&#34;&gt;defined&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$value&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;symbol&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;match&#34;&gt;/\A(?:\d\d\d\d)-\d\d-\d\d\z/a&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;or&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;fail&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;error&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;words&#34;&gt;qw(type)&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;message&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;double&#34;&gt;&#38;quot;date value is not YYYY-MM-DD&#38;quot;&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;value&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$value&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;structure&#34;&gt;})&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;structure&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(You can put this in a separate file and load it with &lt;code&gt;use&lt;/code&gt;, or stick it right in the current program.)&lt;/p&gt;

&lt;p&gt;To use this new type, Santa loads it as part of the call to &lt;code&gt;new&lt;/code&gt;:&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;$rx&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Data::Rx&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;type_plugins&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;words&#34;&gt;qw(&lt;br /&gt;&#38;nbsp;Reindeer::YYYYMMDD&lt;br /&gt;&#38;nbsp;&#38;nbsp;)&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;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now Santa&#38;#39;s program catches the date format error:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;$ perl bin/validate data/rudolph.json data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/donner.json&lt;br /&gt;&#38;nbsp;Checking data/rudolph.json&lt;br /&gt;&#38;nbsp;&#38;nbsp;Failed tag:example.com,EXAMPLE:rx/reindeer-date: date value is not YYYY-MM-DD (error: type at $data-&#38;gt;{start_date})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Santa didn&#38;#39;t have to change anything in the meat of his program. Everything in the &lt;code&gt;foreach&lt;/code&gt; loop stayed the same and he only has the change his Rx specification. Whoever invented Rx and &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; are quickly moving to the top of his Nice list (and that would be the double nice Rik SIGNES).&lt;/p&gt;

&lt;p&gt;I write about this sort of thing quite a bit in &lt;a href=&#34;https://www.effectiveperlprogramming.org/&#34;&gt;Effective Perl Programming&lt;/a&gt; and &lt;a href=&#34;https://www.masteringperl.org/&#34;&gt;Mastering Perl&lt;/a&gt;. Moving things out of logic and into configuration makes the program easier to modify and the data easier to understand.&lt;/p&gt;

&lt;h3 id=&#34;The-schema-as-configuration&#34;&gt;The schema as configuration&lt;/h3&gt;

&lt;p&gt;Since Santa&#38;#39;s &lt;a href=&#34;https://metacpan.org/module/Data::Rx&#34;&gt;Data::Rx&lt;/a&gt; schema is really a data structure, anything that can create that data structure can be its source. For example, Santa could put it in a YAML file (if you&#38;#39;d rather have JSON, you can do that instead):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&#38;nbsp;---&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;//rec&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;required&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;//str&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;start_date&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;tag:example.com,EXAMPLE:rx/reindeer-date&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;optional&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;aliases&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;//arr&#39;&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;//str&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In Santa&#38;#39;s program, he loads his schema with the &lt;a href=&#34;https://metacpan.org/module/YAML&#34;&gt;YAML&lt;/a&gt; module instead of defining it as code. The rest of the program stays the same:&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;YAML&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;$record&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;YAML::LoadFile&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;rx.yml&#39;&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By having the schema outside of the Perl source, Santa can reuse it with other tools. So far, Rx has only very limited support for custom types, so those parts still have to live in code.&lt;/p&gt;

&lt;h3 id=&#34;See-Data::Rx-in-action&#34;&gt;See Data::Rx in action&lt;/h3&gt;

&lt;p&gt;I used a very simple example to give you the flavor of Rx, but I&#38;#39;m using this on actual data structures for &lt;a href=&#34;https://github.com/briandfoy/cpan-security-advisory&#34;&gt;CPAN Security Advisories&lt;/a&gt;. Each reported CPAN distribution has a file dedicated to it, and there are certain pieces of information we want to collect for each distribution and for each advisory in that distribution. Some of that is hand-edited, which inevitably leads to mistakes.&lt;/p&gt;

&lt;p&gt;Adding Data::Rx tests, as in &lt;a href=&#34;https://github.com/briandfoy/cpan-security-advisory/blob/master/xt/validate.t&#34;&gt;xt/validate.t&lt;/a&gt; and &lt;a href=&#34;https://github.com/briandfoy/cpan-security-advisory/blob/master/xt/validate-db.t&#34;&gt;xt/validate-db.t&lt;/a&gt;, allows us to check every data file for structure, format, and values.&lt;/p&gt;

&lt;p&gt;In those tests you&#38;#39;ll see more of Rx&#38;#39;s core types, more custom types, and more complicated structures.&lt;/p&gt;

&lt;h3 id=&#34;Further-Reading&#34;&gt;Further Reading&lt;/h3&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://rx.codesimply.com&#34;&gt;The Rx language&lt;/a&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://metacpan.org/pod/Data::Rx&#34;&gt;The Data::Rx module&lt;/a&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/briandfoy/santas-reindeers-rx-perl-advent-2024&#34;&gt;Santa&#38;#39;s GitHub repo&lt;/a&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.altogetherchristmas.com/traditions/reindeer.html&#34;&gt;The History of Santa&#38;#39;s Reindeer&lt;/a&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://runningreindeer.com&#34;&gt;The Running Reindeer Ranch&lt;/a&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;</summary><updated>2024-12-05T00:00:00Z</updated><category term="Perl"/><author><name>brian d foy</name></author></entry><entry><title>GitHub Actions: A Festive Tale of DevOps Magic</title><link href="https://perladvent.org/2024/2024-12-04.html"/><id>https://perladvent.org/2024/2024-12-04.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;It was a crisp December morning at the North Pole, and Pixie the Elf (whose parents had weird ideas about names) was already hard at work in Santa&#38;rsquo;s Workshop. Her job was critical: maintaining Santa&#38;rsquo;s Good/Bad Child Tracking App, affectionately nicknamed &#38;quot;NaughtyOrNice&#38;quot;. The app, a sprawling Perl application, kept records on every child&#38;rsquo;s behaviour throughout the year. But as Christmas drew nearer, the workload multiplied, and Pixie was feeling the strain.&lt;/p&gt;

&lt;h3 id=&#34;The-Problem:-Manual-Testing-Mayhem&#34;&gt;The Problem: Manual Testing Mayhem&lt;/h3&gt;

&lt;p&gt;For years, Pixie had been managing NaughtyOrNice with a mix of manual testing, haphazard deployment scripts, and a good deal of holiday cheer. But this year, the app had grown exponentially, with new features like integration with Smart Chimney Sensors&#38;trade; and support for multi-timezone sleigh operations.&lt;/p&gt;

&lt;p&gt;Every code change required hours of testing&#38;mdash;on Pixie&#38;#39;s local machine, of course&#38;mdash;followed by an error-prone manual deployment process. One too many late-night debugging sessions left Pixie wondering if she was on Santa&#38;#39;s Naughty list herself.&lt;/p&gt;

&lt;p&gt;&#38;quot;I need help,&#38;quot; she muttered to herself during one particularly long debugging session. She decided to take a break, grabbed a coffee from the machine and started idly browsing r/elftech. Immediately, an article about GitHub Actions caught her attention.&lt;/p&gt;

&lt;h3 id=&#34;What-Are-GitHub-Actions&#34;&gt;What Are GitHub Actions?&lt;/h3&gt;

&lt;p&gt;Pixie had vaguely heard of GitHub Actions before, but she&#38;rsquo;d always assumed it was for the tech-savvy elves in charge of JavaScript or Rust projects. The article made it seem that GitHub Actions were just as useful for Perl developers, and that it could automate much of her workflow.&lt;/p&gt;

&lt;p&gt;Intrigued, Pixie decided to dive in.&lt;/p&gt;

&lt;h3 id=&#34;A-First-Workflow:-Automating-Tests&#34;&gt;A First Workflow: Automating Tests&lt;/h3&gt;

&lt;p&gt;Pixie started with the basics: automating the test suite for NaughtyOrNice. The app already had a robust set of tests written with &lt;a href=&#34;https://metacpan.org/pod/Test::More&#34;&gt;Test::More&lt;/a&gt; , but running them manually was tedious.&lt;/p&gt;

&lt;p&gt;Pixie created her first GitHub Actions workflow file, .github/workflows/test.yml:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Perl Test Suite&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;push&lt;/span&gt;&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;synIdentifier&#34;&gt;branches&lt;/span&gt;&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;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;main&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;pull_request&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;test&lt;/span&gt;&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;synIdentifier&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ubuntu-latest&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Checkout code&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; actions/checkout@v4&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Set up Perl&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; shogo82148/actions-setup-perl@v1&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;with&lt;/span&gt;&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;synIdentifier&#34;&gt;perl-version&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synConstant&#34;&gt;&#39;5.40&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Install dependencies&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; cpanm --quiet --installdeps .&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Run tests&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; prove -lr t/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pixie was impressed. With just a few lines of YAML, she could trigger her tests automatically whenever someone pushed code to the main branch or opened a pull request. No more manual testing! She could focus on writing code instead of running it.&lt;/p&gt;

&lt;h3 id=&#34;Adding-Deployment:-Automating-the-NaughtyOrNice-Rollout&#34;&gt;Adding Deployment: Automating the NaughtyOrNice Rollout&lt;/h3&gt;

&lt;p&gt;With her test suite humming along, Pixie turned her attention to deployments. Previously, deploying the app involved copying files to the production server with a combination of scp commands and festive hope.&lt;/p&gt;

&lt;p&gt;She created another workflow file, .github/workflows/deploy.yml:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Deploy to Production&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;deploy&lt;/span&gt;&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;synIdentifier&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ubuntu-latest&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;steps&lt;/span&gt;&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;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Checkout code&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; actions/checkout@v4&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synStatement&#34;&gt;- &lt;/span&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; Deploy to server&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;env&lt;/span&gt;&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;synIdentifier&#34;&gt;RSYNC_PASSWORD&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; ${{ secrets.RSYNC_PASSWORD }}&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;run&lt;/span&gt;&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;rsync -avz --delete ./ naughtyornice@production-server:/var/www/naughtyornice/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By adding a secret to the GitHub repository for her server credentials, Pixie automated the entire deployment process. The &lt;code&gt;workflow_dispatch&lt;/code&gt; key in the &lt;code&gt;on&lt;/code&gt; section of the workflow added a &#38;quot;Run&#38;quot; button to the &#38;quot;Actions&#38;quot; tab in the GitHub repo, and just pressing that would deploy the application to the production server. No more late nights. No more missed sleigh rides. Pixie even dared to start dreaming about creating a Docker container for NaughtyOrNice and deploying the application that way. The time she would save would allow her to look into long-awaited improvements like that.&lt;/p&gt;

&lt;h3 id=&#34;The-Real-Magic:-Pre-Written-Workflows&#34;&gt;The Real Magic: Pre-Written Workflows&lt;/h3&gt;

&lt;p&gt;Pixie was excited. This was going to make her life much better in so many ways, And then she remembered the main point of the article she had been reading. A group of Perl programmers on GitHub had created a set of reusable workflows. They were aimed at CPAN modules, but as the NaughtyOrNice codebase followed all of the CPAN packaging conventions, she thought she could probably use these workflows for her code.&lt;/p&gt;

&lt;p&gt;Pixie read the instructions for using these workflows (they were online at &lt;a href=&#34;https://github.com/PerltoolsTeam/github_workflows/&#34;&gt;https://github.com/PerltoolsTeam/github_workflows/&lt;/a&gt;) and, in a few minutes, she had a new workflow which looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&gt;&lt;span class=&#34;synIdentifier&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; CI&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;push&lt;/span&gt;&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;synIdentifier&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synSpecial&#34;&gt;[&lt;/span&gt; main &lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;pull_request&lt;/span&gt;&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;synIdentifier&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;synSpecial&#34;&gt;[&lt;/span&gt; main &lt;span class=&#34;synSpecial&#34;&gt;]&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&#34;synIdentifier&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;test&lt;/span&gt;&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;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; PerlToolsTeam/github_workflows/.github/workflows/cpan-test.yml@main&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;coverage&lt;/span&gt;&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;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; PerlToolsTeam/github_workflows/.github/workflows/cpan-coverage.yml@main&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;perlcritic&lt;/span&gt;&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;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; PerlToolsTeam/github_workflows/.github/workflows/cpan-perlcritic.yml@main&lt;br /&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;synIdentifier&#34;&gt;complexity&lt;/span&gt;&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;synIdentifier&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;synSpecial&#34;&gt;:&lt;/span&gt; PerlToolsTeam/github_workflows/.github/workflows/cpan-complexity.yml@main&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pixie was very happy. In a few lines of code, she had replaced and improved on her existing manual processes. The application was now being tested automatically on several versions of Perl and on Linux, Windows and macOS. She was also getting test coverage reports on &lt;a href=&#34;https://coveralls.io/&#34;&gt;Coveralls.io&lt;/a&gt;. But the things she was most excited by were the PerlCritic and Cyclomatic Complexity reports. Both of these gave her a huge number of ideas for improvements to the code. And now that the process was so much more efficient, she might actually have time to implement some of them. Happily, she started adding the suggestions into the NaughtyOrNice issue tracker.&lt;/p&gt;

&lt;h3 id=&#34;The-Result:-A-Smoother-Christmas&#34;&gt;The Result: A Smoother Christmas&lt;/h3&gt;

&lt;p&gt;On Christmas Eve, as Santa prepared for his big journey, he stopped by Pixie&#38;#39;s workstation.&lt;/p&gt;

&lt;p&gt;&#38;quot;Pixie,&#38;quot; Santa said with a twinkle in his eye, &#38;quot;thanks to your hard work, this year the app is the best it&#38;#39;s ever been. You&#38;rsquo;ve truly outdone yourself.&#38;quot;&lt;/p&gt;

&lt;p&gt;Pixie blushed. &#38;quot;It wasn&#38;rsquo;t just me, Santa. I had help&#38;mdash;from GitHub Actions!&#38;quot;&lt;/p&gt;

&lt;h3 id=&#34;Your-Own-Holiday-Automation&#34;&gt;Your Own Holiday Automation&lt;/h3&gt;

&lt;p&gt;Pixie&#38;#39;s story shows that even the busiest developers can save time and reduce stress with GitHub Actions. Whether you&#38;#39;re maintaining a legacy Perl app or building a shiny new one, these workflows can simplify your life.&lt;/p&gt;

&lt;p&gt;To get started, check out the pre-written workflows at &lt;a href=&#34;https://github.com/PerltoolsTeam/github_workflows/&#34;&gt;https://github.com/PerltoolsTeam/github_workflows/&lt;/a&gt;. Who knows? They might just bring a little magic to your own development process.&lt;/p&gt;

&lt;p&gt;Happy holidays, and happy automating!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-04T00:00:00Z</updated><category term="Perl"/><author><name>Dave Cross</name></author></entry><entry><title>Sleigh Bells and Custom Ops: A Jolly Journey with Ref::Util</title><link href="https://perladvent.org/2024/2024-12-03.html"/><id>https://perladvent.org/2024/2024-12-03.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h2 id=&#34;&#34;&gt;&lt;/h2&gt;

&lt;p&gt;Ho ho ho! &#38;#x1F385; As the festive season wraps us in its warm embrace, let&#38;#39;s unwrap a shiny gift for Perl developers that&#38;#39;s been nestled under the tree for a few years now: &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt;. Just like how some of the best presents are hidden in plain sight, &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; brings a sprinkle of efficiency and clarity to our code, turning &#38;quot;naughty&#38;quot; reference checks into &#38;quot;nice&#38;quot; ones. So grab a mug of hot cocoa, and let&#38;#39;s dive into this yuletide tale!&lt;/p&gt;

&lt;h3 id=&#34;A-Christmas-Classic-Worth-Revisiting&#34;&gt;A Christmas Classic Worth Revisiting&lt;/h3&gt;

&lt;p&gt;If &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; sounds familiar, it might be thanks to the &lt;a href=&#34;https://perladvent.org/2016/2016-12-02.html&#34;&gt;Perl Advent Calendar&lt;/a&gt; from 2016, where it was first introduced as a way to reduce common errors in ref-checking and boost performance with custom ops. However, many Perl newcomers and those who missed the original may not yet know about this handy tool. Let&#38;rsquo;s revisit this modern Christmas classic and explore how &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; can make your Perl code cleaner, faster, and more robust.&lt;/p&gt;

&lt;h3 id=&#34;The-Ghost-of-Reference-Checks-Past&#34;&gt;The Ghost of Reference Checks Past &#38;#x1F381;&lt;/h3&gt;

&lt;p&gt;In the days of yore, you might have found yourself writing code like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;code-listing&#34;&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;word&#34;&gt;ref&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$sleigh&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;HASH&#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;&lt;span class=&#34;word&#34;&gt;load_presents&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$sleigh&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;But alas! This is like finding coal in your stocking. &#38;#x1F384; By using &lt;code&gt;ref $sleigh eq &#38;#39;HASH&#38;#39;&lt;/code&gt;, we&#38;#39;re peeking behind the curtains, exposing Perl&#38;#39;s internal workings. We&#38;#39;re grabbing the string representation of a reference and hoping it matches our expectations. If &lt;code&gt;$sleigh&lt;/code&gt; isn&#38;#39;t a reference at all, &lt;code&gt;ref&lt;/code&gt; returns an empty string &#38;mdash; leaving us out in the cold. (At least it&#38;#39;s not &lt;code&gt;undef&lt;/code&gt;, which would likely wreak havoc during runtime).&lt;/p&gt;

&lt;h3 id=&#34;Unwrapping-Ref::Util&#34;&gt;Unwrapping Ref::Util &#38;#x2728;&lt;/h3&gt;

&lt;p&gt;Enter &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt;, our festive hero that&#38;#39;s been around for a while but might not have made it onto your wish list yet! With it, we can rewrite our code as:&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;Ref::Util&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw&#38;lt; is_hashref &#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;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is_hashref&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$sleigh&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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;load_presents&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$sleigh&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;Isn&#38;#39;t that as refreshing as a winter&#38;#39;s first snow? &#38;#x2744;&#38;#xFE0F; The code is cleaner and more readable, but the true magic lies beneath the surface. &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; doesn&#38;#39;t just sprinkle a bit of tinsel on your code &#38;mdash; it &lt;b&gt;supercharges&lt;/b&gt; it! &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; even provides a sleigh full of functions to check all sorts of references:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;is_ref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_scalarref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_arrayref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_hashref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_coderef($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_regexpref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_globref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_formatref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_ioref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;is_refref($variable)&lt;/code&gt;&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And their &lt;code&gt;is_plain_*&lt;/code&gt; and &lt;code&gt;is_blessed_*&lt;/code&gt; counterparts.&lt;/p&gt;

&lt;p&gt;This comprehensive suite lets you check for any type of reference without exposing Perl&#38;#39;s internals, making your code more robust and intention-revealing.&lt;/p&gt;

&lt;p&gt;Example:&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;Ref::Util&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw&#38;lt; is_blessed_arrayref &#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;keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;is_blessed_arrayref&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$elves&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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;assign_tasks&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$elves&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;Instead of:&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;Scalar::Util&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw&#38;lt; blessed reftype &#38;gt;&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;word&#34;&gt;ref_type&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$elves&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;ARRAY&#39;&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;amp;&#38;amp;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;blessed&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$elves&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;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;assign_tasks&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$elves&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;(You might consider calling &lt;code&gt;blessed($elves) eq &#38;#39;ARRAY&#38;#39;&lt;/code&gt; but that doesn&#38;#39;t differentiate between package name and reference type.)&lt;/p&gt;

&lt;h3 id=&#34;Performance:-The-Gift-That-Keeps-on-Giving&#34;&gt;Performance: The Gift That Keeps on Giving &#38;#x1F381;&lt;/h3&gt;

&lt;p&gt;Now, you might be thinking, &#38;quot;Sure, the code looks nicer, but is that all?&#38;quot; Oh, dear reader, the true magic lies in the &lt;b&gt;massive speed improvements&lt;/b&gt;! &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; doesn&#38;#39;t just make your code more readable &#38;mdash; it makes it &lt;b&gt;faster&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;Let&#38;#39;s peek into Santa&#38;#39;s workshop and look at some benchmarks:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Ref::Util::is_plain_arrayref (CustomOP):  2.7951 +/- 0.0062 (0.2%) -- baseline
    ref():                                    5.3350 +/- 0.0180 (0.3%) -- 1.90 times
    Data::Util::is_array_ref:                 5.9074 +/- 0.0075 (0.1%) -- 2.11 times
    ref(), reftype(), !blessed():            15.5450 +/- 0.0310 (0.2%) -- 5.56 times&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, &lt;code&gt;Ref::Util::is_plain_arrayref&lt;/code&gt; using Custom Ops is nearly &lt;b&gt;twice as fast&lt;/b&gt; as &lt;code&gt;ref()&lt;/code&gt; (2.7951 vs. 5.335 seconds) and more than &lt;b&gt;five times faster&lt;/b&gt; than combining &lt;code&gt;ref()&lt;/code&gt;, &lt;code&gt;Scalar::Util::reftype()&lt;/code&gt;, and &lt;code&gt;blessed()&lt;/code&gt; checks (2.7951 vs. 15.545 seconds). It&#38;#39;s like upgrading from a hand-drawn sleigh to a rocket-powered one! &#38;#x1F680;&lt;/p&gt;

&lt;h3 id=&#34;Elves-Under-the-Hood:-The-Magic-of-Op-Codes&#34;&gt;Elves Under the Hood: The Magic of Op Codes &#38;#x1F9D9;&#38;zwj;&#38;#x2640;&#38;#xFE0F;&lt;/h3&gt;

&lt;p&gt;So, how does &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; achieve this festive feat? It&#38;#39;s all thanks to some Christmas magic called &lt;b&gt;Custom Ops&lt;/b&gt;, introduced in Perl &lt;b&gt;5.14&lt;/b&gt; (released in 2011). Let&#38;#39;s unwrap this concept together.&lt;/p&gt;

&lt;h4 id=&#34;What-Are-Op-Codes-and-the-Optree&#34;&gt;What Are Op Codes and the Optree?&lt;/h4&gt;

&lt;p&gt;In Perl, your code gets compiled into a tree of operations called the &lt;b&gt;optree&lt;/b&gt;. Each operation (&lt;b&gt;op&lt;/b&gt;) represents a fundamental action, like addition, subtraction, or fetching a variable. Think of it as the assembly instructions for Santa&#38;#39;s elves.&lt;/p&gt;

&lt;h4 id=&#34;The-Traditional-Methods-Optree&#34;&gt;The Traditional Method&#38;#39;s Optree&lt;/h4&gt;

&lt;p&gt;Let&#38;#39;s take the following one-liner:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl -MO=Concise -e&#38;#39;if ( ref $sleigh eq &#38;quot;HASH&#38;quot; ) {1}&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can see the following optree, which is printed out by &lt;a href=&#34;https://metacpan.org/module/B::Concise&#34;&gt;B::Concise&lt;/a&gt; when loaded with &lt;code&gt;-MO=Concise&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    7  &#38;lt;@&#38;gt; leave[1 ref] vKP/REFC -&#38;gt;(end)
    1     &#38;lt;0&#38;gt; enter v -&#38;gt;2
    2     &#38;lt;;&#38;gt; nextstate(main 1 -e:1) v:{ -&#38;gt;3
    -     &#38;lt;1&#38;gt; null vK/1 -&#38;gt;7
    6        &#38;lt;|&#38;gt; and(other-&#38;gt;7) vK/1 -&#38;gt;7
    5           &#38;lt;2&#38;gt; seq sK/2 -&#38;gt;6
    -              &#38;lt;1&#38;gt; ex-rv2sv sK/1 -&#38;gt;4
    3                 &#38;lt;#&#38;gt; gvsv[*sleigh] s -&#38;gt;4
    4              &#38;lt;$&#38;gt; const[PV &#38;quot;HASH&#38;quot;] s -&#38;gt;5
    -           &#38;lt;@&#38;gt; scope vK -&#38;gt;7
    -              &#38;lt;;&#38;gt; ex-nextstate(main 3 -e:1) v -&#38;gt;-
    -              &#38;lt;0&#38;gt; ex-const v -&#38;gt;-&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This involves multiple ops: fetching the variable, getting its reference type as a string, and then comparing that string.&lt;/p&gt;

&lt;h4 id=&#34;Scalar::Utils-reftype-Optree&#34;&gt;Scalar::Util&#38;#39;s &lt;code&gt;reftype&lt;/code&gt; Optree&lt;/h4&gt;

&lt;p&gt;Using &lt;a href=&#34;https://metacpan.org/module/Scalar::Util&#34;&gt;Scalar::Util&lt;/a&gt;&#38;#39;s &lt;code&gt;reftype&lt;/code&gt;, the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl -MO=Concise -MScalar::Util=reftype -e&#38;#39;if ( reftype($sleigh) eq &#38;quot;HASH&#38;quot; ) {1}&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We get the following optree:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    a  &#38;lt;@&#38;gt; leave[1 ref] vKP/REFC -&#38;gt;(end)
    1     &#38;lt;0&#38;gt; enter v -&#38;gt;2
    2     &#38;lt;;&#38;gt; nextstate(main 49 -e:1) v:{ -&#38;gt;3
    -     &#38;lt;1&#38;gt; null vKP/1 -&#38;gt;a
    9        &#38;lt;|&#38;gt; and(other-&#38;gt;a) vK/1 -&#38;gt;a
    8           &#38;lt;2&#38;gt; seq sK/2 -&#38;gt;9
    6              &#38;lt;1&#38;gt; entersub[t1] sKS/TARG -&#38;gt;7
    -                 &#38;lt;1&#38;gt; ex-list sK -&#38;gt;6
    3                    &#38;lt;0&#38;gt; pushmark s -&#38;gt;4
    -                    &#38;lt;1&#38;gt; ex-rv2sv sKM/1 -&#38;gt;5
    4                       &#38;lt;#&#38;gt; gvsv[*sleight] s -&#38;gt;5
    -                    &#38;lt;1&#38;gt; ex-rv2cv sK/1 -&#38;gt;-
    5                       &#38;lt;#&#38;gt; gv[*reftype] s -&#38;gt;6
    7              &#38;lt;$&#38;gt; const[PV &#38;quot;HASH&#38;quot;] s -&#38;gt;8
    -           &#38;lt;@&#38;gt; scope vK -&#38;gt;a
    -              &#38;lt;;&#38;gt; ex-nextstate(main 51 -e:1) v -&#38;gt;-
    -              &#38;lt;0&#38;gt; ex-const v -&#38;gt;-&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This involves more ops due to the subroutine call to &lt;code&gt;reftype&lt;/code&gt;, adding overhead. Also, notice &lt;code&gt;pushmark&lt;/code&gt; which adds additional variables to the stack, adding variables to the function stack that could be retrieved by calling &lt;code&gt;shift&lt;/code&gt;. (You read this right - even having parameters to a function includes another opcode.)&lt;/p&gt;

&lt;h4 id=&#34;Ref::Util-with-Custom-Ops&#34;&gt;Ref::Util with Custom Ops&lt;/h4&gt;

&lt;p&gt;With &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; on Perl &lt;b&gt;5.14&lt;/b&gt; or newer, and when using a version of &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; that supports Custom Ops, let&#38;#39;s write the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    perl -MO=Concise -MRef::Util=is_hashref -e&#38;#39;if ( is_hashref($sleight) ) {1}&#38;#39;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We get the following optree:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    6  &#38;lt;@&#38;gt; leave[1 ref] vKP/REFC -&#38;gt;(end)
    1     &#38;lt;0&#38;gt; enter v -&#38;gt;2
    2     &#38;lt;;&#38;gt; nextstate(main 73 -e:1) v:{ -&#38;gt;3
    -     &#38;lt;1&#38;gt; null vKP/1 -&#38;gt;6
    5        &#38;lt;|&#38;gt; and(other-&#38;gt;6) vK/1 -&#38;gt;6
    4           &#38;lt;1&#38;gt; is_hashref sK/1 -&#38;gt;5
    -              &#38;lt;1&#38;gt; ex-rv2sv sKM/1 -&#38;gt;4
    3                 &#38;lt;#&#38;gt; gvsv[*sleight] s -&#38;gt;4
    -           &#38;lt;@&#38;gt; scope vK -&#38;gt;6
    -              &#38;lt;;&#38;gt; ex-nextstate(main 75 -e:1) v -&#38;gt;-
    -              &#38;lt;0&#38;gt; ex-const v -&#38;gt;-&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice how &lt;code&gt;is_hashref&lt;/code&gt; becomes its own op in the optree, named &lt;code&gt;is_hashref&lt;/code&gt;. This Custom Op directly checks the reference type at the opcode level, eliminating unnecessary steps.&lt;/p&gt;

&lt;h4 id=&#34;How-Does-This-Magic-Happen&#34;&gt;How Does This Magic Happen?&lt;/h4&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;&lt;b&gt;Custom Ops Implementation (Fastest)&lt;/b&gt; &#38;#x1F385;&lt;/p&gt;

&lt;p&gt;When available, &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; uses &lt;b&gt;Custom Ops&lt;/b&gt; written in C (using XS). These are new operations added directly into Perl&#38;#39;s optree, resulting in minimal overhead and maximal speed. It&#38;#39;s like giving the elves new, more efficient tools.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;XS Implementation (Fast)&lt;/b&gt; &#38;#x1F98C;&lt;/p&gt;

&lt;p&gt;If Custom Ops aren&#38;#39;t available (due to an older Perl version or limitations in the environment), &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; gracefully falls back to an XS implementation without Custom Ops. This means the functions are still written in C for performance but are called as regular functions. It&#38;#39;s still faster than pure Perl but not as swift as using Custom Ops.&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Pure-Perl Implementation&lt;/b&gt; &#38;#x1F6F7;&lt;/p&gt;

&lt;p&gt;When neither Custom Ops nor XS implementations are available (such as on systems without a C compiler), &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; provides a pure-Perl fallback. This version has similar performance to the traditional method but retains the benefits of cleaner syntax and abstraction.&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;The-Spirit-of-the-Season:-Cleaner-and-Faster-Code&#34;&gt;The Spirit of the Season: Cleaner and Faster Code &#38;#x1F385;&lt;/h3&gt;

&lt;p&gt;Beyond performance, &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; embodies the true spirit of the holidays &#38;mdash; making things better for everyone. By using functions like &lt;code&gt;is_hashref&lt;/code&gt;, &lt;code&gt;is_arrayref&lt;/code&gt;, &lt;code&gt;is_coderef&lt;/code&gt;, et. al., our code becomes more expressive, intention-revealing, and helps catch typos in reference checks. It&#38;#39;s easier for others (and future you) to read and understand, reducing those &#38;quot;bah humbug&#38;quot; moments during code reviews.&lt;/p&gt;

&lt;h3 id=&#34;When-Feeling-Grateful&#34;&gt;When Feeling Grateful &#38;#x1F64F;&lt;/h3&gt;

&lt;p&gt;There are plenty of people who worked hard to make &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; possible, and they deserve a lot of credit:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;Vikenty Fesunov&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aaron Crane&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gonzalo Diethelm&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Karen Etheridge&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Yves Orton&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Steffen M&#38;uuml;ller&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jarkko Hietaniemi&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mattia Barbon&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zefram&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tony Cook&lt;/p&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sergey Aleynikov&lt;/p&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;A-New-Years-Resolution&#34;&gt;A New Year&#38;#39;s Resolution &#38;#x1F942;&lt;/h3&gt;

&lt;p&gt;As we bid farewell to the old year and welcome the new, let&#38;#39;s make a resolution to write cleaner, faster, and more maintainable Perl code. &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; is a wonderful tool to help us on that journey.&lt;/p&gt;

&lt;p&gt;So, next time you find yourself writing &lt;code&gt;ref $variable eq &#38;#39;TYPE&#38;#39;&lt;/code&gt;, remember there&#38;#39;s a merrier way. Let &lt;a href=&#34;https://metacpan.org/module/Ref::Util&#34;&gt;Ref::Util&lt;/a&gt; spread joy in your codebase, and may your days be merry and bright!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Happy Holidays and Happy Coding!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;i&gt;May your CPU cycles be short and your code delights be plenty!&lt;/i&gt; &#38;#x1F389;&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-03T00:00:00Z</updated><category term="Perl"/><author><name>Sawyer X</name></author></entry><entry><title>A Trio of Modules to Speed Up Your Web Applications!</title><link href="https://perladvent.org/2024/2024-12-02.html"/><id>https://perladvent.org/2024/2024-12-02.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;p&gt;It is the season of giving, and Perl has a few special packages waiting under the tree that can give your web applications a little extra pep in their step. This holiday, let&#38;#39;s celebrate three modules that make web development smoother and faster: &lt;a href=&#34;https://metacpan.org/module/HTTP::XSHeaders&#34;&gt;HTTP::XSHeaders&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/module/HTTP::XSCookies&#34;&gt;HTTP::XSCookies&lt;/a&gt;, and &lt;a href=&#34;https://metacpan.org/module/URI::XSEscape&#34;&gt;URI::XSEscape&lt;/a&gt;. These tools are like those little surprises that make everything run just a bit better, a bit faster &#38;mdash; without you lifting a finger. Let&#38;#39;s unwrap their magic and see how they bring a new level of efficiency to Perl web applications!&lt;/p&gt;

&lt;h3 id=&#34;HTTP::XSHeaders-and-URI::XSEscape:-Sleigh-Speed-for-Your-Framework&#34;&gt;HTTP::XSHeaders and URI::XSEscape: Sleigh-Speed for Your Framework&lt;/h3&gt;

&lt;p&gt;If you&#38;#39;re using a web framework built on Perl, chances are it relies on &lt;a href=&#34;https://metacpan.org/module/HTTP::Headers&#34;&gt;HTTP::Headers&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/URI::Escape&#34;&gt;URI::Escape&lt;/a&gt; under the hood. These modules are hard workers, responsible for parsing HTTP headers and handling URL encoding, respectively, but they are not the quickest reindeer in the race. Luckily, here come &lt;a href=&#34;https://metacpan.org/module/HTTP::XSHeaders&#34;&gt;HTTP::XSHeaders&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/module/URI::XSEscape&#34;&gt;URI::XSEscape&lt;/a&gt; &#38;mdash; both designed to give your application a boost.&lt;/p&gt;

&lt;p&gt;When your code loads &lt;code&gt;HTTP::XSHeaders&lt;/code&gt; or &lt;code&gt;URI::XSEscape&lt;/code&gt;, they automatically override &lt;code&gt;HTTP::Headers&lt;/code&gt; (even its faster variant, &lt;code&gt;HTTP::Headers::Fast&lt;/code&gt;) and &lt;code&gt;URI::Escape&lt;/code&gt;, making your web framework faster without a single line of modification.&lt;/p&gt;

&lt;p&gt;Even better, if you are using the &lt;a href=&#34;https://metacpan.org/module/Dancer2&#34;&gt;Dancer2&lt;/a&gt; framework, it will automatically detect and load &lt;code&gt;HTTP::XSHeaders&lt;/code&gt; and &lt;code&gt;URI::XSEscape&lt;/code&gt; if they are available, boosting performance without any extra setup. It is like a holiday bonus for your application, giving it speed improvements without any added work!&lt;/p&gt;

&lt;h3 id=&#34;HTTP::XSCookies:-A-Sweet-Swap-for-Framework-Developers&#34;&gt;HTTP::XSCookies: A Sweet Swap for Framework Developers&lt;/h3&gt;

&lt;p&gt;For developers building web frameworks, there is another gift waiting for you: &lt;a href=&#34;https://metacpan.org/module/HTTP::XSCookies&#34;&gt;HTTP::XSCookies&lt;/a&gt;. This module serves as a high-speed replacement for &lt;a href=&#34;https://metacpan.org/module/HTTP::Cookies&#34;&gt;HTTP::Cookies&lt;/a&gt;, designed to speed up cookie handling without the need for reindeer-powered sleighs. Unlike the other two modules, &lt;code&gt;HTTP::XSCookies&lt;/code&gt; doesn&#38;rsquo;t override the original module. So, if you want your application to take advantage of its speed, you will need to replace &lt;code&gt;HTTP::Cookies&lt;/code&gt; with &lt;code&gt;HTTP::XSCookies&lt;/code&gt; directly in your codebase.&lt;/p&gt;

&lt;p&gt;However, if you are using the &lt;code&gt;Dancer2&lt;/code&gt; framework, it will automatically use &lt;code&gt;HTTP::XSCookies&lt;/code&gt; if it is installed, allowing your code to benefit from its performance boost seamlessly.&lt;/p&gt;

&lt;h3 id=&#34;Deck-the-Code-with-Speed&#34;&gt;Deck the Code with Speed&lt;/h3&gt;

&lt;p&gt;These modules aren&#38;rsquo;t just gifts for your application &#38;mdash; they are here to spread holiday cheer to your users too. By loading just a few lines of code, you will be spreading joy in the form of faster response times and more efficient performance. Whether you are a user or a developer, these modules are like holiday magic for your Perl-based web applications.&lt;/p&gt;

&lt;h3 id=&#34;Wishing-You-a-Snappy-and-Speedy-New-Year&#34;&gt;Wishing You a Snappy and Speedy New Year!&lt;/h3&gt;

&lt;p&gt;So this holiday season, consider giving your web application the gift of speed. With &lt;code&gt;HTTP::XSHeaders&lt;/code&gt;, &lt;code&gt;HTTP::XSCookies&lt;/code&gt;, and &lt;code&gt;URI::XSEscape&lt;/code&gt; in your toolkit, you are set for a snappier, speedier new year. Here is to faster code, happy users, and a fantastic Perl-powered holiday!&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-02T00:00:00Z</updated><category term="Perl"/><author><name>Gonzalo Diethelm</name></author></entry><entry><title type="html">While You&#38;#39;re Waiting for Corinna</title><link href="https://perladvent.org/2024/2024-12-01.html"/><id>https://perladvent.org/2024/2024-12-01.html</id><summary type="html">&lt;div class=&#39;pod&#39;&gt;&lt;h2 id=&#34;Introduction&#34;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;It&#38;#39;s getting close to that time of year and you&#38;#39;ve been a good little child and are eagerly awaiting &lt;a href=&#34;https://github.com/Perl-Apollo/Corinna/blob/master/pod/perlclasstut.pod&#34;&gt;Corinna&lt;/a&gt;, which Santa has promised to deliver, fully-assembled, by Christmas.&lt;/p&gt;

&lt;p&gt;Alas, Santa is also a Perl developer and following the Perl tradition, he&#38;#39;s been a bit coy about &lt;i&gt;which&lt;/i&gt; Christmas. Worse, your coworkers freak out at the idea of using the lovely &lt;a href=&#34;https://metacpan.org/module/Object::Pad&#34;&gt;Object::Pad&lt;/a&gt; module and, in any event, you&#38;#39;re stuck on an older version of Perl, meaning you couldn&#38;#39;t use Corinna even if it was Christmas.&lt;/p&gt;

&lt;p&gt;You&#38;#39;d like something a touch more robust and modern than Moo/se, but what can you do? Let&#38;#39;s look in Santa&#38;#39;s Bag and see what we can find.&lt;/p&gt;

&lt;h2 id=&#34;Santas::Bag&#34;&gt;Santas::Bag&lt;/h2&gt;

&lt;p&gt;Turns out that when Santa&#38;#39;s rummaging around in his bag, there are some toys he delivers more than others. He&#38;#39;s realized that an &lt;a href=&#34;https://www.interviewcake.com/concept/java/lru-cache&#34;&gt;LRU cache&lt;/a&gt; is perfect for this. Let&#38;#39;s take a peek inside.&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;Santas::Bag&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::Moose&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;types&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;words&#34;&gt;qw/InstanceOf PositiveOrZeroInt/&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;Hash::Ordered&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;param&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;max_size&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;comment&#34;&gt;# allowed in constructor&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;isa&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;PositiveOrZeroInt&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;number&#34;&gt;20&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;writer&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;trigger&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$max_size&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$old_max_size&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;20&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;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$max_size&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$old_max_size&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;_constrain_cache_size&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;word&#34;&gt;field&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_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;comment&#34;&gt;# forbidden in constructor&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;rwp&#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;isa&lt;/span&gt;     &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;InstanceOf&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;single&#34;&gt;&#39;Hash::Ordered&#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;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;Hash::Ordered&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;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;handles&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;words&#34;&gt;qw/keys/&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;method&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;_constrain_cache_size&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;$max_size&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;max_size&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;operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;$max_size&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;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__cache&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Hash::Ordered&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;structure&#34;&gt;}&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&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;&#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;$cache&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;_cache&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;$cache&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;pop&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;while&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;word&#34;&gt;keys&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;&#38;gt;&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$max_size&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;word&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$value&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;unless&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;max_size&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;_cache&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;unshift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$value&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;_constrain_cache_size&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;method&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;$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;&#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;unless&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;_cache&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;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;span class=&#34;symbol&#34;&gt;$key&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;$value&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;_cache&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;$key&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;_cache&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;unshift&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$key&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$value&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;$value&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 you burst into song!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    What&#38;rsquo;s this? What&#38;rsquo;s this?
    There&#38;rsquo;s methods everywhere
    What&#38;rsquo;s this?
    There&#38;rsquo;s no immutability we&#38;rsquo;ve declared
    What&#38;rsquo;s this?
    I can&#38;rsquo;t believe my eyes
    I must be dreaming. Wake up, Jack! This isn&#38;rsquo;t Moose! What&#38;rsquo;s this?&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What &lt;i&gt;is&lt;/i&gt; this? It looks kind of like &lt;a href=&#34;https://metacpan.org/module/Moose&#34;&gt;Moose&lt;/a&gt;, but we have actual methods? There&#38;#39;s no need to declare &lt;code&gt;$self&lt;/code&gt;? Why isn&#38;#39;t it declared as immutable? And where&#38;#39;s the trailing true value at the end? And it looks like &lt;a href=&#34;https://metacpan.org/module/Type::Tiny&#34;&gt;Type::Tiny&lt;/a&gt; is built in? What is going on?&lt;/p&gt;

&lt;p&gt;Well, an LRU cache is perfect when you have an unequal distribution of toys. Rubik&#38;#39;s Cubes, for example, are not quite as popular as trains or dolls, so when Santa reaches into his bag, he wants to be able to quickly find the most popular toys. Any time you add a toy to the bag, or fetch a toy from the bag, it becomes the &#38;quot;most recently used&#38;quot; toy; the &#38;quot;least recently used&#38;quot; toy is evicted from the cache.&lt;/p&gt;

&lt;p&gt;Santa wants Christmas to go smoothly, so he started to write some tests.&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;br /&gt;&lt;br /&gt;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@presents&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;Lego         Barbie      Doll&lt;br /&gt;&#38;nbsp;&#38;nbsp;PlayStation5 XboxSeriesX TeddyBear&lt;br /&gt;&#38;nbsp;&#38;nbsp;Bicycle      TrainSet    Socks&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;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;Santas::Bag&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;max_size&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;5&lt;/span&gt; &lt;span class=&#34;structure&#34;&gt;);&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set&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;,&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$presents&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;-&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;span class=&#34;word&#34;&gt;for&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;10&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;max_size&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&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;The max_size attribute is 5&#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;@keys&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;keys&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@keys&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;number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;6&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;... and the key order is by most recently added&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&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;number&#34;&gt;9&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;Socks&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;We get the value for key 9&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;@keys&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;keys&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@keys&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;number&#34;&gt;9&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;number&#34;&gt;6&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;... and the key order is still by most recently added&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;set_max_size&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;is&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;scalar&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;keys&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;operator&#34;&gt;,&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;single&#34;&gt;&#39;The cache now has 3 entries after we call set_max_size(3)&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;symbol&#34;&gt;@keys&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;keys&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;cast&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;symbol&#34;&gt;@keys&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;number&#34;&gt;9&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;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;8&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;... and the key order is still preserved&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&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;number&#34;&gt;9&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;Socks&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;    &lt;span class=&#34;single&#34;&gt;&#39;We still get the value for key 9&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&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;number&#34;&gt;8&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;TrainSet&#39;&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;We get the value for key 8&#39;&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;$santas_bag&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;number&#34;&gt;7&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;undef&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt;      &lt;span class=&#34;single&#34;&gt;&#39;We get undef for key 7&#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;done_testing&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After reading his code, and checking it twice, Santa runs those tests.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        ok 1 - The max_size attribute is 5
        ok 2 - ... and the key order is by most recently added
        ok 3 - We get the value for key 9
        ok 4 - ... and the key order is still by most recently added
        ok 5 - The cache now has 3 entries after we call set_max_size(3)
        ok 6 - The key order is preserved, but we only have three entries
        ok 7 - We still get the value for key 9
        ok 8 - We get the value for key 8
        ok 9 - We get undef for key 7
        1..9&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OK, that&#38;#39;s looking pretty good. But how did we get all of that lovely, modern goodness, via the &lt;code&gt;My::Moose&lt;/code&gt; module? It uses the magic of &lt;a href=&#34;https://metacpan.org/module/MooseX::Extended&#34;&gt;MooseX::Extended&lt;/a&gt;, a module that takes what we&#38;#39;ve learned from the design of Corinna and tries to apply as much of that to &lt;a href=&#34;https://metacpan.org/module/Moose&#34;&gt;Moose&lt;/a&gt; as is feasible.&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;My::Moose&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;MooseX::Extended::Custom&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;PerlX::Maybe&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;provided&#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;# If $^P is true, we&#39;re running under the debugger.&lt;br /&gt;#&lt;br /&gt;# When running under the debugger, we disable __PACKAGE__-&#38;gt;meta-&#38;gt;make_immutable&lt;br /&gt;# because the way the debugger works with B::Hooks::AtRuntime will cause&lt;br /&gt;# the class to be made immutable before the we apply everything we need. This&lt;br /&gt;# causes the code to die.&lt;br /&gt;&lt;/span&gt;&lt;span class=&#34;keyword&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;( $class, %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;&lt;span class=&#34;word&#34;&gt;MooseX::Extended::Custom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;create&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;includes&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;words&#34;&gt;qw/method/&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;provided&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$^P&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;excludes&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&#38;gt;&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;immutable&#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;symbol&#34;&gt;%args&lt;/span&gt;    &lt;span class=&#34;comment&#34;&gt;# you need this to allow customization of your customization&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;That&#38;#39;s it! That&#38;#39;s all you have to do to get a fairly modern, clean version of &lt;code&gt;Moose&lt;/code&gt; that works all the way back to Perl v5.20.0, released way back in 2014.&lt;/p&gt;

&lt;p&gt;But what&#38;#39;s going on? Well, as it turns out, Santa has a temper and when he sees boilerplate, he sees red. You&#38;#39;ve been a naughty little child. Boilerplate is bad. In the above, &lt;code&gt;My::Moose&lt;/code&gt; is equivalent to writing the following boilerplate for all of your Moose classes (plus a few extra things, such as the &lt;code&gt;param&lt;/code&gt; and &lt;code&gt;field&lt;/code&gt; functions).&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;My::Class&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;version&#34;&gt;v5.20.0&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;word&#34;&gt;MooseX::StrictConstructor&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;feature&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw( signatures postderef postderef_qq )&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt;&lt;br /&gt;&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;words&#34;&gt;qw( experimental::signatures experimental::postderef )&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;namespace::autoclean&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;Carp&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;mro&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;c3&#39;&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;Function::Parameters&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;# your code here&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&#34;word&#34;&gt;__PACKAGE__&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;meta&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;make_immutable&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&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;Are you really going to add that to all of your modules? Are you going to copy-n-paste? Are you going to remember everything if you do it manually? Are you going to globally go through and change it manually if you need to adjust something?&lt;/p&gt;

&lt;p&gt;Of course, that doesn&#38;#39;t mean Santa&#38;#39;s defaults are going be your defaults. For example, some people don&#38;#39;t like &lt;a href=&#34;https://metacpan.org/module/MooseX::StrictConstructor&#34;&gt;MooseX::StrictConstructor&lt;/a&gt;, so just omit it. Some people want &lt;code&gt;try/catch&lt;/code&gt;, so include it.&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;import&lt;/span&gt; &lt;span class=&#34;prototype&#34;&gt;( $class, %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;&lt;span class=&#34;keyword&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;symbol&#34;&gt;@excludes&lt;/span&gt; &lt;span class=&#34;operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;words&#34;&gt;qw(StrictConstructor)&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;@excludes&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;single&#34;&gt;&#39;immutable&#39;&lt;/span&gt; &lt;span class=&#34;word&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;magic&#34;&gt;$^P&lt;/span&gt;&lt;span class=&#34;structure&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;comment&#34;&gt;# running under the debugger&lt;/span&gt;&lt;br /&gt;&#38;nbsp;&#38;nbsp;&#38;nbsp;&#38;nbsp;&lt;span class=&#34;word&#34;&gt;MooseX::Extended::Custom&lt;/span&gt;&lt;span class=&#34;operator&#34;&gt;-&#38;gt;&lt;/span&gt;&lt;span class=&#34;word&#34;&gt;create&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;includes&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;words&#34;&gt;qw/method try/&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;excludes&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;@excludes&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;comment&#34;&gt;# you need this to allow customization of your customization&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;If you&#38;#39;re already using &lt;code&gt;Moose&lt;/code&gt;, you can read about &lt;a href=&#34;https://metacpan.org/pod/MooseX::Extended::Manual::Tutorial#MIGRATING-FROM-MOOSE&#34;&gt;Migrating from Moose&lt;/a&gt; to see if this works for you (that link goes to the tutorial). And assuming you use roles, &lt;a href=&#34;https://metacpan.org/pod/MooseX::Extended::Role::Custom&#34;&gt;you can customize those, too&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;</summary><updated>2024-12-01T00:00:00Z</updated><category term="Perl"/><author><name>Ovid</name></author></entry></feed>