Hi! Please consider following me on twitter: @hanekomu.
2009年12月13日
Plack::Middleware::Debug
I wrote and released a new middleware for Plack: Plack::Middleware::Debug. It was on miyagawa's wishlist for Plack and is based heavily on the Django Debug Toolbar. miyagawa also helped to get this middleware in proper shape to play nicely with the rest of Plack.
Here is an example of how to use it. Put this in your
app.psgi:
use Plack::Builder; use File::Basename; my $app = sub { ... }; builder { enable 'Debug', panels => [ qw(Environment Response Timer Memory DBITrace) ]; $app; };
The debug middleware offers a configurable set of panels that displays
information about the current request and response. The information is
generated only for responses with a status of 200 (OK) and a
Content-Type that contains text/html and is embedded
in the HTML that is sent back to the browser.
If you pass a list of panel base names to the enable() call,
only those panels will be enabled. If you don't pass an argument, the default
list of panels — Environment, Response,
Timer and Memory — will be enabled.
As of version 0.01, the following panels are available:
DBITracedisplay DBI trace information.Environmentdisplays the PSGI environment from the request.Memorydisplays memory usage before the request and after the response.ModuleVersionsdisplays the loaded modules and their versions.PerlConfigdisplays the configuration information of the Perl interpreter itself.Responsedisplays the status code and response headers.Timerdisplays how long the request took.
The following two screenshots demonstrate the DBITrace and
Environment panels.
You could use the power of the debug toolbar for your own framework or web
application. It is quite easy to write custom panels. Just subclass
Plack::Middleware::Debug::Base, then use your panel in the
enable() call.
If you are interested, join us on #plack on irc.perl.org. The development repository is on GitHub.
posted at: 12:04 | path: /dev | permalink | 0 comments | 1 trackback
2009年10月20日
HTML stack trace from the Perl debugger
Tatsuhiko Miyagawa released Devel::StackTrace::AsHTML and blogged about it.
I thought this would make a neat Perl debugger command, so I wrote DB::Pluggable::StackTraceAsHTML. It is a plugin to DB::Pluggable. It adds the Th command to the
debugger, which displays a stack trace in HTML format, with lexical variables.
It then opens the page in the default browser.
Here is an example of how to use it:
$ perl -d test.pl Loading DB routines from perl5db.pl version 1.3 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(test.pl:14): my $n = 12; DB<1> r main::fib(test.pl:12): return fib($i - 1) + fib($i - 2); DB<1> Th
The result would look something like:

To enable the plugin, just add it to your ~/.perldb, like so:
use DB::Pluggable; use YAML; $DB::PluginHandler = DB::Pluggable->new(config => Load <<EOYAML); global: log: level: error plugins: - module: BreakOnTestNumber - module: StackTraceAsHTML EOYAML $DB::PluginHandler->run;
By the way, to be minimally invasive to the existing Perl debugger, the
command is defined using the debugger's aliasing mechanism. Normally you define
an alias as a regular expression that will change the command the user enters
to a known command, but here we circumvent that and call our command handler
directly. The following method is from DB::Pluggable::Plugin:
sub make_command { my ($self, $cmd_name, $code) = @_; no strict 'refs'; my $sub_name = "DB::cmd_$cmd_name"; *{$sub_name} = $code; $DB::alias{$cmd_name} = "/./; &$sub_name;"; }
To define a new foo command in a plugin, you then use:
package DB::Pluggable::StackTraceAsHTML; use strict; use warnings; use base qw(DB::Pluggable::Plugin); sub register { my ($self, $context) = @_; $self->make_command( foo => sub { # ... } ); }
posted at: 22:09 | path: /dev | permalink | 0 comments | 0 trackbacks
2009年10月05日
Repeatedly installing Task::* distributions
First there were Bundle::* distributions to install several
dependencies at once without the actual bundle distribution doing anything,
but this required magic in the toolchain. A newer concept is that of a
Task::* distribution, as described in Task.
Task modules are just normal Perl modules; they don't require toolchain
magic. When the dependencies listed in Makefile.PL have been
installed, the actual Task module, for example Task::Catalyst, will be installed as well.
This is a problem because if the task's dependencies are being updated,
the task module needs to be updated as well. For example, DBD::SQLite is a dependency of
Task::Catalyst. If there is a new version of
DBD::SQLite, just running
install Task::Catalyst in the CPAN shell won't have any
effect, because the CPAN shell will see that the latest version of
Task::Catalyst is already installed. Of course you can force
with force install Task::Catalyst, but even if that would
work, it would be ugly because I don't want to install dependencies that
fail their tests.
One solution is to prevent the task module itself to be installed. That
way, every time you rerun install Task::Foo in the CPAN
shell, it will see that the task module isn't installed and try to install
it again, with all the desired dependency checking.
How do we prevent the module from being installed? Put these lines in
your Makefile.PL — I always use Module::Install, but I think this should work for all
MakeMaker-based files:
exit 0 if $ENV{AUTOMATED_TESTING}; sub MY::install { "install ::\n" }
First, we don't need to waste the time of CPAN testers by having them
install all the dependencies for a task module that doesn't really do
anything anyway. Second, and more importantly, we override the
install :: target that is generated for the
Makefile to effectively be a no-op.
I have demonstrated this technique in Task::BeLike::hanekomu, a somewhat pointless distribution
that serves mainly as proof-of-concept, and as a way to install my
favourite modules onto a new perl installation. If you don't mind the
dependencies, then go ahead and repeatedly run
install Task::BeLike::hanekomu in the CPAN shell; the
task module itself should never be installed and it should recheck the
dependencies every time.
I think this would be a good thing to put in existing task distributions such as Task::Kensho and Task::Catalyst.
posted at: 12:27 | path: /dev | permalink | 6 comments | 1 trackback
2009年09月14日
Easy metadata for CPAN modules
Earlier I wrote about how to enhance a CPAN distribution by including metadata about that distribution's place in its infrastructure — where to find its homepage, bug tracker and repository.
There are now Module::Install plugins on CPAN that make
this task easier. Tatsuhiko Miyagawa wrote Module::Install::Repository and I wrote Module::Install::Homepage and Module::Install::Bugtracker. So now your
Makefile.PL can simply include these lines:
auto_set_homepage; auto_set_bugtracker; auto_set_repository;
To set the repository link, your distribution's version control is examined. The distribution's homepage is set to its page on http://search.cpan.org, and the distribution's bugtracker is set to its RT queue on http://rt.cpan.org.
posted at: 10:13 | path: /dev | permalink | 0 comments | 0 trackbacks
2009年08月15日
In defense of polymorphism
Laurent Dami blogged Object-oriented accessors considered (sometimes) harmful and Yuval Kogman responded to it.
I agree that accessing hash elements directly can be tempting, but for me the killer argument in favor of accessor methods is polymorphism. Simply put, if you access hash elements directly, you're effectively making it completely impossible for someone else to subclass your class, or for your own class to subclass another class later. That's all.
And I agree strongly with one of Laurent's criticisms: With accessors, you never know where things really happen, making them difficult to debug. To use Laurent's example:
if ($c->request->body->length < $self->min_body) {
You have to step through a lot of methods to get to the right-hand side. It's the same with any expression.
What I would really like in the Perl debugger (but ENOTUITS) is the possibility to inspect the expression in a precedence-based tree-view like this:
1: < 2: $c 3: ->request 4: ->body 5: ->length 6: $self 7: ->min_body
and then being able to set a one-time breakpoint for line 7, that is, when
entering the min_body() method.
posted at: 13:29 | path: /dev | permalink | 1 comment | 0 trackbacks
2009年06月11日
Sending DBI trace output to FirePHP with HTTP::Engine
As promised last time, here is a complete example of how to send DBI trace output to FirePHP in an HTTP::Engine demo web application.
First we create a very small example SQLite database. Let's model a company
that has two departments with two employees each. Create a file called
create.sql and enter the following SQL commands:
CREATE TABLE departments (
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT,
department_id INTEGER
);
INSERT INTO departments (id, name) VALUES (1, 'publishing');
INSERT INTO departments (id, name) VALUES (2, 'accounting');
INSERT INTO employees (id, name, department_id)
VALUES (1, 'Yamamura Masayoshi', 1);
INSERT INTO employees (id, name, department_id)
VALUES (2, 'Ueda Akira', 1);
INSERT INTO employees (id, name, department_id)
VALUES (3, 'Matsumoto Daichi', 2);
INSERT INTO employees (id, name, department_id)
VALUES (4, 'Yasuda Ryota', 2);
Now we create the database itself:
sqlite3 kaisha.db <create.sql
The web application is pretty simple as well. We create an
HTTP::Engine object and run it; the handler connects to the
database, runs a simple query, creates an HTML table from the results and sends
it in a response back to the engine.
#!/usr/bin/env perl use strict; use warnings; use DBI; use HTTP::Engine; use HTTP::Engine::FirePHP; my $engine = HTTP::Engine->new( interface => { module => 'ServerSimple', args => { host => 'localhost', port => '1984', }, request_handler => \&handler, }, )->run; sub handler { my $req = shift; my $dbh = DBI->connect('dbi:SQLite:dbname=kaisha.db', '', ''); my $res = HTTP::Engine::Response->new; $dbh->trace(2, $res->get_fire_php_fh); my $r = $dbh->selectall_arrayref(' SELECT E.name, D.name FROM employees E, departments D WHERE D.id = E.department_id '); $dbh->disconnect; my $body = "<table>\n" . (join "\n" => map { sprintf "<tr><td>%s</td><td>%s</td></tr>\n", @$_ } @$r ) . "</table>\n"; $res->status(200); $res->body($body); $res; }
The relevant command to send DBI trace output to FirePHP is:
$dbh->trace(2, $res->get_fire_php_fh);
In version 0.02, HTTP::Engine::FirePHP gives
HTTP::Engine::Response the method get_fire_php_fh,
which will return a filehandle that has been opened to a PerlIO::via::ToFirePHP layer, so you don't even need to do
that manually anymore.
With this one line, DBI knows that it should trace all calls and send the trace output to the filehandle. The trace output will automatically be put into the HTTP response headers so FirePHP can display them. Here is what the Firebug console looks like when we make a request to the server:
Tags: DBI, FirePHP, HTTP::Engine, web.
posted at: 14:54 | path: /dev | permalink | 0 comments | 0 trackbacks
Log to FirePHP via an PerlIO layer
I have released PerlIO::via::ToFirePHP. This PerlIO
layer sends everything it receives to FirePHP. When constructing a filehandle
using this layer using open(), you need to pass an object of type
FirePHP::Dispatcher that has been initialized with a
HTTP::Headers object.
use PerlIO::via::ToFirePHP; my $fire_php = FirePHP::Dispatcher->new(HTTP::Headers->new); open my $fh, '>:via(ToFirePHP)', $fire_php;
Everything you print on the filehandle will be sent to FirePHP.
A typical use of this PerlIO layer is to send DBI trace output to FirePHP:
use PerlIO::via::ToFirePHP; my $dbh = DBI->connect(...); open my $fh, '>:via(ToFirePHP)', FirePHP::Dispatcher->new($http_headers_object); $dbh->trace(2, $fh);
Now the trace output of all calls to that database handle will be sent to FirePHP.
The PerlIO layer is implemented in PerlIO::via::ToFirePHP
instead of just PerlIO::via::FirePHP because of a bug in
PerlIO::via in perl 5.10.0 and earlier versions. If we used just
PerlIO::via::FirePHP, we would not be able to use the shorthand
layer notation of open my $fh, ':>via(FirePHP), $fire_php.
PerlIO::via would look for a PUSHED method in
package FirePHP. There is no such method, but because FirePHP::Dispatcher has been loaded, the namespace
FirePHP has been autovivified. So PerlIO::via would
stop looking. This bug seems to be fixed in perl 5.10.1.
In the next article I will show a complete example of how to send
DBI trace output to FirePHP in an HTTP::Engine
demo web application.
posted at: 12:05 | path: /dev | permalink | 1 comment | 0 trackbacks
2009年06月10日
Building our own tools
As software engineers, we are in the enviable position to be able to build our own tools. At work one my subprojects involves writing a Web application that using HTTP::Engine and jQuery. This suits me fine because I wanted to learn these technologies anyway. To debug the Web app, I've installed FireBug. To see remote logs in the browser, I've installed the FirePHP plugin, then written a tool to send logs to FirePHP from within HTTP::Engine.
I have written more tools based on this one, and there are also lots of tools to help my workflow — shell and editor configuration files, distribution building tools, and so forth. And because we write software to help us write software, we can make that software available to help others, who also want to write software.
This is a nice way of working, and I think few professions, apart from toolsmiths, are able to make their own tools.
posted at: 20:24 | path: /dev | permalink | 0 comments | 0 trackbacks
PerlIO::via and autovivifying namespaces
PerlIO::via, in perl 5.10.0 at least, has a bug, or let's call
it an unintuitive feature. To start, let's look at this excerpt from its
manpage to see what it should do.
The PerlIO::via module allows you to develop PerlIO layers in Perl,
without having to go into the nitty gritty of programming C with XS as
the interface to Perl.
One example module, PerlIO::via::QuotedPrint, is included with Perl
5.8.0, and more example modules are available from CPAN, such as
PerlIO::via::StripHTML and PerlIO::via::Base64. The
PerlIO::via::StripHTML module for instance, allows you to say:
use PerlIO::via::StripHTML;
open( my $fh, "<:via(StripHTML)", "index.html" );
my @line = <$fh>;
to obtain the text of an HTML-file in an array with all the HTML-tags
automagically removed.
Please note that if the layer is created in the PerlIO::via::
namespace, it does not have to be fully qualified. The PerlIO::via
module will prefix the PerlIO::via:: namespace if the specified
modulename does not exist as a fully qualified module name.
As the manpage later explains, the way to create these I/O layers is to
declare certain subroutines like PUSHED(), OPEN(),
WRITE() or CLOSE() in the layer package.
So I've written a simple layer that uppercases everything that passes
through. Imaginatively, I'm calling this layer Foo, and, for
demonstration purposes, I'm only writing the bare minimum to make it work.
#!/usr/bin/env perl use strict; use warnings; package PerlIO::via::Foo; sub PUSHED { bless {}, shift } sub OPEN { 1 } sub WRITE { print uc $_[1]; length $_[1] } sub CLOSE { 0 } package main; open my $fh, '>:via(Foo)', 'whatever' or die $!; print $fh "Hello\n"; close $fh;
This program runs as expected and prints:
HELLO
To demonstrate the bug, let's add a simple package declaration.
package Foo::Bar;
We didn't declare any subroutines or variables in that package, just the package itself. When we run the program now, it prints:
Function not implemented at ./test.pl line 15.
What's going on?
When we declare the Foo::Bar package, the Foo
namespace seems be autovivified, that is, it seems to magically spring into
existence. PerlIO::via only seems to check whether the unqualified
namespace — Foo in this case — exists and if so,
whether there is a PUSHED() subroutine in it. If there isn't, it
declares ENOSYS, that is, Function not implemented.
It does not try to fallback on the fully qualified namespace —
PerlIO::via::Foo in this case — like AUTOLOAD
would.
I think it should fallback, so I consider it a bug in
PerlIO::via, or at least a very unintuitive feature.
Tags: bug.
posted at: 13:12 | path: /dev | permalink | 0 comments | 0 trackbacks
2009年06月07日
Logging to Firefox with HTTP::Engine::FirePHP
I have released HTTP::Engine::FirePHP on CPAN; the development version is on Github.
If you are developing a web application and don't want to or can't check the error log, the traditional way is to include debug messages in the HTML page. However, this messes up the layout and mixes content with logging; the two really need to be separate.
FirePHP is a Firebug plugin which enables you to log to your Firebug Console by sending certain HTTP headers in the HTTP response. FirePHP is not just useful for PHP, though; any server-side application that can manipulate HTTP headers can log to Firebug.
The FirePHP response headers use the Wildfire protocol. The CPAN module FirePHP::Dispatcher can generate these headers.
This module then integrates FirePHP::Dispatcher with HTTP::Engine. By simply using this module,
HTTP::Engine::Response gets a fire_php() accessor
through which you can log to FirePHP.
Here is an example:
#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use HTTP::Engine; use HTTP::Engine::Response; use HTTP::Engine::FirePHP; HTTP::Engine->new( interface => { module => 'ServerSimple', args => { host => 'localhost', port => '10012', }, request_handler => sub { my $req = shift; my $res = HTTP::Engine::Response->new(body => 'Hello'); $res->fire_php->info('Hello from HTTP::Engine'); $res->fire_php->warn(sprintf 'path was %s', $req->uri->path); $res->fire_php->log(Dumper $req); $res; }, }, )->run;
When you load the response into Firefox, open the Firebug Console and you will find the logged messages there.
When you restart the HTTP::Engine-based server, be sure to do a
shift-reload of the relevant page in Firefox; this ensures that headers aren't
cached. If you don't do this, you might see remnant headers from previous
responses.
Tags: FirePHP, HTTP::Engine, web.
posted at: 14:54 | path: /dev | permalink | 0 comments | 0 trackbacks



