さくらレンタルサーバーで動くWebアプリをArkを使って作ってみた

root権限なく、daemontoolsmemcachedなどを使用せず、さくらレンタルサーバー スタンダードプランで標準的に使えるPerl CGIMySQLだけを利用して動かせるWebアプリをArkで作ってみました。
http://sugyan.sakura.ne.jp/
リポジトリgithub

  • Twitter OAuthでユーザー認証、ログイン
  • OAuthを使ったTwitter APIでタイムラインを取得
  • MeCab形態素解析して各発言を全裸に
  • ユーザーが各全裸発言をお気に入りできる(ユーザーと発言をひもづける)
  • 選択した発言をOAuthでTweet

くらいの機能。


PerlCGIからpsgiを起動する方法については以前書いた通りで。
さくらのレンタルサーバーでArk+CGIのTwitterアプリを作る練習 - すぎゃーんメモ
静的ファイルはデフォルトで"$HOME/www"以下のものを配信してくれるので、.htaccess

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(app.cgi|img/|css/|js/|favicon\.ico)
RewriteRule ^(.*)$ app.cgi/$1 [PT,L] 

と設定して、画像やjs, cssなどはそこにシンボリックリンクを貼った。


O/RマッパーはいまだにDBIx::Classしか使ったことないので普通にDBIx::Classを使っています。特に難しいことしてないし、もっと軽量なもので良いかも…。と色々手を出してみないとなぁ
リクエストごとにTwitter APIにアクセスしていると体感的にすごく遅いので、レスポンスはすぐに返してJavaScriptAPI叩いてタイムラインを取得するようにした。実際、待ち時間の大部分はAPIからデータをとってきてるところのはず。

Ark::Plugin::Authentication::Credential::Twitterの使い方

せっかくなのでTwitter OAuthで認証してログインする方法について書いてみます。ここで躓いた方もいたようで。。
だがしかし - masawoのtech log
自分もソースを読んでやって色々ためしてみてようやくできた、というカンジですが。。


Twitter OAuthでのユーザー認証を使うには"Authentication", "Session"のプラグインを使用します。"Hoge"というアプリケーションの場合、lib/Hoge.pmに

package Hoge;
use Ark;

our $VERSION = '0.01';

use_plugins qw{
    Authentication
    Authentication::Credential::Twitter
    Authentication::Store::Null
    Session
    Session::State::Cookie
    Session::Store::Memory
};

config 'Plugin::Authentication::Credential::Twitter' => {
    consumer_key    => '****',
    consumer_secret => '*********',
};

1;

のように書きます。"Authentication::Store::****", "Session::State::****", "Session::Store::****"は自由に変更して良いと思います。上記は永続化せずにオンメモリだけで動かす用に書いています。通常は"Ark::Plugin::Authentication::Store::DBIx::Class", "Ark::Plugin::Session::Store::Model"などを使ってDBにユーザー情報、memcachedにセッション情報を入れたりするかと思います(さくらレンタルサーバー用にはCache::FastMmapを使っています)。
Ark::Plugin::Authentication::Credential::Twitterには設定情報として"consumer_key", "consumer_secret"を渡す必要があります(Twitter OAuthアプリの設定などについては他に詳しい記事があるので省略)。今回はlib/Hoge.pmに書いていますがこういった設定はconfig.plに書いておく方が良いと思います。
あとはControllerで認証処理をするだけです。lib/Hoge/Controller/Root.pmを以下のように書きます。

package Hoge::Controller::Root;
use Ark 'Controller';

has '+namespace' => default => '';

# default 404 handler
sub default :Path :Args {
    my ($self, $c) = @_;

    $c->res->status(404);
    $c->res->body('404 Not Found');
}

sub index :Path :Args(0) {
    my ($self, $c) = @_;

    my $user = $c->auth->user;
    $c->res->body($user ? $user->{hash}{screen_name} : '<a href="/login">login</a>');
}

sub login :Local :Args(0) {
    my ($self, $c) = @_;

    if ($c->auth->authenticate_twitter(callback => $c->uri_for('/login'))) {
        $c->redirect_and_detach('/');
    }
}

1;

これだけ書いて、"ark.pl newapp"で作成されたapp.psgiを使って"plackup -Ilib app.psgi"とか実行すればwebアプリが立ち上がります。"/"にアクセスすると、indexにて最初は$c->auth->userがundefを返すため"/login"へのリンクが表示されます。
"/login"にアクセスすると、$c->auth->authenticate_twitterによってOAuthの認証画面へリダイレクトされます。このとき、内部ではNet::Twitterのget_authorization_urlが呼ばれます。webアプリの場合は引数にcallbackを与えておかないといけません(Net::Twitter::Role::OAuthを参照)。callbackのURLは"/login"でなくても良いですが、どうせもう一度同じ処理をすることになるので、同じURLのままで良いと思います。
Twitterでの認証が完了し、callbackに戻ってきたときには、oauth_token, oauth_verifierというパラメータが付加されてきます。これがあると$c->auth->authenticate_twitterではそのverifierを使ってaccess_token, access_token_secretなどを取得します(その前段階としてsession情報などもチェックしています)。
また、その認証完了後に

        my $user_obj = $self->find_user(
            $user->{ $self->twitter_user_field }, $user,
        ) or return;
        $self->persist_user($user_obj);

といった処理が行われるので、ここで既に登録済みのユーザーを検出したり新規に登録するなどができます。A::P::A::S::DBIx::Classを使っているときなどはSchema/Result/User.pmにユーザー情報を定義し、Schema/ResultSet/User.pmでfind_userメソッドを実装しupdate_or_insert的なことをしてやれば良さそうです。今回のこの例ではA::P::A::Store::Nullを使っていて、これは無条件で新規にArk::Plugin::Authentication::Userオブジェクトを作成して返してくれます。
こうしてauthenticate_twitterで認証が完了すればA::P::A::Userオブジェクトが得られ、その後は$c->auth->userでUser情報を取得できます。"/"にアクセスすると今度はTwitterでのログインアカウントのscreen_nameが表示されるようになります。


と、こんなカンジで簡潔にサクッとTwitterと連携するアプリを作ることができました。
まだまだArkについては知らないことだらけですが少しずついじって勉強していこうと思います。