Moose data types

A quick reference to the built-in data types you can use in Moose. Use these when declaring a class, as with ‘isa’:

 Data Type Possible Values
Any Note: [`a] is an optional extension
which means any Type 
Item
Bool undef, 0, empty string, or 1
Maybe[`a] undef or [`a]. e.g.: Maybe[Str]
Undef must be undef
Defined must not be undef
Value
Str string
Num looks like a number
Int integer
ClassName string that is name of a class
RoleName …of a role
Ref
ScalarRef[`a] e.g., ScalarRef[Value]
ArrayRef[`a]
HashRef[`a]
CodeRef
RegexpRef
GlobRef
FileHandle IO::Handle or Perl filehandle
Object any blessed reference
any Class e.g., MyClass or SQL::Abstract
assuming your program uses them

Full details, examples, and advanced capabilites are explained at https://metacpan.org/pod/Moose::Manual::Types

Cleanly create SQL with SQL::Abstract::More

Some years ago now, I had written my own wrapper around DBI to make common SQL statements easier to write. Awhile back I found SQL::Abstract and recently revisited some old code to bring it up to the modern era. I found that the related SQL::Abstract::More was almost a direct replacement for what I had written − except with more features and better debugged. Here’s an example:

#!/usr/env/perl

use strict;
use warnings;

use SQL::Abstract::More;

my $sqla = SQL::Abstract::More->new;

# More maintainable with named parameters.
($stmt, @bind) = $sqla->select(
    -columns => [qw(listname id description from_email),
         'mailing_list.active|active',
         "COUNT(subscriber.person_id)|subcribercount",
    ],
    -from => [qw(mailing_list subscriber)],
    -where => {'subscriber.list_id' => {'=', \ 'mailing_list.id'},
           'subscriber.active' => {'<>', \ 0}
    },
    -group_by => "list_id",
    );

print "$stmt\n" . join(',',@bind) . "\n";

The Above example produces the code ready to pass to SQL:

    SELECT listname, id, description, from_email, 
      mailing_list.active AS active, 
      COUNT(subscriber.person_id) AS subcribercount
    FROM mailing_list, subscriber
    WHERE ( ( subscriber.active <> 0 AND 
      subscriber.list_id = mailing_list.id ) ) 
    GROUP BY list_id

After that, it’s just a matter of execution:

my $sth = $dbh->prepare($stmt);
$sqla->bind_params($sth, @bind);
$sth->execute;

You could do the same thing with SQL::Abstract itself, but you have to use somewhat-undocumented features for the “as” and “group by” pieces. Specifically, you can use a scalar reference for a literal field name in the columns part, and shove the “group by” clause into the order field; but you’ll have to build the group clause yourself:

use SQL::Abstract;

my $sql = SQL::Abstract->new;

my ($stmt, @bind) =
    $sql->select([qw(mailing_list subscriber)], 
		 [qw(listname id description from_email),
		  \ "mailing_list.active AS active",
		  \ "COUNT(subscriber.person_id) AS subcribercount"
		 ],
		 {'subscriber.list_id' => \ '= mailing_list.id',
		  'subscriber.active' => \ '<> 0'},
		 "asc GROUP by list_id"
    );
print "$stmt\n" . join(',',@bind) . "\n";

Altogether, SQL::Abstract::More is superior.

Thoroughly Modern Perl

 

An overview of:

  • A bit of Perl history
  • Perl 5 −vs− 6
  • Perlbrew: Running modern and bleeding-edge programs on the same system as legacy
  • Unicode and UTF-8 in the Web world, and how to sort and capitalize names that are more than just English.
  • Perl 5′s heritage Object system −vs− the “new” Moose
  • Databases: Old-school SQL with injection vulnerabilities, SQL::Abstract, and DBIx
  • Tying it all together: Mojolicious

Download/view:

Finding shortest time or distance with Graph::Undirected

The Graph module on CPAN is mostly well documented. One place it falls short is explaining that not only can you create weighted edges, you can also use various edge attributes to calculate different minimum spanning trees (via Dijkstra’s algorithm) based on any given attribute.

Here we create a small network of railway lines between cities, from the example on page 9 in John Armstrong’s “Track Planning for Realistic Operation” (Kalmbach Books, 1986):

    • There are two routes from A to D: A-B-D, and A-C-D.
    • Here we assume that the route via B is longer but faster, and via C shorter but slower.
    • The line continues from D through E and G to H.
    • There is a branch line from E to F.

We calculate the shortest route by distance, and then by time.

Note the undocumented attribute parameter to SPT_Dijkstra().

#!/usr/bin/env perl
use strict;
use warnings;

use Graph::Undirected;

my $station_graph = Graph::Undirected->new();

# Add a few cities

$station_graph->add_path(qw(A B D E G H)); # first route via B
$station_graph->add_path(qw(A C D));	   # second via C
$station_graph->add_path(qw(E F));	   # branch route

# Define characteristics of the alternate routes

# Longer but faster
$station_graph->set_edge_attribute(qw(A B distance), 150);
$station_graph->set_edge_attribute(qw(A B time), 3.1);

# Shorter yet slower
$station_graph->set_edge_attribute(qw(A C distance), 120);
$station_graph->set_edge_attribute(qw(A C time), 3.3);

# Find spanning tree in distance
my $sptg1 = $station_graph->SPT_Dijkstra(attribute => 'distance');

print "Shortest Distance: ". join('|',$sptg1->SP_Dijkstra('A','H'));
print "\n";

# Clear cache (required for recalculation)
$station_graph->SPT_Dijkstra_clear_cache();

# Find spanning tree in time
my $sptg2 = $station_graph->SPT_Dijkstra(attribute => 'time');

print "Shortest Time:     ". join('|',$sptg2->SP_Dijkstra('A','H'));
print "\n";

1;

When run, this shows as follows:

$ perl Graph.pl 
Shortest Distance: A|C|D|E|G|H
Shortest Time:     A|B|D|E|G|H

If you want to use a directed graph, create the graph by replacing that line with:

my $station_graph = Graph::Directed->new();

and be sure to specify the starting node for the spanning trees:

my $sptg1 = $station_graph->SPT_Dijkstra(attribute => 'distance', first_root => 'A');
…
my $sptg2 = $station_graph->SPT_Dijkstra(attribute => 'time', first_root => 'A');

Printing Perl documentation

Why? There’s still nothing like printed documentation when you’re learning a new subject. Or perhaps you’d like a PDF document to view on your tablet.

Here’s the quickest way to create a PDF from any Perl documentation on your system. For example, let’s do the CPAN module SQL::Abstract:

pod2pdf `perldoc -l SQL::Abstract` > /tmp/SQL_Abstract.pdf

Now you can view or print that. I find print 2-up pages, double-sided (4 total pages per sheet) is a handy and space-saving yet still readable. You might find it useful to reduce the PDF margins and let ‘evince’ or ‘okular’ manage the actual 2-up physical margins later:

pod2pdf --margins 18 `perldoc -l SQL::Abstract` > /tmp/SQL_Abstract.pdf

The margins parameter is in printer’s points (1/72 inch) and 18pt = ¼inch (0.635cm). You can also specify –left-margin as well as right, top, and bottom margins individually.

CPAN tricks

What modules are installed on my system?

# cpan -l

From the cpan prompt, how can I tell whether a module is already installed, and what version?

cpan> i module_name

And how can I tell which modules are out-of-date or have updated versions?

cpan> r

…which gives a result like:

Package namespace installed latest in CPAN file
Archive::Extract       0.70   0.72 BINGOS/Archive-…0.72.tar.gz
Attribute::Handlers    0.94   0.96 SMUELLER/Attribute…0.96.tar.gz
AutoLoader             5.73   5.74 SMUELLER/AutoLoader-5.74.tar.gz
B::Debug               1.18   1.19 RURBAN/B-Debug-1.19.tar.gz
Carp                   1.29 1.3301 ZEFRAM/Carp-1.3301.tar.gz
Clone                  0.36   0.37 GARU/Clone-0.37.tar.gz
Compress::Bzip2        2.16   2.17 RURBAN/Compress-….tar.gz
DB_File               1.827  1.831 PMQS/DB_File-1.831.tar.gz
Data::Dumper          2.145  2.151 SMUELLER/Data…151.tar.gz
…

Fix: Perlbrew can’t install 5.21.2

If you find that perlbrew is unable to install perl 5.21.0, and the build log shows the following message:

First let's make sure your kit is complete. Checking...
Looks good...

*** WHOA THERE!!! ***

This is an UNSTABLE DEVELOPMENT release.
The version of this perl5 distribution is 21, that is, odd,
(as opposed to even) and that signifies a development release.
If you want a maintenance release, you want an even-numbered version.

Do NOT install this into production use.
Data corruption and crashes are possible.

It is most seriously suggested that you do not continue any further
unless you want to help in developing and debugging Perl.

If you still want to build perl, you can answer 'y' now,
or pass -Dusedevel to Configure.

Do you really want to continue? [n]

Okay, bye.

This is a known and resolved issue and has been patched. The simplest way to make things work is to simply say:

$ perlbrew self-update
$ perlbrew install 5.21.2   
  ## works now.

WebGUI8 pre-release install

Here’s a successful installation of the nascent WebGUI 8 as it stands, as of early August 2014.

I fired up a barebones Debian 7.6 box. One from Linode would work nicely.

NOTE: If you want to use a later version of Perl than comes bundled with your Linux distribution, you’ll have to install it system-wide. The installer does not yet work with perlbrew.

For now you’ll have to run the installer as root, or with sudo. (The only step that fails is when /etc/nginx/conf.d/webgui8.conf gets created; this should be corrected fairly soon)

To start off, install some basics.

# apt-get install gpm git sudo
### Assume user named bob. Add the devel group:
# addgroup devel
# usermod -a -G devel bob
### optional, personal preferences
# apt-get install screen htop
# apt-get install emacs23-nox emacs-goodies-el

Note: emacs-goodies-el includes autoloading modules such as perldoc which gives you convenient Perl documentation viewing right inside the One True Editor™.

Then, we will need a database. The installer currently has difficulty installing mysql/Percona (Percona fails because at least as of Debian 7.6, the module still depends on openssl0.9.8 which can’t be installed as it’s a downgrade from openssl1.0.x that comes with Debian), and I’d prefer MariaDB in any case, so let’s take care of this now:

Following the instructions from MariaDB  use the following commands on Debian:

# apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
# echo deb http://ftp.utexas.edu/mariadb/repo/10.1/debian wheezy main > /etc/apt/sources.list.d/mariadb.list
# apt-get update
# apt-get install mariadb-server

Note: Recent versions of Debian support putting *.list files in the sources.list.d/ directory, which is a handy way of avoiding management and manual editing of the sources.list file itself. On Ubuntu, you can use add-apt-repository (instead of creating the mariadb.list file as shown above). UBUNTU ONLY:

# sudo add-apt-repository 'deb http://ftp.utexas.edu/mariadb/repo/10.1/debian wheezy main'

You will also need a library for the PNG Imager. Again, the installer should encompass this, but for now, follow instructions from Imager::File::PNG documentation to prevent any dependency resolution failures later:

# apt-get install libpng12-dev

I downloaded the latest pre-release installer into a temporary directory as follows. Note the use of sudo -g to execute the command with devel being the effective group. Also, I set the umask to 2, which gives everyone in the devel group write access to all the created files and directories.

# cd /opt
# umask 002
# sudo -g devel \
  git clone https://github.com/AlliumCepa/webgui

This should place a copy of the git repository in the webgui subdirectory. Now, run the installer:

# cd /opt/webgui/installer/
# perl ./webgui_installer.pl

and follow the prompts.  If something goes wrong, you’ll have to follow the recovery procedure below. But if you get this far:

Running upgrade script…. Installation is wrapping up.
/data/webgui.sh shows how to manually launch WebGUI.

and then:

│┌Comments─────────────────────────────────┐│
││Installation complete.  Go to http://    ││
││www.example.com and set up the new site. ││
││The admin user is "Admin" with password  ││
││"123qwe".                                ││
││Please hit any reasonable key to exit the││
││installer.

you should be ready to, at last:

# service nginx restart
# bash /data/webgui.sh

and browse to your www.example.com (presuming you have defined www.example.com in /etc/hosts to point to 127.0.0.1)

Recovery

To retry the installation, before re-running the installer, you must do:

# userdel webgui_user
# mysql -u root -p
mysql> drop database www_example_com;
mysql> exit
# rm -rf /data

NOTE: You can work around many of the abort-on-error conditions if you need to re-run the installer, by pressing “s” to skip individual commands.

"Si datur citrea, sucus faciunt" (When life gives you lemons, make lemonade)