Tag: Mojolicious

  • Some thoughts on Perl template processing

    Some thoughts on Perl template processing

    Template proces­sors and engines are one of those pieces of soft­ware where it seems every devel­op­er wants to rein­vent the wheel. Goodness knows I’ve done it ear­li­er in my career. Tell me if this sounds familiar:

    1. You need to mix data into a doc­u­ment so you start with Perl’s string inter­po­la­tion in "dou­ble quotes" or sprintf for­mats. (Or maybe you inves­ti­gate formats, but the less said about them the bet­ter.)
    2. You real­ize your doc­u­ments need to dis­play things based on cer­tain con­di­tions, or you want to loop over a list or some oth­er structure.
    3. You add these fea­tures via key­word pars­ing and escape char­ac­ters, think­ing it’s OK since this is just a small bespoke project.
    4. Before you know it you’ve invent­ed anoth­er domain-​specific lan­guage (DSL) and have to sup­port it on top of the appli­ca­tion you were try­ing to deliv­er in the first place.

    Stop. Just stop. Decades of oth­ers who have walked this same path have already done this for you. Especially if you’re using a web frame­work like Dancer, Mojolicious, or Catalyst, where the tem­plate proces­sor is either built-​in or plug­gable from CPAN. Even if you’re not devel­op­ing a web appli­ca­tion there are sev­er­al general-​purpose options of var­i­ous capa­bil­i­ties like Template Toolkit and Template::Mustache. Investigate the alter­na­tives and deter­mine if they have the fea­tures, per­for­mance, and sup­port you need. If you’re sure none of them tru­ly meet your unique require­ments, then maybe, maybe con­sid­er rolling your own.

    Whatever you decide, real­ize that as your appli­ca­tion or web­site grows your invest­ment in that selec­tion will only deep­en. Porting to a new tem­plate proces­sor can be as chal­leng­ing as port­ing any source code to a new pro­gram­ming language.

    Unfortunately, there are about as many opin­ions on how to choose a tem­plate proces­sor as there are tem­plate proces­sors. For exam­ple, in 2013 Roland Koehler wrote a good Python-​oriented arti­cle on sev­er­al con­sid­er­a­tions and the dif­fer­ent approach­es avail­able. Although he end­ed up devel­op­ing his own (quelle sur­prise), he makes a good case that a tem­plate proces­sor ought to at least pro­vide var­i­ous log­ic con­structs as well as embed­ded expres­sions, if not a full pro­gram­ming lan­guage. Koehler specif­i­cal­ly warns against the lat­ter, though, as a tem­plate devel­op­er might change an application’s data mod­el, to say noth­ing of the pos­si­bil­i­ty of exe­cut­ing arbi­trary destruc­tive code.

    I can appre­ci­ate this rea­son­ing. I’ve suc­cess­ful­ly used Perl tem­plate proces­sors like the afore­men­tioned Template::Toolkit (which has both log­ic direc­tives and an option­al facil­i­ty for eval­u­at­ing Perl code) and Text::Xslate (which sup­ports sev­er­al tem­plate syn­tax­es includ­ing a sub­set of Template::Toolkits, but with­out the abil­i­ty to embed Perl code). We use the lat­ter at work com­bined with Text::Xslate::Bridge::TT2Likes emu­la­tion of var­i­ous Template::Toolkit vir­tu­al meth­ods and it’s served us well.

    But using those mod­ules’ DSLs means more sophis­ti­cat­ed tasks need extra time and effort find­ing the cor­rect log­ic and expres­sions. This also assumes that their designer(s) have antic­i­pat­ed my needs either through built-​in fea­tures or exten­sions. I’m already writ­ing Perl; why should I switch to anoth­er, more lim­it­ed lan­guage and envi­ron­ment pro­vid­ed I can remain dis­ci­plined enough to avoid issues like those described above by Koehler?

    So for my per­son­al projects, I favor tem­plate proces­sors that use the full pow­er of the Perl lan­guage like Mojolicious’ embed­ded Perl ren­der­er or the ven­er­a­ble Text::Template for non-​web appli­ca­tions. It saves me time and I’ll like­ly want more than any DSL can pro­vide. This may not apply to your sit­u­a­tion, though, and I’m open to counter-arguments.

    What’s your favorite tem­plate proces­sor and why? Let me know in the comments.

  • 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;
  • Part 7 video of pair programming a Perl web app

    This week we con­sid­ered a view­er’s pull request, added admin­is­tra­tor login, and start­ed on adding the SQLite data­base that will store the admin­is­tra­tor’s accep­tance of assign­ments. We also shored up file upload per­mis­sions for authen­ti­cat­ed users only and added a logout link, learn­ing about some more Mojolicious helpers.

    You can find the whole series here.

  • Part 6 video of pair programming a Perl web app

    Today Gábor and I con­tin­ued our work on a man­age­ment appli­ca­tion for his train­ing cours­es pow­ered by Mojolicious. Big accom­plish­ment this time: we went from a sin­gle upload form for exer­cis­es going to a pre­set file all the way to a multi-​file form where every exer­cise has its own list of files for sub­mis­sion. We also had a lit­tle detour into Docker-land where we sim­pli­fied his launch com­mand into docker-compose up.

    Head on over to Perl Maven if you’d like to catch up with pre­vi­ous install­ments or reg­is­ter with Zoom for the next one sched­uled on Sunday, May 30.

  • 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.