Tag: signatures

  • Moving Perl Mojolicious routes to their own module

    Moving Perl Mojolicious routes to their own module

    A mentee asked me over the week­end if there was a way with­in a Mojolicious web appli­ca­tion to store the routes sep­a­rate­ly from the main appli­ca­tion class. Here’s one way. These instruc­tions assume you’re using Perl 5.34 and Mojolicious 9.19 (the lat­est as of this writ­ing) via the ter­mi­nal com­mand line on a Linux, Unix, or macOS sys­tem; make the appro­pri­ate changes if this does­n’t apply to you.

    First, if you haven’t already, cre­ate your Mojolicious app at your shell prompt:

    $ mojo generate app Local::RouteDemo
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/script
      [write] /Users/mgardner/Projects/blog/local_route_demo/script/local_route_demo
      [chmod] /Users/mgardner/Projects/blog/local_route_demo/script/local_route_demo 744
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/lib/Local
      [write] /Users/mgardner/Projects/blog/local_route_demo/lib/Local/RouteDemo.pm
      [exist] /Users/mgardner/Projects/blog/local_route_demo
      [write] /Users/mgardner/Projects/blog/local_route_demo/local-route_demo.yml
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/lib/Local/RouteDemo/Controller
      [write] /Users/mgardner/Projects/blog/local_route_demo/lib/Local/RouteDemo/Controller/Example.pm
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/t
      [write] /Users/mgardner/Projects/blog/local_route_demo/t/basic.t
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/public
      [write] /Users/mgardner/Projects/blog/local_route_demo/public/index.html
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/templates/layouts
      [write] /Users/mgardner/Projects/blog/local_route_demo/templates/layouts/default.html.ep
      [mkdir] /Users/mgardner/Projects/blog/local_route_demo/templates/example
      [write] /Users/mgardner/Projects/blog/local_route_demo/templates/example/welcome.html.ep
    $ cd local_route_demo

    Create a new Perl mod­ule in your edi­tor for stor­ing your routes. Here we’re using Local::RouteDemo::Routes:

    $ touch lib/Local/RouteDemo/Routes.pm
    $ $EDITOR lib/Local/RouteDemo/Routes.pm

    Make the mod­ule with a func­tion that will cre­ate the routes you want, giv­en a Mojolicious::Routes object. Here we’re just bring­ing over the default route cre­at­ed when we cre­at­ed our app:

    package Local::RouteDemo::Routes;
    use strict;
    use warnings qw(all -experimental::signatures);
    use feature 'signatures';
    use Exporter 'import';
    our @EXPORT_OK = qw(make_routes);
    
    sub make_routes ($router) {
        $router->get('/')->to('Example#welcome');
        # add more routes here
    
        return;
    }
    
    1;

    Adjust the appli­ca­tion class to load your new Routes mod­ule and call its export­ed function:

    package Local::RouteDemo;
    use Mojo::Base 'Mojolicious', -signatures;
    use Local::RouteDemo::Routes 'make_routes';
    
    # This method will run once at server start
    sub startup ($self) {
    
        # Load configuration from config file
        my $config = $self->plugin('NotYAMLConfig');
    
        # Configure the application
        $self->secrets($config->{secrets});
    
        # Make routes
        make_routes($self->routes);
    
        return;
    }
    
    1;

    Finally, run your tests and/​or man­u­al­ly test your routes to be sure every­thing works OK:

    $ prove -vlr t
    t/basic.t .. [2021-06-07 12:21:55.36917] [58779] [debug] [elVGykGVWlOt] GET "/"
    [2021-06-07 12:21:55.36972] [58779] [debug] [elVGykGVWlOt] Routing to controller "Local::RouteDemo::Controller::Example" and action "welcome"
    [2021-06-07 12:21:55.37137] [58779] [debug] [elVGykGVWlOt] Rendering template "example/welcome.html.ep"
    [2021-06-07 12:21:55.37343] [58779] [debug] [elVGykGVWlOt] Rendering template "layouts/default.html.ep"
    [2021-06-07 12:21:55.37495] [58779] [debug] [elVGykGVWlOt] 200 OK (0.005772s, 173.250/s)
    
    ok 1 - GET /
    ok 2 - 200 OK
    ok 3 - content is similar
    1..3
    ok
    All tests successful.
    Files=1, Tests=3,  1 wallclock secs ( 0.02 usr  0.01 sys +  0.38 cusr  0.11 csys =  0.52 CPU)
    Result: PASS
    $ script/local_route_demo get /
    [2021-06-07 12:22:29.55930] [58889] [debug] [f3YoaFhkwJ42] GET "/"
    [2021-06-07 12:22:29.55990] [58889] [debug] [f3YoaFhkwJ42] Routing to controller "Local::RouteDemo::Controller::Example" and action "welcome"
    [2021-06-07 12:22:29.56059] [58889] [debug] [f3YoaFhkwJ42] Rendering template "example/welcome.html.ep"
    [2021-06-07 12:22:29.56269] [58889] [debug] [f3YoaFhkwJ42] Rendering template "layouts/default.html.ep"
    [2021-06-07 12:22:29.56432] [58889] [debug] [f3YoaFhkwJ42] 200 OK (0.005004s, 199.840/s)
    <!DOCTYPE html>
    <html>
      <head><title>Welcome</title></head>
      <body><h2>Welcome to the Mojolicious real-time web framework!</h2>
    <p>
      This page was generated from the template "templates/example/welcome.html.ep"
      and the layout "templates/layouts/default.html.ep",
      <a href="/">click here</a> to reload the page or
      <a href="/index.html">here</a> to move forward to a static page.
    </p>
    </body>
    </html>

    You can find a git repos­i­to­ry of this work on GitHub, and here’s a com­mit of all the changes made to the default Mojolicious appli­ca­tion so you can see the differences.

    Update

    Joel Berger from the Mojolicious project told me at The Perl and Raku Conference that it would be more idiomat­ic to use a Mojolicious plu­g­in rather than a plain mod­ule with an export, so here you go:

    lib/Local/RouteDemo.pm:

    package Local::RouteDemo;
    use Mojo::Base 'Mojolicious', -signatures;
    
    # This method will run once at server start
    sub startup ($self) {
    
        # Load configuration from config file
        my $config = $self->plugin('NotYAMLConfig');
    
        # Configure the application
        $self->secrets($config->{secrets});
    
        # Add routes from plugin
        $self->plugin('Local::RouteDemo::Plugin::Routes');
    
        return;
    }
    
    1;

    lib/Local/RouteDemo/Plugin/Routes.pm:

    package Local::RouteDemo::Plugin::Routes;
    use Mojo::Base 'Mojolicious::Plugin', -signatures;
    
    sub register ($self, $app, $conf) {
        my $r = $app->routes;
    
        $r->get('/')->to('Example#welcome');
        # add more routes here
    
        return;
    }
    
    1;
  • Perl can do that now!

    Perl can do that now!

    Last week saw the release of Perl 5.34.0 (you can get it here), and with it comes a year’s worth of new fea­tures, per­for­mance enhance­ments, bug fix­es, and oth­er improve­ments. It seems like a good time to high­light some of my favorite changes over the past decade and a half, espe­cial­ly for those with more dat­ed knowl­edge of Perl. You can always click on the head­ers below for the full releas­es’ perldelta pages.

    Perl 5.10 (2007)

    This was a big release, com­ing as it did over five years after the pre­vi­ous major 5.8 release. Not that Perl devel­op­ers were idle—but it would­n’t be until ver­sion 5.14 that the lan­guage would adopt a steady year­ly release cadence.

    Due to the build-​up time, many core enhance­ments were made but the most impor­tant was arguably the feature prag­ma, enabling the addi­tion of new syn­tax that would oth­er­wise break Perl’s back­ward com­pat­i­bil­i­ty. 5.10 also intro­duced the defined-​or oper­a­tor (//), state vari­ables that per­sist their pre­vi­ous val­ue, the say func­tion for auto­mat­i­cal­ly append­ing a new­line on out­put (so much saved typ­ing), and a large col­lec­tion of improve­ments to reg­u­lar expres­sions. In addi­tion, this release intro­duced smart match­ing (~~), though ver­sion 5.18 would even­tu­al­ly rel­e­gate it to exper­i­men­tal sta­tus.

    Perl 5.12 (2010)

    This release also saw many new fea­tures added, but if I had to pick one mar­quee item it would be exper­i­men­tal sup­port for plug­gable key­words, which enabled authors to extend the lan­guage itself with­out mod­i­fy­ing the core. Previously one would either use plain func­tions, hacky source fil­ters, or the dep­re­cat­ed Devel::Declare mod­ule to sim­u­late this func­tion­al­i­ty. CPAN authors would go on to cre­ate all kinds of new syn­tax, some­times pro­to­typ­ing fea­tures that would even­tu­al­ly make their way into core.

    Perl 5.14 (2011)

    5.14 had a big list of enhance­ments, includ­ing Unicode 6.0 sup­port and a gag­gle of reg­u­lar expres­sion fea­tures. My favorite of these was the /r switch for non-​destructive sub­sti­tu­tions.

    But as the first year­ly cadence release, the changes in pol­i­cy took cen­ter stage. The Perl 5 Porters (p5p) explic­it­ly com­mit­ted to sup­port­ing the two most recent sta­ble release series, pro­vid­ing secu­ri­ty patch­es only for release series occur­ring in the past three years. They also defined an explic­it com­pat­i­bil­i­ty and dep­re­ca­tion pol­i­cy, with def­i­n­i­tions for fea­tures that may be exper­i­men­tal, dep­re­cat­ed, dis­cour­aged, and removed.

    Perl 5.16 (2012)

    Another year, anoth­er ver­sion bump. This time the core enhance­ments were all over the map (although no enhance­ments to the map function 😀 ).

    May I high­light anoth­er doc­u­men­ta­tion change, though? The perlootut Object-​Oriented Programming in Perl Tutorial replaced the old perltoot, perltooc, perlboot, and perlbot pages, pro­vid­ing an intro­duc­tion to object-​oriented design con­cepts before strong­ly rec­om­mend­ing the use of one of the OO sys­tems from CPAN. Mentioned are Moose, its alter­na­tive Mouse, Class::Accessor, Object::Tiny, and Role::Tinys usage with the lat­ter two. Later ver­sions of perlootut would rec­om­mend Moo rather than Mouse.

    Perl 5.18 (2013)

    As men­tioned ear­li­er, Perl 5.18 ren­dered smart­match exper­i­men­tal, as well as lex­i­cal use of the $_ vari­able. With these came a new cat­e­go­ry of warn­ings for exper­i­men­tal fea­tures and a method for over­rid­ing such warn­ings feature-​by-​feature. Fitting in with the secu­ri­ty and safe­ty theme, hash­es were over­hauled to ran­dom­ize key/​value order, increas­ing their resis­tance to algo­rith­mic com­plex­i­ty attacks.

    But it was­n’t all fenc­ing in bad behav­ior. Lexical sub­rou­tines made their first (exper­i­men­tal) appear­ance, and although I con­fess I haven’t had much call for them in my work, oth­ers have come up with some inter­est­ing uses. Four years lat­er they became non-​experimental.

    Perl 5.20 (2014)

    Three new syn­tax fea­tures arrived in 2014: exper­i­men­tal sub­rou­tine sig­na­tures (of which I’ve writ­ten more about here), key/​value hash slices and index/​value array slices, and exper­i­men­tal post­fix deref­er­enc­ing. This last enables clean­er left-​to-​right syn­tax when deref­er­enc­ing variables:

    • @{ $array_ref } becomes $array_ref->@*
    • %{ $hash_ref } becomes $hash_ref->%*
    • Etc.

    Postfix deref­er­enc­ing became non-​experimental in Perl 5.24, and vig­or­ous dis­cus­sion con­tin­ues on sub­rou­tine sig­na­tures’ future.

    Perl 5.22 (2015)

    Speaking of sub­rou­tine sig­na­tures, their loca­tion moved to between the sub­rou­tine name (if any) and the attribute list (if any). Previously they appeared after attrib­ut­es. The les­son? Remain con­scious of exper­i­men­tal fea­tures in your code, and be pre­pared to make changes when upgrading.

    In addi­tion to the enhance­ments, secu­ri­ty updates, per­for­mance fix­es, and dep­re­ca­tions, devel­op­ers removed the his­tor­i­cal­ly notable CGI mod­ule. First added to core in 1997 in recog­ni­tion of its crit­i­cal role in enabling web devel­op­ment, it’s been sup­plant­ed by bet­ter alter­na­tives on CPAN.

    Perl 5.24 (2016)

    Perl 5.20s post­fix deref­er­enc­ing was no longer exper­i­men­tal, and devel­op­ers removed both lex­i­cal $_ and autoderef­er­enc­ing on calls to push, pop, shift, unshift, splice, keys, values, and each.

    Perl 5.26 (2017)

    The incor­po­ra­tion of exper­i­men­tal fea­tures con­tin­ued, with lex­i­cal sub­rou­tines mov­ing into full sup­port. I like the added read­abil­i­ty enhance­ments, though: indent­ed here-​documents; the /xx reg­u­lar expres­sion mod­i­fi­er for tabs and spaces in char­ac­ter class­es; and @{^CAPTURE}, %{^CAPTURE}, and %{^CAPTURE_ALL} for reg­exp match­es with a lit­tle more self-documentation.

    Perl 5.28 (2018)

    Experimental sub­rou­tine sig­na­ture and attribute order­ing flipped back to its Perl 5.20 sequence of attributes-​then-​signature. Bit of a roller­coast­er ride on this one. You could do worse than using some­thing like Type::Params until this set­tles and get a wide vari­ety of type con­straints in the bargain.

    Perl 5.30 (2019)

    Pour one out for AWK and Fortran pro­gram­mers migrat­ing to Perl: the $[vari­able for set­ting the low­er bound of arrays could no longer be set to any­thing oth­er than zero. This had a long dep­re­ca­tion cycle start­ing in Perl 5.12.

    Perl 5.32 (2020)

    In 2020 Perl’s devel­op­ment moved to GitHub. And once again, I’m going to high­light read­abil­i­ty enhance­ments: the exper­i­men­tal isa oper­a­tor could be used to say:

    if ( $obj isa Some::Class ) { ... }

    instead of

    use Scalar::Util 'blessed';
    if ( blessed($obj) and $obj->isa('Some::Class') { ... }

    You could also chain com­par­i­son oper­a­tors, lead­ing to the more math­e­mat­i­cal­ly con­cise if ( $x < $y <= $z ) {...} rather than if ( $x < $y and $y <= $z ) {...}.

    Perl 5.34 (2021)

    Finally, we come to last week’s release and its intro­duc­tion of exper­i­men­tal try/​catch excep­tion han­dling syn­tax. If you need to sup­port ear­li­er ver­sions of Perl back to 5.14, you can use Feature::Compat::Try. Earlier this year I inter­viewed the fea­ture and mod­ule’s author, Paul LeoNerd” Evans, for Perl.com. This year also marked the debut of Perl’s new gov­er­nance mod­el with the appoint­ment of a Core Team and a three-​member Steering Council.

    What are some of your favorite Perl improve­ments over the years? Check out the perlhist doc­u­ment for a detailed chronol­o­gy and refresh­er with the var­i­ous perldelta pages and leave me a com­ment below.

  • Localizing dates in a Perl web application with JavaScript

    Localizing dates in a Perl web application with JavaScript

    Last week’s arti­cle received a com­ment on a pri­vate Facebook group that amount­ed to just use JavaScript’s built-​in for­mat­ting.” So what would that look like?

    #!/usr/bin/env perl
    
    use Mojolicious::Lite -signatures;
    use DateTime;
    
    get '/' =>
        sub ($c) { $c->render( template => 'index', date => DateTime->today ) };
    
    helper localize_date => sub ( $c, $date = DateTime->today, $style = 'full' ) {
        my $date_params = join ',' => $date->year, $date->month_0, $date->day;
        return
            qq<new Date($date_params).toLocaleString( [], {dateStyle: "$style"})>;
    };
    
    app->start;
    __DATA__
    @@ index.html.ep
    % layout 'default';
    % title 'Today';
    <ul>
        <li><script>
            document.write(<%== localize_date $date %>)
        </script></li>
        % for my $style ( qw(long medium short) ) {
        <li><script>
            document.write(<%== localize_date $date, $style %>)
        </script></li>
        % }
    </ul>
    @@ layouts/default.html.ep
    <!DOCTYPE html>
    <html>
        <head><title><%= title %></title></head>
        <body><%= content %></body>
    </html>

    It’s struc­tured much like the Perl-​only solu­tion, with a default "/" route and a localize_date Mojolicious helper to do the for­mat­ting. I opt­ed to out­put a piece of JavaScript from the helper on lines 11 through 14 since it could be repeat­ed sev­er­al times in a doc­u­ment. You could instead declare a func­tion in the default lay­out’s HTML <head> on line 38 that would receive a date and a for­mat­ting style, out­putting the result­ing for­mat­ted date.

    In the tem­plate’s list from lines 22 through 31 I decid­ed to use JavaScript document.write method calls to add our gen­er­at­ed code. This has a slew of caveats but works for our exam­ple here.

    Worth not­ing is the dou­ble equals sign (<%== %>) when embed­ding a Perl expres­sion. This pre­vents Mojolicious from XML-​escaping spe­cial char­ac­ters, e.g., replac­ing "quotes" with &quot;, <angle brack­ets> with &lt; and &gt;, etc.. This is impor­tant when return­ing HTML and JavaScript code.

    I also chose to use the JavaScript Date objec­t’s toLocaleString() method for my for­mat­ting on line 12. There are oth­er ways to do this:

    Note that line 10 builds the para­me­ters for JavaScript’s Date con­struc­tor using the year, month_0, and day meth­ods of our Perl DateTime object; month_0 because the Date con­struc­tor takes its month as an inte­ger from 0 to 11 rather than 1 to 12. JavaScript Dates can be con­struct­ed in many ways; this seemed the sim­plest with­out hav­ing to explain things like epochs and incon­sis­tent parsing.

    Why are we using Perl DateTimes and a helper any­way? I’m assum­ing that our dates are com­ing from the back­end of our appli­ca­tion, pos­si­bly inflat­ed from a data­base col­umn. If your dates are strict­ly on the fron­tend, you might decide to put your for­mat­ting code there in a JavaScript func­tion, per­haps using a JavaScript-​based tem­plat­ing library.

    The bot­tom line is to do what­ev­er makes sense for your sit­u­a­tion. I pre­fer the Perl solu­tion because I like the lan­guage and its ecosys­tem and per­haps have accli­mat­ed to its quirks. The com­pli­ca­tions of JavaScript brows­er sup­port, com­pet­ing frame­works, and lay­ers of tool­ing make my head hurt. Despite this, I’m still learn­ing; if you have any com­ments or sug­ges­tions, please leave them below.

  • Localizing dates in a Perl web application

    Localizing dates in a Perl web application

    When we’re writ­ing soft­ware for a glob­al audi­ence, it’s nice if we can pro­vide it accord­ing to their native lan­guages and con­ven­tions. Translating all of the text can be a huge under­tak­ing, but we can start small by mak­ing sure that when we show the day and date it appears as the user expects. For exam­ple, to me it’s Tuesday, April 20, 2021; to my friend Paul in the UK it’s Tuesday, 20 April 2021 (note the dif­fer­ence in order), and to my oth­er friend Gabór in Israel it’s יום שלישי, 20 באפריל 2021 (note the dif­fer­ent direc­tion of the text, dif­fer­ent lan­guage, and char­ac­ter set).

    Thankfully, we have a num­ber of tools to assist us:

    • The DateTime::Locale library, which enables our Perl soft­ware to rep­re­sent dates and times glob­al­ly and con­tains a cat­a­log of locales. It works with the DateTime library for stor­ing our dates as objects that can be eas­i­ly manip­u­lat­ed and formatted.
    • The HTTP Accept-​Language head­er, which lets a web brows­er com­mu­ni­cate to the serv­er what nat­ur­al lan­guages and locale vari­ants the user understands.
    • The HTTP::AcceptLanguage mod­ule, which helps us parse the Accept-​Language head­er and select a com­pat­i­ble locale.

    Our sam­ple code uses the Mojolicious frame­work and is very sim­ple; almost half of it is just HTML web page tem­plates. You could eas­i­ly adapt it to oth­er frame­works or tem­plat­ing systems.

    #!/usr/bin/env perl
    
    use Mojolicious::Lite -signatures;
    use DateTime;
    use DateTime::Locale;
    use HTTP::AcceptLanguage;
    
    my %locales
        = map { $_ => DateTime::Locale->load($_) } DateTime::Locale->codes;
    
    get '/' =>
        sub ($c) { $c->render( template => 'index', date => DateTime->today ) };
    
    helper localize_date => sub ( $c, $date = DateTime->today, $format = 'full' )
    {
        my $locale = $locales{ HTTP::AcceptLanguage->new(
                $c->req->headers->accept_language )->match( keys %locales ) };
    
        my $method_name = "date_format_$format";
        return $date->clone->set_locale($locale)
            ->format_cldr( $locale->$method_name );
    };
    
    app->start;
    __DATA__
    @@ index.html.ep
    % layout 'default';
    % title 'Today';
    <ul>
        <li><%= localize_date $date %></li>
        % for my $format ( qw(long medium short) ) {
        <li><%= localize_date $date, $format %></li>
        % }
    </ul>
    @@ layouts/default.html.ep
    <!DOCTYPE html>
    <html>
        <head><title><%= title %></title></head>
        <body><%= content %></body>
    </html>

    Lines 1 through 5 tell our code to use the Perl inter­preter in our exe­cu­tion PATH and load our pre­req­ui­site mod­ules. Note we’re using the micro ver­sion of Mojolicious, Mojolicious::Lite; lat­er you can grow your appli­ca­tion into a well-​structured Mojolicious app. We’re also using Perl sub­rou­tine sig­na­tures, which requires Perl 5.20 or lat­er (released in 2014).

    Lines 7 and 8 pre­load all of the avail­able DateTime::Locale objects so that we can serve requests faster with­out hav­ing to load a new locale every time. We cre­ate a hash where the keys are the locale iden­ti­fiers (for exam­ple, en-US for United States English), and the val­ues are the locale objects.

    Line 10 begins our route han­dler for HTTP GET requests on the default / route in our web appli­ca­tion. When a brows­er hits the home page of our app, it will exe­cute the code in the anony­mous sub in line 11, which is passed the con­troller object as $c. It’s a very sim­ple han­dler that ren­ders a tem­plate called index (described below), pass­ing it a date object with today’s date.

    Lines 13 through 23 are where the smarts of our appli­ca­tion lie. It’s a helper that we’ll call from our tem­plate to local­ize a date object, and it’s anoth­er anony­mous sub. This time it’s passed a Mojolicious con­troller as $c, a $date para­me­ter that defaults to today, and a $format para­me­ter that defaults to full’.

    Lines 14 through 18 in the helper get our locale. Working from the inside out, we get the HTTP Accept-​Language head­er from the request on line 16, cre­ate a new HTTP::AcceptLanguage object in line 15 for pars­ing that head­er, and then match it against the keys in our glob­al %locales hash in line 17. That matched key then looks up the appro­pri­ate DateTime::Locale object from the hash.

    DateTime only allows you to set a locale at object con­struc­tion time, so in line 19 we cre­ate a new object from the old one, set­ting its locale to our newly-​discovered $locale object. Finally, in lines 21 and 22 we deter­mine what method to call on that object to retrieve the CLDR (Common Locale Data Repository) for­mat­ting pat­tern for the request­ed for­mat and then return the for­mat­ted date.

    Finally, line 25 starts the appli­ca­tion. To run it using the devel­op­ment serv­er includ­ed with Mojolicious, do this at the com­mand line:

    $ morbo perl_date_locale.pl

    There are oth­er options for deploy­ing your appli­ca­tion, includ­ing Mojolicious’ built-​in web serv­er, inside a con­tain­er, using oth­er web servers, etc.

    The rest of the above script is in the __DATA__ por­tion and con­tains two pseudo-​files that Mojolicious knows how to read in the absence of actu­al tem­plates and lay­outs. First on line 28 is the actu­al index.html.ep HTML page, which uses Mojolicious’ Embedded Perl (ep) tem­plat­ing sys­tem to select a lay­out of shared HTML to use (the layouts/default.html.ep file start­ing on line 39).

    Lines 32 through 37 ren­der an HTML unordered list that runs through the var­i­ous for­mat­ting options avail­able to our localize_date helper, first with the default full’ for­mat­ting, and then a loop through long’, medi­um’, and short’. Note that we call our helper as an expres­sion, with an equals (=) sign after the per­cent (%) sign.

    If you want to test dif­fer­ent locales with­out chang­ing your brows­er or oper­at­ing sys­tem set­tings, you can invoke the script from the com­mand line along with the HTTP request and head­ers to pass along. Here’s an exam­ple using German:

    $ perl perl_date_locale.pl get -H 'Accept-Language: de' /
    [2021-04-17 16:39:57.81379] [5425] [debug] [LcCSBKMVd90t] GET "/"
    [2021-04-17 16:39:57.81408] [5425] [debug] [LcCSBKMVd90t] Routing to a callback
    [2021-04-17 16:39:57.81610] [5425] [debug] [LcCSBKMVd90t] Rendering template "index.html.ep" from DATA section
    [2021-04-17 16:39:57.81714] [5425] [debug] [LcCSBKMVd90t] Rendering template "layouts/default.html.ep" from DATA section
    [2021-04-17 16:39:57.81792] [5425] [debug] [LcCSBKMVd90t] 200 OK (0.004118s, 242.836/s)
    <!DOCTYPE html>
    <html>
        <head><title>Today</title></head>
        <body>
    <ul>
        <li>Sonntag, 18. April 2021</li>
        <li>18. April 2021</li>
        <li>18.04.2021</li>
        <li>18.04.21</li>
    </ul>
    </body>
    </html>

    And here’s Japanese:

    $ perl perl_date_locale.pl get -H 'Accept-Language: ja' /
    [2021-04-17 16:40:56.10840] [5478] [debug] [Wmr6cN5KUJlP] GET "/"
    [2021-04-17 16:40:56.10874] [5478] [debug] [Wmr6cN5KUJlP] Routing to a callback
    [2021-04-17 16:40:56.11101] [5478] [debug] [Wmr6cN5KUJlP] Rendering template "index.html.ep" from DATA section
    [2021-04-17 16:40:56.11255] [5478] [debug] [Wmr6cN5KUJlP] Rendering template "layouts/default.html.ep" from DATA section
    [2021-04-17 16:40:56.11360] [5478] [debug] [Wmr6cN5KUJlP] 200 OK (0.005164s, 193.648/s)
    <!DOCTYPE html>
    <html>
        <head><title>Today</title></head>
        <body>
    <ul>
        <li>2021年4月18日日曜日</li>
        <li>2021年4月18日</li>
        <li>2021/04/18</li>
        <li>2021/04/18</li>
    </ul>
    </body>
    </html>

    A full list of sup­port­ed locales is pro­vid­ed in the DateTime::Locale::Catalog documentation.

    I hope this arti­cle has helped demon­strate that it’s not too hard to make your Perl web appli­ca­tions respect glob­al audi­ences, if only with dates. For more on local­iza­tion and Perl, start with the Locale::Maketext framework.

  • Video for Better Perl: Subroutine signatures and type validation

    Video for my pre­sen­ta­tion to Houston Perl Mongers last month, based on this blog post. Slides are here. Sorry about all the um“s and ah“s.