Perl

Flash! Aa-aaah! He'll generate random words!


I’ve been reworking the code behind my random word generator, to generalize the methods of modeling the source word sets and creating the output. The first fruit of that is one tuned for Pinnacle’s new Savage Worlds Of Flash Gordon RPG (slowly trickling out for Kickstarter supporters, and being vigorously edited on their forums).

For the source file, I took every Mongo-ish name/word I could find from the original comic strips, series, and movies (all hail the concept of wikis), and added everything Pinnacle has created in their new books. They’ve done a great job of mixing their own names with the originals, IMHO.

Unfortunately, it’s still a fairly small set (285 words at the moment) with wildly divergent phonetic patterns, making it unsuitable for either trigrams or n1grams, so I kept the lessons I learned and threw away most of the code, creating a brand-new digram-based generator that preserves more flavor than the obvious implementation.

As configured, mongowords can generate at least half a million distinct words, and even at the end of the list, a fair number of them are usable. With the new code, it will be easier to merge back the full feature set from the old one, while supporting a wider range of generation techniques.

Now that that’s out of the way, back to finding errors and tyops in the new rulebooks!

Baby's First Ruby Script


  1. rubygems.org is like someone tried to reimplement CPAN with crayons and a dumpster.

  2. most of the Ruby community thinks an API dump is documentation. Some of them don’t even supply that.

  3. 90% of FAQs assume you’re using Rails. Maybe 95%.

  4. I got bad flashbacks to Pascal from having to put function definitions at the top. Yeah, whatever.

  5. “our {} is different” will trip me up for years.

  6. not being able to freely break lines mid-expression is annoying.

  7. googling for help always returns obsolete 10-year-old results (“thanks, pagerank!”).

  8. manual type conversion == stone knives and bearskins.

  9. ri is like someone tried to reimplement perldoc with chalk. In the rain.

Anyway

I had a tiny project that had minimal dependencies. The Perl version flowed from my fingers like water, naturally, but the logic was trivial, and all it needed was a TOML parser and some random numbers, so it seemed like it would be easy to try out in Ruby. And I can even say something nice about the language: shuffle() and uniq() are core array methods.

A related script generates an improved Japanese Diceware ruleset using JMdict, but I haven’t found a full-featured romanization gem, and the advice about XML parsing is all over the map. Perl’s XML::Twig and its simplify() method have really spoiled me; I ended up having to mix Nokogiri::XML::Reader with Nori.parse, and then write my own force_arrays() function to emulate one of the most useful features of simplify(). The result is still only a crude approximation of what I can do with XML::Twig, but it suffices for this project.

While I’m complaining, the following gang-bang expression is not equivalent to the assignment version, which produces a sorted random unique subset of the array. Instead, you get the unsorted complement of the desired slice, unless the array was already unique, in which case Ruby blows chunks. This is, um, non-obvious.

words.uniq!.shuffle!.slice!(0,7776).sort!
words = words.uniq.shuffle.slice(0,7776).sort

On an unrelated note, I was amused to discover that the only useful PDF-generating gem still doesn’t support clipping paths, and while the authors claim it can easily be extended to support additional low-level PDF operators, that feature is not mentioned anywhere in the documentation or code…

Why play with Ruby in the first place? An old friend and on-again co-worker is a real zealot. Of course, our uses of scripting languages are very nearly orthogonal, to the point that many of the reasons I keep bouncing off of Ruby are things he never sees.

And, yes, I continue to be offended by Python’s use of indentation, although I’ll tolerate it at small scale to play with Ren’Py and CircuitPython. It’s heavily pushed on the Raspberry Pi as well, but at least there I can run Emacs and Perl, as Ghod intended.

Rocketbook Isometric Grid Paper


Last week I took another look at the various “digital pen” products, and once again couldn’t find one that was worth buying. I like the idea of the Livescribe, etc, but none of them seem to actually work very well, with poor ergonomics, poor performance, poor support, or “all of the above”.

So I took a look at Rocketbook, which is a series of notebooks with custom paper, marked up to be scanned in with an iPhone/Android app and sent to your email account and/or various cloud services. OCR is not handled by Rocketbook, so unless you send it to a cloud service that does that, you get the image only.

If you use any of the upload options, you can’t really rely on confidentiality, of course, but you can always leave the destination checkboxes blank, and uses your phone’s native sharing services to store the scans. (note that the email goes through a third-party service, then to Rocketbook’s server, then to your email provider, rather than just using the phone’s API)

It looked straightforward, and since the app is free and there are sample PDFs available for download, I could try it out before buying. It recognized their markup quickly and reliably, and produced a decent image, so I went ahead and ordered the Everlast notebook, which has a special paper that turns FriXion erasable pens into wet-erase markers. In theory, the Everlast pages can be reused forever, unlike the 5-6 times their heat-erasable product claims.

I’ve had it for a few days now, and after adding a pen loop to keep the supplied FriXion pen handy (3” strip of gaffer tape, with a 1.75” strip stuck across the middle of the sticky side, leaving room at both ends to attach it to the sturdy edge of the cover), I quite like it. I have some color FriXion pens as well, and it captures them nicely.

But of course I want to print my own, and not just the dot-grid they supply. The first hurdle was that the PDF that works just fine on my office color laser printer is unscannable when printed on my home inkjet. Seriously, if I place two printouts side by side, the laser-printed one is recognized instantly, while the inkjet version leaves the app fumbling for several minutes before it figures out that it’s a black box with a QR code in the lower right corner.

I’ve ripped the PDF apart in Illustrator, so I know there’s no hidden magic that’s not reproducing correctly on the inkjet, but somehow it makes a difference. The ink is just as black, the paper is just as white, the resolution is just fine. One thing I did discover is that there are several different versions of the free PDFs, and the one I originally tested with has a relatively narrow black border. The most recent one has a wider border that works better on an inkjet, but someone at the company hacked it together, so it isn’t really an 8.5x11 page, and the destination icons are bitmaps.

My first attempt at a custom Rocketbook PDF is here, and replaces their dot-grid with an isometric grid. This one’s still a bit finicky on the inkjet, but a lot better than their original PDF.

I did it in Perl with PDF::API2::Lite, so I can tweak it until I figure out exactly what their app is looking for. My guess is that the “V” section in the QR code indicates paper type, and the app has a lookup table containing the aspect ratio and relative location of the destination icons, but that by itself can’t explain the difference between inkjet and laser printouts.

11/29 Update

Just tested with the new version of the app, and it’s much more reliable at scanning inkjet prints of both their PDFs and mine.

MasterCook scripts


Ongoing MasterCook molesting now up on github. I took advantage of the very restricted formatting of their XML-ish MX2 format to write cat/grep/ls tools, as well as a quick-fix for a common format issue in files imported from MXP format. It would be trivial to write those sort of tools for the XML versions, but working in MX2 is handy for things you plan to re-import to MasterCook (since I haven’t written an xmltomx2 converter yet…).

The New Perl Way: "You're doing it wrong"


use CGI;

“I’m sorry, Dave, I can’t do that.”

cpanm CGI

“You really shouldn’t use that any more. It’s bad for you.”

perldoc CGI

“The rationale for this decision is that CGI.pm is no longer considered good practice for developing web applications, including quick prototyping and small web scripts. There are far better, cleaner, quicker, easier, safer, more scalable, more extensible, more modern alternatives available at this point in time. These will be documented with CGI::Alternatives.”

perldoc CGI::Alternatives

No documentation found for “CGI::Alternatives”.

cpanm CGI::Alternatives
perldoc CGI::Alternatives

“Let me build this strawman that doesn’t actually make good use of CGI.pm to show you how you can easily switch to one of half a dozen different frameworks that let you use half a dozen different templating systems launched with half a dozen different embedded web servers, and replace your self-contained 100-line CGI script with half a dozen files located in half a dozen directories. For more fun, my sample code gets mangled if you try to view it as a manpage, so you really should download the raw file from CPAN.”

cpanm --uninstall CGI::Alternatives
cpanm Dancer2
perldoc Dancer2
cpanm --uninstall Dancer2
cpanm Mojolicious
perldoc Mojolicious
perldoc Mojolicious::Lite

use Mojolicious::Lite;
plugin CGI => [ '/' => "trivialscript.cgi" ];
app->start;

use CGI;

more...

Geo::OLC Perl module


[Update: now on CPAN]

There are multiple competing algorithms for converting latitude/longitude locations into something easier for humans to work with. Some of them are proprietary, which makes them pretty useless offline or after that company goes out of business.

Google came up with a plausible rationale for inventing their own Open Location Code rather than adopting geohash or something similar. It’s Open Source up on Github, and they supply APIs for several common languages.

But not Perl. Naturally, I had to fix that, so here’s Geo::OLC (compressed tarball). It has a full test suite, a simple command-line tool, and a CGI script that generates a dynamic labeled grid for Google Earth. I think I’ve about got it cleaned up enough to put it on CPAN.

For amusement, the sample location I use in the POD documentation, 8Q6QMG93+742, is Tenka Gyoza in Osaka, which is exactly the sort of place that you’d have trouble finding with standard addressing methods. Actually, you’d have trouble finding it with Google Maps on your phone, as my sister learned.

I tried not to go overboard with Perlisms, but the code still ended up fairly compact, largely because most of the existing APIs were written while the formatting was still in flux, so they’re more generic than necessary.

[Update: turns out someone did write one, but it never made it to CPAN: Geo::OpenLocationCode. Looks like he converted one of the other APIs instead of writing it from scratch, so he inherited the same bug in recover_nearest.]

Converting FLAC audio to MP3 in an MKV video


Let’s say that you have somehow acquired a video in MKV format, where for no particularly good reason the creator has chosed to encode the audio as FLAC (we shall neglect for the moment their poor taste in embedded fonts for animated karaoke and special-effect subtitling).

If for device-compatibility reasons you would prefer a better-supported audio format like MP3, and you’d really rather not re-encode the video to MP4 with hardsubs, the simplest solution is to extract the FLAC audio with mkvextract (part of Mkvtoolnix), decode it to WAV with Flac, encode it to MP3 with Lame, and then reinsert it with mkvmerge.

You also have to figure out which audio track, if any, is FLAC-encoded, but mkvinfo will do that for you, in a relatively-sane format. I have of course automated the whole task with a small Perl script.

Finding a video player that can smoothly scrub forward and backward through an MKV video for screenshots is left as an exercise in frustration for the reader.

Clean, simple code


I don’t know why people say Perl is hard to understand…

$=$/ and print join $”,reverse map scalar reverse,qw,foo bar baz,

“Need a clue, take a clue,
 got a clue, leave a clue”