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:
ref $app
returns the class name of the main object. In my tests,
replacing this line with the more obvious: $app->attr(db =>…
worked just fine. EDIT: Apparently the ref
business is because he
wanted the class method, although because there isn't an instance
method by the same name of attr
, it doesn't matter.
The attr(...)
call is a Mojolicious method (see here)
that is described as, "Create attribute accessor for hash-based
objects." What that means is, the call actually creates a function
definition as a string, and then does an eval of the string,
thereby creating the named function (db
in this case) in the
given namespace (ref $app)
in this case.
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.
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.
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)
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
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.