tokuhirom's Blog

Perl 5.13.2 がもたらした package NAMESPACE BLOCK 構文があたえるもの

開発版の Perl 5.13.2 がリリースされたが、5.13.2 の目玉はなんといっても package NAMESPACE BLOCK 構文だろう。

use 5.13.2;
use warnings;

package Point {
   use Moose;

   has 'x' => (is => 'rw', isa => 'Int');
   has 'y' => (is => 'rw', isa => 'Int');

   __PACKAGE__->meta->make_immutable;
   no Moose;
};

のように、パッケージを宣言することができるようになった。

この構文は非常に大きい変化をもたらす。

1 ファイルに複数のクラスを書くことが苦でなくなる。package NAME BOCK 構文を利用する場合、BLOCK の中はインデントすることになるため、複数パッケージを1つのファイルにかくことが苦ではなくなってくる。

もう一つは、あのいまわしき "1;" が必要なくなるということ。package NAME BLOCK 構文は true value をかえしているので、通常のクラスを利用する場合には明示的に 1; とかく必要がないようだ(これが明示的な仕様かどうかはまだみてない)。

今迄、ちょっとしたクラスをつくるときに、以下のようにかいていたのも、すっきりするでしょう。

{
    package Foo::Bar;
}


これを利用して、Tatsumaki の eg/demo.pl を書きなおすと以下のようになる。
今迄の Perl5 コードよりも、見通しがいいんじゃないかとおもう。

#!/usr/bin/perl
use 5.13.2;
use warnings;
use Tatsumaki::Error;
use Tatsumaki::Application;
use Tatsumaki::HTTPClient;
use JSON;

package MainHandler {
    use parent qw(Tatsumaki::Handler);

    sub get {
        my $self = shift;
        $self->write("Hello World");
    }
}

package StreamWriter {
    use parent qw(Tatsumaki::Handler);
    __PACKAGE__->asynchronous(1);

    sub get {
        my $self = shift;
        $self->response->content_type('text/plain');

        my $try = 0;
        my $t; $t = AE::timer 0, 0.1, sub {
            $self->stream_write("Current UNIX time is " . time . "\n");
            if ($try++ >= 10) {
                undef $t;
                $self->finish;
            }
        };
    }
}

package FeedHandler {
    use parent qw(Tatsumaki::Handler);

    __PACKAGE__->asynchronous(1);

    sub get {
        my($self, $query) = @_;
        my $client = Tatsumaki::HTTPClient->new;
        $client->get("http://friendfeed-api.com/v2/feed/$query", $self->async_cb(sub { $self->on_response(@_) }));
    }

    sub on_response {
        my($self, $res) = @_;
        if ($res->is_error) {
            Tatsumaki::Error::HTTP->throw(500, $res->status_line);
        }
        my $json = JSON::decode_json($res->content);

        $self->response->content_type('text/html;charset=utf-8');
        $self->write("<p>Fetched " . scalar(@{$json->{entries}}) . " entries from API</p>");
        for my $entry (@{$json->{entries}}) {
            $self->write("<li>" . $entry->{body} . "</li>\n");
        }
        $self->finish;
    }
}

use File::Basename;

my $app = Tatsumaki::Application->new([
    '/stream' => 'StreamWriter',
    '/feed/(\w+)' => 'FeedHandler',
    '/' => 'MainHandler',
]);

if (__FILE__ eq $0) {
    require Tatsumaki::Server;
    Tatsumaki::Server->new(port => 9999)->run($app);
} else {
    return $app;
}