• On Organizing JavaScript and CSS Assets Within MVC Frameworks

    Posted:

    Web Pyramid via @pinboard on Twitter

    (h/t to @pinboard on Twitter for posting this pyramid graphic)

    When I develop web apps (which these days are generally small, intranet business apps), I use Perl 5.2x and Mojolicious for the core stack. MySQL, apache and my custom ORM are also part of that stack, but let’s leave that for a future post.

    My front-end JavaScript and CSS is fairly small: Bootstrap 4.x (which currently requires jQuery), and Font Awesome 5. Because of all the wonderful things ECMAScript 6 has brought, I have been weening myself off jQuery, quite successfully.

    Understand that my web apps are not Single Page Applications (SPA). I have no requirements to make such apps and I have always found them problematic. There is nothing quite like a browser page refresh to re-establish a sane web app state.

    For a while, I have been using the trick of adding the current controller name to an outer wrapper in the layout, something like this:

      <!doctype html>
      <html lang="en">
      <head>
         <meta charset="UTF-8"/>
         <title>Document</title>
      </head>
      <body>
         <div class="container controller $THIS_CONTROLLER">
           $VIEW_CONTENT
         </div>
      </body>
      </html>    
    

    Since the layout is implemented by a server-side templating engine (usually Template Toolkit), it is easy to get the name of the current controller and insert that into the layout even before the view-specific content is available.

    Why do this? Scoping you DOM in this manner makes it trivial to scope CSS elements to specific views. Image I have a controller called Users. I might have controller-specific CSS that looks like this:

      h3 { font-size: 1.5 rem; color: yellow }
      .Users h3 { font-size: 120%; color: pink; font-style: italic }
    

    This ensures that all the H3 tags on any view generated by the Users controller appears larger, pinker and slantier than H3 tags found on other pages.

    Scoping CSS brings a great deal of structure to CSS quite easily.

    ECMAScript 6 defines modules, which create a compartmentalization of code long lacking in JS. The top level HTML makes a top-level JS script as a module like this:

      <script type="module" src="./top-level.js"></script>
    

    Inside of that top level JS script, you can use import statements (which syntactally borrow a lot form python) to pull in other JS modules using the Fetch API. Finally, ECMAScript 6 introduces a class statement along with an extends mechanism so that you never have to think about prototype again.

    Just like CSS can be scoped to controllers, it occurred to me that JS modules can be scoped to controllers!

      <!doctype html>
      <html lang="en">
      <head>
         <meta charset="UTF-8"/>
         <title>Document</title>
      </head>
      <body>
         <div class="container controller $THIS_CONTROLLER">
           $VIEW_CONTENT
         </div>
         <script type="module" src="js/$THIS_CONTROLLER.js"></script>
      </body>
      </html>      
    

    Common code can be factored into classes used by multiple controller-level modules. This brings a tremendous amount of structure and sanity to web apps that have long been missing.

    Hope this inspires you to try out this pattern in your own projects.

  • Using gmail to send mail with Perl’s Email::Sender

    Posted:

    Every couple of years, someone in the Perl community writes a new wrapper around sending email. Although an old protocol, stuff like security and multimedia attachments present a lot of wrinkles that some libraries handle poorly.

    As you may know, you can use gmail to send mail, but you need to jump through some hoops. In the following example, I am going to show how to connect to gmail using SSL/TLS and the PLAIN AUTH protocol for passing SASL credentials.

    Be warned: you need to allow “less secure” apps from your Google settings. This allows you to use your gmail username and password for authorization credentials. The more secure way is to use 2-step auth, which will generate a seperate password for just this app. I leave this as an excerise for the reader.

    You will need (according to the docs and my own experiments), the following modules:

    • Email::Sender
    • Email::Simple

    If you install these with cpan or carton, the dependencies will be handled for you. There are Moose deps, but it is worth it.

    You will need the following use statements:

      use Email::Sender::Simple ('sendmail');
      use Email::Sender::Transport::SMTP;
      use Email::Simple;
      use Email::Simple::Creator;
    

    First, create the transport object. This contains the details talking to the remote SMTP server (which, in this case, is smtp.gmail.com).

      my $transport = Email::Sender::Transport::SMTP->new({
                                                           host => 'smtp.gmail.com',
                                                           port => '587',
                                                           ssl => 'starttls',
                                                           sasl_username => 'ACCOUNT@gmail.com',
                                                           sasl_password => 'SECRET_WORD',
                                                           debug => 0,
                                                          });
    
    

    I believe most of these parameters are self-explanatory, but note the debug switch. Set this to 1 if your messages are not being delivered. It helps a great deal.

    Next, we need to create the email message.

     my $email = Email::Simple->create(
                                        header => [
                                                   To => 'SOMEONE@SOMEWHERE.TLD',
                                                   From => 'ACCOUNT@gmail.com',
                                                   Subject => 'Some important announcements',
                                                  ],
                                        body => $msg,
                                       );
    

    If you understand SMTP, then header section may make more sense. You can put any allowable SMPT header here. In this case, I am only sending simple ASCII messages, so a dumb string works for the whole of the body. If you wanted to send HTML, you would need to figure out attachments, which is a task I send you on with my blessings.

    Finally, bring these two objects together using the sendmail function exported from Email::Sender::Simple module like this:

    sendmail($email, {transport => $transport});
    

    If this should fail, it will die. You may want to wrap this in an eval if you want to suppress that behavior.

    I hope this helps you on your journey with Perl.

  • Testing iWriter from iPhone

    Posted:

    This is a test of iwriter from my iphone.

    Can I change the file name? Yes.

    Looks like there is spell check.

    Seems OK, actually.

    Does dropbox sync work? Yes.

  • NarraScope 2019

    Posted:

    Narrascope Program

    2019 saw the first big outreach project of the Interactive Fiction Technology Foundation in the form of a conference named NarraScope. I was luckily enough to attend it, as it happen essentially in my backyard. Since this is the only conference I will be going to this year, NarraScope is sort of my Comicon and The Perl Conference bundled into one event.

    The three day conference kicked off Friday night in the Stata building of MIT. If you have never been inside this building, it does indeed appear to be the site of a tragic Star Trek teleporter accident.

    Although I missed the opening day on Friday, I did make it on Saturday, where I was greeted by Jason Scott in full conference regalia. This was our first meeting IRL, which went swimmingly. We compared our statin prescriptions and I missed most of the keynote talk.

    I then attended the State of Twine talk by Chris Klimas, which was really motivating for me. I do not have the time for a lot of side projects, so I feed vicariously off those who do. Twine is having very typical growing pains that are common for open source projects. Logistical issues like deciding what is the right feedback back mechanism for the community, who’s steering the project, etc. elicited deja vu. Inform’s [Graham Nelson], who was in attendence at the time, echoed the same issues. Some of this is where IFTF can and should help. Other aspects, these projects will have to figure out. I was super jazzed to be in the room.

    After lunch, I attended the performance of Squinky’s “How Making Videogames Turned Me Into a Depressed Gay Communist” and it was intense. To describe it will not help. You have to see it for yourself.

    I then chatted with Graham Nelson for a long while, professing my admiration for Inform, which I compared favorably to Perl. Inform will be open sourced under Larry Wall’s Artistic License, about which I am gladden. Also connected with Aaron Reed of Blue Lacuna fame.

    Although the conference continued, I needed to get back home. My thanks to IFTF for pulling together a great conference. Here’s hoping for bigger and better things next year.

  • Installing Plerd from head with no root access

    Posted:

    This post is more of some admin notes as I try to get a clean install of plerd from github.

    First, you need a modern perl (I’m using perl 5.24 via plenv).

    You should install Carton next. It will make everything easier.

      $ git clone git@github.com:jmacdotorg/plerd.git
      $ cd plerd
      $ carton
    

    Create a new file called env.sh. Make it look like this:

      #!/bin/bash
      # source me: ./env.sh 
      #
      export APP_HOME=[REPLACE WITH YOUR PATH TO plerd]
      export PERL5LIB=$APP_HOME/local/lib/perl5
      export PATH="$APP_HOME/local/bin:$PATH"
    

    Replace items within [] using appropriate values.
    Save this file and then source it:

      $ source ./env.sh
    

    Now you can run:

      $ perl Makefile.PL --PREFIX=./local
      $ make && make install
    

    I think there is a way to force plerd into the local/lib/perl5 structure. For now, I just moved these manually.

      $ mv -v local/share/perl/5.24.1/*pm local/lib/perl5/
      $ mv -v local/share/perl/5.24.1/Plerd local/lib/perl5/
    

    You will find plerdall and plerdwatcher in ./local/bin and both should be in your PATH.

    Time to make the default plerd config:

      $ plerdall --init
    

    Go ahead and edit that file in the proscribed way.

    To run plerdall correctly, you will probably want to change into the newly created plerd directory. This will be inside the git sandbox but whatevs.

      $ cd plerd
      $ plerdall
    

    Interestingly, the following files were empty:

      atom.xml
      feed.json
      recent.xml
    

    Some of the tag/* files were also empty.

    UPDATE: I believe my local disk was full when I tried this, so this is not a plerd problem.

  • A Public Kaizen on Blogging

    Posted:

    Going through my old blog posts from over ten years ago has been a humbling experience. A good deal of my writing style was cloying and needy. Joe Michael Straczinski has suggested that to be a good writer, you need to burn about 100,000 words writing poorly. I think Joe was a quick study.

    Often, this blog attempted to provide insights into the tech industry. These attempts were meager and weak. At best, I only amplified common knowledge and at worst I put my ignorance on public display.

    A significant chunk of the blog corpus reads like a kind of proto-Twitter. There is a lot of personal stuff that lacks context or perspective. It is tempting to attribute these shortcomings to the age of the writer, but there are many examples of great writers who manifested skill early in their careers.

    These maudlin observations are an attempt at a ”kaizen”, which is a Japanese word meaning “improvement”. A kaizen event is a time dedicated away from normal activities to reflect on what went well and what could have been improved in you life and work.

    The outcome of my personal kaizen for this blog is to produce work of higher quality that provides better insights into whatever subject moves me. A good deal of my headspace is in boardgame design, but I also want to share what I’ve learned about software engineering in the web application space over these past twenty years.

    Here is the part of the post were I put a call to action like “subscribe to my atom feed” or “follow me on twitter!”. Instead, I will focus narrowly on my content and let the Deep Learning AI at search engines handle the marketing.

  • A Public Kaizen on Blogging

    Posted:

    Going through my old blog posts from over ten years ago has been a humbling experience. A good deal of my writing style was cloying and needy. Joe Michael Straczinski has suggested that to be a good writer, you need to burn about 100,000 words writing poorly. I think Joe was a quick study.

    Often, this blog attempted to provide insights into the tech industry. These attempts were meager and weak. At best, I only amplified common knowledge and at worst I put my ignorance on public display.

    A significant chunk of the blog corpus reads like a kind of proto-Twitter. There is a lot of personal stuff that lacks context or perspective. It is tempting to attribute these shortcomings to the age of the writer, but there are many examples of great writers who manifested skill early in their careers.

    These maudlin observations are an attempt at a ”kaizen”, which is a Japanese word meaning “improvement”. A kaizen event is a time dedicated away from normal activities to reflect on what went well and what could have been improved in your life and work.

    The outcome of my personal kaizen for this blog is to produce work of higher quality that provides better insights into whatever subject moves me. A good deal of my headspace is in boardgame design, but I also want to share what I’ve learned about software engineering in the web application space over these past twenty years.

    Here is the part of the post were I put a call to action like “subscribe to my atom feed” or “follow me on twitter!”. Instead, I will focus narrowly on my content and let the Deep Learning AI at search engines handle the marketing.

  • On Single-play games

    Posted:

    A friend of mine sent me a meme today featuring a picture of someone filling a cup using two spouts of a soda fountain at the same time. One spout was labelled “Legacy system” and the other “Literally every other game”. On the hand holding the cup was the name of a well-known game designer who is famous for producing legacy games.

    “Sick burn,” I remarked.

    However, this meme got me thinking more about Legacy and single-use games.

    Legacy games are versions of existing games with new rules and mechanics that permanently change the components of the game. This means that these kinds of games usually can only be played once (or if campaign-based, only played a set number of times). The most popular version of this kind of game is Legacy Pandemic, which now ships in seasons. This is to say, each season is an isolated campaign that players are expected to finish before going on to the next one.

    If you are a traditional or causal boardgammer, you may find the idea of destroying cards or marking up the playing board a startling idea. Let your mind be at ease; It is. However, legacy games are a subset of single-use games, many of which have been with us for some time.

    “Real life” escape rooms have become extremely popular. The idea of solving puzzles in real-team with your friends and family is quite fun for a certain class of people. This kind of entertainment is inherently single-use. Sure, you can try to go through the puzzle again, but it probably won’t be as much fun.

    It isn’t surprising that boardgames have tried to capture some of that fun at home using a variety of traditional boardgame elements (particularly cards). Games like the Exit series come to mind. My family has played a couple of these to great effect. Like their real-life counterparts, these boardgames are single-use.

    Recently, Z-Man games introduced a Choose Your Own Adventure IP-branded game that plays like one of the classic CYOA books I grew up reading. The designers did a great job of translating the experience of those books to a cooperative gaming environment. Some have complained that it isn’t much of a game, to which I agree. However, it was a fantastic, single-use activity.

    It only recently occurred to me that there is little new about the genre of single-use games. One of the most successful games of my youth was inherently single-use although it was careful never to point out this fact. Trivial Pursuit, with its admittedly large, but fixed collection of question-answer pairs, surely must be included in this group of games. Trivial Pursuit later sold new “editions” of thematically linked questions (e.g. the Silver Screen edition, the Baby Boomer edition, etc.) that pointed to the single-use nature of the core mechanic.

    Others have pointed out that criticism the “disposable” nature of single-use games ignores that most boardgames are played but a handful of times, even the wildly popular ones. If you can get a couple of hours out of game that you can’t play again, you have a story that will last a lifetime.

  • plerd-cli - an interactive tool for Plerd

    Posted:

    My self-imposed Twitter exile has already paid off with the creation of this little program: plerd-cgi. This is designed to be an interactive tool to “manage” your plerd instance. Currently, the tool can regenerate all your content or from your sources directory or just a single post. This is very helpful when you are on the host running plerd and you are changing stylesheets or other settings.

    I will ping the plerd development team (hi, Jmac) and see if there is interest in adding this tool to the plerd/bin directory (which is where I have it).

    Increasing, my version of plerd is drifting from the released version. When plerd gets tags (jmac’s re-implementation of a feature I contributed), then I will try to merge these codebases together again.

    From previous post, you can see that I am experimenting with managing photos. Plerd’s killer feature is its integration with Dropbox. This really enables a powerful and flexible blogging platform for those used to composing in text editors, like me.

    #!/usr/bin/env perl
    
    use warnings;
    use strict;
    
    use FindBin;
    use lib ("$FindBin::Bin/../lib");
    
    use Cwd ('abs_path');
    use File::Basename;
    use Getopt::Long;
    use Path::Class::File;
    use YAML qw( LoadFile );
    
    use Plerd;
    
    our %gKnownCommands =
        ('regen_all' => \&cmd_regenerate_all, 'regen' => \&cmd_regenerate_file, 'help' => \&cmd_help);
    
    main();
    exit;
    
    #-----
    # Subs
    #-----
    sub main {
        my $opts = usage();
    
        my $plerd = init_plerd();
    
        # Only do one command
        while (my ($command, $action) = each %gKnownCommands) {
            if (defined $opts->{$command}) {
                return $action->($plerd, $opts);
            }
        }
    
        return;
    } # end sub main
    
    
    sub usage {
        my %opts;
    
        GetOptions(
            "help"        => \$opts{help},
            "R|regen-all" => \$opts{regen_all},
            "r|regen:s"   => \$opts{regen},
            "v|verbose"   => \$opts{verbose},
        );
    
        if ($opts{help}) {
            dump_usage();
            exit;
        }
    
        my $has_command = 0;
        for my $command (keys %gKnownCommands) {
            $has_command |= defined $opts{$command};
        }
    
        if (!$has_command) {
            die("No command given.  See --help for details.\n");
        }
    
        return \%opts;
    } # end sub usage
    
    
    sub dump_usage {
        print <<"EOT";
    $0 - command line interface to plerd
    
    USAGE:
    
      $0 [OPTIONS]
    
    OPTIONS:
    
      --help           # this screen
      --verbose        # provide more verbose output
      --regen-all      # regenerate all HTML files from known sources
      --regen FILE     # regenerate just this source file
    
    
    EOT
    
    } # end sub dump_usage
    
    
    sub init_plerd {
        my ($opts) = @_;
    
        my $conf_file = "$FindBin::Bin/../conf/plerd.conf";
        if (!-e $conf_file) {
            die("Cannot find '$conf_file'\n");
        }
    
        my $config_ref = LoadFile($conf_file);
        my $plerd      = Plerd->new($config_ref);
    
        return $plerd;
    } # end sub init_plerd
    
    
    sub cmd_help {
        my ($plerd, $opts) = @_;
        dump_usage();
        return;
    } # end sub cmd_help
    
    
    sub cmd_regenerate_all {
        my ($plerd, $opts) = @_;
    
        my $start = time();
        if ($opts->{verbose}) {
            print("Regenerating all content.  This may take a while.\n");
        }
    
        $plerd->publish_all();
    
        if ($opts->{verbose}) {
            printf("Regeneratation completed in %0.2f minutes.\n", (time() - $start) / 60);
        }
    
    } # end sub cmd_regenerate_all
    
    
    sub cmd_regenerate_file {
        my ($plerd, $opts) = @_;
    
        my $source_file = abs_path($opts->{regen});
    
        if (!-e $source_file) {
            die("Cannot find '$source_file'\n");
        }
    
        # is the source file within the source_directory?
        my $dirname = dirname($source_file);
        if ($dirname ne $plerd->source_directory) {
            die(sprintf(
                    "File '%s' does not appear to be in the source directory '%s'\n",
                    $source_file, $plerd->source_directory
                )
            );
        }
    
        if ($opts->{verbose}) {
            print("Publishing '$source_file'\n");
        }
    
        my $post = Plerd::Post->new(
            source_file => Path::Class::File->new($source_file),
            plerd       => $plerd
        );
        eval {
            $post->publish;
            1;
        } or do {
            die(sprintf("Could not publish '%s': %s\n", $source_file, $@));
        };
    
        # Publishing this new post triggers updates on these other pages
        for my $action (
            'publish_tag_indexes', 'publish_archive_page',
            'publish_recent_page', 'publish_rss',
            'publish_jsonfeed'
        ) {
            if ($opts->{verbose}) {
                print("-> $action\n");
            }
            $plerd->$action;
        }
    
        if ($opts->{verbose}) {
            printf("View your new post here: %s\n", $post->uri);
        }
    
        return;
    } # end sub cmd_regenerate_file
    
    

    UPDATE: I spoke to Jmac about this and he pointed out his intention for the exisiting plerdall program to fill this niche. Additionally, the publish all functionality is already there. So I will talk a crack at porting what I have here when I am up and running with the current tip of plerd.