<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/perl/</id>
  <link href="https://www.endpointdev.com/blog/tags/perl/"/>
  <link href="https://www.endpointdev.com/blog/tags/perl/" rel="self"/>
  <updated>2025-08-05T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>A Rusty Web? An Excursion of a Perl Guy into Rust Land</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/08/a-rusty-web/"/>
      <id>https://www.endpointdev.com/blog/2025/08/a-rusty-web/</id>
      <published>2025-08-05T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/08/a-rusty-web/wood-chain-ship-rust-iceland-reptile-1359649-pxhere.com.webp&#34; alt=&#34;Several rusty chains are tied into the side of a rusty metal structure.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo https://pxhere.com/en/photo/1359649 CC0 Public Domain --&gt;
&lt;p&gt;In my programming career centered around web applications I&amp;rsquo;ve always used dynamic, interpreted languages: Perl, JavaScript, Python, and Ruby. However, I&amp;rsquo;ve always been curious about compiled, strongly typed languages and if they can be useful to me and to my clients. Based on my recent findings, &lt;a href=&#34;https://www.rust-lang.org/&#34;&gt;Rust&lt;/a&gt; would be my first choice. It&amp;rsquo;s a modern language, has &lt;a href=&#34;https://doc.rust-lang.org/stable/book/&#34;&gt;excellent documentation&lt;/a&gt; and it&amp;rsquo;s quite popular. However, it&amp;rsquo;s &lt;em&gt;very&lt;/em&gt; different from the languages I know.&lt;/p&gt;
&lt;p&gt;I read most of &lt;a href=&#34;https://doc.rust-lang.org/book/&#34;&gt;&lt;em&gt;the book&lt;/em&gt;&lt;/a&gt; a couple of years ago, but given that I didn&amp;rsquo;t do anything with it, my knowledge quickly evaporated. This time I read the book and immediately after that I started to work on a non-trivial project involving downloading XML data from different sources, database operations, indexing and searching documents, and finally serving JSON over HTTP. My goal was to replace at least part of a Django application which &lt;em&gt;seemed&lt;/em&gt; to have performance problems. The Django application uses Xapian (which is written in C++) via its &lt;a href=&#34;https://xapian.org/docs/bindings/python3/&#34;&gt;bindings&lt;/a&gt; to provide the core functionality. Indexing documents would be delegated to a &lt;a href=&#34;https://docs.celeryq.dev/en/stable/index.html&#34;&gt;Celery&lt;/a&gt; task queue.&lt;/p&gt;
&lt;p&gt;Unfortunately Xapian does not have &lt;a href=&#34;https://xapian.org/docs/bindings/&#34;&gt;bindings&lt;/a&gt; for Rust so far.&lt;/p&gt;
&lt;p&gt;My reasoning was: I could use the &lt;a href=&#34;https://www.postgresql.org/docs/current/textsearch.html&#34;&gt;PostgreSQL full text search feature&lt;/a&gt; instead of Xapian, simplifying the setup (updating a row would trigger an index update, instead of delegating the operation to Celery).&lt;/p&gt;
&lt;p&gt;After reading the Rust book I truly liked the language. Its main feature is that it (normally) gives you no room for nasty memory management bugs which plague languages like C. Being compiled to machine code, it&amp;rsquo;s faster than interpreted languages by an order of magnitude. However, having to state the type of variables, arguments, and return values was at first kind of a culture shock, but I got used to it.&lt;/p&gt;
&lt;p&gt;When writing Perl, I&amp;rsquo;m used to constructs like these:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$res&lt;/span&gt; = download_url(&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which are not possible any more. Instead you have to use the &lt;code&gt;match&lt;/code&gt; &lt;a href=&#34;https://doc.rust-lang.org/stable/book/ch06-02-match.html&#34;&gt;construct&lt;/a&gt; and extract values from &lt;code&gt;Option&lt;/code&gt; (&lt;code&gt;Some&lt;/code&gt;/​&lt;code&gt;None&lt;/code&gt;) and &lt;code&gt;Result&lt;/code&gt; (&lt;code&gt;Ok&lt;/code&gt;, &lt;code&gt;Err&lt;/code&gt;) enumerations. This is the standard way to handle errors and variables which may or may not have values. There is nothing like an &lt;code&gt;undef&lt;/code&gt; and this is one of the main Rust features. Instead, you need to cover all the cases with something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;download_url(url.clone())&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Ok&lt;/span&gt;(res)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;       &lt;/span&gt;...&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;},&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Err&lt;/span&gt;(e)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;println!&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Error &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{url}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{e}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which can also be written as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Ok&lt;/span&gt;(res)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;download_url(url.clone())&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;...&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You must be consistent with the values you are declaring and returning, and take care of the mutability and the borrowing of the values. In Rust you can&amp;rsquo;t have a piece of memory which can be modified in multiple places: for example, once you pass a string or a data structure to a function, you can&amp;rsquo;t use it any more. This is without a doubt a &lt;em&gt;good thing&lt;/em&gt;. When in Perl you pass a reference of hash to a function, you don&amp;rsquo;t know what happens to it. Things can be modified as a side effect, and you are going to realize later at debugging time why that piece of data is not what you expect.&lt;/p&gt;
&lt;p&gt;In Rust land, everything feels under control, and the compiler throws errors at you which most of the time make sense. It explains to you why you can&amp;rsquo;t use that variable at that point, and even suggests a fix. It&amp;rsquo;s amazing the amount of work behind the language and its ability to analyze the code.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://doc.rust-lang.org/stable/book/ch08-02-strings.html&#34;&gt;string management&lt;/a&gt; feels a bit weird because it&amp;rsquo;s normally anchored to the UTF-8 encoding, while e.g. Perl has an &lt;a href=&#34;/blog/2025/04/encoding-in-perl/&#34;&gt;abstract way&lt;/a&gt; to handle it, so I&amp;rsquo;m used to thinking differently about it.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; feature is nice, but present in most of the modern languages (Perl included!), so I don&amp;rsquo;t think that should be considered the main reason to use Rust.&lt;/p&gt;
&lt;p&gt;Bottom line: I like the language. It&amp;rsquo;s &lt;em&gt;very&lt;/em&gt; different to what I was used to, but I can see many advantages. The downside is that you can&amp;rsquo;t write all those “quick and dirty” scripts which are the daily bread of the sysadmin. It lacks that practical, informal approach I&amp;rsquo;m used to.&lt;/p&gt;
&lt;p&gt;Once I got acquainted with the language, I went shopping for “crates” (which is what modules are called in Rust) here: &lt;a href=&#34;https://www.arewewebyet.org/&#34;&gt;https://www.arewewebyet.org/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lately I have a bit of a dislike for object–relational mappings (ORM), so I didn&amp;rsquo;t go with &lt;a href=&#34;https://diesel.rs/&#34;&gt;diesel&lt;/a&gt; nor &lt;a href=&#34;https://docs.rs/sqlx/latest/sqlx/&#34;&gt;sqlx&lt;/a&gt;, but I went straight for &lt;a href=&#34;https://docs.rs/tokio-postgres/latest/tokio_postgres/&#34;&gt;tokio_postgres&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This saved me quite a bit of documentation reading and gave me direct access to the database. Nothing weird to report here. It feels like using any other DB driver in any other language, with a statement, the placeholders and the arguments. The difference, of course, is that you need to care about the data types which are coming out of the DB (again the &lt;code&gt;Option&lt;/code&gt; Enum is your friend and the error messages are helpful).&lt;/p&gt;
&lt;p&gt;To get data from the Internet, &lt;a href=&#34;https://crates.io/crates/reqwest&#34;&gt;reqwest&lt;/a&gt; did the trick just fine without any surprise.&lt;/p&gt;
&lt;p&gt;For XML deserialization, &lt;a href=&#34;https://serde.rs/&#34;&gt;serde&lt;/a&gt; was paired with &lt;a href=&#34;https://docs.rs/quick-xml/latest/quick_xml/de/&#34;&gt;quick-xml&lt;/a&gt;. This is one of the interesting bits.&lt;/p&gt;
&lt;p&gt;You start defining your data structures like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;serde::Deserialize;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#[derive(Debug, Deserialize)]&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OaiPmhResponse&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#[serde(rename = &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;responseDate&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;)]&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;response_date: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;request: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;error: &lt;span style=&#34;color:#038&#34;&gt;Option&lt;/span&gt;&amp;lt;ResponseError&amp;gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#[serde(rename = &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ListRecords&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;)]&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;list_records: &lt;span style=&#34;color:#038&#34;&gt;Option&lt;/span&gt;&amp;lt;ListRecords&amp;gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// more definitions follow, to match the structure we expect
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then you feed the XML string to the &lt;code&gt;from_str&lt;/code&gt; function like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;quick_xml::de::from_str;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;parse_response&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(xml: &lt;span style=&#34;color:#080&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;str&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&amp;gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;OaiPmhResponse&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;from_str(xml)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Ok&lt;/span&gt;(res)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;res,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// return a dummy one with no records in it in case of errors
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Err&lt;/span&gt;(e)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;OaiPmhResponse&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;response_date: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;::from(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;NOW&amp;#34;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;request: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;::from(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Invalid&amp;#34;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;error: &lt;span style=&#34;color:#038&#34;&gt;Some&lt;/span&gt;(ResponseError&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;code: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;::from(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Invalid XML&amp;#34;&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                &lt;/span&gt;message: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;e&lt;/span&gt;.to_string(),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;}),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;            &lt;/span&gt;list_records: &lt;span style=&#34;color:#038&#34;&gt;None&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;},&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;which takes care of the parsing and gives you back either an &lt;code&gt;Ok&lt;/code&gt; with the data structure you defined inside and the tags properly mapped, or an error. The structs can have methods attached so they provide a nice OOP-like encapsulation.&lt;/p&gt;
&lt;p&gt;Once the data collection was successful, I moved to the web application itself.&lt;/p&gt;
&lt;p&gt;I chose the &lt;a href=&#34;https://github.com/tokio-rs/axum&#34;&gt;Axum&lt;/a&gt; framework, maintained by the &lt;a href=&#34;https://tokio.rs/&#34;&gt;Tokio project&lt;/a&gt; and glued all the pieces together.&lt;/p&gt;
&lt;p&gt;The core of the application is something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#[derive(Serialize, Debug)]&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Entry&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;entry_id: &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;i32&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;rank: &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;f32&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;title: &lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;search&lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;State(pool): &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;State&lt;/span&gt;&amp;lt;ConnectionPool&amp;gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;Query(params): &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Query&lt;/span&gt;&amp;lt;HashMap&amp;lt;&lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;String&lt;/span&gt;&amp;gt;&amp;gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&amp;gt; (StatusCode,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Json&amp;lt;&lt;span style=&#34;color:#038&#34;&gt;Vec&lt;/span&gt;::&amp;lt;Entry&amp;gt;&amp;gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;conn&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;pool.get().&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt;.expect(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Failed to get a connection from the pool&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;sql&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;#&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;SELECT entry_id, title, ts_rank_cd(search_vector, query) AS rank
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;FROM entry, websearch_to_tsquery($1) query
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;WHERE search_vector @@ query
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;ORDER BY rank DESC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;LIMIT 10;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;#&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;query&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;params.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;Some&lt;/span&gt;(value)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;value,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;None&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;};&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;out&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;conn.query(sql,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&amp;amp;[&amp;amp;query]).&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt;.expect(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Query should be valid&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;.iter().map(|row|&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                    &lt;/span&gt;Entry&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                        &lt;/span&gt;entry_id: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;row&lt;/span&gt;.get(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                        &lt;/span&gt;title: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;row&lt;/span&gt;.get(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                        &lt;/span&gt;rank: &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;row&lt;/span&gt;.get(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;),&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;                    &lt;/span&gt;}).collect();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;tracing::&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;debug!&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{:?}&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&amp;amp;out);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;(StatusCode::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;OK&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Json(out))&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which simply runs the query using the input provided by the user, runs the full text search, and returns the serialized data as JSON.&lt;/p&gt;
&lt;p&gt;During development it &lt;em&gt;felt&lt;/em&gt; fast. The disappointment came when I populated the database with about 30,000 documents of various sizes. The Django application, despite returning more data and the facets, was still way faster. With the two applications running on the same (slow) machine I got a response in 925 milliseconds from the Rust application, and in 123 milliseconds for the Django one!&lt;/p&gt;
&lt;p&gt;Now, most of the time is spent in the SQL query, so the race here is not Python vs. Rust, but Xapian vs. PostgreSQL&amp;rsquo;s full text search, with Xapian (Python is just providing an interface to the fast C++ code) winning by a large measure. Even if the Axum application is as fast as it can get, because it&amp;rsquo;s stripped to the bare minimum (it has no sessions, no authorization, no templates), the time saved is not enough to compensate for the lack of a dedicated and optimized full text search engine like Xapian. Of course I shouldn&amp;rsquo;t be too surprised.&lt;/p&gt;
&lt;p&gt;To actually compete with Django + Xapian, I should probably use &lt;a href=&#34;https://github.com/quickwit-oss/tantivy&#34;&gt;Tantivy&lt;/a&gt;, instead of relying on the PostgreSQL full text search. But that would be another adventure&amp;hellip;&lt;/p&gt;
&lt;p&gt;The initial plan turned out to be a failure, but this was really a nice and constructive excursion, as I could learn a new language, using its libraries to do common and useful tasks like downloading data, building small web applications, and interfacing with the database. Rust appears to have plenty of quality crates.&lt;/p&gt;
&lt;p&gt;Beside the fact that this was just an excuse to study a new language, it remains true that rewriting existing, working applications is extremely unrewarding and most likely ineffective. Reaching parity with the current features requires a lot of time (and budget), and at the end of the story the gain could be minimal and better achieved with optimization (here I think about all our clients &lt;a href=&#34;/expertise/perl-interchange/&#34;&gt;running Interchange&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;However, if there is a need for a microservice doing a small task where speed is critical and where the application overhead should be minimal, Rust would be a viable option.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Handling text encoding in Perl</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/04/encoding-in-perl/"/>
      <id>https://www.endpointdev.com/blog/2025/04/encoding-in-perl/</id>
      <published>2025-04-29T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/04/encoding-in-perl/hieroglyphics-1246926-pxhere-com.webp&#34; alt=&#34;Columns of Egyptian hieroglyphics carved into stone&#34;&gt;&lt;/p&gt;
&lt;!-- Photo https://pxhere.com/en/photo/1246926 CC0 Public Domain --&gt;
&lt;p&gt;When we are dealing with legacy applications, it&amp;rsquo;s very possible that the code we are looking at does not deal with Unicode characters, instead assuming all text is ASCII. This will cause a myriad of glitches and visual errors.&lt;/p&gt;
&lt;p&gt;In 2025, after more than 30 years since Unicode was born, how is that possible that old applications still survive while ignoring or working around the whole issue?&lt;/p&gt;
&lt;p&gt;Well, if your audience is mainly English speaking, it&amp;rsquo;s possible that you just experience glitches sometimes, with some characters like typographical quotes, non breaking spaces, etc. which are not really mission-critical. If, on the contrary, you need to deal every day with diacritics or even different languages (say, Italian and Slovenian), your application simply won&amp;rsquo;t survive without a good understanding of encoding.&lt;/p&gt;
&lt;p&gt;In this article we are going to focus on Perl, but other languages face the same problems.&lt;/p&gt;
&lt;h3 id=&#34;back-to-the-bytes&#34;&gt;Back to the bytes&lt;/h3&gt;
&lt;p&gt;As we know, machines work with numbers and bytes. A string of text is made of bytes, and each of them is 8 bits (each bit is a 0 or a 1). So one byte allows 256 possible combinations of bits.&lt;/p&gt;
&lt;p&gt;Plain ASCII is made by 128 characters (7 bits), so it fits nicely in one byte, leaving room for more. One character is exactly one byte, and one byte carries a character.&lt;/p&gt;
&lt;p&gt;However, ASCII is not enough for most of languages, even if they use the Latin alphabet, because they use diacritics like é, à, č, and ž.&lt;/p&gt;
&lt;p&gt;To address this problem, the &lt;a href=&#34;https://en.wikipedia.org/wiki/ISO/IEC_8859&#34;&gt;ISO 8859&lt;/a&gt; encoding standards appeared (there are others, like the Windows code pages, using the same idea, but of course using different code points). These standards use 8th bit not used by ASCII, still using a single byte for each character but double the combinations from ASCII, allowing 256 possible characters. That&amp;rsquo;s better, but still not great. It suffices for handling text in a couple of languages if they share the same characters, but not more. For this reason, there are various ISO 8859 encoding standards (8859-1, 8859-2, etc.) — one for each group of related languages (e.g. 8859-1 is for Western Europe, 8859-2 for Central Europe and so on, and even revisions of the same encoding, like 8859-15 and 8859-16).&lt;/p&gt;
&lt;p&gt;The problem is that if you have a random string, you have to guess which is the correct encoding. The same byte value could represent an &amp;ldquo;È&amp;rdquo; or a &amp;ldquo;Č&amp;rdquo;. You need to look at the context (which language is this?) or search for an encoding declaration. Most important, you are simply not able to type È and Č in the same plain text document. If your company works in Italy using the 8859-15 encoding, it means you can&amp;rsquo;t even accept the correct name of a customer from Slovenia, a neighbour country, because the encoding simply doesn&amp;rsquo;t have a place for characters with a caron (like &amp;ldquo;č&amp;rdquo;) and you have to work around this real problem.&lt;/p&gt;
&lt;p&gt;So finally came the &lt;a href=&#34;https://en.wikipedia.org/wiki/Unicode&#34;&gt;Unicode&lt;/a&gt; age. This standard allows for more than a million characters, which should be enough. You can finally type English, Italian, Russian, Arabic, and emojis all in the same plain text. This is truly great, but it creates a complication for the programmer: the assumption that one byte is one character is not true anymore. The common encoding for Unicode is UTF-8, which is also backward compatible with ASCII. This means that if you have ASCII text, it is also valid UTF-8. Any other character which is not ASCII will instead take from two to three bytes and the programming language needs to be aware of this.&lt;/p&gt;
&lt;h3 id=&#34;into-the-language-and-back-to-the-world&#34;&gt;Into the language and back to the world&lt;/h3&gt;
&lt;p&gt;Text manipulation is a very common task. If you need to process a string, say &amp;ldquo;ÈČ&amp;rdquo;, like in this document, you should be able to tell that it is a string with two characters representing two letters. You want to be able to use regular expression on it, and so on.&lt;/p&gt;
&lt;p&gt;Now, if we read it as a string of bytes, we get 4 of them and the newline, which is not what we want.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Data::Dumper::Concise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# sample.txt contains ÈČ and a new line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$fh&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sample.txt&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt; = &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;&amp;lt;$fh&amp;gt;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt; =~ &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;m/\w\w/&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Found two characters\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; Dumper(&lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;close&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$fh&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$fh&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;:encoding(UTF-8)&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sample.txt&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt; = &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;&amp;lt;$fh&amp;gt;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt; =~ &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;m/\w\w/&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Found two characters\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; Dumper(&lt;span style=&#34;color:#369&#34;&gt;$l&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#038&#34;&gt;close&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$fh&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ÈČ
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;\303\210\304\214\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wide character in print at test.pl line 24, &amp;lt;$fh&amp;gt; line 1.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ÈČ
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Found two characters
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;\x{c8}\x{10c}\n&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the first block the file is read verbatim, without any decoding.  The regular expression doesn&amp;rsquo;t work, we have basically 4 bytes which don&amp;rsquo;t seem to mean much.&lt;/p&gt;
&lt;p&gt;In the second block we decoded the input, converting it in the Perl internal representation. Now we can use regular expressions and have a consistent approach to text manipulation.&lt;/p&gt;
&lt;p&gt;In the first block, we got a warning:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Wide character in print at test.pl line 25, &amp;lt;$fh&amp;gt; line 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s because we printed something to the screen, but given that the string is now made by characters (decoded for internal use), Perl warns us that we need to encode it back to bytes (for the outside world to consume). A wide character is basically a character which needs to be encoded.&lt;/p&gt;
&lt;p&gt;This can either be done by calling the &lt;code&gt;encode()&lt;/code&gt; method from the &lt;code&gt;Encode&lt;/code&gt; API:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Encode&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; encode(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;\x{c8}\x{10c}\n&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or, better, by declaring the global encoding for the standard output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;binmode&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;STDOUT&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;:encoding(UTF-8)&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;\x{c8}\x{10c}\n&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, the golden rule is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;decode the string on input and get characters out of bytes&lt;/li&gt;
&lt;li&gt;work with it in your program as a string of characters&lt;/li&gt;
&lt;li&gt;encode the string on output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any other approach is going to lead to double encoded characters (seeing things like Ã and Ä in English text is a clear symptom of this), corrupted text, and confusion.&lt;/p&gt;
&lt;h3 id=&#34;encoding-strategies&#34;&gt;Encoding strategies&lt;/h3&gt;
&lt;p&gt;If you are dealing with standard input/​output on the shell, you should have this in your script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;binmode STDIN,  &amp;#34;:encoding(UTF-8)&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;binmode STDOUT, &amp;#34;:encoding(UTF-8)&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;binmode STDERR, &amp;#34;:encoding(UTF-8)&amp;#34;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So you&amp;rsquo;re decoding on input and encoding on output automatically.&lt;/p&gt;
&lt;p&gt;For files, you can add the layer in the second argument of &lt;code&gt;open&lt;/code&gt; like in the sample script above, or use a handy module like &lt;code&gt;Path::Tiny&lt;/code&gt;, which provides methods like &lt;code&gt;slurp_utf8&lt;/code&gt; and &lt;code&gt;spew_utf8&lt;/code&gt; to read and write files using the correct encoding.&lt;/p&gt;
&lt;p&gt;Interactions with web frameworks should always happen with the internal Perl representation. When you receive the input from a form, it &lt;em&gt;should be considered already decoded&lt;/em&gt;. It&amp;rsquo;s also the framework&amp;rsquo;s responsibility to handle the encoding on output. Here at End Point we have many Interchange applications. Interchange &lt;em&gt;can&lt;/em&gt; support this, via the &lt;code&gt;MV_UTF8&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;The same rules apply to databases. It&amp;rsquo;s responsibility of the driver to take your strings and encode/​decode them when talking to the database. E.g. &lt;a href=&#34;https://metacpan.org/pod/DBD::Pg&#34;&gt;DBD::Pg&lt;/a&gt; has the &lt;code&gt;pg_enable_utf8&lt;/code&gt; option, while &lt;a href=&#34;https://metacpan.org/pod/DBD::mysql&#34;&gt;DBD::mysql&lt;/a&gt; has &lt;code&gt;mysql_enable_utf8&lt;/code&gt;. These options should usually be turned on or off explicitly. Not specifying the option is usually source of confusion because of the heuristic approach it requires for understanding the code.&lt;/p&gt;
&lt;h3 id=&#34;debugging-strategies&#34;&gt;Debugging strategies&lt;/h3&gt;
&lt;p&gt;It may not be the most correct approach, but I&amp;rsquo;ve been using &lt;code&gt;Dumper&lt;/code&gt; for more than a decade and it works. You simply use &lt;code&gt;Data::Dumper&lt;/code&gt; or &lt;code&gt;Data::Dumper::Concise&lt;/code&gt; and call &lt;code&gt;Dumper&lt;/code&gt; on the string you want to examine.&lt;/p&gt;
&lt;p&gt;If you see hexadecimal codepoints like &lt;code&gt;\x{c8}\x{10c}&lt;/code&gt;, it means the string is decoded and you&amp;rsquo;re working with the characters. If you see the raw bytes or characters with diacritics (the latter would happing if the terminal is interpreting the bytes and showing you the characters), you&amp;rsquo;re dealing with an encoded string. If you see weird characters in an English context, it probably means the text has been encoded more than once.&lt;/p&gt;
&lt;h3 id=&#34;migrate-a-web-application-to-unicode&#34;&gt;Migrate a web application to Unicode&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re still using legacy encoding systems like ISO 8859 or the similar Windows code pages, or worse, you simply don&amp;rsquo;t know and you&amp;rsquo;re relying on the browsers&amp;rsquo; heuristics (they&amp;rsquo;re quite good at guessing) you should change to handle the input and the output correctly along the whole application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Convert the templates from the encoding you are using to UTF-8 (&lt;code&gt;iconv&lt;/code&gt; should do the trick).&lt;/li&gt;
&lt;li&gt;Inspect and possibly convert the existing DB data&lt;/li&gt;
&lt;li&gt;Make sure the DB drivers handle the I/O correctly&lt;/li&gt;
&lt;li&gt;Make sure the web framework is decoding the input and encoding the output&lt;/li&gt;
&lt;li&gt;Make sure the files you read and write are correctly handled&lt;/li&gt;
&lt;li&gt;Clean up any workarounds you may have had in place&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This looks like a challenging task, and it can be, but it&amp;rsquo;s totally worth it because fancy and well-supported characters nowadays are the norm. Typographical quotes like “this” and ‘this’ are very common and inserted by word processors automatically. So are emojis. People and customers simply expect them to work.&lt;/p&gt;
&lt;h3 id=&#34;band-aids&#34;&gt;Band-aids&lt;/h3&gt;
&lt;p&gt;If your client is on a budget or can&amp;rsquo;t deal with a large upgrade like this one, which has the potential to be disruptive and expose bugs which are lurking around, you can try to downgrade the Unicode characters to ASCII with tools like &lt;a href=&#34;https://metacpan.org/pod/Text::Unidecode&#34;&gt;Text::Unidecode&lt;/a&gt; (which has been ported to other languages as well). So typographical quotes will become the plain ASCII ones, diacritics will be stripped, and various other characters will get their ASCII representation. Not great, but better than dealing with unexpected behavior!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Django and Mojolicious: a quick comparison of two popular web frameworks</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/02/django-mojolicious/"/>
      <id>https://www.endpointdev.com/blog/2025/02/django-mojolicious/</id>
      <published>2025-02-06T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/02/django-mojolicious/architecture-structure-wood-building-beam-construction-1063818-pxhere.webp&#34; alt=&#34;A view upward toward the wooden framing of a house under construction against a blue sky.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo https://pxhere.com/en/photo/1063818 CC0 Public Domain --&gt;
&lt;p&gt;Recently I&amp;rsquo;ve been working on a project with a &lt;a href=&#34;https://vuejs.org/&#34;&gt;Vue&lt;/a&gt; front-end and two back-ends, one in Python using the &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; framework and one in Perl using the &lt;a href=&#34;https://www.mojolicious.org/&#34;&gt;Mojolicious&lt;/a&gt; framework. So, it&amp;rsquo;s a good time to spend some words to share the experience and do a quick comparison.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2022/04/perl-web-frameworks/&#34;&gt;Previously&lt;/a&gt; I wrote a post about Perl web frameworks, and now I&amp;rsquo;m expanding the subject into another language.&lt;/p&gt;
&lt;p&gt;Django was chosen for this project because it&amp;rsquo;s been around for almost 20 years now and provides the needed maturity and stability to be long-running and low-budget. In this regard, it has proved a good choice so far. Recently it saw a major version upgrade without any problems to speak of. It could be argued that I should have used the &lt;a href=&#34;https://github.com/encode/django-rest-framework&#34;&gt;Django REST Framework&lt;/a&gt; instead of plain Django. However, at the time the decision was made, adding a framework on top of another seemed a bit excessive. I don&amp;rsquo;t have many regrets about this, though.&lt;/p&gt;
&lt;p&gt;Mojolicious is an old acquaintance. It used to have fast-paced development but seems very mature now, and it&amp;rsquo;s even been &lt;a href=&#34;https://mojojs.org/&#34;&gt;ported&lt;/a&gt; to JavaScript.&lt;/p&gt;
&lt;p&gt;Both frameworks have just a few dependencies (which is fairly normal in the Python world, but not in the Perl one) and excellent documentation. They both follow the model-view-controller pattern. Let&amp;rsquo;s examine the components.&lt;/p&gt;
&lt;h3 id=&#34;views&#34;&gt;Views&lt;/h3&gt;
&lt;p&gt;Both frameworks come with a built-in template system (which can be swapped out with something else), but in this project we can skip the topic altogether as both frameworks are used only as back-end for transmitting JSON, without any HTML rendering involved.&lt;/p&gt;
&lt;p&gt;However, let&amp;rsquo;s see how the rendering looks for the API we&amp;rsquo;re writing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Base&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Mojolicious::Controller&amp;#39;&lt;/span&gt;, -signatures;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;check&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;render(json =&amp;gt; { status =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;OK&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.http&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; JsonResponse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;status&lt;/span&gt;(request):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; JsonResponse({ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;:  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;OK&amp;#34;&lt;/span&gt; })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing complicated here, just provide the right call.&lt;/p&gt;
&lt;h3 id=&#34;models&#34;&gt;Models&lt;/h3&gt;
&lt;h4 id=&#34;django&#34;&gt;Django&lt;/h4&gt;
&lt;p&gt;Usually a model in context of web development means a database and here we are going to keep this assumption.&lt;/p&gt;
&lt;p&gt;Django comes with a comprehensive &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/topics/db/queries/&#34;&gt;object-relational mapping&lt;/a&gt; (ORM) system and it feels like the natural thing to use. I don&amp;rsquo;t think it makes much sense to use another ORM, or even to use raw SQL queries (though it is &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/topics/db/sql/&#34;&gt;possible&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;You usually start a Django project by defining the model. The Django ORM gives you the tools to manage the migrations, providing abstraction from the SQL. You need to define the field types and the relationships (joins and foreign keys) using the appropriate class methods.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;User&lt;/span&gt;(AbstractUser):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = models.EmailField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    site = models.ForeignKey(Site, on_delete=models.CASCADE, related_name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;site_users&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    libraries = models.ManyToManyField(Library, related_name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;affiliated_users&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expiration = models.DateTimeField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created = models.DateTimeField(auto_now_add=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_modified = models.DateTimeField(auto_now=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These calls provide not only the SQL type to use, but also the validation. For example, the &lt;code&gt;blank&lt;/code&gt; parameter is a validation option specifying whether Django will accept an empty value. It is different from the &lt;code&gt;null&lt;/code&gt; option, which directly correlates to SQL. You can see we&amp;rsquo;re quite far from working with SQL, at least two layers of abstraction away.&lt;/p&gt;
&lt;p&gt;In the example above, we&amp;rsquo;re also defining a foreign key between a site and a user (many-to-one), so each user belongs to one site. We also define a many-to-many relationship with the libraries record. I like how these relationships are defined, it&amp;rsquo;s very concise.&lt;/p&gt;
&lt;p&gt;Thanks to these definitions, you get a whole &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/ref/contrib/admin/&#34;&gt;admin console&lt;/a&gt; almost for free, which your admin users are sure to like. However, I&amp;rsquo;m not sure this is a silver bullet for solving all problems. With large tables and relationships the admin pages load slowly and they could become unusable very quickly. Of course, you can tune that by filtering out what you need and what you don&amp;rsquo;t, but that means things are not as simple as &amp;ldquo;an admin dashboard for free&amp;rdquo; — at the very least, there&amp;rsquo;s some configuring to do.&lt;/p&gt;
&lt;p&gt;As for the query syntax, you usually need to call &lt;code&gt;Class.objects.filter()&lt;/code&gt;. As you would expect from an ORM, you can chain the calls and finally get objects out of that, representing a database row, which, in turn, you can update or delete.&lt;/p&gt;
&lt;p&gt;The syntax for the &lt;code&gt;filter()&lt;/code&gt; call is based on the double underscore separator, so you can query over the relationships like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; agent &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; (Agent.objects.filter(canonical_agent_id__isnull=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .prefetch_related(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;canonical_agent&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .order_by(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;canonical_agent__name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .all()):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Dummy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.save()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, provided that we defined the foreign keys and the attributes in the model, we can search/​order across the relationship. The &lt;code&gt;__isnull&lt;/code&gt; suffix, as you can imagine, results in a &lt;code&gt;WHERE canonical_agent_id IS NOT NULL&lt;/code&gt; query, while in the &lt;code&gt;order_by&lt;/code&gt; call we sort over the joined table using the &lt;code&gt;name&lt;/code&gt; column. Looks nice and readable, with a touch of magic.&lt;/p&gt;
&lt;p&gt;Of course things are never so simple, so you can build complex queries with the &lt;code&gt;Q&lt;/code&gt; class combined with bytewise operators (&lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of a simple case-insensitive search for a name containing multiple words:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db.models&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; Q
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;api_list&lt;/span&gt;(request)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    term = request.GET.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; term
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        words = [ w &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; w &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; re.split(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;\W+&amp;#39;&lt;/span&gt;, term) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; w ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; words:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query = Q(name__icontains=words.pop())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; words:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                query = query &amp;amp; Q(name__icontains=words.pop())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;# logger.debug(query)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            agents = Agent.objects.filter(query).all()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To sum up, the ORM is providing everything you need to stay away from the SQL. In fact, it seems like Django doesn&amp;rsquo;t like you doing raw SQL queries.&lt;/p&gt;
&lt;h4 id=&#34;mojolicious-and-perl&#34;&gt;Mojolicious and Perl&lt;/h4&gt;
&lt;p&gt;In the Perl world things are a bit different.&lt;/p&gt;
&lt;p&gt;The Mojolicious &lt;a href=&#34;https://docs.mojolicious.org/Mojolicious/Guides/Tutorial&#34;&gt;tutorial&lt;/a&gt; doesn&amp;rsquo;t even mention the database. You can use any ORM or no ORM at all, if you prefer so. However, Mojolicious makes the DB handle available everywhere in the application.&lt;/p&gt;
&lt;p&gt;You could use &lt;a href=&#34;https://metacpan.org/pod/DBIx::Connector&#34;&gt;DBIx::Connector&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt;, &lt;a href=&#34;https://docs.mojolicious.org/Mojo/Pg&#34;&gt;Mojo::Pg&lt;/a&gt; (which was developed with Mojolicious), or whatever you prefer.&lt;/p&gt;
&lt;p&gt;For example, to use Mojo::Pg in the main application class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Base&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Mojolicious&amp;#39;&lt;/span&gt;, -signatures;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Pg&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Data::Dumper::Concise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;plugin(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NotYAMLConfig&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;log&lt;/span&gt;-&amp;gt;info(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Starting up with &amp;#34;&lt;/span&gt; . Dumper(&lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;helper(pg =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      state &lt;span style=&#34;color:#369&#34;&gt;$pg&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt;-&amp;gt;{dbi_connection_string});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the routes you can call &lt;code&gt;$self-&amp;gt;pg&lt;/code&gt; to get the database object.&lt;/p&gt;
&lt;p&gt;The three approaches I&amp;rsquo;ve mentioned here are different.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DBIx::Connector&lt;/code&gt; is basically a way to get you a safe DBI handle across forks and DB connection failures.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Mojo::Pg&lt;/code&gt; gives you the ability to do abstract queries but also gives some convenient methods to get the results. I wouldn&amp;rsquo;t call it a ORM; from a query you usually gets hashes, not objects, you don&amp;rsquo;t need to define the database layout, and it won&amp;rsquo;t produce migrations for you, though there is some &lt;a href=&#34;https://docs.mojolicious.org/Mojo/Pg/Migrations&#34;&gt;migration support&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of standard and abstract queries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;list_texts&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;param(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sid&amp;#39;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SELECT * FROM texts WHERE sid = ? ORDER BY sorting_index&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;db&lt;/span&gt;-&amp;gt;query(&lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hashes&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;each&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;render(json =&amp;gt; { texts =&amp;gt; \&lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The query above can be rewritten with an abstract query, using the same module.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;db&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;select&lt;/span&gt;(texts =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             { sid =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             { order_by =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sorting_index&amp;#39;&lt;/span&gt; })-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hashes&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;each&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If it&amp;rsquo;s a simple, static query, it&amp;rsquo;s basically a matter of taste; do you prefer to see the SQL or not? The second version is usually nicer if you want to build a different query depending on the parameters, so you add or remove keys to the hashes which maps to query and finally execute it.&lt;/p&gt;
&lt;p&gt;Now, speaking of taste, for complex queries with a lot of joins I honestly prefer to see the SQL query instead of wondering if the abstract one is producing the correct SQL. This is true regardless of the framework. I have the impression that it is faster, safer, and cleaner to have the explicit SQL in the code rather than leaving future developers (including future me) to wonder if the magic is happening or not.&lt;/p&gt;
&lt;p&gt;Finally, nothing stops you from using &lt;code&gt;DBIx::Class&lt;/code&gt;, which is the best ORM for Perl, even if it&amp;rsquo;s not exactly light on dependencies.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s very versatile, it can build queries of arbitrary complexity, and you usually get objects out of the queries you make. It doesn&amp;rsquo;t come with an admin dashboard, it doesn&amp;rsquo;t enforce the data types and it doesn&amp;rsquo;t ship any validation by default (of course, you can implement that manually). The query syntax is very close to the &lt;code&gt;Mojo::Pg&lt;/code&gt; one (which is basically &lt;a href=&#34;https://metacpan.org/pod/SQL::Abstract&#34;&gt;SQL::Abstract&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The gain here is that, like in Django&amp;rsquo;s ORM, you can attach your methods to the classes representing the rows, so the data definitions live with the code operating on them.&lt;/p&gt;
&lt;p&gt;However, the fact that it builds an object for each result means you&amp;rsquo;re paying a performance penalty which sometimes can be very high. I think this is a problem common to all ORMs, regardless of the language and framework you&amp;rsquo;re using.&lt;/p&gt;
&lt;p&gt;The difference with Django is that once you have chosen it as your framework, you are basically already sold to the ORM. With Mojolicious and other Perl frameworks (Catalyst, Dancer), you can still make the decision and, at least in theory, change it down the road.&lt;/p&gt;
&lt;p&gt;My recommendation would be to keep the model, both code and business logic, decoupled from the web-specific code. This is not really doable with Django, but is fully doable with the Perl frameworks. Just put the DB configuration in a dedicated file and the business code in appropriate classes. Then you should be able to, for example, run a script without loading the web and the whole framework configuration. In this ideal scenario, the web framework just provides the glue between the user and your model.&lt;/p&gt;
&lt;h3 id=&#34;controllers&#34;&gt;Controllers&lt;/h3&gt;
&lt;p&gt;Routes are defined similarly between Django and Mojolicious. Usually you put the code in a class and then point to it, attaching a name to it so you can reference it elsewhere. The language is different, the style is different, but they essentially do the same thing.&lt;/p&gt;
&lt;p&gt;Django:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.urls&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; path
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; views
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;api/agents/&amp;lt;int:agent_id&amp;gt;&amp;#34;&lt;/span&gt;, views.api_agent_view, name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;api_agent_view&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The function &lt;code&gt;views.api_agent_view&lt;/code&gt; will receive the request with the &lt;code&gt;agent_id&lt;/code&gt; as a parameter.&lt;/p&gt;
&lt;p&gt;Mojolicious:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# ....&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;routes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt;-&amp;gt;get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/list/:sid&amp;#39;&lt;/span&gt;)-&amp;gt;to(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;API#list_texts&amp;#39;&lt;/span&gt;)-&amp;gt;name(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;api_list_texts&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-&amp;gt;to&lt;/code&gt; method is routing the request to the &lt;code&gt;Myapp::Controller::API::list_texts&lt;/code&gt;, which will receive the request with the &lt;code&gt;sid&lt;/code&gt; as parameter.&lt;/p&gt;
&lt;p&gt;This is pretty much the core business of every web framework: routing a request to a given function.&lt;/p&gt;
&lt;p&gt;Mojolicious has also the ability to &lt;a href=&#34;https://docs.mojolicious.org/Mojolicious/Guides/Routing#Under&#34;&gt;chain the routes&lt;/a&gt; (pretty much taken from Catalyst). The typical use is authorization:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;routes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$api&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt;-&amp;gt;under(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/api/v1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; ($c) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;req&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;headers&lt;/span&gt;-&amp;gt;header(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;X-API-Key&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;testkey&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;render(text =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Authentication required!&amp;#39;&lt;/span&gt;, status =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;401&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$api&lt;/span&gt;-&amp;gt;get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/check&amp;#39;&lt;/span&gt;)-&amp;gt;to(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;API#check&amp;#39;&lt;/span&gt;)-&amp;gt;name(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;api_check&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the request to &lt;code&gt;/api/v1/check&lt;/code&gt; will first go in the first block and the chain will abort if the API key is not set in the header. Otherwise it will proceed to run the &lt;code&gt;API&lt;/code&gt; module&amp;rsquo;s &lt;code&gt;check&lt;/code&gt; function.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m Perl guy and so I&amp;rsquo;m a bit biased toward Mojolicious, but I also have a pragmatic approach to programming. Python is widely used — they teach it in schools — while Perl is seen as old-school, if not dead (like all the mature technologies). So, Python could potentially attract more developers to your project, and this is important to consider.&lt;/p&gt;
&lt;p&gt;Learning a new language like Python is not a big leap; it and Perl are quite similar despite the different syntax. I&amp;rsquo;d throw Ruby in the same basket.&lt;/p&gt;
&lt;p&gt;Of course both languages provide high quality modules you can use, and these two frameworks are an excellent example.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>The Perl and Raku Conference 2024</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2024/08/the-perl-and-raku-conference-2024/"/>
      <id>https://www.endpointdev.com/blog/2024/08/the-perl-and-raku-conference-2024/</id>
      <published>2024-08-02T00:00:00+00:00</published>
      <author>
        <name>Andrew Baerg</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2024/08/the-perl-and-raku-conference-2024/next-generation.webp&#34; alt=&#34;A conference room with around 30 people visible watching a speaker talk, in front of a TPRC banner.&#34;&gt;
Next Generation of Perl&lt;/p&gt;
&lt;!--Photo by Andrew Baerg, 2024.---&gt;
&lt;p&gt;I attended &lt;a href=&#34;https://tprc.us/tprc-2024-las/&#34;&gt;The Perl and Raku Conference&lt;/a&gt; in Las Vegas, NV, which took place June 25–28, 2024. It was HOT outside (over 40 °C/110 °F) but we stayed cool inside at the Alexis Park Resort.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://curtispoe.org/&#34;&gt;Curtis Poe (Ovid)&lt;/a&gt; got things started with the keynote encouraging us to &lt;a href=&#34;https://www.youtube.com/watch?v=22-7yP0inu8&#34;&gt;Party Like It&amp;rsquo;s 19100+e^iπ&lt;/a&gt;, and reminded us that Vegas is lexically scoped (what happens in Vegas stays in Vegas)! More importantly he reminded us that Perl is about people, not just the technology. The Perl community has been &lt;a href=&#34;https://bit.ly/perl-events&#34;&gt;meeting all over the world&lt;/a&gt; since 1999, with this being the 25th anniversary of the first The Perl Conference (aka YAPC::NA).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2024/08/the-perl-and-raku-conference-2024/ovid-keynote.webp&#34; alt=&#34;A man with a beard presents on a small stage.&#34;&gt;
Ovid Keynote&lt;/p&gt;
&lt;!--Photo by Andrew Baerg, 2024.---&gt;
&lt;p&gt;Meeting in person with people who you interact with primarily through digital channels, code commits, and &lt;a href=&#34;https://metacpan.org&#34;&gt;MetaCPAN&lt;/a&gt; documentation really highlighted the importance of the community. On the first day, I messed up timezones, showed up an hour before registration opened, and witnessed the conference organizers and core members arrive and greet each other with hugs. I also enjoyed visiting with one of the very welcoming board members of &lt;a href=&#34;https://www.perlfoundation.org/&#34;&gt;The Perl and Raku Foundation&lt;/a&gt; (TPRF).&lt;/p&gt;
&lt;p&gt;Many of the speakers and attendees put a &amp;ldquo;Hallway++&amp;rdquo; sticker on their badge which simply meant &amp;ldquo;talk to me, I&amp;rsquo;m here to meet and get to know people&amp;rdquo;. At breakfast one morning, I had the privilege of sitting with &lt;a href=&#34;https://cromedome.net/&#34;&gt;Jason Crome&lt;/a&gt;, the core maintainer of &lt;a href=&#34;https://perldancer.org&#34;&gt;Dancer&lt;/a&gt;, a framework that I have used extensively. It was amazing to be able to pick the brain of one of the people who has intimate knowledge of the software.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2024/08/the-perl-and-raku-conference-2024/dancing-with-cromedome.webp&#34; alt=&#34;Two men sit at a table in front of their computers, one shows the other something and has a slight smile&#34;&gt;
Dancing with Cromedome&lt;/p&gt;
&lt;!--Photo by Andrew Baerg, 2024.---&gt;
&lt;p&gt;The Perl community is large and diverse, which is reflected in the &lt;a href=&#34;https://perlcommunity.org/science/&#34;&gt;Science Perl Committee&lt;/a&gt; and the all-Perl &lt;a href=&#34;https://koha-community.org/&#34;&gt;Koha Library Software&lt;/a&gt; in use at over 4,000 libraries and with its own annual conference. It was cool to hear about the &lt;a href=&#34;https://leejo.github.io/acme-glue-talk/presentation.html#1&#34;&gt;Glue Photo Project&lt;/a&gt;, making &lt;a href=&#34;https://github.com/ology/Perl-Algorithmic-Music-2024&#34;&gt;algorithmic music&lt;/a&gt;, and gaming with the &lt;a href=&#34;https://youtu.be/7wTmA4xm6i4&#34;&gt;TinyNES&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Every community will experience conflict and this one is no different. The impact of &lt;a href=&#34;https://youtu.be/Q1H9yKf8BI0&#34;&gt;Sawyer&amp;rsquo;s resignation at TPRC 2023&lt;/a&gt; could be felt at this conference, and in response the community is focused on making things better with a &lt;a href=&#34;https://news.perlfoundation.org/post/new-standaards-of-conduct&#34;&gt;new standards of conduct&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It wouldn&amp;rsquo;t be a conference in 2024 without talk of AI. We had some &lt;a href=&#34;https://youtu.be/y3llSkCJnWk&#34;&gt;Musings on Generative AI&lt;/a&gt; and an introduction to &lt;a href=&#34;https://youtu.be/Agw6E1omIvY&#34;&gt;PerlGPT, A Code Llama LLM Fine-Tuned For Perl&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There were, of course, a lot of talks about actual Perl code!&lt;/p&gt;
&lt;p&gt;One of the things I enjoy about attending conferences is discovering things that I wasn&amp;rsquo;t looking for. &lt;a href=&#34;https://blogs.perl.org/users/chad_exodist_granum/&#34;&gt;Chad Granum&lt;/a&gt; gave a lightning talk on &lt;a href=&#34;https://metacpan.org/pod/goto::file&#34;&gt;goto::file&lt;/a&gt; and I noticed the use of &lt;a href=&#34;https://perldoc.perl.org/perlsyn#Plain-Old-Comments-%28Not!%29&#34;&gt;line directives&lt;/a&gt; which can be extremely helpful in debugging &lt;code&gt;eval&lt;/code&gt;ed code. For example, let&amp;rsquo;s say you are &lt;code&gt;eval&lt;/code&gt;ing subs into a hashref and then calling them like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub1&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sub {\n  print &amp;#39;foo&amp;#39;;\n  print &amp;#39;bar&amp;#39;;\n print &amp;#39;baz&amp;#39;;\n}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub2&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sub {\n  print &amp;#39;foo&amp;#39;;\n  print &amp;#39;bar&amp;#39;;\n warn &amp;#39;baz&amp;#39;;\n}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sub1 =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;eval&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sub2 =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;eval&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt;-&amp;gt;{sub1}-&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt;-&amp;gt;{sub2}-&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Any warn or die output will give you a line number, but no context as to which sub it originated from:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;baz at (&lt;span style=&#34;color:#038&#34;&gt;eval&lt;/span&gt; 2) line 4.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Making use of a line directive when &lt;code&gt;eval&lt;/code&gt;ing the subs like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub1&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sub {\n  print &amp;#39;foo&amp;#39;;\n  print &amp;#39;bar&amp;#39;;\n print &amp;#39;baz&amp;#39;;\n}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sub2&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;sub {\n  print &amp;#39;foo&amp;#39;;\n  print &amp;#39;bar&amp;#39;;\n warn &amp;#39;baz&amp;#39;;\n}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sub1 =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;eval&lt;/span&gt; &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;qq(#line 1 &amp;#34;sub1&amp;#34;\n$sub1)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  sub2 =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;eval&lt;/span&gt; &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;qq(#line 1 &amp;#34;sub2&amp;#34;\n$sub2)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt;-&amp;gt;{sub1}-&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$Sub&lt;/span&gt;-&amp;gt;{sub2}-&amp;gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Will now result in a much more friendly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;baz at sub2 line 4.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And in true open source fashion, this has been turned into a &lt;a href=&#34;https://github.com/interchange/interchange/pull/150&#34;&gt;pull request for Interchange&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2024/08/the-perl-and-raku-conference-2024/the-sphere.webp&#34; alt=&#34;A large spherical building with a spherical screen&#34;&gt;
The Las Vegas Sphere&lt;/p&gt;
&lt;!--Photo by Andrew Baerg, 2024.---&gt;
&lt;p&gt;I always enjoy hearing from others in the community about &lt;a href=&#34;https://metacpan.org&#34;&gt;CPAN modules&lt;/a&gt; that are in their toolbox. I learned about &lt;a href=&#34;https://metacpan.org/pod/DBIx::QuickDB&#34;&gt;DBIx::QuickDB&lt;/a&gt;, which you can use to spin up a database server on the fly, which removes the need for a running database server for tests, and also enables running concurrent tests which require a database server that would otherwise conflict. Combine this with a &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; schema and &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class::Fixtures&#34;&gt;DBIx::Class::Fixtures&lt;/a&gt; and you have a very nice way to run some tests against fixed data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBIx::QuickDB&lt;/span&gt; PSQL_DB  =&amp;gt; {driver =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;PostgreSQL&amp;#39;&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;PSQL_DB&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;connect&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$schema&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Some::Schema&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;connect&lt;/span&gt;( &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$dbh&lt;/span&gt; }, { on_connect_do =&amp;gt; [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;] } );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$schema&lt;/span&gt;-&amp;gt;deploy();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$fixtures&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;DBIx::Class::Fixtures&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;({ config_dir =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;...&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$fixtures&lt;/span&gt;-&amp;gt;populate({ no_deploy =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, schema =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$schema&lt;/span&gt;, directory =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;...&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ok(&lt;span style=&#34;color:#369&#34;&gt;$schema&lt;/span&gt;-&amp;gt;resultset(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Foo&amp;#39;&lt;/span&gt;)-&amp;gt;count &amp;gt;= &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;database populated&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&#34;http://damian.conway.org&#34;&gt;Damian Conway&lt;/a&gt; gave a keynote on &lt;a href=&#34;https://youtu.be/0x9LD8oOmv0&#34;&gt;The Once and Future Perl&lt;/a&gt;, showing how far Perl has come as a language and how its rich history can be leveraged into the future: &amp;ldquo;if you can envisage what you could have done better in the past, then you can probably think of ways to make the future brighter!&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;He showed off his new &lt;a href=&#34;https://metacpan.org/pod/Multi::Dispatch&#34;&gt;Multi::Dispatch&lt;/a&gt; module which you can use now to write incredibly extensible (and beautiful) code. Here it is in action with a simple Data::Dumper clone in 5 lines of code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;v5&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;.26&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Multi::Dispatch&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;multi dd :before :where(VOID) (&lt;span style=&#34;color:#369&#34;&gt;@data&lt;/span&gt;)   { say &amp;amp;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;next&lt;/span&gt;::variant }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;multi dd (&lt;span style=&#34;color:#369&#34;&gt;$k&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$v&lt;/span&gt;)                       { dd(&lt;span style=&#34;color:#369&#34;&gt;$k&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; =&amp;gt; &amp;#39;&lt;/span&gt; . dd(&lt;span style=&#34;color:#369&#34;&gt;$v&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;multi dd (&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; :where(ARRAY))          { &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;[&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#038&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#038&#34;&gt;map&lt;/span&gt; {dd(&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;)}                 &lt;span style=&#34;color:#369&#34;&gt;@$data&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;]&amp;#39;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;multi dd (&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; :where(HASH))           { &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;{&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#038&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#038&#34;&gt;map&lt;/span&gt; {dd(&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;-&amp;gt;{&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;})} &lt;span style=&#34;color:#038&#34;&gt;keys&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;%$data&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;}&amp;#39;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;multi dd (&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;)                        { &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#038&#34;&gt;quotemeta&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;say dd [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;, { bar =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;baz&amp;#34;&lt;/span&gt; }];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With Smartmatch (&lt;code&gt;given&lt;/code&gt;/&lt;code&gt;when&lt;/code&gt;) &lt;a href=&#34;https://perldoc.perl.org/5.40.0/perldeprecation#Smartmatch&#34;&gt;scheduled for deprecation in 5.42&lt;/a&gt; he has written a drop-in replacement that uses Multi::Dispatch: &lt;a href=&#34;https://metacpan.org/pod/Switch::Right&#34;&gt;Switch::Right&lt;/a&gt;, which addresses the issues with the original implementation that was in core.&lt;/p&gt;
&lt;p&gt;Also on display was the new &lt;a href=&#34;https://perldoc.perl.org/5.40.0/perlclass&#34;&gt;class syntax&lt;/a&gt; introduced into core in 5.38 with &lt;code&gt;use feature &#39;class&#39;&lt;/code&gt;. Here&amp;rsquo;s an example of how it looks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;v5&lt;/span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;.40&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;feature&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;class&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class Point {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  field &lt;span style=&#34;color:#369&#34;&gt;$x&lt;/span&gt; :param = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  field &lt;span style=&#34;color:#369&#34;&gt;$y&lt;/span&gt; :param = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  method describe () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      say &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;A point at ($x, $y)\n&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Point&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(x =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;, y =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;)-&amp;gt;describe;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are waiting for Perl 7, Damian is here to tell you that the future is now; you don&amp;rsquo;t have to wait for Perl 7 (or 17), it&amp;rsquo;s Perl 5 with Multi::Dispatch and &lt;code&gt;use feature &#39;class&#39;&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;The last day of the conference provided an opportunity to go deeper into learning the new class syntax with a workshop on building a &lt;a href=&#34;https://github.com/perigrin/going-rogue-class&#34;&gt;roguelike adventure game&lt;/a&gt; from scratch.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s next? The &lt;a href=&#34;https://act.yapc.eu/lpw2024/&#34;&gt;London Perl &amp;amp; Raku Workshop&lt;/a&gt; is taking place on October 26, 2024 and Perl 5.42 is just around the corner!&lt;/p&gt;
&lt;p&gt;—JAPH (Just Another Perl Hacker)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl Web Frameworks</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/04/perl-web-frameworks/"/>
      <id>https://www.endpointdev.com/blog/2022/04/perl-web-frameworks/</id>
      <published>2022-04-19T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/04/perl-web-frameworks/nature-wing-black-and-white-web-line-insect-1244977.webp&#34; alt=&#34;Spider webs and spiders&#34;&gt;&lt;/p&gt;
&lt;!-- Photo: https://pxhere.com/en/photo/1244977 CC0 Public Domain --&gt;
&lt;h3 id=&#34;cgi&#34;&gt;CGI&lt;/h3&gt;
&lt;p&gt;When I started programming, back in the day, CGI (the Common Gateway Interface) was still widely
used. Usually the Apache webserver would just execute a script or a
binary with some environment variables set and serve whatever the
executable sent to the standard output, while keeping the standard
error in the logs.&lt;/p&gt;
&lt;p&gt;This simple and straightforward mechanism can still
be used for small programs, but larger applications usually want to
save the start-up time and live longer than just a single request.&lt;/p&gt;
&lt;p&gt;At that time Perl was used far more often than now, and it had (and still
has) the &lt;a href=&#34;https://metacpan.org/pod/CGI&#34;&gt;CGI.pm&lt;/a&gt; module to help the
programmer to get the job done.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CGI&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$q&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CGI&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$q&lt;/span&gt;-&amp;gt;header;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Marco&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$q&lt;/span&gt;-&amp;gt;p(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello $name&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it will output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./cgi.pl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: text/html; charset=ISO-8859-1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;p&amp;gt;Hello Marco&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here the script mixes logic and formatting and the encoding it
produces by default tells us that this comes from another age. But
if you want something which is seldom used and gets executed on demand
without persisting in the machine’s memory, this is still an option.&lt;/p&gt;
&lt;p&gt;Please note that there are frameworks which can work in CGI mode, so
there is no reason to use CGI.pm, beside having to maintain legacy
programs.&lt;/p&gt;
&lt;h3 id=&#34;mojolicious&#34;&gt;Mojolicious&lt;/h3&gt;
&lt;p&gt;Fast-forward to 2022.&lt;/p&gt;
&lt;p&gt;Nowadays Perl is just another language among dozens of them. But it
still gets the job done and lets you write nice, maintainable code like
any other modern language.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mojolicious.org/&#34;&gt;Mojolicious&lt;/a&gt; is currently the top choice if
you want to do web development in Perl. It is an amazing framework,
with a large and active community, and appears to have collected the
best concepts that other web frameworks from other languages have to
offer.&lt;/p&gt;
&lt;p&gt;Let’s hack an app in a couple of minutes in a single file, like during
the CGI days:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojolicious::Lite&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;get &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;) = &lt;span style=&#34;color:#369&#34;&gt;@_&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;stash(name =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Marco&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;render(template =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;index&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;app&lt;/span&gt;-&amp;gt;start;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;__DATA__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;@@&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;index&lt;/span&gt;.html.ep
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello &amp;lt;&lt;span style=&#34;color:#369&#34;&gt;%&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;name&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;%&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here the structure is a bit different.&lt;/p&gt;
&lt;p&gt;First, there’s a Domain Specific Language (DSL) to give you some sugar.
This is the “Lite” version, while in a well-structured Mojolicious app one
prefers to write class methods. We declare that the root (&lt;code&gt;/&lt;/code&gt;) URL path of the
application is going to execute some code. It populates the “stash”
with some variables, and finally renders a template which can access
the stashed variables. If you execute the script, you get:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./mojo.pl cgi 2&amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Status: 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: Fri, 08 Apr 2022 12:33:52 GMT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: text/html;charset=UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello Marco&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The logging to the standard error stream is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:33:52.92508] [163133] [debug] [82ae3iV2] GET &amp;#34;/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:33:52.92532] [163133] [debug] [82ae3iV2] Routing to a callback
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:33:52.92565] [163133] [debug] [82ae3iV2] Rendering template &amp;#34;index.html.ep&amp;#34; from DATA section
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:33:52.92610] [163133] [debug] [82ae3iV2] 200 OK (0.001021s, 979.432/s)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is basically what a modern framework is supposed to do.&lt;/p&gt;
&lt;p&gt;The nice thing in this example is that we created a single-file
prototype and launched it as a CGI. But we can also launch it as
daemon and visit the given address with a browser, which is how you
should normally deploy it, usually behind a reverse proxy like
&lt;a href=&#34;https://nginx.org/en/&#34;&gt;nginx&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./mojo.pl daemon
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:48:42.01827] [163409] [info] Listening at &amp;#34;http://*:3000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Web application available at http://127.0.0.1:3000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:48:48.53687] [163409] [debug] [CwM6zoUQ] GET &amp;#34;/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:48:48.53715] [163409] [debug] [CwM6zoUQ] Routing to a callback
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:48:48.53752] [163409] [debug] [CwM6zoUQ] Rendering template &amp;#34;index.html.ep&amp;#34; from DATA section
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2022-04-08 14:48:48.53808] [163409] [debug] [CwM6zoUQ] 200 OK (0.001209s, 827.130/s)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want you can even launch it with HTTPS as well (please note the
syntax to pass the certificates).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./mojo.pl daemon -l &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://[::]:8080?cert=./ssl/fullchain.pem&amp;amp;key=./ssl/privkey.pem&amp;#39;&lt;/span&gt; -m production&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For a small application listening on a high port this is already
enough and the whole deployment problem goes away.&lt;/p&gt;
&lt;p&gt;Speaking about deployment, Mojolicious has basically no dependencies
other than the core modules and comes with a lot of goodies, for example
a &lt;a href=&#34;https://docs.mojolicious.org/Mojo/UserAgent&#34;&gt;non blocking user-agent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently a legacy application needed to make some API calls. To speed up
the process, we wanted to make the requests in parallel. And here’s
the gist of the code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Async&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# ... more modules here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::UserAgent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# .... other methods here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;example&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;test@example.com&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$ua&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::UserAgent&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$list&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;get_lists) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$promise&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$ua&lt;/span&gt;-&amp;gt;post_p(&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_url(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/api/v2/endpoint/$list-&amp;gt;{code}&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                  json =&amp;gt; { email =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          -&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$tx&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$res&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$tx&lt;/span&gt;-&amp;gt;result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$res&lt;/span&gt;-&amp;gt;code =~ &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;m/^2/&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_update_db(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                         &lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$tx&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;req&lt;/span&gt;-&amp;gt;url . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$res&lt;/span&gt;-&amp;gt;code;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;push&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@promises&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$return&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Promise&lt;/span&gt;-&amp;gt;all(&lt;span style=&#34;color:#369&#34;&gt;@promises&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$return&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; }, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; { &lt;span style=&#34;color:#369&#34;&gt;$return&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;})-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;wait&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So a bunch of requests are run in parallel and then synced before
returning. Does it remind you of JavaScript? Of course. A lot of
common paradigms taken from other languages and frameworks were
implemented here, and you can find the best of them in this nice
package.&lt;/p&gt;
&lt;p&gt;But the point here is that it doesn’t need dozens of new modules
installed or upgraded. It’s just a single module in pure Perl that you
can even install in your application tree. This is a huge advantage if
you’re dealing with a legacy application which uses an old Perl tree
and you want to play safe.&lt;/p&gt;
&lt;p&gt;So, if you’re starting from scratch, go with Mojolicious. It lets you
prototype fast and doesn’t let you down later.&lt;/p&gt;
&lt;p&gt;However, starting from scratch is not always an option. Actually, it’s
a rare opportunity. There’s a whole world of legacy applications and
they generate real money every day. It’s simply not possible or even
desirable to throw away something that works for something that would
do the same thing but in a “cooler” way. In ten years, the way we’re
coding will look old anyway.&lt;/p&gt;
&lt;h3 id=&#34;interchange&#34;&gt;Interchange&lt;/h3&gt;
&lt;p&gt;Wait. Isn’t &lt;a href=&#34;https://www.interchangecommerce.org&#34;&gt;Interchange&lt;/a&gt; an old
e-commerce framework? Yes, it’s not exactly a generic web framework,
on the contrary, it’s a specialized one, but it’s still a framework and
you can still do things in a maintainable fashion. The key is using
the so-called action maps:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ActionMap jump &amp;lt;&amp;lt;EOR
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sub {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # get the path parameters
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my ($action, @args) = split(/\//, shift);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # get the query/body parameters
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $param = $CGI-&amp;gt;{param};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # redirect to another page
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $Tag-&amp;gt;deliver({ location =&amp;gt; $final });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # or serve JSON
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $Tag-&amp;gt;deliver({ type =&amp;gt; &amp;#39;application/json&amp;#39;, body =&amp;gt; $json_string });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # or serve a file
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $Tag-&amp;gt;deliver({ type =&amp;gt; &amp;#39;text/plain&amp;#39;, body =&amp;gt; $bigfile });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # or populate the &amp;#34;stash&amp;#34; and serve a template page
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $Tag-&amp;gt;tmp(stash_variable =&amp;gt; &amp;#34;Marco&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $CGI-&amp;gt;{mv_nextpage} = &amp;#34;test.html&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EOR&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;pages/test.html&lt;/code&gt; you would put this template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;Hello [scratch stash_variable]&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, I can’t show you a simple script which demonstrates this and
you’ll have to take my word for it since we can’t go through the
installation process here for a demo.&lt;/p&gt;
&lt;p&gt;Interchange is old, and it shows its
years, but it is actively maintained. It lacks many of Mojo’s
goodies, &lt;em&gt;but&lt;/em&gt; you can still do things in a reasonable way.&lt;/p&gt;
&lt;p&gt;In the example the code will execute
when a path starting with &lt;code&gt;/jump/&lt;/code&gt; is requested. The whole path is
passed to the routine, so you can split at &lt;code&gt;/&lt;/code&gt;, apply your logic, and
finally either set &lt;code&gt;$CGI-&amp;gt;{mv_nextpage}&lt;/code&gt; to a file in the &lt;code&gt;pages&lt;/code&gt;
directory or output the response body directly with &lt;code&gt;deliver&lt;/code&gt;. This
way you can easily build, as a classical example, an API.&lt;/p&gt;
&lt;p&gt;It’s a bit of a poor man’s
&lt;a href=&#34;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&#34;&gt;MVC&lt;/a&gt;
but it works. That’s basically the core of what a framework like
&lt;a href=&#34;https://metacpan.org/pod/Dancer2&#34;&gt;Dancer&lt;/a&gt; does.&lt;/p&gt;
&lt;h3 id=&#34;dancer-1--2&#34;&gt;Dancer (1 &amp;amp; 2)&lt;/h3&gt;
&lt;p&gt;Dancer is basically Ruby’s
&lt;a href=&#34;https://github.com/sinatra/sinatra&#34;&gt;Sinatra&lt;/a&gt; ported to Perl. As
already mentioned, ideas developed in other languages and frameworks
are often ported to Perl, and this is no exception.&lt;/p&gt;
&lt;p&gt;Let’s see it in an action:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Dancer2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;get &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Marco&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello $name\n&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;start;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Start the script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Dancer2 v0.400000 server 22969 listening on http://0.0.0.0:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Try it with &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl -D - http://0.0.0.0:3000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.0 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: Mon, 11 Apr 2022 07:22:18 GMT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server: Perl Dancer2 0.400000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server: Perl Dancer2 0.400000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: text/html; charset=UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello Marco&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If in the script you say &lt;code&gt;use Dancer;&lt;/code&gt; instead of &lt;code&gt;use Dancer2&lt;/code&gt;, you get:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl -D - http://0.0.0.0:3000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HTTP/1.0 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server: Perl Dancer 1.3513
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Length: 12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type: text/html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;X-Powered-By: Perl Dancer 1.3513
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello Marco&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dancer’s core doesn’t do much more than routing. And you’ll also
notice that the syntax is very similar to Mojolicious::Lite. So to
get something done you need to start installing plugins which will
provide the needed glue to interact with a database, work with your
template system of choice, and more.&lt;/p&gt;
&lt;p&gt;Today you would wonder why you should use Dancer and not Mojolicious,
but when Dancer was at the peak of its popularity the games were still
open. There were plenty of plugins being written and published on CPAN.&lt;/p&gt;
&lt;p&gt;Around 2013 Dancer’s development team decided to rewrite it to make
it better. The problem was that plugins and templates needed to be
adapted as well. I’m under the impression that the energy got divided
and the momentum was lost. Now there are two codebases and two
plugin namespaces which do basically the same thing, because for
the end user there is not much difference.&lt;/p&gt;
&lt;h3 id=&#34;catalyst&#34;&gt;Catalyst&lt;/h3&gt;
&lt;p&gt;So what was attracting people to Dancer? When Dancer came out, Perl
had a great MVC framework, which is still around,
&lt;a href=&#34;http://catalyst.perl.org/&#34;&gt;Catalyst&lt;/a&gt;. (And note that the main
Mojolicious developer was on the Catalyst team.)&lt;/p&gt;
&lt;p&gt;Now, the problem is that to get started with Catalyst, even if it has
plenty of documentation, you need to be already acquainted with a lot
of concepts and technologies. For example, the
&lt;a href=&#34;https://metacpan.org/dist/Catalyst-Manual/view/lib/Catalyst/Manual/Tutorial/03_MoreCatalystBasics.pod&#34;&gt;tutorial&lt;/a&gt;
starts to talk about
&lt;a href=&#34;http://www.template-toolkit.org/&#34;&gt;Template Toolkit&lt;/a&gt; and
the &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; ORM very early.&lt;/p&gt;
&lt;p&gt;These two modules are great and powerful and they deserve to be
studied, but for someone new to modern web development, or even to
Perl, it feels (and actually is) overwhelming.&lt;/p&gt;
&lt;p&gt;So, why would you choose Catalyst today? Catalyst has the stability
which Mojo, at least at the beginning, lacked, while
backward compatibility is a priority for Catalyst. The other way to look
at this is that Catalyst doesn’t see much current
&lt;a href=&#34;https://metacpan.org/dist/Catalyst-Runtime/changes&#34;&gt;development&lt;/a&gt;, but
someone could see this as a feature.&lt;/p&gt;
&lt;p&gt;Even if Catalyst predates all the hyper-modern features that Mojo has,
it’s still a modern framework, and a good one. I can’t show you a self
contained script (you need a tree of files), but I’d like to show you
what makes it very nice and powerful:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Controller::Root&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Moose&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;namespace::autoclean&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BEGIN&lt;/span&gt; { extends &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Catalyst::Controller&amp;#39;&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# start the chain with /foo/XX&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;foo&lt;/span&gt; :Chained(&amp;#39;/&amp;#39;) CaptureArgs(1) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$arg&lt;/span&gt;) = &lt;span style=&#34;color:#369&#34;&gt;@_&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;stash(name =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;$arg&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# /foo/XX/bar/YY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;bar&lt;/span&gt; :Chained(&amp;#39;foo&amp;#39;) Args(1) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$arg&lt;/span&gt;) = &lt;span style=&#34;color:#369&#34;&gt;@_&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;detach(&lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;view(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;JSON&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# /foo/XX/another/YY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;another&lt;/span&gt; :Chained(&amp;#39;foo&amp;#39;) Args(1) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$arg&lt;/span&gt;) = &lt;span style=&#34;color:#369&#34;&gt;@_&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;detach(&lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;view(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;HTML&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, if you hit &lt;code&gt;/foo/marco/bar/test&lt;/code&gt; the second path fragment will be
processed by the first method (&lt;code&gt;CaptureArgs(1)&lt;/code&gt;) and saved in the
stash. Then the second &lt;code&gt;bar&lt;/code&gt; method will be chained to it and the
&lt;code&gt;name&lt;/code&gt; will be available in the stash. The last method will be hit
with &lt;code&gt;/foo/marco/another/test2&lt;/code&gt;. (Incidentally, please note that
Mojolicious has
&lt;a href=&#34;https://docs.mojolicious.org/Mojolicious/Guides/Routing#Nested-routes&#34;&gt;nested&lt;/a&gt;
routes as well.)&lt;/p&gt;
&lt;p&gt;Now, I think it’s clear that in this way you can build deep hierarchies
of paths with reusable components. This works really great with the
DBIx::Class ORM, where you can
chain queries as well. As you can imagine, this is far from a simple
setup. On the contrary, this is an advanced setup for people who
already know their way around web frameworks.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So, to sum up this excursion in the amazing land of Perl web
frameworks: If you build something from scratch, go with Mojolicious.
It’s your best bet. If nothing else, it’s super-easy to install, with
basically no dependencies.&lt;/p&gt;
&lt;p&gt;However, there’s no need to make a religion
out of it. Rewriting code without a clear gain is a waste of time and
money. A good developer should still be able to write maintainable
code with the existing tools.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Full-text search on a budget: Xapian</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/08/full-text-search-xapian/"/>
      <id>https://www.endpointdev.com/blog/2021/08/full-text-search-xapian/</id>
      <published>2021-08-19T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/08/full-text-search-xapian/looking-glass.jpg&#34; alt=&#34;Mounted telescope monocular pointing through a wire fence&#34;&gt;&lt;/p&gt;
&lt;!-- Photo from Pixabay: https://pixabay.com/photos/looking-glass-magnification-glass-919017/ --&gt;
&lt;p&gt;Over the years I’ve seen and implemented different full-text search
applications using various technologies: plain SQL,
&lt;a href=&#34;https://www.postgresql.org/docs/13/textsearch.html&#34;&gt;PostgreSQL&lt;/a&gt;,
&lt;a href=&#34;https://www.elastic.co/elasticsearch/&#34;&gt;Elasticsearch&lt;/a&gt;,
&lt;a href=&#34;https://solr.apache.org/&#34;&gt;Solr&lt;/a&gt;, and most recently
&lt;a href=&#34;https://xapian.org/&#34;&gt;Xapian&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While Solr and Elasticsearch are very well known, Xapian, despite the fact
that it’s available and packaged in all the major GNU/Linux
distributions, doesn’t seem to be so popular, at least not among project
managers.&lt;/p&gt;
&lt;p&gt;But Xapian is fast, advanced, can be configured to do faceted searches
(so the user can filter the search results), and my favorite, is fast
to build and has virtually no maintenance overhead.&lt;/p&gt;
&lt;p&gt;Its main feature is that it’s not a stand-alone application, like Solr
or Elasticsearch, but instead it’s a library written in C++ which has
bindings for all the major languages (as advertised on its
&lt;a href=&#34;https://xapian.org/&#34;&gt;homepage&lt;/a&gt;). It has also great
&lt;a href=&#34;https://github.com/xapian/xapian-docsprint&#34;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, being in the e-commerce business, my typical use-case is that the
client’s shop needs something faster and better than a search using a
SQL query against the products table. And beware, even implementing a
non-trivial SQL-based search can burn more hours than setting up
Xapian.&lt;/p&gt;
&lt;p&gt;With Xapian you can prototype very quickly, without losing hours
wading through obscure options, setting up services and configuring
firewalls. And yet, the prototype will allow you to build more
advanced features once you need them.&lt;/p&gt;
&lt;p&gt;I’m a Perl guy, so I will show you some Perl code, but the procedure
is the same for the other languages. Even the
documentation can be built specifically for your language!&lt;/p&gt;
&lt;p&gt;Typically, to add a search engine to your site you need two pieces: an
indexer to which you feed the data (from static files or databases or
even fetching remote pages or whatever you need) and the search itself
in the site.&lt;/p&gt;
&lt;p&gt;Both the indexer and the search code need to load the Xapian library
and point to the same Xapian database, which is usually a directory
(or a file pointing to a directory).&lt;/p&gt;
&lt;h3 id=&#34;indexing&#34;&gt;Indexing&lt;/h3&gt;
&lt;p&gt;Now, stripped down to the minimum, this is what a typical indexer&amp;rsquo;s code
looks like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian&lt;/span&gt; (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;:all&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;JSON&lt;/span&gt; &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;qw/encode_json/&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dblocation&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;xapiandb&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$xapian&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::WritableDatabase&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$dblocation&lt;/span&gt;, DB_CREATE_OR_OPEN);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$indexer&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::TermGenerator&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$indexer&lt;/span&gt;-&amp;gt;set_database(&lt;span style=&#34;color:#369&#34;&gt;$xapian&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$indexer&lt;/span&gt;-&amp;gt;set_stemmer(&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::Stem&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;english&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;@entries&lt;/span&gt; = ({ uri =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/blog/1&amp;#39;&lt;/span&gt;, title =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;T1&amp;#39;&lt;/span&gt;, text =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Marco loves pizza&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               { uri =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/blog/2&amp;#39;&lt;/span&gt;, title =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;T2&amp;#39;&lt;/span&gt;, text =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;They love chapati&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;@entries&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$doc&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::Document&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$qterm&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;-&amp;gt;{uri};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$doc&lt;/span&gt;-&amp;gt;add_term(&lt;span style=&#34;color:#369&#34;&gt;$qterm&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$doc&lt;/span&gt;-&amp;gt;set_data(encode_json({ uri =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;-&amp;gt;{uri}, title =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;-&amp;gt;{title} }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$indexer&lt;/span&gt;-&amp;gt;set_document(&lt;span style=&#34;color:#369&#34;&gt;$doc&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$indexer&lt;/span&gt;-&amp;gt;index_text(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;-&amp;gt;{text});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$xapian&lt;/span&gt;-&amp;gt;replace_document_by_term(&lt;span style=&#34;color:#369&#34;&gt;$qterm&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$doc&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code will create a &lt;code&gt;xapiandb&lt;/code&gt; directory with the Xapian database,
indexing the blog posts in the &lt;code&gt;@entries&lt;/code&gt; array. In a real script,
they would come from the database.&lt;/p&gt;
&lt;p&gt;Still, there are a couple of things worth noting in this minimal code.&lt;/p&gt;
&lt;p&gt;We set the &lt;a href=&#34;https://en.wikipedia.org/wiki/Stemming&#34;&gt;stemmer&lt;/a&gt; for the
given language, so the text passed to the indexer via the &lt;code&gt;index_text&lt;/code&gt;
call is parsed.&lt;/p&gt;
&lt;p&gt;Then we store the data structure we want to retrieve later with
&lt;code&gt;set_data&lt;/code&gt;. The best thing to do is probably to serialize it with
JSON, in this case I’m excluding the full text, which we don’t need in
the output (but it would be wise to add a teaser).&lt;/p&gt;
&lt;p&gt;Also, we use a &lt;code&gt;Q&lt;/code&gt;
&lt;a href=&#34;https://xapian.org/docs/omega/termprefixes.html&#34;&gt;prefix&lt;/a&gt; to produce
an unique term to update the entry when it already exists.&lt;/p&gt;
&lt;p&gt;Of course the indexer will need to grow if you need more power and
more structured data (like filtering or searching a specific field),
but at this point we want just to show something to our hypothetical
client.&lt;/p&gt;
&lt;p&gt;The database can be inspected very easily. Xapian comes with a tool
called &lt;code&gt;delve&lt;/code&gt; (or &lt;code&gt;xapian-delve&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ xapian-delve xapiandb -a -v -1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;All terms in database (termfreq):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Q/blog/1 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Q/blog/2 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zchapati 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zlove 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zmarco 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zpizza 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zthey 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chapati 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;love 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;loves 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;marco 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pizza 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;they 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And you can also try a search from the command line with &lt;code&gt;quest&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ quest -d xapiandb &amp;#34;loves NOT chapati&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Parsed Query: Query((Zlove@1 AND_NOT Zchapati@2))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Exactly 1 matches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MSet:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1: [0.0953102]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&amp;#34;title&amp;#34;:&amp;#34;T1&amp;#34;,&amp;#34;uri&amp;#34;:&amp;#34;/blog/1&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ quest -d xapiandb &amp;#34;pizza OR chapati&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Parsed Query: Query((Zpizza@1 OR Zchapati@2))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Exactly 2 matches
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MSet:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1: [0.405465]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&amp;#34;title&amp;#34;:&amp;#34;T1&amp;#34;,&amp;#34;uri&amp;#34;:&amp;#34;/blog/1&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2: [0.405465]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&amp;#34;uri&amp;#34;:&amp;#34;/blog/2&amp;#34;,&amp;#34;title&amp;#34;:&amp;#34;T2&amp;#34;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the example above shows, it should be clear that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;the search works as you would expect (with logical operators) out of
the box&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the stemming works, searching for &amp;ldquo;loves&amp;rdquo; and &amp;ldquo;love&amp;rdquo; is the same.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the results give us back the JSON we stored in the index.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;searching&#34;&gt;Searching&lt;/h3&gt;
&lt;p&gt;So let’s call it done and move to the next part, the searcher.&lt;/p&gt;
&lt;p&gt;Now, while the indexer is a single script, the search needs to be
plugged into the live code of your site. For the purposes of this
article, I will provide a script instead, which does basically the
same thing as &lt;code&gt;quest&lt;/code&gt;. Plugging it into the web application is left as
an exercise for the reader. I would also suggest to put both the
indexing and searching code in a single shared module, keeping the
logic in a single location.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;:all&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;JSON&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$cgi&lt;/span&gt;) = &lt;span style=&#34;color:#038&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;@ARGV&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$dblocation&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;xapiandb&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$database&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::Database&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$dblocation&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$enquire&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::Enquire&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$database&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::QueryParser&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt;-&amp;gt;set_database(&lt;span style=&#34;color:#369&#34;&gt;$database&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt;-&amp;gt;set_stemmer(&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Search::Xapian::Stem&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;english&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt;-&amp;gt;set_stemming_strategy(STEM_SOME);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt;-&amp;gt;set_default_op(OP_AND);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$query&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$qp&lt;/span&gt;-&amp;gt;parse_query(&lt;span style=&#34;color:#369&#34;&gt;$cgi&lt;/span&gt;, FLAG_PHRASE|FLAG_BOOLEAN|FLAG_WILDCARD);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$enquire&lt;/span&gt;-&amp;gt;set_query(&lt;span style=&#34;color:#369&#34;&gt;$query&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# fetch the first 50 results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$mset&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$enquire&lt;/span&gt;-&amp;gt;get_mset(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;50&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Total results: &amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$mset&lt;/span&gt;-&amp;gt;get_matches_estimated . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$json_pretty&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;JSON&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;-&amp;gt;pretty(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)-&amp;gt;utf8(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;)-&amp;gt;canonical(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$m&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$mset&lt;/span&gt;-&amp;gt;items) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; = decode_json(&lt;span style=&#34;color:#369&#34;&gt;$m&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;get_document&lt;/span&gt;-&amp;gt;get_data);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# decode and reencode the json in a human-readable fashion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$json_pretty&lt;/span&gt;-&amp;gt;encode(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’re wondering what those constants are and where to look for
more, they are in the module’s
&lt;a href=&#34;https://metacpan.org/pod/Search::Xapian#EXPORT&#34;&gt;documentation&lt;/a&gt;, in
plain sight (we asked for them when loading the module with the &lt;code&gt;:all&lt;/code&gt;
argument).&lt;/p&gt;
&lt;p&gt;Most of the code shown here is boilerplate, but that could change once
you build up. Notably we set the stemmer for the current language and
the query parser options, so we can use wildcard (e.g. &lt;code&gt;piz*&lt;/code&gt;), the
&lt;code&gt;AND&lt;/code&gt;/&lt;code&gt;OR&lt;/code&gt; operators, and quoting.&lt;/p&gt;
&lt;p&gt;Let’s see the script in action.&lt;/p&gt;
&lt;p&gt;Wildcard:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;piz&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;piz*&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;title&amp;#34; : &amp;#34;T1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;uri&amp;#34; : &amp;#34;/blog/1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Operators:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;pizza OR chapati&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;title&amp;#34; : &amp;#34;T1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;uri&amp;#34; : &amp;#34;/blog/1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;title&amp;#34; : &amp;#34;T2&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;uri&amp;#34; : &amp;#34;/blog/2&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;pizza AND chapati&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Quoting (beware here the double quotes to escape the shell):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;&amp;#34;loves chapati&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./search.pl &amp;#39;&amp;#34;love chapati&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Total results: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;title&amp;#34; : &amp;#34;T2&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &amp;#34;uri&amp;#34; : &amp;#34;/blog/2&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The whole thing already looks pretty good. Way better (and way faster
to code and to execute) than a home-baked SQL search.&lt;/p&gt;
&lt;p&gt;As already noted, this is just scratching the surface. Xapian can
do &lt;a href=&#34;https://getting-started-with-xapian.readthedocs.io/en/latest/howtos/index.html&#34;&gt;much more&lt;/a&gt;:
filtering, range queries, facets, sorting, even spelling corrections!&lt;/p&gt;
&lt;p&gt;I don’t doubt that Solr &amp;amp; co. have their use-cases, but for the common
scenario of a small/​mid-sized e-shop or site, I think that this
solution is more affordable and maintainable than having a whole
separate application (like a Solr server) to maintain, upgrade and
secure. Don’t forget that here we haven’t done a single HTTP request.
We didn’t have to manage daemons, opening/​closing ports, and the like. We
didn’t have to configure a schema and a tokenizer in a separate
application (and keep that aligned with the handling code). It’s all
there in our (Perl) code in two files (as already noted, the logic
should live in a single module).&lt;/p&gt;
&lt;p&gt;We just installed a library (there is a very good chance that it’s already
installed) and a Perl module.&lt;/p&gt;
&lt;p&gt;The Xapian database lives on the disk and your code has full control
over it. Also it’s normally your GNU/Linux distribution taking care of
the security upgrades.&lt;/p&gt;
&lt;p&gt;If your client is on a budget, building a full-text search Xapian can
be the right choice, and you can scale it up on the go, as more
features are required.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Regular Expression Inconsistencies With Unicode</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/01/regular-expression-inconsistencies-with-unicode/"/>
      <id>https://www.endpointdev.com/blog/2018/01/regular-expression-inconsistencies-with-unicode/</id>
      <published>2018-01-23T00:00:00+00:00</published>
      <author>
        <name>Phineas Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/01/regular-expression-inconsistencies-with-unicode/mud-run.jpg&#34; alt=&#34;A mud run&#34;&gt;&lt;br/&gt;
&lt;small&gt;A casual stroll through the world of Unicode and regular expressions—​&lt;a href=&#34;https://www.flickr.com/photos/presidioofmonterey/7025086135&#34;&gt;Photo&lt;/a&gt; by Presidio of Monterey&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Character classes in regular expressions are an extremely useful and widespread feature, but there are some relatively recent changes that you might not know of.&lt;/p&gt;
&lt;p&gt;The issue stems from how different programming languages, locales, and character encodings treat predefined character classes. Take, for example, the expression &lt;code&gt;\w&lt;/code&gt; which was introduced in Perl around the year 1990 (along with &lt;code&gt;\d&lt;/code&gt; and &lt;code&gt;\s&lt;/code&gt; and their inverted sets &lt;code&gt;\W&lt;/code&gt;, &lt;code&gt;\D&lt;/code&gt;, and &lt;code&gt;\S&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;\w&lt;/code&gt; shorthand is a character class that matches “word characters” as the C language understands them: &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;. At least when ASCII was the main player in the character encoding scene that simple fact was true. With the standardization of Unicode and UTF-8, the meaning of &lt;code&gt;\w&lt;/code&gt; has become a more foggy.&lt;/p&gt;
&lt;h4 id=&#34;perl&#34;&gt;Perl&lt;/h4&gt;
&lt;p&gt;Take this example in a recent Perl version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5.012&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# use 5.012 or higher includes Unicode support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# necessary for Unicode string literals&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt; =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;  =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Perl is treating &lt;code&gt;\w&lt;/code&gt; differently here because the characters “اسم” (“ism” meaning “name” in Arabic) definitely don’t fall within &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Beginning with Perl 5.12 from the year 2010, character classes are handled differently. Documentation on the topic is found in &lt;a href=&#34;https://perldoc.perl.org/perlrecharclass.html#Backslash-sequences&#34;&gt;perlrecharclass&lt;/a&gt;. The rules aren’t as simple as with some languages, but can be generalized as such:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\w&lt;/code&gt; will match Unicode characters with the “Word” property (equivalent to &lt;code&gt;\p{Word}&lt;/code&gt;), unless the &lt;code&gt;/a&lt;/code&gt; (ASCII) flag is enabled, in which case it will be equivalent to the original &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s see the &lt;code&gt;/a&lt;/code&gt; flag in action.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5.012&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt; =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;a; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;  =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;a; &lt;span style=&#34;color:#888&#34;&gt;# 0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, you should know that for code points below 256, these rules can change depending on whether Unicode or locale rules are on, so if you’re unsure, consult the &lt;a href=&#34;https://perldoc.perl.org/perlre.html&#34;&gt;perlre&lt;/a&gt; and &lt;a href=&#34;https://perldoc.perl.org/perlrecharclass.html&#34;&gt;perlrecharclass&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Keep in mind that these same questions of what the character classes include can apply to every predefined character class in whatever language you’re using, so remember to check language-specific implementations for other character class shorthands, such as &lt;code&gt;\s&lt;/code&gt; and &lt;code&gt;\d&lt;/code&gt;, not just &lt;code&gt;\w&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every language seems to do regular expressions a little bit differently, so here’s a short, incomplete guide for several other languages we use frequently.&lt;/p&gt;
&lt;h4 id=&#34;python&#34;&gt;Python&lt;/h4&gt;
&lt;p&gt;Take this example in Python 3.6.2:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;7&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python is also treating &lt;code&gt;\w&lt;/code&gt; differently here. Let’s take a look at &lt;a href=&#34;https://docs.python.org/3/library/re.html#regular-expression-syntax&#34;&gt;the Python docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;\w&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For Unicode (str) patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Matches Unicode word characters; this includes most characters that can be part of a word in any language, as well as numbers and the underscore. If the ASCII flag is used, only [a-zA-Z0-9_] is matched (but the flag affects the entire regular expression, so in such cases using an explicit [a-zA-Z0-9_] may be a better choice).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For 8-bit (bytes) patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Matches characters considered alphanumeric in the ASCII character set; this is equivalent to [a-zA-Z0-9_]. If the LOCALE flag is used, matches characters considered alphanumeric in the current locale and the underscore.
&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;So &lt;code&gt;\w&lt;/code&gt; includes “most characters that can be part of a word in any language, as well as numbers and the underscore”. A list of the characters that includes is difficult to pin down, so it would be best to use the &lt;code&gt;re.ASCII&lt;/code&gt; flag as suggested when you’re unsure if you want letters from other languages matched:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;,  flags=re.ASCII)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, flags=re.ASCII)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;ruby&#34;&gt;Ruby&lt;/h4&gt;
&lt;p&gt;Ruby’s &lt;a href=&#34;https://ruby-doc.org/core-2.5.0/Regexp.html#class-Regexp-label-Character+Classes&#34;&gt;Regexp class&lt;/a&gt; documentation gives a simple and useful explanation: backslash character classes (e.g. &lt;code&gt;\w&lt;/code&gt;, &lt;code&gt;\s&lt;/code&gt;, &lt;code&gt;\d&lt;/code&gt;) are ASCII-only, while POSIX-style bracket expressions (e.g. &lt;code&gt;[[:alnum:]]&lt;/code&gt;) include other Unicode characters.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;001&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;         =~ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;002&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^[[:word:]]+$/&lt;/span&gt; =~ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;javascript&#34;&gt;JavaScript&lt;/h4&gt;
&lt;p&gt;JavaScript doesn’t support POSIX-style bracket expressions, and its backslash character classes are simple, straightforward lists of ASCII characters. The &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Using_special_characters&#34;&gt;MDN&lt;/a&gt; has simple explanations for each one.&lt;/p&gt;
&lt;p&gt;JavaScript regular expressions do accept a &lt;code&gt;/u&lt;/code&gt; flag, but it does not affect shorthand character classes. Consider these examples in Node.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userﺎﺴﻣ&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userﺎﺴﻣ&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can see that the &lt;code&gt;/u&lt;/code&gt; flag has no effect on what &lt;code&gt;\w&lt;/code&gt; matches. Now let’s look at Unicode character lengths in JavaScript:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;❤&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;👩&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;🀄️&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because of the way Unicode is implemented in JavaScript, strings with Unicode characters outside the BMP (Basic Multilingual Plane) will appear to be longer than they are.&lt;/p&gt;
&lt;p&gt;This can be accounted for in regular expressions with the &lt;code&gt;/u&lt;/code&gt; flag, which only corrects character parsing, and does not affect shorthand character classes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; mystr = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hi👩there&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;undefined&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; mystr.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi.there/&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi.there/u&lt;/span&gt;.test(mystr);  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; note the /u from here on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/u&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hi👩👩there&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The excellent article &lt;a href=&#34;http://blog.jonnew.com/posts/poo-dot-length-equals-two&#34;&gt;&amp;quot;💩&amp;quot;.length === 2&lt;/a&gt; by Jonathan New goes into detail about the why this is, and explores various solutions. It also addresses some legacy inconsistencies, like how the old HEAVY BLACK HEART character and other older Unicode symbols might be represented differently.&lt;/p&gt;
&lt;h4 id=&#34;php&#34;&gt;PHP&lt;/h4&gt;
&lt;p&gt;PHP’s documentation explains that &lt;code&gt;\w&lt;/code&gt; matches letters, digits, and the underscore as defined by your locale. It’s not totally clear about how Unicode is treated, but it uses the PCRE (Perl Compatible Regular Expressions) library which supports a &lt;code&gt;/u&lt;/code&gt; flag that can be used to enable Unicode matching in character classes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;),  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/u&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/u&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;),  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;net&#34;&gt;.NET&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions&#34;&gt;.NET Quick Reference&lt;/a&gt; has a comprehensive guide to character classes. For word characters, it defines a specific group of Unicode categories including letters, modifiers, and connectors from many languages, but also points out that setting the &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options#ECMAScript&#34;&gt;ECMAScript Matching Behavior&lt;/a&gt; option will limit &lt;code&gt;\w&lt;/code&gt; to &lt;code&gt;[a-zA-Z_0-9]&lt;/code&gt;, among other things. Microsoft’s documentation is clear and comprehensive with great examples, so I recommend referring to it frequently.&lt;/p&gt;
&lt;h4 id=&#34;go&#34;&gt;Go&lt;/h4&gt;
&lt;p&gt;Go follows the regular expression syntax used by &lt;a href=&#34;https://github.com/google/re2/wiki/Syntax&#34;&gt;Google’s RE2 engine&lt;/a&gt;, which has easy syntax for specifying whether you want Unicode characters to be captured or not:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;main&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;regexp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;func&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;main&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Perl-style&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\w+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\w+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// POSIX-style&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^[[:word:]]+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^[[:word:]]+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Unicode character class&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\pL+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\pL+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see this code in action &lt;a href=&#34;https://play.golang.org/p/Y0HEhWXgXYa&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;grep&#34;&gt;grep&lt;/h4&gt;
&lt;p&gt;Implementations of grep vary widely across platforms and versions. On my personal computer with GNU grep 3.1, &lt;code&gt;\w&lt;/code&gt; doesn&amp;rsquo;t work at all with default settings, matches only ASCII characters with the &lt;code&gt;-P&lt;/code&gt; (PCRE) option, and matches Unicode characters with &lt;code&gt;-E&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)  &lt;span style=&#34;color:#888&#34;&gt;# no match&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -P &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -P &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;)   &lt;span style=&#34;color:#888&#34;&gt;# no match&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -E &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -E &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;userاسم&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, implementations vary a lot, so double check on your system before doing anything important.&lt;/p&gt;
&lt;h3 id=&#34;other-links&#34;&gt;Other links&lt;/h3&gt;
&lt;p&gt;As great as Unicode and regular expressions are, their implementations vary widely across various languages and tools, and that introduces far more unexpected behavior than I can write about in this post. Whenever you&amp;rsquo;re going to use something with Unicode and regular expressions, make sure to check language specifications to make sure everything will work as expected.&lt;/p&gt;
&lt;p&gt;Of course, this topic has already been discussed and written about at great length. Here are some links worth checking out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/&#34;&gt;The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)&lt;/a&gt; - This is an oft-referenced article by Joel Spolsky. It was written in 2003 but the wealth of valuable information within is still very relevant and it helps greatly in going from Unicode noob to having a comfortable, useful knowledge of many common issues.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mathiasbynens.be/notes/es-regexp-proposals&#34;&gt;ECMAScript regular expressions are getting better!&lt;/a&gt; - This article by a V8 developer at Google shows some nice JavaScript regular expression improvements planned for ES2018, including Unicode property escapes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/LuminosoInsight/python-ftfy&#34;&gt;ftfy for Python&lt;/a&gt; - ftfy is a Python library that takes corrupt Unicode text and attempts to fix it as best it can. I haven’t yet had a chance to use it, but the examples are compelling and it’s definitely worth knowing about.&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Postgres migrating SQL_ASCII to UTF-8 with fix_latin</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/07/postgres-migrating-sqlascii-to-utf-8/"/>
      <id>https://www.endpointdev.com/blog/2017/07/postgres-migrating-sqlascii-to-utf-8/</id>
      <published>2017-07-21T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/07/postgres-migrating-sqlascii-to-utf-8/image-0.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;395&#34; data-original-width=&#34;500&#34; src=&#34;/blog/2017/07/postgres-migrating-sqlascii-to-utf-8/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;(&lt;a href=&#34;https://flic.kr/p/fZRp6G&#34;&gt;photograph&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/people/usoceangov/&#34;&gt;NOAA National Ocean Service&lt;/a&gt;)&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;Upgrading &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;Postgres&lt;/a&gt; is not quite as painful as it used to be, thanks
primarily to the &lt;a href=&#34;https://www.postgresql.org/docs/current/static/pgupgrade.html&#34;&gt;pg_upgrade program&lt;/a&gt;, but there are times when it simply cannot be used.
We recently had an existing End Point client come to us requesting help upgrading from their current
Postgres database (version 9.2) to the latest version (9.6—​but soon to be 10). They also wanted
to finally move away from their SQL_ASCII encoding to &lt;a href=&#34;https://en.wikipedia.org/wiki/UTF-8&#34;&gt;UTF-8&lt;/a&gt;. As this meant
that pg_upgrade could not be used, we also took the opportunity to enable
checksums as well (this change cannot be done via pg_upgrade). Finally, they
were moving their database server to new hardware. There were many lessons learned and bumps
along the way for this migration, but for this
post I’d like to focus on one of the most vexing problems, the database encoding.&lt;/p&gt;
&lt;p&gt;When a Postgres database is created, it is set to a specific encoding. The most
common one (and the default) is “UTF8”.  This covers
99% of all user’s needs. The second most common one is the
poorly-named “SQL_ASCII” encoding, which should be named
“DANGER_DO_NOT_USE_THIS_ENCODING”, because it causes nothing but trouble.
The SQL_ASCII encoding basically means no encoding at all, and simply stores
any bytes you throw at it. This usually means the database ends up containing a
whole mess of different encodings, creating a “byte soup” that will be
difficult to sanitize by moving to a real encoding (i.e. UTF-8).&lt;/p&gt;
&lt;p&gt;Many tools exist which convert text from one encoding to another. One of the
most popular ones on Unix boxes is “iconv”. Although this program works great
if your source text is using one encoding, it fails when it encounters
byte soup.&lt;/p&gt;
&lt;p&gt;For this migration, we first did a &lt;a href=&#34;https://www.postgresql.org/docs/current/static/app-pgdump.html&#34;&gt;pg_dump&lt;/a&gt; from the old database to
a newly created UTF-8 test database, just to see which tables had encoding problems.
Quite a few did—​but not all of them!—​so we wrote a script to import tables
in parallel, with some filtering for the problem ones. As mentioned above,
iconv was not particularly helpful: looking at the tables closely showed
evidence of many different encodings in each one: Windows-1252, ISO-8859-1,  Japanese,
Greek, and many others. There were even large bits that were plainly
binary data (e.g. images) that simply got shoved into a text field somehow.
This is the big problem with SQL_ASCII: it accepts &lt;em&gt;everything&lt;/em&gt;, and does no
validation whatsoever. The iconv program simply could not handle these tables,
even when adding the //IGNORE option.&lt;/p&gt;
&lt;p&gt;To better explain the problem and the solution, let’s create a small text
file with a jumble of encodings. Discussions of how UTF-8 represents
characters, and its interactions with Unicode, are avoided here, as
Unicode is a dense, complex subject, and this article is dry enough already. :)&lt;/p&gt;
&lt;p&gt;First, we want to add some items using the encodings ‘Windows-1252’ and ‘Latin-1’. These encoding
systems were attempts to extend the basic ASCII character set to include more characters. As these encodings
pre-date the invention of UTF-8, they do it in a very inelegant (and incompatible)
way. Use of the “echo” command is a great way to add arbitrary bytes to a file as it
allows direct hex input:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo -e &amp;#34;[Windows-1252]   Euro: \x80   Double dagger: \x87&amp;#34; &amp;gt; sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo -e &amp;#34;[Latin-1]   Yen: \xa5   Half: \xbd&amp;#34; &amp;gt;&amp;gt; sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo -e &amp;#34;[Japanese]   Ship: \xe8\x88\xb9&amp;#34; &amp;gt;&amp;gt; sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo -e &amp;#34;[Invalid UTF-8]  Blob: \xf4\xa5\xa3\xa5&amp;#34; &amp;gt;&amp;gt; sample.data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This file looks ugly. Notice all the “wrong” characters when we simply view the file directly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cat sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Windows-1252]   Euro: �   Double dagger: �
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Latin-1]   Yen: �   Half: �
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Japanese]   Ship: 船
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Invalid UTF-8]  Blob: ����&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running iconv is of little help:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## With no source encoding given, it errors on the Euro:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ iconv -t utf8 sample.data &amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iconv: illegal input sequence at position 23
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## We can tell it to ignore those errors, but it still barfs on the blob:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ iconv -t utf8//ignore sample.data &amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iconv: illegal input sequence at position 123
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Telling it the source is Window-1252 fixes some things, but still sinks the Ship:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ iconv -f windows-1252 -t utf8//ignore sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Windows-1252]   Euro: €   Double dagger: ‡
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Latin-1]   Yen: ¥   Half: ½
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Japanese]   Ship: èˆ¹
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Invalid UTF-8]  Blob: ô¥£¥&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After testing a few other tools, we discovered the nifty &lt;a href=&#34;https://metacpan.org/pod/Encoding::FixLatin&#34;&gt;Encoding::FixLatin&lt;/a&gt;, a Perl module which provides a command-line program called “fix_latin”. Rather than being authoritative like iconv, it tries its best to fix things up with educated guesses. Its documentation gives a good summary:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The script acts as a filter, taking source data which may contain a mix of
ASCII, UTF8, ISO8859-1 and CP1252 characters, and producing output will be
all ASCII/UTF8.&lt;/p&gt;
&lt;p&gt;Multi-byte UTF8 characters will be passed through unchanged (although
over-long UTF8 byte sequences will be converted to the shortest normal
form). Single byte characters will be converted as follows:&lt;/p&gt;
&lt;p&gt;0x00 - 0x7F   ASCII - passed through unchanged
0x80 - 0x9F   Converted to UTF8 using CP1252 mappings
0xA0 - 0xFF   Converted to UTF8 using Latin-1 mappings&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;While this works great for fixing the Windows-1252 and Latin-1 problems (and
thus accounted for at least 95% of our table’s bad encodings), it still allows
“invalid” UTF-8 to pass on through. Which means that Postgres will still refuse
to accept it. Let’s check our test file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ fix_latin sample.data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Windows-1252]   Euro: €   Double dagger: ‡
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Latin-1]   Yen: ¥   Half: ½
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Japanese]   Ship: 船
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Invalid UTF-8]  Blob: ����
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Postgres will refuse to import that last part:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#34;SELECT E&amp;#39;&amp;#34;  &amp;#34;$(fix_latin sample.data)&amp;#34;  &amp;#34;&amp;#39;;&amp;#34; | psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  invalid byte sequence for encoding &amp;#34;UTF8&amp;#34;: 0xf4 0xa5 0xa3 0xa5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Even adding iconv is of no help:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#34;SELECT E&amp;#39;&amp;#34;  &amp;#34;$(fix_latin sample.data | iconv -t utf-8)&amp;#34;  &amp;#34;&amp;#39;;&amp;#34; | psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  invalid byte sequence for encoding &amp;#34;UTF8&amp;#34;: 0xf4 0xa5 0xa3 0xa5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href=&#34;https://tools.ietf.org/html/rfc3629&#34;&gt;UTF-8 specification&lt;/a&gt; is rather dense and puts many requirements on
encoders and decoders. How well programs implement these requirements (and optional
bits) varies, of course. But at the end of the day, we needed that data to go
into a UTF-8 encoded Postgres database without complaint. When in doubt, go
to the source! The relevant file in the Postgres source code responsible for
rejecting bad UTF-8 (as in the examples above) is src/backend/utils/mb/wchar.c
Analyzing that file shows a small but elegant piece of code whose job is
to ensure only “legal” UTF-8 is accepted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bool
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pg_utf8_islegal(const unsigned char *source, int length)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  unsigned char a;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  switch (length)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    default:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      /* reject lengths 5 and 6 for now */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case 4:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      a = source[3];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      if (a &amp;lt; 0x80 || a &amp;gt; 0xBF)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      /* FALL THRU */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case 3:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      a = source[2];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      if (a &amp;lt; 0x80 || a &amp;gt; 0xBF)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      /* FALL THRU */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case 2:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      a = source[1];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      switch (*source)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        case 0xE0:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          if (a &amp;lt; 0xA0 || a &amp;gt; 0xBF)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        case 0xED:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          if (a &amp;lt; 0x80 || a &amp;gt; 0x9F)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        case 0xF0:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          if (a &amp;lt; 0x90 || a &amp;gt; 0xBF)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        case 0xF4:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          if (a &amp;lt; 0x80 || a &amp;gt; 0x8F)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          if (a &amp;lt; 0x80 || a &amp;gt; 0xBF)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      /* FALL THRU */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case 1:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      a = *source;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      if (a &amp;gt;= 0x80 &amp;amp;&amp;amp; a &amp;lt; 0xC2)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      if (a &amp;gt; 0xF4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return false;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      break;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  return true;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that we know the UTF-8 rules for Postgres, how do we ensure our data follows it?
While we could have made another standalone filter to run after fix_latin, that would
increase the migration time. So I made a quick patch to the fix_latin program itself, rewriting
that C logic in Perl. A new option “&amp;ndash;strict-utf8” was added. Its job is to simply enforce the
rules found in the Postgres source code. If a character is invalid, it is replaced with
a question mark (there are other choices for a replacement character, but we decided simple
question marks were quick and easy—​and the surrounding data was unlikely to be read or even used anyway).&lt;/p&gt;
&lt;p&gt;Voila! All of the data was now going into Postgres without a problem. Observe:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#34;SELECT E&amp;#39;&amp;#34;  &amp;#34;$(fix_latin  sample.data)&amp;#34;  &amp;#34;&amp;#39;;&amp;#34; | psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ERROR:  invalid byte sequence for encoding &amp;#34;UTF8&amp;#34;: 0xf4 0xa5 0xa3 0xa5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#34;SELECT E&amp;#39;&amp;#34;  &amp;#34;$(fix_latin --strict-utf8 sample.data)&amp;#34;  &amp;#34;&amp;#39;;&amp;#34; | psql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   ?column?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;----------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [Windows-1252]   Euro: €   Double dagger: ‡+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [Latin-1]   Yen: ¥   Half: ½                +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [Japanese]   Ship: 船                       +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [Invalid UTF-8]  Blob: ????
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(1 row)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What are the lessons here? First and foremost, &lt;em&gt;&lt;strong&gt;never&lt;/strong&gt;&lt;/em&gt; use SQL_ASCII. It’s outdated,
dangerous, and will cause much pain down the road. Second, there are an amazing number
of client encodings in use, especially for old data, but the world has pretty much standardized
on UTF-8 these days, so even if you are stuck with SQL_ASCII, the amount of Windows-1252 and
other monstrosities will be small. Third, don’t be afraid to go to the source. If Postgres
is rejecting your data, it’s probably for a very good reason, so find out exactly why.
There were other challenges to overcome in this migration, but the encoding was certainly
one of the most interesting ones. Everyone, the client and us, is very happy to finally
have everything using UTF-8!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Postal code pain and fun</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/05/postal-code-pain-and-fun/"/>
      <id>https://www.endpointdev.com/blog/2017/05/postal-code-pain-and-fun/</id>
      <published>2017-05-10T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-0.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;We do a lot of ecommerce development at End Point. You know the usual flow as a customer: Select products, add to the shopping cart, then check out. Checkout asks questions about the buyer, payment, and delivery, at least. Some online sales are for “soft goods”, downloadable items that don’t require a delivery address. Much of online sales are still for physical goods to be delivered to an address. For that, a postal code or zip code is usually required.&lt;/p&gt;
&lt;h3 id=&#34;no-postal-code&#34;&gt;No postal code?&lt;/h3&gt;
&lt;p&gt;I say &lt;em&gt;usually&lt;/em&gt; because there are some countries that do not use postal codes at all. An ecommerce site that expects to ship products to buyers in one of those countries needs to allow for an empty postal code at checkout time. Otherwise, customers may leave thinking they aren’t welcome there. The more creative among them will make up something to put in there, such as “00000” or “99999” or “NONE”.&lt;/p&gt;
&lt;p&gt;Someone has helpfully assembled and maintains a machine-readable (in Ruby, easily convertible to JSON or other formats) &lt;a href=&#34;https://web.archive.org/web/20201128190754/https://gist.github.com/kennwilson/3902548&#34;&gt;list of the countries that don’t require a postal code&lt;/a&gt;. You may be surprised to see on the list such countries as Hong Kong, Ireland, Panama, Saudi Arabia, and South Africa. Some countries on the list actually do have postal codes but do not require them or commonly use them.&lt;/p&gt;
&lt;h3 id=&#34;do-you-really-need-the-customers-address&#34;&gt;Do you really need the customer’s address?&lt;/h3&gt;
&lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-1.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;When selling both downloadable and shipped products, it would be nice to not bother asking the customer for an address at all. Unfortunately even when there is no shipping address because there’s nothing to ship, the billing address is still needed if payment is made by credit card through a normal credit card payment gateway—​as opposed to PayPal, Amazon Pay, Venmo, Bitcoin, or other alternative payment methods.&lt;/p&gt;
&lt;p&gt;The credit card &lt;a href=&#34;https://en.wikipedia.org/wiki/Address_Verification_System&#34;&gt;Address Verification System (AVS)&lt;/a&gt; allows merchants to ask a credit card issuing bank whether the mailing address provided matches the address on file for that credit card. Normally only two parts are checked: (1) the street address numeric part, for example, “123” if “123 Main St.” was provided; (2) the zip or postal code, normally only the first 5 digits for US zip codes, and often non-US postal code AVS doesn’t work at all with non-US banks.&lt;/p&gt;
&lt;p&gt;Before sending the address to AVS, validating the &lt;em&gt;format&lt;/em&gt; of postal codes is simple for many countries: 5 digits in the US (allowing an optional &lt;em&gt;-nnnn&lt;/em&gt; for ZIP+4), and 4 or 5 digits in most others countries—​see the Wikipedia &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_postal_codes&#34;&gt;List of postal codes&lt;/a&gt; in various countries for a high-level view. Canada is slightly more complicated: 6 characters total, alternating a letter followed by a number, formally with a space in the middle, like K1A 0B1 as explained in Wikipedia’s &lt;a href=&#34;https://en.wikipedia.org/wiki/Postal_codes_in_Canada#Components_of_a_postal_code&#34;&gt;components of a Canadian postal code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So most countries’ postal codes can be validated in software with simple regular expressions, to catch typos such as transpositions and missing or extra characters.&lt;/p&gt;
&lt;h3 id=&#34;uk-postcodes&#34;&gt;UK postcodes&lt;/h3&gt;
&lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-2.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;The most complicated postal codes I have worked with is the United Kingdom’s, because they can be from 5 to 7 characters, with an unpredictable mix of letters and numbers, normally formatted with a space in the middle. The benefit they bring is that they encode a lot of detail about the address, and it’s possible to catch transposed character errors that would be missed in a purely numeric postal code. The Wikipedia article &lt;a href=&#34;https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom&#34;&gt;Postcodes in the United Kingdom&lt;/a&gt; has the gory details.&lt;/p&gt;
&lt;p&gt;It is common to use a regular expression to validate UK postcodes in software, and many of these regexes are to some degree wrong. Most let through many invalid postcodes, and some disallow valid codes.&lt;/p&gt;
&lt;p&gt;We recently had a client get a customer report of a valid UK postcode being rejected during checkout on their ecommerce site. The validation code was using a regex that is widely copied in software in the wild:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]?[0-9][ABD-HJLN-UW-Z]{2}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(This example removes support for the odd exception GIR 0AA for simplicity’s sake.)&lt;/p&gt;
&lt;p&gt;The customer’s valid postcode that doesn’t pass that test was W1F 0DP, in London, which the Royal Mail website confirms is valid. The problem is that the regex above doesn’t allow for F in the third position, as that was not valid at the time the regex was written.&lt;/p&gt;
&lt;p&gt;This is one problem with being too strict in validations of this sort: The rules change over time, usually to allow things that once were not allowed. Reusable, maintained software libraries that specialize in UK postal codes can keep up, but there is always lag time between when updates are released and when they’re incorporated into production software. And copied or customized regexes will likely stay the way they are until someone runs into a problem.&lt;/p&gt;
&lt;p&gt;The ecommerce site in question is running on the &lt;a href=&#34;http://www.icdevgroup.org/&#34;&gt;Interchange&lt;/a&gt; ecommerce platform, which is based on Perl, so the most natural place to look for an updated validation routine is on CPAN, the Perl network of open source library code. There we find the nice module &lt;a href=&#34;https://metacpan.org/pod/Geo::UK::Postcode&#34;&gt;Geo::UK::Postcode&lt;/a&gt; which has a more current validation routine and a nice interface. It also has a function to format a UK postcode in the canonical way, capitalized (easy) and with the space in the correct place (less easy).&lt;/p&gt;
&lt;p&gt;It also presents us with a new decision: Should we use the basic “valid” test, or the “strict” one? This is where it gets a little trickier. The “valid” check uses a regex validation approach will still let through some invalid postcodes, because it doesn’t know what all the current valid delivery destinations are. This module has a “strict” check that uses a &lt;a href=&#34;https://github.com/mjemmeson/Geo-UK-Postcode-Regex/blob/master/lib/Geo/UK/Postcode/Regex.pm#L664-L3652&#34;&gt;comprehensive list of all the “outcode” data&lt;/a&gt;—​which as you can see if you look at that source code, is extensive.&lt;/p&gt;
&lt;p&gt;The bulkiness of that list, and its short shelf life—​the likelihood that it will become outdated and reject a future valid postcode—​makes strict validation checks like this of questionable value for basic ecommerce needs. Often it is better to let a few invalid postcodes through now so that future valid ones will also be allowed.&lt;/p&gt;
&lt;p&gt;The ecommerce site I mentioned also does in-browser validation via JavaScript before ever submitting the order to the server. Loading a huge list of valid outcodes would waste a lot of bandwidth and slow down checkout loading, especially on mobile devices. So a more lax regex check there is a good choice.&lt;/p&gt;
&lt;h3 id=&#34;when-christmas-comes&#34;&gt;When Christmas comes&lt;/h3&gt;
&lt;p&gt;There’s no Christmas gift of a single UK postal code validation solution for all needs, but there are some fun trivia notes in the Wikipedia page covering &lt;a href=&#34;https://en.wikipedia.org/wiki/Postal_code#Non-geographic_codes&#34;&gt;Non-geographic postal codes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A fictional address is used by UK Royal Mail for letters to Santa Claus:&lt;/p&gt;
&lt;p&gt;Santa’s Grotto&lt;br&gt;
Reindeerland XM4 5HQ&lt;/p&gt;
&lt;p&gt;Previously, the postcode SAN TA1 was used.&lt;/p&gt;
&lt;p&gt;In Finland the special postal code 99999 is for Korvatunturi, the place where Santa Claus (Joulupukki in Finnish) is said to live, although mail is delivered to the Santa Claus Village in Rovaniemi.&lt;/p&gt;
&lt;p&gt;In Canada the amount of mail sent to Santa Claus increased every Christmas, up to the point that Canada Post decided to start an official Santa Claus letter-response program in 1983. Approximately one million letters come in to Santa Claus each Christmas, including from outside of Canada, and they are answered in the same languages in which they are written. Canada Post introduced a special address for mail to Santa Claus, complete with its own postal code:&lt;/p&gt;
&lt;p&gt;SANTA CLAUS&lt;br&gt;
NORTH POLE  H0H 0H0&lt;/p&gt;
&lt;p&gt;In Belgium bpost sends a small present to children who have written a letter to Sinterklaas. They can use the non-geographic postal code 0612, which refers to the date Sinterklaas is celebrated (6 December), although a fictional town, street and house number are also used. In Dutch, the address is:&lt;/p&gt;
&lt;p&gt;Sinterklaas&lt;br&gt;
Spanjestraat 1&lt;br&gt;
0612 Hemel&lt;/p&gt;
&lt;p&gt;This translates as “1 Spain Street, 0612 Heaven”. In French, the street is called “Paradise Street”:&lt;/p&gt;
&lt;p&gt;Saint-Nicolas&lt;br&gt;
Rue du Paradis 1&lt;br&gt;
0612 Ciel&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;That UK postcode for Santa doesn’t validate in some of the regexes, but the simpler Finnish, Canadian, and Belgian ones do, so if you want to order something online for Santa, you may want to choose one of those countries for delivery. :)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl Dancer Conference 2016 Day 1</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/11/perl-dancer-conference-2016-day-1/"/>
      <id>https://www.endpointdev.com/blog/2016/11/perl-dancer-conference-2016-day-1/</id>
      <published>2016-11-30T00:00:00+00:00</published>
      <author>
        <name>Sam Batschelet</name>
      </author>
      <content type="html">
        &lt;img border=&#34;0&#34; height=&#34;306&#34; src=&#34;/blog/2016/11/perl-dancer-conference-2016-day-1/image-0.jpeg&#34; width=&#34;320&#34;/&gt;
&lt;h3 id=&#34;perl-dancer-conference-day-1&#34;&gt;Perl Dancer Conference Day 1&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://perl.dance/&#34;&gt;Perl Dancer Conference&lt;/a&gt; is a great event, now in its third year. &lt;img align=&#34;left&#34; border=&#34;0&#34; height=&#34;90&#34; src=&#34;/blog/2016/11/perl-dancer-conference-2016-day-1/image-1.png&#34; width=&#34;130&#34;/&gt; The event took place in the same location as last year in Vienna, Austria at the Hotel Schani Wien. For those of you who have never visited Vienna, it is a perfect place to bring the family. From visiting the beautiful parks to taking a scenic ride on the Danube River, the beautiful and historic city is known for its rich art and musical culture, and has much to offer.&lt;/p&gt;
&lt;p&gt;I was very excited to not only attend but also give a talk this year. My talk titled &lt;a href=&#34;https://www.perl.dance/talks/45-dancing-in-the-clouds&#34;&gt;“Dancing in the Clouds”&lt;/a&gt; also coincided with the release of 2 new Perl modules &lt;a href=&#34;https://metacpan.org/release/HEXFUSION/Etcd3-0.007/view/lib/Etcd3.pm&#34;&gt;Etcd3&lt;/a&gt; and &lt;a href=&#34;https://metacpan.org/pod/Dancer2::Plugin::Etcd&#34;&gt;Dancer2::Plugin::Etcd&lt;/a&gt;. This article will be the first of a 3 part series, with the final article a focus on my talk and usage examples with the new modules.&lt;/p&gt;
&lt;h3 id=&#34;sawyer-x-sawyer-x--a-bus-tour-through-dancer-core&#34;&gt;Sawyer X (Sawyer X) — A bus tour through Dancer core&lt;/h3&gt;
&lt;p&gt;The Captain of Dancer core, SawyerX, took us on a bus tour through the core functionality of Dancer2. Using practical examples of code blocks from core, he explained how different areas of the code base worked. I personally enjoyed his explanation of how hooks are implemented and created. Learning from the 1st iteration of Dancer, the second version shows maturity and stability.&lt;/p&gt;
&lt;h3 id=&#34;stefan-hornburg-racke--no-act-on-act&#34;&gt;Stefan Hornburg (Racke) — No Act on ACT&lt;/h3&gt;
&lt;p&gt;If you have ever taken the time to review a Perl conference’s website or even purchase tickets to attend you have no doubt been in contact with ACT. “Act (A Conference Toolkit) is a multilingual, template-driven, multi-conference website that can manage the users, talks, schedule, and payment for your conference.” While this package has been around for many years, it is somewhat dreaded because of its lack of features.&lt;/p&gt;
&lt;p&gt;Stefan outlines his work with Interchange6::Schema and the perl.dance website painting a picture of the replacement for ACT. Utilizing Dancer2, DBIx::Class, Moo and other modern Perl tools the infrastructure outlined is very compelling. The package has a user admin, e-commerce, and even a module to print out the passes. Although he notes that this is not a plug and play replacement for ACT yet, with a bit of work and support, it could be the future of Perl conference management.&lt;/p&gt;
&lt;h3 id=&#34;andrew-beverly--implementing-i18n-in-a-dancer-application-using-pluginlogreport&#34;&gt;Andrew Beverly — Implementing i18n in a Dancer application using Plugin::LogReport&lt;/h3&gt;
&lt;p&gt;Andrew extended his talk last year about internationalization with the Dancer2::Plugin::LogReport module. Using great examples, he not only outlined the usage of the module, but also got into details of how the process works on a technical level. Explaining the different ways that internationalization can be done, he begins to outline how he achieved his task of deploying i18n in a Dancer app.&lt;/p&gt;
&lt;h3 id=&#34;theo-van-hoesel--quickstep&#34;&gt;Theo van Hoesel — Quickstep&lt;/h3&gt;
&lt;p&gt;Theo was a great addition to the conference this year. He was able to make the event on very short notice after Dancer core Jason Crome was not able to attend due to injury. Theo outlined the Act::Voyager project briefly and the general requirements of adding user friendly features to the ACT toolkit. He also spent a good amount of time explaining the concept of web caching and how many of the existing modules failed in the task of complying with RFC7234. He then explained how all of this brought him to create HTTP::Caching and how it has “The RFC 7234 compliant brains to do caching right”. Along with this contribution part of the HTTP::Bundle, his Dancer focused Dancer2::Plugin::HTTP::Bundle was explained.&lt;/p&gt;
&lt;h3 id=&#34;job-van-achterberg-jkva--dancing-with-disabilities&#34;&gt;Job van Achterberg (jkva) — Dancing with Disabilities&lt;/h3&gt;
&lt;p&gt;Job’s talk was a very interesting look into how taking a considerate approach to development and small changes to your code can improve a disabled web user’s experience. Using the tools in macOS Job showed how simple things such as naming a list are reflected in a disabled users ability to get information. What I found very interesting in this presentation was how awkward the tools were to use even for an experienced pro like Job. It really made me think a lot about the challenges the disabled face in something many of us take for granted.&lt;/p&gt;
&lt;h3 id=&#34;jason-lewis--the-lazy-programmers-guide-to-building-html-tables-in-dancer2&#34;&gt;Jason Lewis — The Lazy Programmer’s Guide to building HTML tables in Dancer2&lt;/h3&gt;
&lt;p&gt;Jason has become a regular on the #dancer freenode IRC channel. This year he decided to travel all the way from Australia to give his presentation on his experiences replacing Crystal Reports with Dancer2 Template::Toolkit and DataTables. Although a great deal of the presentation was focused on the features of the jQuery plugin DataTables, he gave nice examples of code he used to populate reports and the hurdles he faced replacing Crystal Reports functionality. The reports looked beautiful and were very easy to convert to other data types such as PDF and CSV.&lt;/p&gt;
&lt;h3 id=&#34;stefan-seifert-nine--perl-5-and-perl-6--a-great-team&#34;&gt;Stefan Seifert (nine) — Perl 5 and Perl 6 — a great team&lt;/h3&gt;
&lt;p&gt;Stefan is a great presence at the conference, and his fun and witty personality carried over to his presentation. After opening with a really funny skit as a reporter reading the the news, he continued to outline the current state of Perl6 and how important it is for all of us as a community to embrace the fact that we are all in this together. He reminded us of the perils of Python 3’s launch and the lack of support even today. He then began to focus on the capabilities of using Perl 5 with Perl 6 together with Inline::Perl5 and Inline::Perl6 modules. To be honest before his talk I had given Perl 6 very little time. Stefan’s talk opened my eyes to the possibilities of utilizing the two versions together and the advantages that ecosystem has.&lt;/p&gt;
&lt;p&gt;Please stop back for links to day 2 of the conference and a breakdown of my talk outlining &lt;a href=&#34;https://coreos.com/etcd/&#34;&gt;etcd&lt;/a&gt; integration with Perl and &lt;a href=&#34;https://perldancer.org/&#34;&gt;Dancer2&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Use Java along with Perl</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/10/use-java-along-with-perl/"/>
      <id>https://www.endpointdev.com/blog/2016/10/use-java-along-with-perl/</id>
      <published>2016-10-18T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;While working with one of our clients, I was tasked with integrating a Java project with a Perl project. The Perl project is a web application which has a specific URL for the Java application to use. To ensure that the URL is called only from the Java application, I wanted to send a special hash value calculated using the request parameters, a timestamp, and a secret value.&lt;/p&gt;
&lt;p&gt;The Perl code calculating the hash value looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;strict&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;warnings&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;LWP::UserAgent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Digest::HMAC_SHA1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Data::Dumper&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$uri&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;param1/param2/params3&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$ua&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;LWP::UserAgent&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$hmac&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Digest::HMAC_SHA1&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;secret_something&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$ts&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;time&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$hmac&lt;/span&gt;-&amp;gt;add(&lt;span style=&#34;color:#369&#34;&gt;$_&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#038&#34;&gt;split&lt;/span&gt; (&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;m{/}&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$uri&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$hmac&lt;/span&gt;-&amp;gt;add(&lt;span style=&#34;color:#369&#34;&gt;$ts&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$calculated_hash&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$hmac&lt;/span&gt;-&amp;gt;hexdigest;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My first try for calculating the same hash in the Java code looked something like this (without class/package overhead):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;javax.crypto.Mac&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;javax.crypto.spec.SecretKeySpec&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;org.apache.commons.codec.binary.Hex&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;calculateHash&lt;/span&gt;(String[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;values)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throws&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;NoSuchAlgorithmException,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;UnsupportedEncodingException,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;InvalidKeyException&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;java.&lt;span style=&#34;color:#369&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;Date&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;date=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;java.&lt;span style=&#34;color:#369&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;Date&lt;/span&gt;();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;Integer&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;timestamp&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;date.&lt;span style=&#34;color:#369&#34;&gt;getTime&lt;/span&gt;()/1000;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;Mac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Mac.&lt;span style=&#34;color:#369&#34;&gt;getInstance&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;HmacSHA1&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;SecretKeySpec&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;signingKey&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;SecretKeySpec(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;secret_something&amp;#34;&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;(),&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;HmacSHA1&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;init&lt;/span&gt;(signingKey);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt;(String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;value:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;values)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(value.&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;());&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(timestamp.&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;());&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;byte&lt;/span&gt;[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;rawHmac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;doFinal&lt;/span&gt;();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;byte&lt;/span&gt;[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;hexBytes&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Hex().&lt;span style=&#34;color:#369&#34;&gt;encode&lt;/span&gt;(rawHmac);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String(hexBytes,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code looks good and successfully calculated a hash. However, using the same parameters for the Perl and Java code, they were returning different results. After some debugging, I found that the only parameter causing problems was the timestamp. My first guess was that the problem was caused by the use of Integer as the timestamp type instead of some other numeric type. I tried a few things to get around that, but none of them worked.&lt;/p&gt;
&lt;p&gt;Another idea was to check why it works for the String params, but not for Integer. I found that Perl treats the timestamp as a string and passes a string to the hash calculating method, so I tried emulating this by converting the timestamp into a String before using the getBytes() method:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;javax.crypto.Mac&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;javax.crypto.spec.SecretKeySpec&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;org.apache.commons.codec.binary.Hex&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;calculateHash&lt;/span&gt;(String[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;values)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throws&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;NoSuchAlgorithmException,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;UnsupportedEncodingException,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;InvalidKeyException&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;java.&lt;span style=&#34;color:#369&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;Date&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;date=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;java.&lt;span style=&#34;color:#369&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;Date&lt;/span&gt;();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;Integer&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;timestamp&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;int&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;date.&lt;span style=&#34;color:#369&#34;&gt;getTime&lt;/span&gt;()/1000;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;Mac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Mac.&lt;span style=&#34;color:#369&#34;&gt;getInstance&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;HmacSHA1&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;SecretKeySpec&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;signingKey&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;SecretKeySpec(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;secret_something&amp;#34;&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;(),&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;HmacSHA1&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;init&lt;/span&gt;(signingKey);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt;(String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;value:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;values)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(value.&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;());&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(timestamp.&lt;span style=&#34;color:#369&#34;&gt;toString&lt;/span&gt;().&lt;span style=&#34;color:#369&#34;&gt;getBytes&lt;/span&gt;());&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;byte&lt;/span&gt;[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;rawHmac&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mac.&lt;span style=&#34;color:#369&#34;&gt;doFinal&lt;/span&gt;();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;byte&lt;/span&gt;[]&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;hexBytes&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;=&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Hex().&lt;span style=&#34;color:#369&#34;&gt;encode&lt;/span&gt;(rawHmac);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String(hexBytes,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;);&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This worked perfectly, and there were no other problems with calculating the hash in Perl and Java.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Reach customers and drive sales with MailChimp</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/08/reach-customers-and-drive-sales-with/"/>
      <id>https://www.endpointdev.com/blog/2016/08/reach-customers-and-drive-sales-with/</id>
      <published>2016-08-22T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s a good idea for ecommerce stores to regularly contact their customers. This not only reminds customers that your business exists, but also allows the sharing of new products and resources that can enrich the lives of your customers and clients. One of the easiest ways to stay in touch is by using an email newsletter service, such as &lt;a href=&#34;http://mailchimp.com/&#34;&gt;MailChimp&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img alt=&amp;quot;&amp;quot; border=&amp;quot;0&amp;quot; height=&amp;quot;200&amp;quot; src=&amp;quot;/blog/2016/08/reach-customers-and-drive-sales-with/image-0.png&amp;quot; title=&amp;quot;Freddie, the MailChimp mascot&amp;quot; width=&amp;quot;191&amp;quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MailChimp offers the regular suite of email newsletter services: lists, campaigns, and reports—​but in addition, they allow an ecommerce store to &lt;a href=&#34;http://mailchimp.com/connect-your-store/&#34;&gt;integrate sales data back into MailChimp&lt;/a&gt;. When you have &lt;strong&gt;detailed shopping statistics&lt;/strong&gt; for each subscriber, it opens new possibilities for customized marketing campaigns.&lt;/p&gt;
&lt;h3 id=&#34;endless-possibilities&#34;&gt;Endless possibilities&lt;/h3&gt;
&lt;p&gt;For example, imagine you have an email mailing list with 1,000 recipients. Instead of mailing the same generic newsletter to each subscriber, what if you could segment the list to identify your 100 best customers, and email them a special campaign?&lt;/p&gt;
&lt;p&gt;Additional ideas could include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reach out to inactive subscribers, offering a coupon&lt;/li&gt;
&lt;li&gt;Invite your best customers to a secret sale&lt;/li&gt;
&lt;li&gt;Re-engage customers who placed items in their cart, but left without purchasing&lt;/li&gt;
&lt;li&gt;Offer complementary products to purchasers of &lt;em&gt;Product X&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;automatic-marketing&#34;&gt;Automatic marketing&lt;/h3&gt;
&lt;p&gt;Once your store has sales data for subscribers, and you’ve decided on the campaigns you want to run with this data, the next step is to &lt;strong&gt;automate the process&lt;/strong&gt;. This is where MailChimp’s &lt;a href=&#34;http://mailchimp.com/features/automation/&#34;&gt;Automation&lt;/a&gt; feature comes in. Spend some time up-front to craft the automated campaigns, then sit back and let MailChimp run them for you; day in, day out.&lt;/p&gt;
&lt;h3 id=&#34;steps-to-implement&#34;&gt;Steps to implement&lt;/h3&gt;
&lt;p&gt;There are several off-the-shelf &lt;a href=&#34;https://connect.mailchimp.com/collections/e-commerce&#34;&gt;integrations for ecommerce stores&lt;/a&gt;, including Magento and BigCommerce.&lt;/p&gt;
&lt;p&gt;Users of Perl and &lt;a href=&#34;/expertise/perl-interchange/&#34;&gt;Interchange&lt;/a&gt; can use our newly-released toolsets of the &lt;a href=&#34;http://p3rl.org/Mail::Chimp3&#34;&gt;Mail::Chimp3 CPAN module&lt;/a&gt; and the &lt;a href=&#34;https://github.com/jdigory/interchange-extras/tree/master/mailchimp&#34;&gt;integration for Interchange5&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/contact/&#34;&gt;Contact us&lt;/a&gt; today for expert help integrating one of these solutions with your ecommerce store.&lt;/p&gt;
&lt;h3 id=&#34;go-beyond-the-simple-newsletter&#34;&gt;Go beyond the simple newsletter&lt;/h3&gt;
&lt;p&gt;Most businesses already have an email newsletter. Hopefully, you are sending regular email campaigns with it. This is a great first step. Going beyond this to segment your email list and reach out to these segments with relevant information to each of them, is the next step. Not only can this &lt;strong&gt;increase your sales&lt;/strong&gt;, but it also &lt;strong&gt;respects your clients’ and customers’ time and preferences&lt;/strong&gt;. It’s a win-win for all.&lt;/p&gt;
&lt;p&gt;Additional resource: &lt;a href=&#34;http://mailchimp.com/resources/guides/mailchimp-for-online-sellers/&#34;&gt;MailChimp for Online Sellers&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Report on The Perl Conference 2016</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/07/report-on-perl-conference-2016/"/>
      <id>https://www.endpointdev.com/blog/2016/07/report-on-perl-conference-2016/</id>
      <published>2016-07-27T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;In June, I traveled to Orlando, Florida to attend the event formerly known as &lt;em&gt;Yet Another Perl Conference&lt;/em&gt; (or YAPC::NA), now known as &lt;a href=&#34;http://www.yapcna.org/yn2016/&#34;&gt;The Perl Conference&lt;/a&gt;. This was my second time in a row to attend this conference (after my first attendance back in 2007).&lt;/p&gt;
&lt;p&gt;Conferences are a great place to learn how others are using various tools, hear about new features, and interact with the community. If you are speaking, it’s a great opportunity to brush up on your subject, which was true for me in the extreme, as I was able to give a talk on the PostgreSQL database, which I hadn’t used in a long time (more on that later).&lt;/p&gt;
&lt;h3 id=&#34;the-conference-name&#34;&gt;The conference name&lt;/h3&gt;
&lt;p&gt;The event organizers were able to license the name &lt;em&gt;The Perl Conference&lt;/em&gt; from O’Reilly Media, as O’Reilly doesn’t hold conferences by this name anymore. This name is now preferred over “YAPC” as it is more friendly to newcomers and more accurately describes the conference. &lt;a href=&#34;http://www.yapcna.org/yn2016/news/1397&#34;&gt;More on the name change.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;notes-from-the-conference&#34;&gt;Notes from the conference&lt;/h3&gt;
&lt;p&gt;Over the three days of the conference, I was able to take in many talks. Here are some of my more interesting notes from various sessions:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both;&#34;&gt;
&lt;blockquote class=&#34;twitter-tweet&#34; data-lang=&#34;en&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;
    &lt;p dir=&#34;ltr&#34; lang=&#34;en&#34;&gt;
        Yes, this is happening &lt;a href=&#34;https://twitter.com/YAPCNA&#34;&gt;@YAPCNA&lt;/a&gt;
        &lt;br/&gt;
        &lt;a href=&#34;https://twitter.com/JayceHall/status/745616157768482816&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;256&#34; src=&#34;/blog/2016/07/report-on-perl-conference-2016/image-0.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;
    &lt;/p&gt;
    &lt;cite style=&#34;float:right&#34;&gt;— Jason Hall (@JayceHall) &lt;a href=&#34;https://twitter.com/JayceHall/status/745616157768482816&#34;&gt;June 22, 2016&lt;/a&gt;&lt;/cite&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://metacpan.org/&#34;&gt;MetaCPAN&lt;/a&gt; is the best way to browse and search for Perl modules. Anyone can help with development of this fine project, via &lt;a href=&#34;https://github.com/metacpan/metacpan-web&#34;&gt;their GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://metacpan.org/author/RJBS&#34;&gt;Ricardo Signes&lt;/a&gt; says “use &lt;a href=&#34;http://perldoc.perl.org/perlsub.html#Signatures&#34;&gt;subroutine signatures&lt;/a&gt;!” They are “experimental”, but are around to stay.&lt;/li&gt;
&lt;li&gt;Perl6 is written in Perl6 (and something called “&lt;a href=&#34;https://github.com/perl6/nqp&#34;&gt;Not Quite Perl&lt;/a&gt;”). This allows one to read the source to figure out how something is done. &lt;em&gt;(There were many talks on Perl6, which is viewed as a different programming language, not a replacement for Perl5.)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://stedolan.github.io/jq/&#34;&gt;jq&lt;/a&gt; is a command-line utility that can pretty-print JSON (non Perl, but nice!)&lt;/li&gt;
&lt;li&gt;Ricardo Signes gave a &lt;a href=&#34;https://www.youtube.com/watch?v=TmTeXcEixEg&#34;&gt;talk on encoding&lt;/a&gt; that was over my head, but very interesting.&lt;/li&gt;
&lt;li&gt;The presenter of &lt;em&gt;Emacs as Perl IDE&lt;/em&gt; couldn’t attend, so Damian Conway spoke on &lt;a href=&#34;https://www.youtube.com/watch?v=9u6O0dLuqhI&#34;&gt;VIM as Perl IDE&lt;/a&gt; (photo above)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From &lt;a href=&#34;http://www.yapcna.org/yn2016/talk/6599&#34;&gt;John Anderson’s talk&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Just say “no” to system Perl. Use &lt;a href=&#34;https://github.com/tokuhirom/plenv&#34;&gt;plenv&lt;/a&gt; or the like.&lt;/li&gt;
&lt;li&gt;There’s a DuckDuckGo &lt;a href=&#34;https://duckduckgo.com/bang&#34;&gt;bang command&lt;/a&gt; for searching MetaCPAN: !cpan [module]&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://metacpan.org/pod/JSON::MaybeXS&#34;&gt;JSON::MaybeXS&lt;/a&gt; over the plain JSON module.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://metacpan.org/pod/Moo&#34;&gt;Moo&lt;/a&gt; for object-oriented programming in Perl, or Moose if you must.&lt;/li&gt;
&lt;li&gt;Subscribe to &lt;a href=&#34;http://perlweekly.com/&#34;&gt;Perl Weekly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Submit your module on &lt;a href=&#34;http://prepan.org/&#34;&gt;PrePAN&lt;/a&gt; first, to receive feedback before posting to CPAN.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lee Johnson gave a talk called &lt;a href=&#34;http://www.yapcna.org/yn2016/talk/6545&#34;&gt;Battling a legacy schema with DBIx::Class&lt;/a&gt; (&lt;a href=&#34;https://www.youtube.com/watch?v=ltckzIJYwHg&#34;&gt;video&lt;/a&gt;). Key takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://programmers.stackexchange.com/questions/304520/when-should-i-use-perls-dbixclass/304557#304557&#34;&gt;When should I use DBIC?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Something that has grown organically could be considered “legacy,” as it accumulates &lt;a href=&#34;https://en.wikipedia.org/wiki/Technical_debt&#34;&gt;technical debt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;With &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIC&lt;/a&gt;, you can start to manage that debt by adding relationships to your model, even if they aren’t in your database&lt;/li&gt;
&lt;li&gt;RapidApp’s &lt;a href=&#34;https://metacpan.org/pod/Plack::App::RapidApp::rDbic&#34;&gt;rdbic&lt;/a&gt; can help you visualize an existing database&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=EXPElOT2fRE&#34;&gt;D. Ruth Bavousett spoke&lt;/a&gt; on &lt;a href=&#34;https://metacpan.org/pod/Perl::Critic&#34;&gt;Perl::Critic&lt;/a&gt;, which is a tool for encouraging consistency. Basically, Perl::Critic looks at your source code, and makes suggestions for improvement, etc. These suggestions are known as “policies” and can be configured to enable or disable any of them, or to even write new policies. One suggestion was to create a &lt;a href=&#34;https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks&#34;&gt;Git hook&lt;/a&gt; to run the perlcritic command at the time code is committed to the source code repository (possibly using &lt;a href=&#34;https://metacpan.org/pod/App::GitHooks&#34;&gt;App::GitHooks&lt;/a&gt;). End Point has its own perlcritic configuration, which I have started trying to use more.&lt;/p&gt;
&lt;p&gt;Logan Bell shared &lt;a href=&#34;https://www.youtube.com/watch?v=uW4UX8UBAjg&#34;&gt;Strategies for leading a remote team&lt;/a&gt;. Some of the tools and techniques he uses include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://tmate.io/&#34;&gt;tmate&lt;/a&gt; for terminal sharing&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.hipchat.com/&#34;&gt;HipChat&lt;/a&gt;, with a chat room just for complaining called “head to desk”&lt;/li&gt;
&lt;li&gt;Holds one-on-one meetings every Monday for those he works with and directs&lt;/li&gt;
&lt;li&gt;Has new team members work on-site with another team member their first week or so, to help understand personalities that don’t often come across well over chat&lt;/li&gt;
&lt;li&gt;Tries to have in-person meetings every quarter or at least twice a year, to bring the team together&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;my-talk&#34;&gt;My talk&lt;/h3&gt;
&lt;p&gt;Finally, my own talk was titled &lt;a href=&#34;http://www.yapcna.org/yn2016/talk/6631&#34;&gt;Stranger in a Strange Land: PostgreSQL for MySQL users&lt;/a&gt; (&lt;a href=&#34;https://www.youtube.com/watch?v=sH41r_MOSH0&#34;&gt;video&lt;/a&gt;). I hadn’t used Postgres in about seven years, and I wanted to get re-acquainted with it, so naturally, I submitted a talk on it to spur myself into action!&lt;/p&gt;
&lt;p&gt;In my talk, I covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the history of MySQL and Postgres&lt;/li&gt;
&lt;li&gt;how to pronounce “PostgreSQL”&lt;/li&gt;
&lt;li&gt;why one might be preferred over the other&lt;/li&gt;
&lt;li&gt;how to convert an existing database to Postgres&lt;/li&gt;
&lt;li&gt;and some tools and tips.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I enjoyed giving this talk, and hope others found it helpful. All in all, The Perl Conference was a great experience, and I hope to continue attending in the future!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/user/yapcna/search?query=2016&#34;&gt;All videos from this year’s conference&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>2015 Perl Dancer Conference videos</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/12/2015-perl-dancer-conference-videos/"/>
      <id>https://www.endpointdev.com/blog/2015/12/2015-perl-dancer-conference-videos/</id>
      <published>2015-12-30T00:00:00+00:00</published>
      <author>
        <name>Sam Batschelet</name>
      </author>
      <content type="html">
        &lt;p&gt;The 2015 &lt;a href=&#34;https://www.perl.dance&#34;&gt;Perl Dancer Conference&lt;/a&gt; has recently released the presentation videos. This year the conference was hosted in beautiful Vienna, Austria. Josh Lavin and I were both honored to attend the conference as well as give talks. Earlier, Josh wrote summaries of the conference:&lt;/p&gt;
&lt;h3 id=&#34;conference-recap&#34;&gt;Conference Recap&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report/&#34;&gt;Training Days&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/&#34;&gt;Conference Days&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conference-presentations&#34;&gt;Conference Presentations&lt;/h3&gt;
&lt;h4 id=&#34;spacecamps-the-final-frontier&#34;&gt;SpaceCamps “The Final Frontier”&lt;/h4&gt;
&lt;p&gt;I gave a talk exploring new technologies for End Point’s own &lt;a href=&#34;http://devcamps.org&#34;&gt;DevCamps&lt;/a&gt; development tool. During the presentation I detailed my research into containers and what a cloud-based development environment might look like.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=IYKs8FF8--Y&#34;&gt;SpaceCamps Presentation Video&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;angularjs--dancer-for-modern-web-development&#34;&gt;AngularJS &amp;amp; Dancer for Modern Web Development&lt;/h4&gt;
&lt;p&gt;Josh detailed his experience migrating legacy applications utilizing Dancer, AngularJS, and modern Perl techniques. Josh highlighted the challenges he faced during the process, as well as lessons he learned along the way.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=m7q6QbSHrkA&#34;&gt;AngularJS &amp;amp; Dancer for Modern Web Development Presentation Video&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;lightning-talks&#34;&gt;Lightning Talks&lt;/h3&gt;
&lt;p&gt;Josh and I both gave short “lightning talks.” Josh’s was on &lt;a href=&#34;https://youtu.be/-eg21qxxIAA?t=12m47s&#34;&gt;Writing Unit Tests for a Legacy App (Interchange 5)&lt;/a&gt;, and mine was on &lt;a href=&#34;https://youtu.be/-eg21qxxIAA?t=19m11s&#34;&gt;Plack &amp;amp; Interchange 5.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To review the rest of the presentations please checkout the &lt;a href=&#34;https://www.youtube.com/channel/UCWtnsx6yRuHvO2e-xX2Yyng&#34;&gt;Perl Dancer Conference YouTube channel&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;The Perl Dancer community continues to flourish and the conference this year hosted a record 5 core &lt;a href=&#34;http://perldancer.org&#34;&gt;Dancer&lt;/a&gt; developers. Dancer is about to release the finalized version of its long awaited plugin infrastructure for &lt;a href=&#34;https://github.com/PerlDancer/Dancer2&#34;&gt;Dancer2&lt;/a&gt;. A lot of work on this was completed during the conference. Being an organizer of the conference, it brings me great joy to see this success. This news along with the release of Perl 6, I am certain 2016 will be a wonderful year for not only Dancer but the entire &lt;a href=&#34;https://www.perl.org/&#34;&gt;Perl&lt;/a&gt; community.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Top 7 Funniest Perl Modules</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/11/funniest-perl-modules/"/>
      <id>https://www.endpointdev.com/blog/2015/11/funniest-perl-modules/</id>
      <published>2015-11-04T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;And now for something completely different &amp;hellip;&lt;/p&gt;
&lt;p&gt;Programmers in general, and Perl programmers in particular, seem to have excellent, if warped, senses of humor. As a result, the CPAN library is replete with modules that have oddball names, or strange and wonderful purposes, or in some delightful cases—​both!&lt;/p&gt;
&lt;p&gt;Let’s take a look.&lt;/p&gt;
&lt;h3 id=&#34;1-boneeasy&#34;&gt;1. &lt;a href=&#34;http://search.cpan.org/~mschwern/Bone-Easy-0.04/lib/Bone/Easy.pm&#34;&gt;Bone::Easy&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m going to take the coward’s way out on this one right away. Go see for yourself, or don’t.&lt;/p&gt;
&lt;h3 id=&#34;2-acmeeyedrops&#34;&gt;2. &lt;a href=&#34;http://search.cpan.org/~asavige/Acme-EyeDrops-1.62/lib/Acme/EyeDrops.pm&#34;&gt;Acme::EyeDrops&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Really, anything in the Acme::* (meaning “perfect”) namespace is just programmer-comedy gold, depending on what you find amusing and what is just plain forehead-smacking stupid to you. This one allows you to transform your Perl programs (small ones work better) from this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hello world\n&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jeff@Wyvern:~$ perl bin/eyedrops.pl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 shapes completed.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use re &amp;#39;eval&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                      &amp;#39;&amp;#39;=~(&amp;#39;(&amp;#39;.&amp;#39;?&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           .&amp;#39;{&amp;#39;.(                   &amp;#39;`&amp;#39;|&amp;#39;%&amp;#39;).(&amp;#34;\[&amp;#34;^
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#39;-&amp;#39;).(&amp;#39;`&amp;#39;|                &amp;#39;!&amp;#39;).(&amp;#39;`&amp;#39;|&amp;#39;,&amp;#39;).&amp;#39;&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; .(&amp;#39;[&amp;#39;^&amp;#39;+&amp;#39;)  .(&amp;#39;[&amp;#39;^              &amp;#39;)&amp;#39;).(&amp;#39;`&amp;#39;|&amp;#39;)&amp;#39;).(&amp;#39;`&amp;#39;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#39;.&amp;#39;).(&amp;#39;[&amp;#39;^&amp;#39;/&amp;#39;).(&amp;#39;{&amp;#39;^            &amp;#39;[&amp;#39;).&amp;#39;\\&amp;#39;.&amp;#39;&amp;#34;&amp;#39;.(&amp;#39;`&amp;#39;|&amp;#39;(&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;).(&amp;#39;`&amp;#39;|&amp;#39;%&amp;#39;).(&amp;#39;`&amp;#39;|&amp;#39;,&amp;#39;)          .(&amp;#39;`&amp;#39;|&amp;#39;,&amp;#39;).(&amp;#39;`&amp;#39;|&amp;#34;\/&amp;#34;).(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#39;{&amp;#39;^&amp;#39;[&amp;#39;).(&amp;#39;[&amp;#39;^&amp;#39;,&amp;#39;).        (&amp;#39;`&amp;#39;|&amp;#39;/&amp;#39;).(&amp;#39;[&amp;#39;^&amp;#39;)&amp;#39;).(&amp;#34;\`&amp;#34;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;#39;,&amp;#39;).(&amp;#39;`&amp;#39;|&amp;#39;$&amp;#39;)      .&amp;#39;\\&amp;#39;.&amp;#39;\\&amp;#39;.(&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;).&amp;#39;\\&amp;#39;.&amp;#39;&amp;#34;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      .&amp;#39;;&amp;#39;.(&amp;#39;!&amp;#39;^&amp;#39;+&amp;#39;)     .&amp;#39;&amp;#34;&amp;#39;.&amp;#39;}&amp;#39;.&amp;#39;)&amp;#39;);$:=&amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;#39;(&amp;#39;;$^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;   $/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;$,=&amp;#39;(&amp;#39;^&amp;#39;}&amp;#39;;$\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    =&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#39;*&amp;#39;   |&amp;#39;`&amp;#39;;$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $\=&amp;#39;,&amp;#39;^&amp;#34;\|&amp;#34;;$:=  &amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;$^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;$/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;$,=&amp;#39;(&amp;#39;^&amp;#39;}&amp;#39;;$\  =&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#39;*&amp;#39;|&amp;#39;`&amp;#39;;$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,  =&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\=&amp;#39;,&amp;#39;^&amp;#39;|&amp;#39;;$:=&amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;$/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;$,=&amp;#39;(&amp;#39;^&amp;#39;}&amp;#39;;$\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#39;*&amp;#39;|&amp;#39;`&amp;#39;;$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\=&amp;#39;,&amp;#39;^&amp;#39;|&amp;#39;;$:=&amp;#39;.&amp;#39;^
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;$^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;$/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;$,=&amp;#39;(&amp;#39;^&amp;#39;}&amp;#39;;$\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     ;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#39;*&amp;#39;|&amp;#39;`&amp;#39;;$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#39;,&amp;#39;^&amp;#39;|&amp;#39;;$:=&amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;$^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;$/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;$,=&amp;#39;(&amp;#39;^
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &amp;#39;}&amp;#39;;$\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#39;*&amp;#39;|&amp;#39;`&amp;#39;;$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\=&amp;#39;,&amp;#39;^&amp;#39;|&amp;#39;;$:=&amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;$^=&amp;#34;\)&amp;#34;^ &amp;#39;[&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         ;$/=&amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;$,=&amp;#39;(&amp;#39;^&amp;#39;}&amp;#39;;$\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#39;*&amp;#39;  |((
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;#39;`&amp;#39;));$^=&amp;#39;+&amp;#39;^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\ =&amp;#39;,&amp;#39;^&amp;#39;|&amp;#39;  ;$:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            =&amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39;|&amp;#39;(&amp;#39;;$^=&amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;;$/=&amp;#39;`&amp;#39;|  &amp;#39;.&amp;#39;;$,=   &amp;#39;(&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             ^&amp;#39;}&amp;#39;; $\=&amp;#39;`&amp;#39;|&amp;#39;!&amp;#39;;$:=&amp;#39;)&amp;#39;^&amp;#39;}&amp;#39;;$~=&amp;#34;\*&amp;#34;|   &amp;#39;`&amp;#39;;$^=   &amp;#39;+&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   ^&amp;#39;_&amp;#39;;$/=&amp;#39;&amp;amp;&amp;#39;|&amp;#39;@&amp;#39;;$,=&amp;#39;[&amp;#39;&amp;amp;&amp;#39;~&amp;#39;;$\    =(&amp;#39;,&amp;#39;)^   &amp;#39;|&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   ;$:=&amp;#39;.&amp;#39;^    &amp;#39;~&amp;#39;;$~=&amp;#39;@&amp;#39; |&amp;#34;\(&amp;#34;;     $^=&amp;#39;)&amp;#39;   ^+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &amp;#39;[&amp;#39;;$/=     &amp;#39;`&amp;#39;|&amp;#39;.&amp;#39;;   $,=&amp;#39;(&amp;#39;     ^&amp;#34;\}&amp;#34;;  $\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   =(&amp;#39;`&amp;#39;)|     &amp;#34;\!&amp;#34;;$:=   &amp;#34;\)&amp;#34;^       &amp;#34;\}&amp;#34;;  (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   ($~))=      &amp;#39;*&amp;#39;|&amp;#39;`&amp;#39;;   ($^)         =&amp;#39;+&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   ^&amp;#34;\_&amp;#34;;     $/=(&amp;#39;&amp;amp;&amp;#39;)|   &amp;#39;@&amp;#39;;         ($,)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    =&amp;#39;[&amp;#39;&amp;amp;     &amp;#34;\~&amp;#34;;$\=    &amp;#39;,&amp;#39;^         &amp;#39;|&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ($:)=     &amp;#39;.&amp;#39;^&amp;#39;~&amp;#39;     ;$~=         &amp;#39;@&amp;#39;|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;#39;(&amp;#39;;     $^=&amp;#39;)&amp;#39;       ^&amp;#39;[&amp;#39;         ;$/=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;#39;`&amp;#39;|     &amp;#39;.&amp;#39;          ;$,=         &amp;#39;(&amp;#39;^
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;#39;}&amp;#39;;      $\=         &amp;#39;`&amp;#39;          |((
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;#39;!&amp;#39;        ));       $:=           &amp;#39;)&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ^((         &amp;#39;}&amp;#39;     ));            $~=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &amp;#39;*&amp;#39;          |((   &amp;#39;`&amp;#39;              ))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ;(             ($^))=               ((
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ((              &amp;#39;+&amp;#39;))               ))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    ^+             &amp;#34;\_&amp;#34;;$/=             ((
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &amp;#39;&amp;amp;&amp;#39;            ))|+ &amp;#34;\@&amp;#34;;            $,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  =((           &amp;#39;[&amp;#39;))&amp;amp;  &amp;#39;~&amp;#39;;           $\=
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &amp;#39;,&amp;#39;^         &amp;#34;\|&amp;#34;;$:=   &amp;#39;.&amp;#39;          ^&amp;#39;~&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               ;($~)=                                (&amp;#39;@&amp;#39;)|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &amp;#34;\(&amp;#34;;$^=                               &amp;#39;)&amp;#39;^&amp;#39;[&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Oh, that’s not just a picture of a camel. That’s actual Perl code; you can run that, and it executes in the exact same way as the original one-liner. So much more stylish. Plus, you can impress your boss/cow-orker/&lt;a href=&#34;https://nightvale.fandom.com/wiki/Carlos_the_Scientist&#34;&gt;heroic scientist boyfriend&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;3-commonsense&#34;&gt;3. &lt;a href=&#34;http://search.cpan.org/~mlehmann/common-sense-3.74/sense.pod&#34;&gt;common::sense&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one makes the list because (a) it is just so satisfying to see&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;common::sense&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;atop a Perl program, and (b) a citation of this on our company IRC chat is what planted the seed for this article.&lt;/p&gt;
&lt;p&gt;Another is &lt;a href=&#34;https://metacpan.org/pod/sanity&#34;&gt;sanity.pm&lt;/a&gt;, as in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;sanity&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Seems like a good approach.&lt;/p&gt;
&lt;h3 id=&#34;4-sillywerder&#34;&gt;4. &lt;a href=&#34;https://metacpan.org/pod/Silly::Werder&#34;&gt;Silly::Werder&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Not a terribly interesting name, but it produces some head-scratching output. For instance,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Broringers isess ailerwreakers paciouspiris dests bursonsinvading buggers companislandet despa ascen?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I suppose you might use this to generate some &lt;em&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Lorem_ipsum&#34;&gt;Lorem ipsum&lt;/a&gt;&lt;/em&gt;-type text, or maybe temporary passwords? Dialog for your science fiction novel?&lt;/p&gt;
&lt;h3 id=&#34;5-moose&#34;&gt;5. Moose*&lt;/h3&gt;
&lt;p&gt;Any module with the word “Moose” in it. “Moose” is a funny word.&lt;/p&gt;
&lt;h3 id=&#34;6-doh&#34;&gt;6. &lt;a href=&#34;https://metacpan.org/pod/D::oh&#34;&gt;D::oh&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The humor here is a bit obscure: you have to have been around for Perl4-style namespace addressing, when you would have had to load this via:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;D&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;&amp;#39;&lt;/span&gt;oh;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;7-your&#34;&gt;7. &lt;a href=&#34;https://metacpan.org/pod/your&#34;&gt;your&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;your&lt;/span&gt; &lt;span style=&#34;color:#2b2;background-color:#f0fff0&#34;&gt;qw($wits %head @tools)&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here the name is the funny bit; the module itself is all business.&lt;/p&gt;
&lt;p&gt;Well, that seems like enough to get you started. If you find others, post them here in the comments!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>AngularJS &amp; Dancer for Modern Web Development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/10/angularjs-dancer-for-modern-web/"/>
      <id>https://www.endpointdev.com/blog/2015/10/angularjs-dancer-for-modern-web/</id>
      <published>2015-10-30T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;At the &lt;a href=&#34;http://perl.dance&#34;&gt;Perl Dancer Conference 2015&lt;/a&gt;, I gave a talk on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/11-angularjs-%26-dancer-for-modern-web-development&#34;&gt;AngularJS &amp;amp; Dancer for Modern Web Development&lt;/a&gt;&lt;/em&gt;. This is a write-up of the talk in blog post form.&lt;/p&gt;
&lt;h3 id=&#34;legacy-apps&#34;&gt;Legacy Apps&lt;/h3&gt;
&lt;p&gt;It’s a fact of life as a software developer that a lot of us have to work with legacy software. There are many older platforms out there, still being actively used today, and still supporting valid businesses. Thus, legacy apps are unavoidable for many developers. Eventually, older apps are migrated to new platforms. Or they die a slow death. Or else the last developer maintaining the app dies.&lt;/p&gt;
&lt;h3 id=&#34;oh-to-migrate&#34;&gt;Oh, to migrate&lt;/h3&gt;
&lt;p&gt;It would be wonderful if I could migrate every legacy app I work on to something like &lt;a href=&#34;http://perldancer.org/&#34;&gt;Perl Dancer&lt;/a&gt;. This isn’t always practical, but a developer can dream, right?&lt;/p&gt;
&lt;p&gt;Of course, every circumstance is different. At the very least, it is helpful to consider ways that old apps can be migrated. Using new technologies can speed development, give you new features, and breathe new life into a project, often attracting new developers.&lt;/p&gt;
&lt;p&gt;As I considered how to prepare my app for migration, here are a few things I came up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Break out of the Legacy App Paradigm
&lt;ul&gt;
&lt;li&gt;Consider that there are better ways to do things than the way they’ve always been done&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use Modern Perl
&lt;ul&gt;
&lt;li&gt;Use &lt;a href=&#34;http://www.modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html&#34;&gt;object-oriented Perl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://metacpan.org/pod/Test::Stream&#34;&gt;tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://metacpan.org/&#34;&gt;CPAN&lt;/a&gt; modules and don’t reinvent the wheel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Organize business logic
&lt;ul&gt;
&lt;li&gt;Try to avoid placing logic in front-end code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;you-are-in-a-legacy-codebase&#34;&gt;You are in a legacy codebase&lt;/h3&gt;
&lt;p&gt;I explored how to start using testing, but I soon realized that this requires methods or subroutines. This was the sad realization that up till now, my life as a Perl programmer had been spent doing scripting. My code wasn’t testable, and looked like a relic with business logic strewn about.&lt;/p&gt;
&lt;h3 id=&#34;change&#34;&gt;Change&lt;/h3&gt;
&lt;p&gt;I set out to change my ways. I started exploring &lt;a href=&#34;http://perldoc.perl.org/perlootut.html&#34;&gt;object-oriented Perl&lt;/a&gt; using &lt;a href=&#34;https://metacpan.org/pod/Moo&#34;&gt;Moo&lt;/a&gt;, since Dancer2 uses Moo. I started trying to write unit tests, and started to use classes and methods in my code.&lt;/p&gt;
&lt;p&gt;Essentially, I began breaking down problems into smaller problems. This, after all, is how the best methods are written: short and simple, that do &lt;a href=&#34;https://en.wikipedia.org/wiki/Single_responsibility_principle&#34;&gt;just one thing&lt;/a&gt;. I found that writing code this way was fun.&lt;/p&gt;
&lt;h3 id=&#34;crash&#34;&gt;Crash&lt;/h3&gt;
&lt;p&gt;I quickly realized that I wasn’t able to run tests in my Legacy App, as it couldn’t be called from the command line (at least not out of the box, and not without weird hacks). Thus, if my modules depended on Legacy App code, I wouldn’t be able to call them from tests, because I couldn’t run these tests from the shell.&lt;/p&gt;
&lt;p&gt;This led me to a further refinement: &lt;em&gt;abstract away all Legacy App-specific code from my modules&lt;/em&gt;. Or, at least all the modules I could (I would still need a few modules to rely on the Legacy App, or else I wouldn’t be using it it all). This was a good idea, it turned out, as it follows the principle of &lt;a href=&#34;https://en.wikipedia.org/wiki/Separation_of_concerns&#34;&gt;Separation of Concerns&lt;/a&gt;, and the idea of &lt;em&gt;Web App + App&lt;/em&gt;, which was mentioned frequently at the conference.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Now&lt;/em&gt; I was able to run tests on my modules!&lt;/p&gt;
&lt;h3 id=&#34;move-already&#34;&gt;Move already&lt;/h3&gt;
&lt;p&gt;This whole process of “getting ready to migrate” soon began to look like &lt;a href=&#34;http://catb.org/jargon/html/Y/yak-shaving.html&#34;&gt;yak shaving&lt;/a&gt;. I realized that I should have moved to Dancer earlier, instead of trying to do weird hacks to get the Legacy App doing things as Dancer would do them.&lt;/p&gt;
&lt;p&gt;However, it was a start, a step in the right direction. Lesson for me, tip for you.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;And&lt;/em&gt;, the result was that my back-end code was all the more ready for working with Dancer. I would just need to change a few things, and presto! (More on this below.)&lt;/p&gt;
&lt;h3 id=&#34;front-end&#34;&gt;Front-End&lt;/h3&gt;
&lt;p&gt;With the back-end looking tidier, I now turned to focus on the front-end. There was a lot of business logic in my front-end code that needed to be cleaned up.&lt;/p&gt;
&lt;p&gt;Here is an example of my Legacy App front-end code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_TOP_@
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;[scratch page_title]&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[perl]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   my $has_course;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   for (grep {$_-&amp;gt;{mv_ib} eq &amp;#39;course&amp;#39;} @$Items) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      $has_course++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   return $has_course ? &amp;#39;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;You have a course!&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;&amp;#39; : &amp;#39;&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/perl]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;Buy [if cgi items]more[else]now[/else][/if]&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_BOTTOM_@&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the Legacy App allowed the embedding of all sorts of code into the HTML page. I had Legacy App tags (in the brackets), plus something called “embedded perl”, plus regular HTML. Add all this together and you get &lt;em&gt;Tag Soup&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This kind of structure won’t look nice if you attempt to view it on your own machine in a web browser, absent from the Legacy App interpreting it. But let’s face it, this code doesn’t look nice anywhere.&lt;/p&gt;
&lt;h3 id=&#34;separation-of-concerns&#34;&gt;Separation of Concerns&lt;/h3&gt;
&lt;p&gt;I thought about how to apply the principle of &lt;em&gt;Separation of Concerns&lt;/em&gt; to my front-end code. One thing I landed on, which isn’t a new idea by any means, is the use of “HTML + placeholders”, whereby I would use some placeholders in my HTML, to be later replaced and filled in with data. Here is my first attempt at that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_TOP_@
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[my-tag-attr-list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    page_title=&amp;#34;[scratch page_title]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    has_course=&amp;#34;[perl] ... [/perl]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    buy_phrase=&amp;#34;Buy [if cgi items]more[else]now[/else][/if]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;{PAGE_TITLE}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {HAS_COURSE?}&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;You have a course!&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;{/HAS_COURSE?}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;{BUY_PHRASE}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/my-tag-attr-list]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_BOTTOM_@&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What I have here uses the Legacy App’s built-in placeholder system. It attempts to set up all the code in the initial “my-tag-attr-list”, then the HTML uses placeholders (in braces) which get replaced upon the page being rendered. (The question-mark in the one placeholder is a conditional.)&lt;/p&gt;
&lt;p&gt;This worked OK. However, the logic was &lt;em&gt;still&lt;/em&gt; baked into the HTML page. I wondered how I could be more ready for Dancer? (Again, I should have just gone ahead and migrated.) I considered using &lt;a href=&#34;http://p3rl.org/Template::Toolkit&#34;&gt;Template::Toolkit&lt;/a&gt;, since it is used in Dancer, but it would be hard to add to my Legacy App.&lt;/p&gt;
&lt;h3 id=&#34;enter-angularjs-or-your-favorite-javascript-framework&#34;&gt;Enter AngularJS (or your favorite JavaScript framework)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://angularjs.org/&#34;&gt;AngularJS&lt;/a&gt; is a JavaScript framework for front-end code. It displays data on your page, which it receives from your back-end via &lt;a href=&#34;http://json.org/&#34;&gt;JSON&lt;/a&gt; feeds. This effectively allows you to separate your front-end from your back-end. It’s almost as if your front-end is consuming an API. (Novel idea!)&lt;/p&gt;
&lt;p&gt;After implementing AngularJS, my Legacy App page looked like this (not showing JavaScript):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_TOP_@
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;ng-bind&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;page.title&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;ng-if&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;items.course&amp;#34;&lt;/span&gt;&amp;gt;You have a course!&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;ng-show&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&amp;gt;Buy more&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;ng-hide&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;items&amp;#34;&lt;/span&gt;&amp;gt;Buy now&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@_BOTTOM_@&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now all my Legacy App is doing for the front-end is basically “includes” to get the header/footer (the TOP and BOTTOM tags). The rest is HTML code with ng- attributes. These are what AngularJS uses to “do” things.&lt;/p&gt;
&lt;p&gt;This is much cleaner than before. I am still using the Legacy App back-end, but all it has to do is “routing” to call the right module and deliver JSON (and do authentication).&lt;/p&gt;
&lt;p&gt;Here’s a quick example of how the JavaScript might look:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;ng-app&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;MyApp&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;angular.min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  angular.module / factory / controller
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  $scope.items = ...;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is very simplified, but via its modules/factories/controllers, the AngularJS code handles how the JSON feeds are displayed in the page. It pulls in the JSON and can massage it for use by the ng- attributes, etc.&lt;/p&gt;
&lt;p&gt;I don’t have to use AngularJS to do this—​I could use a Template::Toolkit template delivered by Dancer, or any number of other templating systems. However, I like this method, because it doesn’t require a Perl developer to use. Rather, any competent JavaScript developer can take this and run with it.&lt;/p&gt;
&lt;h3 id=&#34;migration&#34;&gt;Migration&lt;/h3&gt;
&lt;p&gt;Now the migration of my entire app to Dancer is much easier. I gave it a whirl with a handful of routes and modules, to test the waters. It went great.&lt;/p&gt;
&lt;p&gt;For my modules that were the “App” (not the “Web App” and dependent on the Legacy App), very few changes were necessary. Here is an example of my original module:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Feedback&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$app&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;( ... );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;list&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$app&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Need code&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$rows&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$app&lt;/span&gt;-&amp;gt;dbh(&lt;span style=&#34;color:#369&#34;&gt;$feedback_table&lt;/span&gt;)-&amp;gt;...;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$rows&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You’ll see that I am using a class called MyApp. I did this to get a custom die and a database handle. This isn’t really the proper way to do this (I’m learning), but it worked at the time.&lt;/p&gt;
&lt;p&gt;Now, after converting that module for use with Dancer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Feedback&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Moo&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;with &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::&lt;/span&gt;HasDatabase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;list&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;die&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Need code&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$rows&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;dbh&lt;/span&gt;-&amp;gt;...;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$rows&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My custom die has been replaced with a Perl die. Also, I am now using a &lt;a href=&#34;https://metacpan.org/pod/Moo::Role&#34;&gt;Moo::Role&lt;/a&gt; for my database handle. And that’s all I changed!&lt;/p&gt;
&lt;h4 id=&#34;before&#34;&gt;Before&lt;/h4&gt;
&lt;p&gt;The biggest improvements were in things that I “stole” from Dancer. (Naturally, Dancer would do things better than I.) This is my Legacy App’s route for displaying and accepting feedback entries. It does not show any authentication checks. It handles feeding back an array of entries for an item (“list”), a single entry (GET), and saving an entry (POST):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;_route_feedback&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;shift&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; (&lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$sub_action&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt;) = &lt;span style=&#34;color:#038&#34;&gt;split&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;route;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt; ||= &lt;span style=&#34;color:#369&#34;&gt;$sub_action&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_status(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;400 Bad Request&amp;#39;&lt;/span&gt;);   &lt;span style=&#34;color:#888&#34;&gt;# start with 400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Feedback&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$sub_action&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        when (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;list&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedbacks&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;list(&lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_tmp( to_json(&lt;span style=&#34;color:#369&#34;&gt;$feedbacks&lt;/span&gt;) );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;special/json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_content_type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;application/json; charset=UTF-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_status(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedbacks&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;method) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                when (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;get(&lt;span style=&#34;color:#369&#34;&gt;$code&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#080&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_route_error;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_tmp( to_json(&lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;) );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;special/json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_content_type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;application/json; charset=UTF-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_status(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                when (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$params&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;body_parameters
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#080&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_route_error;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$params&lt;/span&gt; = from_json(&lt;span style=&#34;color:#369&#34;&gt;$params&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;save(&lt;span style=&#34;color:#369&#34;&gt;$params&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_status(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;200 OK&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;special/json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;_set_content_type(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;application/json; charset=UTF-8&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;after&#34;&gt;After&lt;/h4&gt;
&lt;p&gt;Here are those same routes in Dancer:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;prefix &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/feedback&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp::Feedback&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    get &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/list/:id&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;list( param &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    get &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/:code&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;get( param &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;code&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$feedback&lt;/span&gt;-&amp;gt;save( &lt;span style=&#34;color:#038&#34;&gt;scalar&lt;/span&gt; params );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dancer gives me a lot for free. It is a &lt;em&gt;lot&lt;/em&gt; simpler. There’s still no authentication shown here, but everything else is done. (And I can use an &lt;a href=&#34;http://p3rl.org/Dancer2::Plugin::Auth::Extensible&#34;&gt;authentication plugin&lt;/a&gt; to make even that easy.)&lt;/p&gt;
&lt;h3 id=&#34;tmtowtdi&#34;&gt;TMTOWTDI&lt;/h3&gt;
&lt;p&gt;For the front-end, we have options on how to use Dancer. We could have Dancer deliver the HTML files that contain AngularJS. Or, we could have the web server deliver them, as there is nothing special about them that says Dancer must deliver them. In fact, this is especially easy if our AngularJS code is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Single-page_application&#34;&gt;Single Page App&lt;/a&gt;, which is a single static HTML file with AngularJS “routes”. If we did this, and needed to handle authentication, we could look at using &lt;a href=&#34;http://jwt.io/&#34;&gt;JSON Web Tokens&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;now-starring-dancer&#34;&gt;Now starring Dancer&lt;/h3&gt;
&lt;p&gt;In hindsight, I probably should have moved to Dancer right away. The Legacy App was a pain to work with, as I built my own Routing module for it, and I also built my own Auth checking module. Dancer makes all this simpler.&lt;/p&gt;
&lt;p&gt;In the process, though, I learned something&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;dancer-is-better&#34;&gt;Dancer is better?&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;I learned you can use tools improperly&lt;/em&gt;. You can do Dancer “wrong”. You can write tag soup in anything, even the best modern tools.&lt;/p&gt;
&lt;p&gt;You can stuff all your business logic into Template::Toolkit tags. You can stuff logic into Dancer routes. You can do AngularJS “wrong” (I probably do).&lt;/p&gt;
&lt;h3 id=&#34;dancer-is-better-1&#34;&gt;Dancer is better:&lt;/h3&gt;
&lt;p&gt;Dancer is better when (thanks to &lt;a href=&#34;https://twitter.com/shadowcat_mst&#34;&gt;Matt S Trout&lt;/a&gt; for these):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Routes contain code specific to the Web.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routes call non-Dancer modules&lt;/strong&gt; (where business logic lives; again, &lt;em&gt;Web App + App&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The route returns the data in the appropriate format.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These make it easy to test. You are effectively talking to your back-end code as if it’s an API. &lt;em&gt;Because it is.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The point is: start improving somewhere. Maybe you cannot write tests in everything, but you can try to write smart code.&lt;/p&gt;
&lt;h3 id=&#34;lessons-learned&#34;&gt;Lessons learned&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Separate concerns&lt;/li&gt;
&lt;li&gt;Keep it testable&lt;/li&gt;
&lt;li&gt;Just start somewhere&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The end. Or maybe the beginning&amp;hellip;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl Dancer Conference 2015 Report—​Conference Days</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/10/perl-dancer-conference-2015-report_30/"/>
      <id>https://www.endpointdev.com/blog/2015/10/perl-dancer-conference-2015-report_30/</id>
      <published>2015-10-30T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;In my &lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report/&#34;&gt;last post&lt;/a&gt;, I shared about the Training Days from the &lt;a href=&#34;https://www.perl.dance/&#34;&gt;Perl Dancer&lt;/a&gt; 2015 conference, in Vienna, Austria. This post will cover the two days of the conference itself.&lt;/p&gt;
&lt;p&gt;While there were &lt;em&gt;several&lt;/em&gt; wonderful talks, &lt;a href=&#34;https://www.perl.dance/users/21&#34;&gt;Gert van der Spoel&lt;/a&gt; did a great job of writing recaps of all of them (&lt;a href=&#34;https://www.perl.dance/wiki/node/2015%20Day%201%20Summary&#34;&gt;Day 1&lt;/a&gt;, &lt;a href=&#34;https://www.perl.dance/wiki/node/2015%20Day%202%20Summary&#34;&gt;Day 2&lt;/a&gt;), so here I’ll cover the ones that stood out most to me.&lt;/p&gt;
&lt;h3 id=&#34;day-one&#34;&gt;Day One&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center; float:right&#34;&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-0.jpeg&#34;/&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;small&gt;&lt;a href=&#34;https://twitter.com/sukria/status/657098210989776896&#34;&gt;Dancer Conference, by Alexis Sukrieh&lt;/a&gt; (used with permission)&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/PerlSawyer&#34;&gt;Sawyer X&lt;/a&gt; spoke on the &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/17-state-of-dancer&#34;&gt;State of Dancer&lt;/a&gt;&lt;/em&gt;. One thing mentioned, which came up again later in the conference, was: &lt;strong&gt;Make the effort, move to Dancer 2! Dancer 1 is frozen.&lt;/strong&gt; There have been some recent changes to Dancer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Middlewares for static files, so these are handled outside of Dancer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;New &lt;a href=&#34;http://p3rl.org/Hash::MultiValue&#34;&gt;Hash::MultiValue&lt;/a&gt; parameter keywords (route_parameters, query_parameters, body_parameters; covered in my &lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report/&#34;&gt;earlier post&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://metacpan.org/pod/Dancer2::Manual#Delayed-responses-Async-Streaming&#34;&gt;Delayed responses&lt;/a&gt; (asynchronous) with delayed keyword:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Runs on the server after the request has finished.&lt;/li&gt;
&lt;li&gt;Streaming is also asynchronous, feeding the user chunks of data at a time.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Items coming soon to Dancer may include: Web Sockets (supported in &lt;a href=&#34;http://p3rl.org/Plack&#34;&gt;Plack&lt;/a&gt;), per-route &lt;a href=&#34;https://metacpan.org/pod/Dancer2::Manual#Serializers1&#34;&gt;serialization&lt;/a&gt; (currently enabling a serializer such as JSON affects the entire app—​later on, &lt;a href=&#34;https://twitter.com/veryrusty&#34;&gt;Russell&lt;/a&gt; &lt;a href=&#34;http://p3rl.org/Dancer2::Plugin::SendAs&#34;&gt;released a module&lt;/a&gt; for this, which may make it back into the core), Dancer2::XS, and &lt;a href=&#34;https://github.com/PerlDancer/perl-lint-policy-dancer2&#34;&gt;critic/linter policies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/domm_favors_irc&#34;&gt;Thomas Klausner&lt;/a&gt; shared about &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/18-oauth2%2C-resty-apis%2C-microservices&#34;&gt;OAuth &amp;amp; Microservices&lt;/a&gt;&lt;/em&gt;. Microservices are a good tool to manage complexity, but you might want to aim for “monolith first”, &lt;a href=&#34;http://martinfowler.com/bliki/MonolithFirst.html&#34;&gt;according to Martin Fowler&lt;/a&gt;, and only later break up your app into microservices. In the old days, we had “fat” back-ends, which did everything and delivered the results to a browser. Now, we have “fat” front-ends, which take info from a back-end and massage it for display. One advantage of the microservice way of thinking is that mobile devices (or even third parties) can access the same APIs as your front-end website.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://oauth.net/&#34;&gt;OAuth&lt;/a&gt; allows a user to login at your site, using their credentials from another site (such as Facebook or Google), so they don’t need a password for your site itself. This typically happens via JavaScript and cookies. However, to make your back-end “stateless”, you could use &lt;a href=&#34;https://jwt.io/&#34;&gt;JSON Web Tokens&lt;/a&gt; (JWT). Thomas showed some examples of all this in action, using the &lt;a href=&#34;http://p3rl.org/OX&#34;&gt;OX Perl module&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing I found interesting that Thomas mentioned: &lt;em&gt;Plack middleware is the correct place to implement most of the generic part of a web app. The framework is the wrong part.&lt;/em&gt; I think this mindset goes along with Sawyer’s comments about &lt;em&gt;Web App + App&lt;/em&gt; in the Training Days.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/0xMickey&#34;&gt;Mickey Nasriachi&lt;/a&gt; &lt;a href=&#34;https://www.perl.dance/talks/25-ponapi%3A-eliminate-the-bikesheding&#34;&gt;shared&lt;/a&gt; his development on &lt;a href=&#34;https://github.com/mickeyn/ponapi&#34;&gt;PONAPI&lt;/a&gt;, which implements the &lt;a href=&#34;http://jsonapi.org/&#34;&gt;JSON API&lt;/a&gt; specification in Perl. The JSON API spec is a standard for creating APIs. It essentially absolves you from having to make decisions about how you should structure your API.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center; float:right&#34;&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-1-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-1.jpeg&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;small&gt;Panorama from the south tower of St. Stephen’s cathedral, by this author&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;Gert presented on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/9-social-logins-for-e-commerce-sites&#34;&gt;Social Logins &amp;amp; eCommerce&lt;/a&gt;&lt;/em&gt;. This built on the earlier OAuth talk by Thomas. Here are some of the pros/cons to social login which Gert presented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pros—​customer:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Alleviates “password fatigue”&lt;/li&gt;
&lt;li&gt;Convenience&lt;/li&gt;
&lt;li&gt;Brand familiarity (with the social login provider)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pros—​eCommerce website:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Expected customer retention&lt;/li&gt;
&lt;li&gt;Expected increase in sales&lt;/li&gt;
&lt;li&gt;Better target customers&lt;/li&gt;
&lt;li&gt;“Plug &amp;amp; Play” (if you pay)—​some services exist to make it simple to integrate social logins, where you just integrate with them, and then you are effectively integrated with whatever social login providers they support. These include Janrain and LoginRadius&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons—​customer:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Privacy concerns (sharing their social identity with your site)&lt;/li&gt;
&lt;li&gt;Security concerns (if their social account is hacked, so are all their accounts where they have used their social login)&lt;/li&gt;
&lt;li&gt;Confusion (especially on how to leave a site)&lt;/li&gt;
&lt;li&gt;Usefulness (no address details are provided by the social provider in the standard scope, so the customer still has to enter extra details on your site)&lt;/li&gt;
&lt;li&gt;Social account hostages (if you’ve used your social account to login elsewhere, you are reluctant to shut down your social account)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons—​eCommerce website:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Legal implications&lt;/li&gt;
&lt;li&gt;Implementation hurdles&lt;/li&gt;
&lt;li&gt;Usefulness&lt;/li&gt;
&lt;li&gt;Provider problem is your problem (e.g., if the social login provider goes down, all your customers who use it to login are unable to login to your site)&lt;/li&gt;
&lt;li&gt;Brand association (maybe you don’t want your site associated with certain social sites)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons—​social provider:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Šimun Kodžoman spoke on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/22-dancer-%2B-meteor-%3D-mobile-app&#34;&gt;Dancer + Meteor = mobile app&lt;/a&gt;&lt;/em&gt;. &lt;a href=&#34;https://www.meteor.com/&#34;&gt;Meteor&lt;/a&gt; is a JavaScript framework for both server-side and client-side. It seems one of the most interesting aspects is you can use Meteor with the Android or iOS SDK to auto-generate a true mobile app, which has many more advantages than a simple HTML “app” created with &lt;a href=&#34;http://phonegap.com/&#34;&gt;PhoneGap&lt;/a&gt;. Šimun is using Dancer as a back-end for Meteor, because the server-side Meteor aspect is still new and unstable, and is also dependent on &lt;a href=&#34;https://www.mongodb.org/&#34;&gt;MongoDB&lt;/a&gt;, which cannot be used for everything.&lt;/p&gt;
&lt;p&gt;End Point’s own Sam Batschelet shared his work on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/4-space-camp---the-final-frontier&#34;&gt;Space Camp&lt;/a&gt;&lt;/em&gt;, a new container-based setup for development environments. This pulls together several pieces, including &lt;a href=&#34;https://coreos.com/&#34;&gt;CoreOS&lt;/a&gt;, &lt;a href=&#34;http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html&#34;&gt;systemd-nspawn&lt;/a&gt;, and &lt;a href=&#34;https://coreos.com/etcd/&#34;&gt;etcd&lt;/a&gt; to provide a futuristic version of &lt;a href=&#34;http://www.devcamps.org/&#34;&gt;DevCamps&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;day-two&#34;&gt;Day Two&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center; float:right&#34;&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-2-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/image-2.jpeg&#34;/&gt;&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;&lt;small&gt;&lt;a href=&#34;https://twitter.com/sbatschelet/status/657493819135541248&#34;&gt;Conference goers, by Sam&lt;/a&gt;&lt;br/&gt;(used with permission)&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/pullingshots&#34;&gt;Andrew Baerg&lt;/a&gt; spoke on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/20-taming-a-thousand-pound-gorilla&#34;&gt;Taming the 1000-lb Gorilla&lt;/a&gt;&lt;/em&gt; that is &lt;a href=&#34;https://www.interchangecommerce.org/&#34;&gt;Interchange 5&lt;/a&gt;. He shared how they have endeavored to manage their Interchange development in more modern ways, such as using unit tests and &lt;a href=&#34;http://p3rl.org/DBIx::Class&#34;&gt;DBIC&lt;/a&gt;. One item I found especially interesting was the use of &lt;a href=&#34;http://p3rl.org/DBIx::Class::Fixtures&#34;&gt;DBIx::Class::Fixtures&lt;/a&gt; to allow saving bits of information from a database to keep with a test. This is helpful when you have a bug from some database entry which you want to fix and ensure stays fixed, as databases can change over time, and without a “fixture” your test would not be able to run.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/veryrusty&#34;&gt;Russell Jenkins&lt;/a&gt; shared &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/24-howto-contributeto-dancer2&#34;&gt;HowTo Contribute to Dancer 2&lt;/a&gt;&lt;/em&gt;. He went over the use of &lt;a href=&#34;https://git-scm.com/&#34;&gt;Git&lt;/a&gt;, including such helpful commands and tips as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;git status &amp;ndash;short &amp;ndash;branch&lt;/li&gt;
&lt;li&gt;Write good commit messages: one line summary, less than 50 characters; longer description, wrapped to 72 characters; refer to and/or close issues&lt;/li&gt;
&lt;li&gt;Work in a branch (you shall not commit to master)&lt;/li&gt;
&lt;li&gt;“But I committed to master” &amp;ndash;&amp;gt; branch and reset&lt;/li&gt;
&lt;li&gt;git log &amp;ndash;oneline &amp;ndash;since=2.weeks&lt;/li&gt;
&lt;li&gt;git add &amp;ndash;fixup &lt;SHA1 hash&gt;&lt;/li&gt;
&lt;li&gt;The use of branches named with “feature/whatever” or “bugfix/whatever” can be helpful (this is Russell’s convention)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several &lt;a href=&#34;https://github.com/PerlDancer/Dancer2/issues?q=is%3Aopen+is%3Aissue+label%3A%22Beginner+Suitable%22&#34;&gt;Dancer 2 issues tagged “beginner suitable”&lt;/a&gt;, so it is easy for nearly anyone to contribute. The &lt;a href=&#34;http://perldancer.org/&#34;&gt;Dancer website&lt;/a&gt; is &lt;a href=&#34;https://github.com/PerlDancer/perldancer-website&#34;&gt;also on GitHub&lt;/a&gt;. You can even make simple edits directly in GitHub!&lt;/p&gt;
&lt;p&gt;It was great to have the author of Dancer, &lt;a href=&#34;https://twitter.com/sukria&#34;&gt;Alexis Sukrieh&lt;/a&gt;, in attendance. He shared his original vision for Dancer, which filled a gap in the Perl ecosystem back in 2009. The goal for Dancer was to create a DSL (&lt;a href=&#34;https://en.wikipedia.org/wiki/Domain-specific_language&#34;&gt;Domain-specific language&lt;/a&gt;) to provide a very simple way to develop web applications. The DSL provides “&lt;a href=&#34;https://metacpan.org/pod/Dancer2::Manual#DSL-KEYWORDS&#34;&gt;keywords&lt;/a&gt;” for use in the Dancer app, which are specific to Dancer (basically extra functionality for Perl). One of the core aspects of keeping it simple was to avoid the use of $self (a standby of object-oriented Perl, one of the things that you just “have to do”, typically).&lt;/p&gt;
&lt;p&gt;Alexis mentioned that &lt;strong&gt;Dancer 1 is frozen—​Dancer 2 full-speed ahead!&lt;/strong&gt; He also shared some of his learnings along the way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fill a gap (define clearly the problem, present your solution)&lt;/li&gt;
&lt;li&gt;Stick to your vision&lt;/li&gt;
&lt;li&gt;Code is not enough (opensource needs attention; marketing matters)&lt;/li&gt;
&lt;li&gt;Meet in person (collaboration is hard; online collaboration is very hard)&lt;/li&gt;
&lt;li&gt;Kill the ego—​you are not your code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While at the conference, Alexis even wrote a Dancer2 plugin, &lt;a href=&#34;https://metacpan.org/pod/Dancer2::Plugin::ProbabilityRoute&#34;&gt;Dancer2::Plugin::ProbabilityRoute&lt;/a&gt;, which allows you to do &lt;a href=&#34;https://en.wikipedia.org/wiki/A/B_testing&#34;&gt;A/B Testing&lt;/a&gt; in your Dancer app. (Another similar plugin is &lt;a href=&#34;https://metacpan.org/pod/Dancer2::Plugin::Sixpack&#34;&gt;Dancer2::Plugin::Sixpack&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Also check out &lt;a href=&#34;https://web.archive.org/web/20151108214937/http://blog.sukria.net/2015/10/22/perl-dancer-2015-report/&#34;&gt;Alexis’ recap&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I was privileged to speak as well, on &lt;em&gt;&lt;a href=&#34;https://www.perl.dance/talks/11-angularjs-%26-dancer-for-modern-web-development&#34;&gt;AngularJS &amp;amp; Dancer for Modern Web Development&lt;/a&gt;&lt;/em&gt;. Since this post is already pretty long, I’ll save the details for &lt;a href=&#34;/blog/2015/10/angularjs-dancer-for-modern-web/&#34;&gt;another post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;In summary, the Perl Dancer conference was a great time of learning and building community. If I had to wrap it all up in one insight, it would be: &lt;strong&gt;Web App + App&lt;/strong&gt;—​that is, your &lt;strong&gt;application should be a compilation of: Plack middleware, Web App (Dancer), and App (Perl classes and methods)&lt;/strong&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl Dancer Conference 2015 Report — Training Days</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/10/perl-dancer-conference-2015-report/"/>
      <id>https://www.endpointdev.com/blog/2015/10/perl-dancer-conference-2015-report/</id>
      <published>2015-10-28T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;I just returned from the &lt;a href=&#34;https://www.perl.dance/&#34;&gt;Perl Dancer Conference&lt;/a&gt;, held in Vienna, Austria. It was a jam-packed schedule of two days of training and two conference days, with five of the nine Dancer core developers in attendance.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34; title=&#34;Vienna&#34;&gt;&lt;img alt=&#34;[image of Vienna]&#34; border=&#34;0&#34; src=&#34;/blog/2015/10/perl-dancer-conference-2015-report/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;small&gt;Kohlmarkt street, Wien, by this author&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;If you aren’t familiar with &lt;a href=&#34;http://www.perldancer.org/&#34;&gt;Perl Dancer&lt;/a&gt;, it is a modern framework for Perl for building web applications. Dancer1 originated as a port of Ruby’s Sinatra project, but has officially been replaced with a rewrite called Dancer2, based on &lt;a href=&#34;https://metacpan.org/pod/Moo&#34;&gt;Moo&lt;/a&gt;, with Dancer1 being frozen and only receiving security fixes. The Interchange 5 e-commerce package is gradually being replaced by Dancer plugins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 1&lt;/strong&gt; began with a training on Dancer2 by &lt;a href=&#34;https://twitter.com/PerlSawyer&#34;&gt;Sawyer X&lt;/a&gt; and &lt;a href=&#34;https://twitter.com/0xMickey&#34;&gt;Mickey Nasriachi&lt;/a&gt;, two Dancer core devs. During the training, the attendees worked on adding functionality to a &lt;a href=&#34;https://github.com/xsawyerx/dancer-training-vienna&#34;&gt;sample Dancer app&lt;/a&gt;. Some of my takeaways from the training:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Think of your app as a Dancer Web App &lt;em&gt;plus&lt;/em&gt; an App. These should ideally be two separate things, where the Dancer web app provides the URL routes for interaction with your App.&lt;/li&gt;
&lt;li&gt;The lib directory contains all of your application. The recommendation for large productions is to separate your app into separate namespaces and classes. Some folks use a routes directory just for routing code, with lib reserved for the App itself.&lt;/li&gt;
&lt;li&gt;It is recommended to add an empty .dancer file to your app’s directory, which indicates that this is a Dancer app (other Perl frameworks do similarly).&lt;/li&gt;
&lt;li&gt;When running your Dancer app in development, you can use plackup -R lib bin/app.psgi which will restart the app automatically whenever something changes in lib.&lt;/li&gt;
&lt;li&gt;Dancer handles all the standard HTTP verbs, except note that we must use del, not &lt;em&gt;delete&lt;/em&gt;, as &lt;em&gt;delete&lt;/em&gt; conflicts with the Perl keyword.&lt;/li&gt;
&lt;li&gt;There are new keywords for retrieving parameters in your routes. Whereas before we only had param or params, it is now recommended to use:
&lt;ul&gt;
&lt;li&gt;route_parameters,&lt;/li&gt;
&lt;li&gt;query_parameters, or&lt;/li&gt;
&lt;li&gt;body_parameters&lt;/li&gt;
&lt;li&gt;all of which can be used with -&amp;gt;get(&amp;lsquo;foo&amp;rsquo;) which is always a single scalar, or -&amp;gt;get_all(&amp;lsquo;foo&amp;rsquo;) which is always a list.&lt;/li&gt;
&lt;li&gt;These allow you to specify which area you want to retrieve parameters from, instead of being unsure which param you are getting, if identical names are used in multiple areas.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Day 2&lt;/strong&gt; was &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt; training, led by &lt;a href=&#34;https://twitter.com/PerlRacke&#34;&gt;Stefan Hornburg&lt;/a&gt; and Peter Mottram, with assistance from &lt;a href=&#34;https://twitter.com/ribasushi&#34;&gt;Peter Rabbitson&lt;/a&gt;, the DBIx::Class maintainer.&lt;/p&gt;
&lt;p&gt;DBIx::Class (a.k.a. DBIC) is an &lt;a href=&#34;https://en.wikipedia.org/wiki/Object-relational_mapping&#34;&gt;Object Relational Mapper&lt;/a&gt; for Perl. It exists to provide a standard, object-oriented way to deal with SQL queries. I am new to DBIC, and it was a lot to take in, but at least one advantage I could see was helping a project be able to change database back-ends, without having to rewrite code (cue PostgreSQL vs MySQL arguments).&lt;/p&gt;
&lt;p&gt;I took copious notes, but it seems that the true learning takes place only as one begins to implement and experiment. Without going into too much detail, some of my notes included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Existing projects can use dbicdump to quickly get a DBIC schema from an existing database, which can be modified afterwards. For a new project, it is recommended to write the schema first.&lt;/li&gt;
&lt;li&gt;DBIC allows you to place business logic in your application (not your web application), so it is easier to test (once again, the recurring theme of &lt;em&gt;Web App + App&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;ResultSet&lt;/em&gt; is a representation of a query before it happens. On any ResultSet you can call -&amp;gt;as_query to find the actual SQL that is to be executed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://p3rl.org/DBIx::Class::Schema::Config&#34;&gt;DBIx::Class::Schema::Config&lt;/a&gt; provides credential management for DBIC, and allows you to move your DSN/username/password out of your code, which is especially helpful if you use Git or a public GitHub.&lt;/li&gt;
&lt;li&gt;DBIC is all about relationships (belongs_to, has_many, might_have, and has_one). many_to_many is not a relationship per se but a convenience.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://p3rl.org/DBIx::Class::Candy&#34;&gt;DBIx::Class::Candy&lt;/a&gt; provides prettier, more modern metadata, but cannot currently be generated by dbicdump.&lt;/li&gt;
&lt;li&gt;For deployment or migration, two helpful tools are &lt;a href=&#34;http://sqitch.org/&#34;&gt;Sqitch&lt;/a&gt; and &lt;a href=&#34;http://p3rl.org/DBIx::Class::DeploymentHandler&#34;&gt;DBIx::Class::DeploymentHandler&lt;/a&gt;. Sqitch is better for raw SQL, while DeploymentHandler is for DBIC-managed databases. These provide easy ways to migrate, deploy, upgrade, or downgrade a database.&lt;/li&gt;
&lt;li&gt;Finally, &lt;a href=&#34;http://www.rapidapp.info/&#34;&gt;RapidApp&lt;/a&gt; can read a database file or DBIC schema and provide a nice web interface for interacting with a database. As long as you define your columns properly, RapidApp can generate image fields, rich-text editors, date-pickers, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The training days were truly like drinking from a firehose, with so much good information. I am looking forward to putting this into practice!&lt;/p&gt;
&lt;p&gt;Stay tuned for my &lt;a href=&#34;/blog/2015/10/perl-dancer-conference-2015-report_30/&#34;&gt;next blog post&lt;/a&gt; on the Conference Days.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>YAPC::NA 2015 Conference Report</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/09/yapcna-2015-conference-report/"/>
      <id>https://www.endpointdev.com/blog/2015/09/yapcna-2015-conference-report/</id>
      <published>2015-09-18T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;In June, I attended the &lt;a href=&#34;http://www.yapcna.org/yn2015/&#34;&gt;Yet Another Perl Conference (North America)&lt;/a&gt;, held in Salt Lake City, Utah. I was able to take in a training day on &lt;a href=&#34;https://metacpan.org/pod/Moose&#34;&gt;Moose&lt;/a&gt;, as well as the full 3-day conference.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.yapcna.org/yn2015/masters.html#Moose&#34;&gt;Moose Master Class&lt;/a&gt; (&lt;a href=&#34;https://github.com/moose/intro-to-moose&#34;&gt;slides and exercises here&lt;/a&gt;) was taught by Dave Rolsky (a Moose core developer), and was a full day of hands-on training and exercises in the Moose object-oriented system for Perl 5. I’ve been experimenting a bit this year with the related project &lt;a href=&#34;https://metacpan.org/pod/Moo&#34;&gt;Moo&lt;/a&gt; (essentially the best two-thirds of Moose, with quicker startup), and most of the concepts carry over, with just slight differences.&lt;/p&gt;
&lt;p&gt;Moose and Moo allow the modern Perl developer to quickly write &lt;a href=&#34;http://www.modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html&#34;&gt;OO Perl code&lt;/a&gt;, saving quite a bit of work from the &lt;a href=&#34;http://perltricks.com/article/25/2013/5/20/Old-School-Object-Oriented-Perl&#34;&gt;older&lt;/a&gt; “&lt;a href=&#34;https://perlmaven.com/getting-started-with-classic-perl-oop&#34;&gt;classic&lt;/a&gt;” methods of writing OO Perl. Some of the highlights of the Moose class include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sub-classing is discouraged; this is better done using Roles&lt;/li&gt;
&lt;li&gt;Moose eliminates more typing; more typing can often equal more bugs&lt;/li&gt;
&lt;li&gt;Using &lt;a href=&#34;https://metacpan.org/pod/namespace::autoclean&#34;&gt;namespace::autoclean&lt;/a&gt; at the top is a best practice, as it cleans up after Moose&lt;/li&gt;
&lt;li&gt;Roles are what a class &lt;em&gt;does&lt;/em&gt;, not what it &lt;em&gt;is&lt;/em&gt;. Roles add functionality.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&#34;https://en.wikipedia.org/wiki/Type_system&#34;&gt;types&lt;/a&gt; with &lt;a href=&#34;https://metacpan.org/pod/MooseX::Types&#34;&gt;MooseX::Types&lt;/a&gt; or &lt;a href=&#34;https://metacpan.org/pod/Type::Tiny&#34;&gt;Type::Tiny&lt;/a&gt; (for Moo)&lt;/li&gt;
&lt;li&gt;Attributes can be objects (see slide 231)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional helpful resources for &lt;a href=&#34;http://perldoc.perl.org/perlootut.html&#34;&gt;OO Perl&lt;/a&gt; and &lt;a href=&#34;http://kablamo.org/slides-intro-to-moo/#/&#34;&gt;Moo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the YAPC::NA conference days, I attended all joint sessions, and breakout sessions that piqued my interest. Here are some of the things I noted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The author of &lt;a href=&#34;https://metacpan.org/pod/Form::Diva&#34;&gt;Form::Diva&lt;/a&gt; gave a lightning talk (approx. 5 minutes) about this module, which allows easier HTML form creation. I was able to chat with the author during a conference mixer, and the next time I need a long HTML form, I will be giving this a try.&lt;/li&gt;
&lt;li&gt;One lightning talk presenter suggested making &lt;em&gt;comments stand out&lt;/em&gt;, by altering your editor’s code highlight colors. Comments are often muted, but making them more noticeable helps developers, as comments are often important guides to the code.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/tokuhirom/plenv&#34;&gt;plenv&lt;/a&gt; (which allows one to install multiple versions of Perl) can remember which Perl version you want for a certain directory (plenv local)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://metacpan.org/pod/pinto&#34;&gt;pinto&lt;/a&gt; is useful for managing modules for a project&lt;/li&gt;
&lt;li&gt;Sawyer did a &lt;a href=&#34;http://www.yapcna.org/yn2015/talk/6077&#34;&gt;talk on web scraping&lt;/a&gt; in which he demonstrated the use of &lt;a href=&#34;https://metacpan.org/pod/Web::Query&#34;&gt;Web::Query&lt;/a&gt;, which provides jQuery-like syntax for finding elements in the page you wish to scrape. There are many tools for web scraping, but this one seems easy to use, if you know jQuery.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIC’s&lt;/a&gt; “deploy” will create new tables in a database, based on your schema. &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class::Fixtures&#34;&gt;DBIx::Class::Fixtures&lt;/a&gt; can grab certain data into files for tests to use, so you can keep data around to ensure a bug is still fixed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The presenter of &lt;em&gt;&lt;a href=&#34;http://www.yapcna.org/yn2015/talk/6046&#34;&gt;What is this “testing” people keep talking about?&lt;/a&gt;&lt;/em&gt; did a great job researching a topic which he knew nothing about until after his talk was accepted. If there is ever a good way to learn something, it’s teaching it! &lt;a href=&#34;http://deanza.edu/faculty/metcalfkevin/whatistesting.pdf&#34;&gt;Slides are here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.yapcna.org/yn2015/talk/5915&#34;&gt;talk on Docker&lt;/a&gt; (&lt;a href=&#34;https://www.slideshare.net/lembark/perl-inside-a-box-building-perl-for-docker&#34;&gt;slides&lt;/a&gt;) was interesting. Highlights I noted: use &lt;a href=&#34;https://en.wikipedia.org/wiki/BusyBox&#34;&gt;busybox&lt;/a&gt;, then install Perl on top of busybox (you can run &lt;a href=&#34;http://plackperl.org/&#34;&gt;Plack&lt;/a&gt; &lt;em&gt;from&lt;/em&gt; this Perl); Gentoo is easy to dockerize, as about half the size of Ubuntu; &lt;a href=&#34;https://github.com/perl/docker-perl&#34;&gt;Perl Dockerfiles&lt;/a&gt;; build Perl on a local system, then copy to Docker image, in Docker file.&lt;/p&gt;
&lt;p&gt;I attended some talks on the long-awaited Perl 6, which is apparently to be released by the end of this year. While I’m not sure how practical Perl 6 will be for a while, one of the most interesting topics was that Perl 6 knows &lt;a href=&#34;https://www.slideshare.net/Ovid/perl-6-for-mere-mortals&#34;&gt;how to do math&lt;/a&gt;, such as: &lt;code&gt;solve for &amp;quot;x&amp;quot;: x = 7 / 2&lt;/code&gt;. Perl 6 gets this “right”, as far as humans are concerned. It was interesting that many in attendance did not feel the answer should be “3.5”, due to what I suspect is prolonged exposure to how computers do math.&lt;/p&gt;
&lt;p&gt;One talk not related to Perl was &lt;a href=&#34;http://www.yapcna.org/yn2015/talk/6031&#34;&gt;Scrum for One&lt;/a&gt; (&lt;a href=&#34;https://youtu.be/Zh7dXvQY-hg&#34;&gt;video&lt;/a&gt;), which discussed how to use the principles of &lt;a href=&#34;https://en.wikipedia.org/wiki/Scrum_%28software_development%29&#34;&gt;Scrum&lt;/a&gt; in one’s daily life. Helpful hints included thinking of your tasks in the &lt;a href=&#34;https://en.wikipedia.org/wiki/User_story&#34;&gt;User Story&lt;/a&gt; format: “as a $Person, I would like $Thing, so that $Accomplishment”; leave murky stories on the backlog, as you must know what “done” looks like; the current tasks should include things doable in the next week—​this prevents you from worrying about &lt;em&gt;all&lt;/em&gt; tasks in your list. Personally, I’ve started using &lt;a href=&#34;https://trello.com/&#34;&gt;Trello&lt;/a&gt; boards to implement this, such as: Done, Doing, ToDo, Later.&lt;/p&gt;
&lt;p&gt;Finally, while a great technical conference, YAPC’s biggest strength is bringing together the Perl community. I found this evident myself, as I had the opportunity to meet another attendee from my city. We were introduced at the conference, not knowing each other previously. When you have two Perl developers in the same city, it is time to resurrect your local &lt;a href=&#34;https://www.pm.org/&#34;&gt;Perl Mongers&lt;/a&gt; group, which is &lt;a href=&#34;http://bend.pm.org/&#34;&gt;what we did&lt;/a&gt;!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Perl’s CPAN is 20 years old</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/08/perls-cpan-is-20-years-old/"/>
      <id>https://www.endpointdev.com/blog/2015/08/perls-cpan-is-20-years-old/</id>
      <published>2015-08-17T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float: right; text-align: center; margin-bottom: 1em&#34;&gt;&lt;a href=&#34;/blog/2015/08/perls-cpan-is-20-years-old/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 0.5em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/08/perls-cpan-is-20-years-old/image-0.png&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;(Photo by &lt;a href=&#34;https://flic.kr/p/8VPGbE&#34;&gt;emma.kate on Flickr&lt;/a&gt;)&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;This is just a short note to celebrate the fact that &lt;a href=&#34;http://blogs.perl.org/users/neilb/2015/08/cpan-is-20.html&#34;&gt;the Comprehensive Perl Archive Network (CPAN) turned 20 years old&lt;/a&gt; yesterday!&lt;/p&gt;
&lt;p&gt;CPAN began as a way to collect, mirror, and distribute open-source Perl packages. Over time it led to development of better packaging and module naming conventions; formed a community for feedback, testing, and contributions; and became one of the great strengths of the Perl world.&lt;/p&gt;
&lt;p&gt;It is rare to find some needed functionality that is not available on CPAN. These days a more common problem is finding too much choice there, and needing to choose between several modules based on which are better maintained and supported on current versions of Perl, or kept current against external dependencies.&lt;/p&gt;
&lt;p&gt;Perl does not get as much press these days as it once did, but it continues to thrive and improve. On that topic, our former co-worker Steph Skardal sent me an article called &lt;a href=&#34;http://www.virtuouscode.com/2015/08/07/were-still-catching-up-to-perl/&#34;&gt;We’re still catching up to Perl&lt;/a&gt; by Avdi Grimm of Ruby fame. It is not an in-depth language comparison, just a brief observation to his fellow Rubyists that there is plenty to be learned from Perl. (Of course Perl has plenty to learn from Ruby and other languages too.)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Converting from Pivotal Tracker to Trello for project management</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/08/converting-from-pivotal-tracker-to/"/>
      <id>https://www.endpointdev.com/blog/2015/08/converting-from-pivotal-tracker-to/</id>
      <published>2015-08-03T00:00:00+00:00</published>
      <author>
        <name>Josh Lavin</name>
      </author>
      <content type="html">
        &lt;p&gt;For larger client projects, I find it helpful to maintain a list of tasks, with the ability to re-order, categorize, and mark tasks as completed. Add in the ability to share this list with coworkers or project owners, and you have a recipe for a better record and task-list for development.&lt;/p&gt;
&lt;p&gt;I had been using &lt;a href=&#34;https://www.pivotaltracker.com/&#34;&gt;Pivotal Tracker&lt;/a&gt; for this purpose, but I found a lot of its features were too complicated for a small team. On the simpler side, &lt;a href=&#34;https://trello.com/&#34;&gt;Trello&lt;/a&gt; offers project “boards” that meet many needs for project management. Plus, you can accomplish a lot with the free level of Trello.&lt;/p&gt;
&lt;h3 id=&#34;no-import&#34;&gt;No import&lt;/h3&gt;
&lt;p&gt;After being convinced that switching from Pivotal to Trello was the right move for my current project, I was dismayed to find that Trello offers no Import functionality, at least that I could find for front-end users. (They do have an API, but I didn’t relish taking time to learn this.) I could easily export my Pivotal project, but how to get those tasks into Trello cards?&lt;/p&gt;
&lt;h3 id=&#34;one-idea&#34;&gt;One idea&lt;/h3&gt;
&lt;p&gt;In my search of Trello for an import feature, I found a feature called &lt;em&gt;Email-to-board&lt;/em&gt;. Trello provides a custom email address for each Board you create, allowing you to send emails to this address, containing information for a new Trello card. (This email address is unique for each board, containing random letters and numbers, so only the Board owner can use it.)&lt;/p&gt;
&lt;p&gt;What if I wrote a quick script that processed a Pivotal CSV export file, and sent an email to Trello for each row (task) in the file? The script might send out quite a few emails, but would it work? Time to try it.&lt;/p&gt;
&lt;h3 id=&#34;perl-to-the-rescue&#34;&gt;Perl to the rescue&lt;/h3&gt;
&lt;p&gt;I started cooking up a simple &lt;a href=&#34;https://www.perl.org/&#34;&gt;Perl&lt;/a&gt; script to test the idea. With the help of some &lt;a href=&#34;https://metacpan.org/&#34;&gt;CPAN&lt;/a&gt; modules to easily process the CSV file and send the emails, I landed on something that worked. After running the script, each row in the CSV export became an email to my Trello board, containing the item’s title, description, estimate (difficulty level), and list of tasks required to complete it.&lt;/p&gt;
&lt;p&gt;The script should work for most exports from Pivotal Tracker, and I have published it to my &lt;a href=&#34;https://github.com/jdigory&#34;&gt;GitHub account&lt;/a&gt;, in case it is helpful for others who decide to move to Trello.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://github.com/jdigory/pivotal2trello&#34;&gt;Find the pivotal2trello script on GitHub.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you happen to try it, let me know if you experience any problems, or have any suggestions!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Documenting web services with Perl POD and AJAX</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/06/documenting-web-services-with-perl-pod/"/>
      <id>https://www.endpointdev.com/blog/2015/06/documenting-web-services-with-perl-pod/</id>
      <published>2015-06-26T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;Perl POD is a handy, convenient, but low-tech approach to embedded documentation. Consider a web service in &lt;a href=&#34;http://www.perldancer.org&#34;&gt;Dancer&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;get &lt;span style=&#34;color:#038&#34;&gt;time&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;scalar&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;localtime&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Disclaimer: my actual use-case of this technique was even more legacy: I was documenting &lt;a href=&#34;http://interchange.rtfm.info/icdocs/config/ActionMap.html&#34;&gt;Interchange Actionmaps&lt;/a&gt; that returned images, JSON, etc.)&lt;/p&gt;
&lt;p&gt;Your application might have several, or even dozens of these, with various parameters, returning data in JSON or TXT or CSV or who-knows-what.
I chose to document these in Perl &lt;a href=&#34;http://perldoc.perl.org/perlpod.html&#34;&gt;POD (Plain Old Documentation)&lt;/a&gt; format, e.g.,&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=pod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=head1 time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Retrieves the current time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=over 3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=item Parameters
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;None.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=item Example
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=begin html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;script src=&amp;#34;/js/example-time.js&amp;#34; type=&amp;#34;text/javascript&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=end html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=back
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=cut&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This block gets inserted right in-line with the web service code, so it’s immediately obvious to anyone maintaining it (and thus has the best chance of being maintained if and when the code changes!). Now I can generate an HTML page directly from my Perl code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ pod2html MyPackage.pm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your output looks something like this (excerpted for clarity):&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id=&#34;time&#34;&gt;&lt;a href=&#34;&#34;&gt;time&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Retrieves the current time
&lt;strong&gt;&lt;a href=&#34;&#34;&gt;Parameters&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;None.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;&#34;&gt;Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Where the magic comes in is the JavaScript code that allows an in-line example, live and accurate, within the documentation page. You’ll actually get something more like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id=&#34;time-1&#34;&gt;&lt;a href=&#34;&#34;&gt;time&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Retrieves the current time
&lt;strong&gt;&lt;a href=&#34;&#34;&gt;Parameters&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;None.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;&#34;&gt;Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;input type=&#34;submit&#34; value=&#34;Get data&#34;&gt;
&lt;input type=&#34;button&#34; value=&#34;Hide result&#34; name=&#34;hide&#34;&gt;
&lt;p&gt;(results appear here)&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Note that the code I have below is not factored by choice; I could move a lot of it out to a common routine, but for clarity I’m leaving it all in-line. I am breaking up the script into a few chunks for discussion, but you can and should construct it all into one file (in my example, “js/example-time.js”).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* example-time.js */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$(&lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;).ready(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;script[src$=&amp;#34;/example-time.js&amp;#34;]&amp;#39;&lt;/span&gt;).after(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;form action=&amp;#39;\&amp;#34;/time\&amp;#34;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* Note 1 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;input data\&amp;#34;=&amp;#34;&amp;#34; type=&amp;#39;\&amp;#34;submit\&amp;#34;&amp;#39; value=&amp;#39;\&amp;#34;Get&amp;#39;/&amp;gt;&amp;#34;&lt;/span&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;input name=&amp;#34;&lt;/span&gt;hide&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34; none\&amp;#34;=&amp;#34;&amp;#34; result\&amp;#34;=&amp;#34;&amp;#34; style=&amp;#39;\&amp;#34;display:&amp;#39; type=&amp;#39;\&amp;#34;button\&amp;#34;&amp;#39; value=&amp;#39;\&amp;#34;Hide&amp;#39;/&amp;gt;&amp;#34;&lt;/span&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;/form&amp;gt;&amp;#34;&lt;/span&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;div id=&amp;#39;\&amp;#34;time-result\&amp;#34;&amp;#39;&amp;gt;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note 1: This being a painfully simple example of a web service, there are no additional inputs. If you have some, you would add them to the HTML being assembled into the &lt;form&gt; tag, and then using jQuery, add them below to the url parameter, or into the data structure as required by your particular web service.&lt;/p&gt;
&lt;p&gt;This step just inserts a simple &lt;form&gt; into the document. I chose to embed the form into the JavaScript code, rather than the POD, because it reduces the clutter and separates the example from the web service.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; $form = $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;form[action=&amp;#34;/time&amp;#34;]&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $form.submit(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      $.ajax(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;: $form.attr(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#888&#34;&gt;/* Note 1 also */&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;: {},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;dataType&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;async&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(data){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;#time-result&amp;#39;&lt;/span&gt;).html($(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;pre;//&amp;gt;&amp;#39;&lt;/span&gt;).html(data))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     .addClass(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             },&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we have a submit handler that performs a very simple AJAX submit using the form’s information, and upon success, inserts the results into a result &lt;div&gt; as a pre-formatted block. I added a “json” class which just tweaks the font and other typographic presentation a bit; you can provide your own if you wish.&lt;/p&gt;
&lt;p&gt;I’m aware that there are various jQuery &lt;a href=&#34;http://malsup.com/jquery/form/&#34;&gt;plug-ins&lt;/a&gt; that will handle AJAX-ifying a form, but I couldn’t get the exact behavior I wanted on my first tries, so I bailed out and just constructed this approach.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;#time-result&amp;#39;&lt;/span&gt;).html(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Error retrieving data!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     .removeClass(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;/* */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(That stray-looking comment above is just a work-around for the syntax highlighter.)&lt;/p&gt;
&lt;p&gt;Error handling goes here. If you have something more comprehensive, such as examining the result for error codes or messages, this is where you’d put it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;complete&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 $form.find(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[name=&amp;#34;hide&amp;#34;]&amp;#39;&lt;/span&gt;).show();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }).find(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;input[type=&amp;#34;button&amp;#34;]&amp;#39;&lt;/span&gt;).click(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;#time-result&amp;#39;&lt;/span&gt;).html(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And just a bit of UI kindness: we have a “hide” button to make the example go away. Some of my actual examples ran to dozens of lines of JSON output, so I wanted a way to clean up after the example.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Postgres “unsupported frontend protocol” mystery</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/05/postgres-unsupported-frontend-protocol/"/>
      <id>https://www.endpointdev.com/blog/2015/05/postgres-unsupported-frontend-protocol/</id>
      <published>2015-05-28T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float: right; text-align: center; padding-left: 4em;&#34;&gt;&lt;a href=&#34;/blog/2015/05/postgres-unsupported-frontend-protocol/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/05/postgres-unsupported-frontend-protocol/image-0.png&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;&#34;&lt;a href=&#34;https://flic.kr/p/bWd997&#34;&gt;Koala Portrait&lt;/a&gt;&#34; by &lt;a href=&#34;https://www.flickr.com/photos/bareego/&#34;&gt;James Niland&lt;/a&gt;&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;The wonderful &lt;a href=&#34;https://bucardo.org/wiki/Tail_n_mail&#34;&gt;tail_n_mail program&lt;/a&gt; continues to provide me with new mysteries from our &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;Postgres&lt;/a&gt; clients. One of the main functions it provides is to send an immediate email to us when an unexpected FATAL (or ERROR or PANIC) message appears in the Postgres logs. While these are often simple application errors, or deeper problems such as running out of disk space, once in a blue moon you see something completely unexpected. Some time  ago, I saw a bunch of these messages appear in an email from a tail_n_mail email:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[1] From files A to B Count: 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;First: [A] 2015-12-01T06:30:00 server1 postgres[1948]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Last:  [B] 2015-12-01T06:30:00 server2 postgres[29107]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FATAL: unsupported frontend protocol 65363.19778: server supports 1.0 to 3.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I knew what caused this error in general, but decided to get to the bottom of the
problem. Before we go into the specific error, let’s review what causes this
particular message to appear. When a Postgres client (such as psql or &lt;a href=&#34;https://metacpan.org/pod/DBD::Pg&#34;&gt;DBD::Pg&lt;/a&gt;)
connects to Postgres, the first thing it does is to issue a &lt;a href=&#34;https://www.postgresql.org/docs/current/static/protocol-flow.html&#34;&gt;startup message&lt;/a&gt;.
One of the things included in this request is the
version of the Postgres protocol the client wishes to use. Since
&lt;a href=&#34;https://bucardo.org/postgres_all_versions.html#version_7.4&#34;&gt;2003&lt;/a&gt;,
Postgres servers have been using version 3.1. It is very rare to see a client
or server that uses anything else. Because this protocol number request occurs
at the very start of the connection request, non-Postgres programs often
trigger this error, because the server is expecting a number at the start of
the request.&lt;/p&gt;
&lt;p&gt;We can verify this by use of a small &lt;a href=&#34;https://www.perl.org/&#34;&gt;Perl&lt;/a&gt; script that connects to the server,
and sends an invalid protocol request:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#!/usr/bin/env perl
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use strict;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use warnings;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;use IO::Socket;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my $server = IO::Socket::UNIX-&amp;gt;new(&amp;#39;/tmp/.s.PGSQL.5432&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  or die &amp;#34;Could not connect!: $@&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my $packet = pack(&amp;#39;nn&amp;#39;, 1234,56789) . &amp;#34;user\0pg\0\0&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$packet = pack(&amp;#39;N&amp;#39;, length($packet) + 4). $packet;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$server-&amp;gt;send($packet, 0);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After running the above program, a new error pops up in the Postgres logs as
expected:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tail -1 /var/lib/pgsql/data/pg_log/postgres-2015-05-20.log
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2015-05-21 12:00:00 EDT [unknown]@[unknown] [10281] FATAL:  unsupported frontend protocol 1234.56789: server supports 1.0 to 3.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is our error, as expected. The “unknown”s are because my log_line_prefix looks like this: &lt;strong&gt;%t %u@%d [%p]&lt;/strong&gt;. While the timestamp (%t) and the process ID (%p) are easily filled in, the login failed, so both the user (%u) and database (%d) are still unknown.&lt;/p&gt;
&lt;p&gt;Now on to our specific error, which you will recall is “unsupported frontend protocol 65363.19778”.
The above program shows that the protocol number is sent in a specific format. Let’s use Perl to display the
numbers 65363.19778 and see if there are any clues buried within it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ perl -e &amp;#39;print pack &amp;#34;nn&amp;#34;, 65363,19778&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;�SMB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some sort of unprintable character in there; let’s take a deeper look just for
completeness:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ perl -e &amp;#39;print pack &amp;#34;nn&amp;#34;, 65363,19778&amp;#39; | hd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00000000  ff 53 4d 42                                       |.SMB|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00000004&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Aha! &lt;strong&gt;SMB&lt;/strong&gt; is not just a random placement of three letters, it is a big clue as to what is
causing this message. SMB stands for &lt;a href=&#34;https://en.wikipedia.org/wiki/Server_Message_Block&#34;&gt;Server Message Block&lt;/a&gt;, and is used by a variety of things. We can guess that this is either some program randomly hitting
the Postgres port without realizing what it is, or some sort of purposeful port scanner. Why would something
want to connect to the port but not log in? For one, you can &lt;a href=&#34;/blog/2010/05/finding-postgresql-version-without/&#34;&gt;determine the version of Postgres
without logging in&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To cut to the chase, the culprit is the &lt;a href=&#34;https://nmap.org/&#34;&gt;nmap&lt;/a&gt; program. In addition to simply
scanning ports, it has the ability to do a &lt;a href=&#34;https://nmap.org/book/man-version-detection.html&#34;&gt;deeper inspection&lt;/a&gt; to determine not only what is running on each port, but what version it is as well (with the “&lt;strong&gt;-sV&lt;/strong&gt;” argument). Let’s see nmap in action. We will use a non-standard Postgres port so as not to give it any additional hints about what is on that port:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ nmap -p 5930 localhost -sV
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Starting Nmap 6.40 ( http://nmap.org ) at 2015-05-20 12:00 EDT
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Nmap scan report for localhost (127.0.0.1)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host is up (0.000088s latency).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PORT     STATE SERVICE    VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;5930/tcp open  postgresql PostgreSQL DB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF-Port5930-TCP:V=6.40%I=7%D=5/21%Time=504C5445%P=i686-pc-linux-emu%r(Kerb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:eros,85,&amp;#34;E\0\0\0\x84SFATAL\0C0A000\0Munsupported\x20frontend\x20protoco
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:l\x2027265\.28208:\x20server\x20supports\x201\.0\x20to\x203\.0\0Fpostma
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:ster\.c\0L1834\0RProcessStartupPacket\0\0&amp;#34;)%r(SMBProgNeg,85,&amp;#34;E\0\0\0\x8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:4SFATAL\0C0A000\0Munsupported\x20frontend\x20protocol\x2065363\.19778:\
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:x20server\x20supports\x201\.0\x20to\x203\.0\0Fpostmaster\.c\0L1834\0RPr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:ocessStartupPacket\0\0&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Nmap done: 1 IP address (1 host up) scanned in 6.73 seconds&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It looks like it triggered the “unsupported protocol” message, based on what
was returned. Taking a peek at the Postgres 9.3 logs shows our mystery message:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tail -1 /var/lib/pgsql/pg9.3/pg_log/postgres-2015-05-20.log
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2015-05-21 12:00:00 EDT [unknown]@[unknown] [2318] FATAL:  unsupported frontend protocol 65363.19778: server supports 1.0 to 3.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a final check, let’s confirm that nmap is using SMB when it runs the version check:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ nmap localhost -p 5930 -sV --version-trace 2&amp;gt;/dev/null | grep SMB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Service scan sending probe SMBProgNeg to 127.0.0.1:5930 (tcp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Service scan match (Probe SMBProgNeg matched with SMBProgNeg line 10662): 127.0.0.1:5930 is postgresql.  Version: |PostgreSQL DB|||
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SF:ster\.c\0L1834\0RProcessStartupPacket\0\0&amp;#34;)%r(SMBProgNeg,85,&amp;#34;E\0\0\0\x8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Bingo. Mystery solved. If you see that error in your logs, it is most likely caused by someone running nmap
in version detection mode.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange Loop Optimization</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/interchange-loop-optimization/"/>
      <id>https://www.endpointdev.com/blog/2015/02/interchange-loop-optimization/</id>
      <published>2015-02-09T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;It’s important to understand both how loops work in Interchange and the (very) fundamental differences between interpolating Interchange tag language (ITL) and the special loop tags (typically referred to as [PREFIX-*] in the literature). Absent this sometimes arcane knowledge, it is very easy to get stuck with inefficient loops even with relatively small loop sets. I’ll discuss both the function of loops and interpolation differences between the tag types while working through a [query] example. While all loop tags&amp;ndash;[item-list], [loop], [search-list], and [query]&amp;ndash;process similarly, it is to [query] where most complex loops will gravitate over time (to optimize the initiation phase of entering the loop) and where we have the most flexibility for coming up with alternative strategies to mitigate sluggish loop-processing.&lt;/p&gt;
&lt;h3 id=&#34;loop-processing&#34;&gt;Loop Processing&lt;/h3&gt;
&lt;p&gt;All loop tags are &lt;em&gt;container&lt;/em&gt; tags in Interchange, meaning they have an open and close tag, and in between is the body. Only inside this body is it valid to define [PREFIX-&lt;em&gt;] tags (notable exception of [PREFIX-quote] for the sql arg of [query]). This is because the [PREFIX-&lt;/em&gt;] tags are not true ITL. They are tightly coupled with the structure of the underlying rows of data and they are processed by distinct, optimized regular expressions serially. Outside the context of the row data from a result set, they are meaningless.&lt;/p&gt;
&lt;p&gt;Moreover, the body of a loop tag is slurped into a scalar variable (as all bodies of container tags are handled via the ITL parser) and for each row in the record set of the loop, the contents are acted upon according to the [PREFIX-&lt;em&gt;] tags defined within the body. The first important distinction to recognize here is, the per-row action on this scalar is limited to &lt;em&gt;only&lt;/em&gt; the [PREFIX-&lt;/em&gt;] tags. The action occurring at loop time ignores any embedded ITL.&lt;/p&gt;
&lt;p&gt;At the end of each row’s processing, the copy of the body tied to that one row is then concatenated to the results of all previous rows thus processed. For a loop with N rows (assuming no suppression by [if-PREFIX-*] conditionals) that means every instance of ITL originally placed into the loop body is now present N times in the fully assembled body string. Simple example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[loop list=&amp;#39;1 2 3&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk][loop-code][/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare [loop-code] to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. [loop-code] is ordinary.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/loop]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once this result set with N=3 is processed, but &lt;strong&gt;before&lt;/strong&gt; Interchange returns the results, the assembled return looks like the following string:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]1[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 1 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 1 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]2[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 2 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 2 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp junk]3[/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch junk == 2]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;I declare 3 to be special!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Meh. 3 is oridinary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some important observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn’t take much ITL to turn a loop body into a monster interpolation process. One must consider the complexity of the ITL in the body by a factor of the number of rows (total, or the “ml” matchlimit value).&lt;/li&gt;
&lt;li&gt;ITL does &lt;em&gt;nothing&lt;/em&gt; to short-circuit action of the [PREFIX-*] tags. Having [PREFIX-param], [PREFIX-calc], etc. inside an ITL [if] means all those loop tags parse regardless of the truth of the if condition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;itl-vs-loop-tags&#34;&gt;ITL vs. Loop Tags&lt;/h3&gt;
&lt;p&gt;ITL maps to routines, both core and user-defined, determined at compile time. They are processed in order of discovery within the string handed to the ::interpolate_html() routine and have varied and complex attributes that must be resolved for each individual tag. Further, for many (if not most) tags, the return value is itself passed through a new call to ::interpolate_html(), acting on all embedded tags, in an action referred to as &lt;em&gt;reparse&lt;/em&gt;. There is, relatively speaking, a good deal of overhead in processing through ::interpolate_html(), particularly with reparse potentially spawning off a great many more ::interpolate_html() calls.&lt;/p&gt;
&lt;p&gt;Loop tags, by contrast, map to a pre-compiled set of regular expressions. In contrast to crawling the string and acting upon the tags in the order of discovery, each regex in turn is applied globally to the string. The size of the string is limited to the exact size of the single loop body, and there is no analogue to ITL’s reparse. Further, given this processing pattern, a careful observer might have noted that the order of operations can impact the structure. Specifically, tags processed earlier cannot depend on tags processed later. E.g., [PREFIX-param] processes ahead of [PREFIX-pos], and so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-PREFIX-pos 2 eq [PREFIX-param bar]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;will work, but:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-PREFIX-param bar eq [PREFIX-pos 2]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;will not. While the above is a somewhat contrived exampled, the impacts of loop tag processing can be more easily seen in an example using [PREFIX-next]:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[PREFIX-next][PREFIX-param baz][/PREFIX-next]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Code I only want to run when baz is false, like this [PREFIX-exec foo][/PREFIX-exec] call&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because [PREFIX-next] is the absolute last loop tag to run, &lt;em&gt;every other loop tag in the block is run&lt;/em&gt; before the next condition is checked. All [PREFIX-next] does is suppress the resulting body from the return, unlike Perl’s next, which short-circuits the remaining code in the loop block.&lt;/p&gt;
&lt;h3 id=&#34;an-optimization-example&#34;&gt;An Optimization Example&lt;/h3&gt;
&lt;p&gt;As long as you’re familiar with the idiosyncracies of [PREFIX-*] tags, you should make every effort to use them instead of ITL because they are substantially lighter weight and faster to process. A classic case that can yield remarkable performance gains is to directly swap an embedded [perl] or [calc] block with an equivalent [PREFIX-calc] block.&lt;/p&gt;
&lt;p&gt;Let’s take a typical query with little consideration given to whether we use loop tags or ITL, not unlike many I’ve seen where the resource has just become unusably slow. This code originally was developed processing 50 records per page view, but the team using it has requested over time to increase that count.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500 [comment]Ouch! That&amp;#39;s a big N[/comment]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SELECT *
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if global DEVELOPMENT]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if cgi show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [either][sql-param is_shipped][or]pending[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [inventory type=shipped sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [inventory type=onhand sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [inventory type=sold sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [inventory type=shipping sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                href=order_view
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                form=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    order=[sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_status=[either][cgi show_status][or]active[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_inventory=[cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Considering this block out of context, it doesn’t seem all that unreasonable. However, let’s look at some of the pieces individually and see what can be done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We use [if] in 3 different circumstances in the block. However, those values they test are static. They don’t change on any iteration. (We are excluding the potential of any other ITL present in the block from changing their values behind the scenes.)&lt;/li&gt;
&lt;li&gt;[convert-date] may be convenient, but it is only one of a number of ways to address date formatting. Our database itself almost certainly has date-formatting routines, but one of the benefits of [convert-date] is you could have a mixed format underlying the data and it can make sense out of the date to some degree. So perhaps that’s why the developer has used [convert-date] here.&lt;/li&gt;
&lt;li&gt;Good chance that stats_crunch() is pretty complicated and that’s why the developer wrote a catalog or global subroutine to handle it. Since we only want to see it in the development environment, it’d be nice if it only ran when it was needed. Right now, because of ITL happening on reparse, stats_crunch() fires for every row even if we have no intention of using its output.&lt;/li&gt;
&lt;li&gt;We need that link to view our order, but on reparse it means ::interpolate_html() has to parse 500 [area] tags along with [either] and [cgi] x 500. All of these tags are lightweight, but parsing numbers are really going to catch up to us here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our goal here is to replace any ITL we can with an equivalent use of a loop tag or, absent the ability to remove ITL logically, to wrap that ITL into a subroutine that can itself be called in loop context with [PREFIX-exec]. The first thing I want to address are those [if] and [either] tags, the lowest hanging fruit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;b&amp;gt;SELECT *,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#39;[if global DEVELOPMENT]1[/if]&amp;#39; AS is_development,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [sql-quote][cgi show_inventory][/sql-quote] AS show_inventory,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            COALESCE(is_shipped,&amp;#39;pending&amp;#39;) AS show_inventory_shipped&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param is_development]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param show_inventory]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[if-sql-param show_inventory eq all]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: &amp;lt;b&amp;gt;[sql-param show_inventory_shipped]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [inventory type=shipped sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [inventory type=onhand sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [inventory type=sold sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [inventory type=shipping sku=[sql-param sku]]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[/if-sql-param]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[area
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                href=order_view
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                form=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    order=[sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_status=[either][cgi show_status][or]active[/either]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    show_inventory=[cgi show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By moving those evaluations into the SELECT list of the query, we’ve reduced the number of interpolations to arrive at those static values to 1 or, in the case of the [either] tag, 0 as we’ve offloaded the calculation entirely to the database. If is_shipped could be something perly false but not null, we would have to adjust our field accordingly, but in either case could still be easily managed as a database calculation. Moreover, by swapping in [if-sql-param is_development] for [if global DEVELOPMENT], we have kept stats_crunch() from running at all when in the production environment.&lt;/p&gt;
&lt;p&gt;Next, we’ll consider [convert-date]:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [convert-date format=&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/convert-date]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My first attempt would be to address this similarly to the [if] and [either] conditions, and try to render the formatted date from a database function as an aliased field. However, let’s assume the underlying structure of the data varies and that’s not easily accomplished, and we still want [convert-date]. Luckily, Interchange supports that same tag as a filter, and [PREFIX-filter] is a loop tag:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: &amp;lt;b&amp;gt;[sql-filter convert_date.&amp;#34;%b %d, %Y at %T&amp;#34;]&amp;lt;/b&amp;gt;[sql-param order_date]&amp;lt;b&amp;gt;[/sql-filter]&amp;lt;/b&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;[PREFIX-filter] is very handy to keep in mind as many transformation tags have a filter wrapper for them. E.g., [currency] -&amp;gt; [PREFIX-filter currency]. And if the one you’re looking at doesn’t, you can build your own, easily.&lt;/p&gt;
&lt;p&gt;Now to look at that [inventory] tag. The most direct approach assumes that the code inside [inventory] can be run in Safe, which often it can even if [inventory] is global. However, if [inventory] does run-time un-Safe things (such as creating an object) then it may not be possible. In such a case, we would want to create a global sub, like our hypothetical stats_crunch(), and invoke it via [PREFIX-exec]. However, let us assume we can safely (as it were) invoke it via the $Tag object to demonstrate another potent loop option: [PREFIX-sub].&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[sql-sub show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $arg = shift;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;inventory({ type =&amp;gt; $arg, sku =&amp;gt; $Row-&amp;gt;{sku} });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [sql-param show_inventory_shipped]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: &amp;lt;b&amp;gt;[sql-exec show_inventory]shipped[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: &amp;lt;b&amp;gt;[sql-exec show_inventory]on_hand[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: &amp;lt;b&amp;gt;[sql-exec show_inventory]sold[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: &amp;lt;b&amp;gt;[sql-exec show_inventory]shipping[/sql-exec]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s go over what this gives us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[PREFIX-sub] creates an in-line catalog sub that is compiled at the start of processing, before looping actually begins. As such, the [PREFIX-sub] definitions can occur anywhere within the loop body and are then removed from the body to be parsed.&lt;/li&gt;
&lt;li&gt;The body of the [PREFIX-exec] is passed to the sub as the first argument. We use that here for our static values to the &amp;ldquo;type&amp;rdquo; arg. If we also wanted to access [sql-param sku] from the call, we would have to include that in the body and set up a parser to extract it out of the one (and only) arg we can pass in. Instead, we can reference the $Row hash within the sub body just as we can do when using a [PREFIX-calc], with one minor adjustment to our [query] tag&amp;ndash;we have to indicate to [query] we are operating on a row-hash basis instead of the default row-array basis. We do that by adding the hashref arg to the list:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;b&amp;gt;hashref=1&amp;lt;/b&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We still have access to the full functionality of [inventory] but we’ve removed the impact of having to parse that tag 2000 times (in the worst-case scenario) if left as ITL in the query body. If we run into Safe issues, that same sub body can either be created as a pre-compiled global sub or, if available, we can set our catalog AllowGlobal in which case catalog subs will no longer run under Safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, all we have left to address is [area] and its args which themselves have ITL. I will leverage [PREFIX-sub] again as an easy way to manage the issue:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;b&amp;gt;[sql-sub area_order_view]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $show_status = $CGI-&amp;gt;{show_status} || &amp;#39;active&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;area({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        href =&amp;gt; &amp;#39;order_view&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form =&amp;gt; &amp;#34;order=$Row-&amp;gt;{order_number}\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_status=$show_status\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_inventory=$CGI-&amp;gt;{show_inventory}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]&amp;lt;/b&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;&amp;lt;b&amp;gt;[sql-exec area_order_view][/sql-exec]&amp;lt;/b&amp;gt;&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By packaging all of [area]’s requirements into the sub body, I can address all of the ITL at once.&lt;/p&gt;
&lt;p&gt;So now, let’s put together the entire [query] rewrite to see the final product:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[query
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ml=500
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hashref=1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sql=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SELECT *,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#39;[if global DEVELOPMENT]1[/if]&amp;#39; AS is_development,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [sql-quote][cgi show_inventory][/sql-quote] AS show_inventory,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            COALESCE(is_shipped,&amp;#39;pending&amp;#39;) AS show_inventory_shipped
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FROM transactions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        WHERE status = &amp;#39;pending&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ORDER BY order_date DESC
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order [sql-param order_number]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param is_development]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Show [sql-exec stats_crunch][sql-param stats][/sql-exec], only of interest to developers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date: [sql-filter convert_date.&amp;#34;%b %d, %Y at %T&amp;#34;][sql-param order_date][/sql-filter]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[sql-sub show_inventory]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $arg = shift;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;inventory({ type =&amp;gt; $arg, sku =&amp;gt; $Row-&amp;gt;{sku} });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Inv:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if-sql-param show_inventory eq all]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipped: [sql-param show_inventory_shipped]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Count: [sql-exec show_inventory]shipped[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* On Hand: [sql-exec show_inventory]on_hand[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Sold: [sql-exec show_inventory]sold[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Shipping: [sql-exec show_inventory]shipping[/sql-exec]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if-sql-param]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[sql-sub area_order_view]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my $show_status = $CGI-&amp;gt;{show_status} || &amp;#39;active&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return $Tag-&amp;gt;area({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        href =&amp;gt; &amp;#39;order_view&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        form =&amp;gt; &amp;#34;order=$Row-&amp;gt;{order_number}\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_status=$show_status\n&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              . &amp;#34;show_inventory=$CGI-&amp;gt;{show_inventory}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/sql-sub]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Order details:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;a href=&amp;#34;[sql-exec area_order_view][/sql-exec]&amp;#34;&amp;gt;View [sql-param order_number]&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/query]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Voila! Our new query body is functionally identical to the original body, though admittedly a little more complicated to set up. However, the trade-off in efficiency is likely to be substantial.&lt;/p&gt;
&lt;p&gt;I recently worked on a refactor for a client that was overall very similar to the above example, with a desired N value of 250. The code prior to refactoring took ~70s to complete. Once we had completed the refactor using the same tools as I’ve identified here, we brought down processing time to just under 3s, losing no functionality.&lt;/p&gt;
&lt;p&gt;Time taken optimizing Interchange loops will almost always pay dividends.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Filling in header elements with Dancer and Template::Flute</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/02/filling-in-header-elements-with-dancer/"/>
      <id>https://www.endpointdev.com/blog/2015/02/filling-in-header-elements-with-dancer/</id>
      <published>2015-02-05T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;Inserting content into &lt;a href=&#34;http://perldancer.org/&#34;&gt;Dancer&lt;/a&gt; output files involves using a templating system. One such is &lt;a href=&#34;http://search.cpan.org/~hornburg/Template-Flute-0.0160/lib/Template/Flute.pm&#34;&gt;Template::Flute&lt;/a&gt;. In its simplest possible explanation, it takes a Perl hash, an XML file, and an HTML template, and produces a finished HTML page.&lt;/p&gt;
&lt;p&gt;For a project involving Dancer and Template::Flute, I needed a way to prepare each web page with its own set of JavaScript and CSS files. One way is to construct separate layout files for each different combination of .js and .css, but I figured there had to be a better way.&lt;/p&gt;
&lt;p&gt;Here’s what I came up with: I use one layout for all my typical pages, and within the header, I have:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/css/additional.css&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/javascripts/additional.js&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The trick here is, there’s no such files “additional.css” and “additional.js”. Instead, those are placeholders for the actual CSS and JS files I want to link into each HTML file.&lt;/p&gt;
&lt;p&gt;My Perl object has these fields (in addition to the other content):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;-&amp;gt;{additional_styles}    = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/css/checkout.css&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/css/colorbox.css&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;-&amp;gt;{additional_scripts}   = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/javascripts/sprintf.js&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/javascripts/toCurrency.js&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/javascripts/jquery.colorbox-min.js&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/javascripts/checkout.js&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;while my XML file looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;specification&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;list&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;iterator=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_styles&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;param&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;field=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;target=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;list&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;iterator=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_scripts&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;param&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;field=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;target=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/specification&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So we have all the elements, but unless you have used all this before, you may not realize how we get the output. (Skip to the punchline if that’s not true.)&lt;/p&gt;
&lt;p&gt;The XML file is a connector that tells Template::Flute how to mix the Perl hash into the HTML template. Usually you connect things via class names, so in the case of:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/css/additional.css&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;the class name in the HTML and the name field in the XML connect, while the iterator field in the XML and the hash key in the Perl hashref do as well. The case of a&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;list&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;means that the hash value must be an arrayref of hashrefs, i.e.,&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; =&amp;gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		{ url =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		...,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Important note: if the hash value is undefined, you’ll get a run-time error when you try to expand the HTML template, and if you have an empty arrayref, the result of the expansion is an empty string (which is just what you want).&lt;/p&gt;
&lt;p&gt;And so, through the magic of Template::Flute, what the browser sees is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/css/checkout.css&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/css/colorbox.css&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_style&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/css/admin/admin.css&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt;/&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/javascripts/sprintf.js&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/javascripts/toCurrency.js&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/javascripts/jquery.colorbox-min.js&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;additional_script&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/javascripts/checkout.js&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
</feed>
