WWW::Curlを使ってアプリを書いてみた

WWW::Curlを使う練習。もともとは LWP::UserAgentを使っていたんですが、
ちょっとでも早くなったらいいかなと思って書き直した。


ネットワークが一番ボトルネックになるから、そんな大差はない
気がするけど、早めにレスポンスが返るときはより早くなったかな
という気分。


まあ XML::RSSXML::RSS::LibXMLに変えたのが一番効いていそうな
気もしますけど。


何回かベンチマークとってみたけど、結果がバラつきすぎて
いいのか悪いのかよくわからなかった。localhostのファイルを
使ってテストすると差が出るのかもしれないですね。

#!/usr/bin/env perl
package Hatena::HotEntry;
use Mouse;

with 'MouseX::Getopt';

our $VERSION = '0.01';

use WWW::Curl::Easy;
use XML::RSS::LibXML;

has 'threshold' => (
    is => 'rw',
    isa => 'Int',
    default => 3,
    documentation => "threshold of Hatena bookmark",
);

has 'limit' => (
    is => 'rw',
    isa => 'Int',
    default => 10,
    documentation => "limit of printing entries",
);

has '_key' => (
    accessor => 'key',
    isa => 'Str',
);

has '_ua' => (
    accessor => 'ua',
    isa => 'WWW::Curl::Easy',
    lazy_build => 1,
);

has '_content' => (
    accessor => 'content',
    isa => 'ScalarRef',
);

has '_url' => (
    accessor => 'url',
    isa => 'Str',
);

sub _build__ua {
    my $self = shift;
    my $curl = WWW::Curl::Easy->new;

    my @attrs = ("UserAgent: " . __PACKAGE__ . "/$VERSION");

    my $content;
    $self->content(\$content);

    open my $fh, ">", \$content;
    $curl->setopt( CURLOPT_WRITEDATA, $fh);
    $curl->setopt( CURLOPT_HTTPHEADER, [ @attrs ]);

    $self->_entry_url();
    $curl->setopt( CURLOPT_URL, $self->url);

    $curl;
}

sub _entry_url {
    my $self = shift;
    my $tmpl = 'http://b.hatena.ne.jp/t/#KEY#?sort=hot&threshold=#THRESHOLD#&mode=rss';

    my ($key, $threshold) = ($self->key, $self->threshold);
    $key = uri_escape_utf8($key);
    $tmpl =~ s{#KEY#}{$key};
    $tmpl =~ s{#THRESHOLD#}{$threshold};

    $self->url($tmpl);
}

has '_rss' => (
    accessor => 'rss',
    isa => 'XML::RSS::LibXML',
    default => sub {
        XML::RSS::LibXML->new;
    },
);

has '_infos' => (
    accessor => 'infos',
    isa => 'ArrayRef[HashRef]',
    default => sub {
        +[];
    },
);

__PACKAGE__->meta->make_immutable;

no Mouse;

use Carp qw(croak);
use Encode;
use URI::Escape;

sub run {
    my ($self, $key) = @_;

    die $self->usage unless defined $key;
    $self->key(decode_utf8($key));

    my $content_ref = $self->_get_content_ref();

    $self->_parse_rss($content_ref);
    $self->_print;
}

sub _parse_rss {
    my ($self, $content_ref) = @_;

    $self->rss->parse($$content_ref);

    for my $item (@{$self->rss->{'items'}}) {
        push @{$self->infos}, {
            title => $item->{'title'},
            link  => $item->{'link'},
            description => $item->{'description'},
        };
    }
}

sub _get_content_ref {
    my $self = shift;

    my $ret = $self->ua->perform();
    croak "Can't download ", $self->url, "\n" if $ret != 0;

    my $decoded_content = decode_utf8(${$self->content});

    \$decoded_content;
}

sub _print {
    my $self = shift;

    my $i = 1;
    my $limit = $self->limit <= @{$self->infos}
        ? ($self->limit - 1) :(scalar @{$self->infos} - 1);
    for my $info (@{$self->infos}[0..$limit]) {
        printf "%2d: %s\n", $i++, encode_utf8($info->{'title'});
    }
}

package main;
use strict;
use warnings;

unless (caller) {
    my $app = Hatena::HotEntry->new_with_options();
    $app->run(shift @{$app->extra_argv});
}


仕事中にこそこそ起動しています。サボりじゃなくて
情報収集のためです、と言っておきます。

% hotentry.pl perl
 1: PlackとText::Xslateでケータイからtwitterするwebアプリを書いた - だるろぐ
 2: Net::Amazon の謎 - punitanのメモ
 3:  split はデフォルトで後続する空フィールドを削除する - daily dayflower
 4: Term::ANSIColor::Relaxで愛されモテカワ端末に大変身!? - 愛と勇気と缶ビール
 5:         vim-ref で perldoc その他を読む     - blog.remora.cx
 6: colordiffの文字単位版ccdiffを作ったid:yappo++ - ヒルズで働くholidays-lの技ログ 7:  Time::Piece::MySQL とタイムゾーン - Yet Another Hackadelic 8: perlstudy
 9:  WWW::Curl で使えるオプション一覧 - Craftworks Tech Blog - Branch
10: Log::Minimal meets Plack. Log::Minimalを使って環境に適したログ出力を - blog.nomadscafe.jp

速さが重要にならなければ、WWW::Curlを使う必要は僕にはないかなという
印象。LWP::UserAgentでできるあの機能を使うにはって探すのに時間が
かかりすぎる。はじめは LWP::UserAgentで書いて遅かったら置き換えると
いう方針で僕には十分だと思いました。