Mojolicious and DBI handles

Tags:

From http://toroid.org/ams/etc/mojolicious-db-handles we find this bit of code:

sub startup {
  my $app = shift;

  my ($user, $pass);
  # read $user and $pass from config file
  (ref $app)->attr(db => sub {
    DBI->connect("dbi:Mysql:database=$db",
                 $user, $pass)
    }
  );
}

The concept is that Morbo, or Starman, or whatever PSGI server is running your program, can use any kind of forking, but each instance of your program will execute the startup code and create its own database handle.

Some of the code is not too obvious:

And here's how you get the data back out:

  $app->get('/test')->to(cb =>
               sub{ 
               my $self = shift;
               $Data::Dumper::Indent = 1;
               $self->render( 'text' => "TEST: ATTR of foo" . $self->app->foo .
                    "\n<pre>\n" . Dumper($self) . "</pre>" );
               });

It's really a better idea to use DBIx::Connector anyway, but the startup attribute trick might come in handy elsewhere. One note on DBIX::Connector − Under the heading, Execution Methods we see the code:

  <span class="sh_variable">$conn</span><span class="sh_symbol">-></span><span class="sh_function">run</span><span class="sh_symbol">(</span><span class="sh_keyword">sub</span> <span class="sh_cbracket">{</span> <span class="sh_variable">$_</span><span class="sh_symbol">-></span><span class="sh_keyword">do</span><span class="sh_symbol">(</span><span class="sh_variable">$query</span><span class="sh_symbol">)</span> <span class="sh_cbracket">}</span><span class="sh_symbol">);</span>

It is not documented, but by reading the source (let's hear it for free software!) what happens is that $_ is set, via the «local» keyword, to temporarily be the database handle (dbh) of the connection within execution of the code reference.

Thanks to mst and others on #perl for the assistance.

Postfix on bare CentOS install

Tags:

Starting from scratch with a new CentOS 7 Linode, I found that iptables is set by default to block (although not reject) packets to the IMAP and POP3 services.

Rackspace has a good primer on setting up Dovecot that includes these instructions, but here's the short answer:

sudo iptables -I INPUT 2 -p tcp --dport 587 -j ACCEPT sudo iptables -I INPUT 3 -p tcp --dport 110 -j ACCEPT sudo iptables -I INPUT 4 -p tcp --dport 143 -j ACCEPT sudo iptables -I INPUT 5 -p tcp --dport 993 -j ACCEPT sudo iptables -I INPUT 6 -p tcp --dport 995 -j ACCEPT sudo /etc/init.d/iptables save sudo /etc/init.d/iptables restart

That adds a rule to accept the IMAP and POP ports, both the regular and SSL versions. Then we save the chain table and restart iptables. Now you should be able to get in: $ telnet myhost.wlindley.com imap Trying myhost.wlindley.com... Connected to myhost.wlindley.com. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS LOGINDISABLED] Dovecot ready.

Perlbrew a 64-bit Perl on Linode

Tags:

For some reason, even the 64-bit kernel on my Linode wasn't sufficient for perlbrew to generate a 64-bit Perl. I was able to get one by doing:

perlbrew install 5.20.0 --64int

which resolved the problem I was having trying to install Minion with cpanm, namely the error "Perl with support for quads is required!" (from this bit of code, thanks mst on undernet #perl)

Moose data types

Tags:

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

Tags:

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.