理系学生日記

おまえはいつまで学生気分なのか

WebService::LivedoorReader ちょっとだけ進めた

git によって僅かばかりやる気がでたので、WebService::LivedoorReader をちょっとだけ進めた。
とはいえ、前回作っていた状態とほぼ変化はなく、機能的にはむしろ退化したりしてる。

コンストラクタは内部にて WWW::Mechanize を構築する程度で、今後何をパラメータにするかを考えたいが、他の機能を実装するまで後回しにする予定。Moose 化も考えたものの、Moose の勉強に相当かかりそうな気がしたので、とりあえずハッシュベースで。

sub new {
    my ($class, $param) = @_;
    my $self = bless {
        mech => WWW::Mechanize->new,
        user => $param->{username},
        pass => $param->{password},
    }, $class;
    $self;
}

LDR の API の多くは、POST するパラメータとして ApiKey を要求する。この ApiKey は LDR のログイン時に設定されるクッキー中にあるが、これを取得するあたりは Plagger で使われているコードをほぼ拝借してる。(コアに含まれていない)WWW::Mechanize を使っているのは、単にぼくがメンドくさかっただけ。エラー処理はもっとやらないといけないんだけど、当面は機能側のコーディング進めたい。

sub apikey {
    my $self = shift;
    $self->{apikey} and return $self->{apikey};

    $self->{mech}->get( $LOGIN_URL );
    $self->{mech}->submit_form(
        form_name => 'loginForm',
        fields => {
            livedoor_id => $self->{user},
            password    => $self->{pass},
        }
    );
    $self->{mech}->cookie_jar->scan(
        sub {
            my ($key, $val) = @_[1, 2];
            $key =~ /reader_sid$/ or return;
            $self->{apikey} = $val;
        }
    );
    $self->{apikey};
}

LDR の各 API はそれぞれが URL を持っていて、その URL に POST することで JSON 形式のデータを返すように一貫されている。というわけなので、リクエスト・レスポンス処理の共通部分と思われるところを括り出してみた。

sub _request {
    my ($self, $api, $param) = @_;
    ($api =~ m|^/|) or confess "api must start with '/'";

    my $res = $self->{mech}->post( $API_URL . $api => {
        ApiKey => $self->apikey,
        $param? %$param : (),
    });
    from_json( $res->decoded_content );
}

これを使って、今日は pin 関係の API を実装。例えば、pin を立てる API はこんな風になる。

sub add_pin {
    my ($self, $url, $title)  = @_;
    my $res = $self->_request( '/pin/add' => {
        link => $url,
        $title? ( title => $title ) : (),
    });
    $res->{isSuccess};
}

/api/pin/add のように、何か意味のあるもの(例えば URL の一覧など)を返さない API は、isSuccess と ErrorCode をキーとして持つ JSON データを返す。そのため、メソッドからはとりあえず成功かどうかを返すようにしてみた。

pin を立てた全てのページ情報を取得する API として /api/pin/all があるが、この API は link、created_on、title の 3 つの情報をハッシュとし、その配列を JSON で返す。とりあえずそのまま呼出元に返すだけで良いかとか思ったので、これだけのメソッドになった。

sub all_pin {
    my $self = shift;
    $self->_request( '/pin/all' );
}

今回はわりとマジメにテストを書いたが、なにがなにやらだんだん分からなくなってきた。これ、pin 関係のテスト。

use strict;
use warnings;
use Test::More;
use WebService::LivedoorReader;
use List::MoreUtils qw(any none);
use Scalar::Util qw(looks_like_number);
use Account;

my $account = ac_info( 'account.yaml' );
my $ldr = WebService::LivedoorReader->new( $account->{ldr} );
isa_ok( $ldr, 'WebService::LivedoorReader' );
can_ok( $ldr, (qw(all_pin add_pin count_pin remove_pin clear_pin)));

my $yahoo = 'http://www.yahoo.co.jp';

$ldr->remove_pin( $yahoo );

my $count = $ldr->count_pin;
ok( looks_like_number( $count ), 'number of pins' );

ok( $ldr->add_pin( $yahoo => 'test' ), 'add pin' );
is( $count + 1, $ldr->count_pin, 'number of pins after addition' );
my $pins = $ldr->all_pin;
is( ref $pins => 'ARRAY', 'retrieves pins' );

ok( any( sub {
               $_->{link}  eq $yahoo
           and $_->{title} eq 'test' 
    }, @$pins), 'pins contain newly added pin' );


ok( $ldr->remove_pin( $yahoo ), 'remove pin' );

$pins = $ldr->all_pin;
ok( none ( sub {
               $_->{link}  eq $yahoo
           and $_->{title} eq 'test' 
    }, @$pins), 'pins do not contain newly added pin' );

done_testing;