動いてしかるべき物が別のOSだと動かなかったりしたときにはちゃんと調べてパッチを書こう。
パッチを書こう
パッチを書こうパッチを書こうパッチを書こうパッチを書こう
パッチを書こうパッチを書こうパッチを書こうパッチを書こう
パッチを書こう
動いてしかるべき物が別のOSだと動かなかったりしたときにはちゃんと調べてパッチを書こう。
パッチを書こう
パッチを書こうパッチを書こうパッチを書こうパッチを書こう
パッチを書こうパッチを書こうパッチを書こうパッチを書こう
パッチを書こう
perlbrew 最新版では修正済みです。patchperl にパッチ投げておきました。
perlbrewも最新のpatchperl を使うようになったので、失敗する場合はもう一度perlbrew の最新版を再インストールしてください(perlbrew のバージョン番号は変わってないので注意)
追記終わり
http://d.hatena.ne.jp/Itisango/20110504/1304487042 の原因。
Configure はデフォルトで
/usr/local/lib /lib /usr/lib /usr/lib64
を読みに行くところ、Ubuntu Natty からは libm などのライブラリが
/lib/i386-linux-gnu /usr/lib/i386-linux-gnu
などに移動しているためリンクできずにリンカがエラーを吐く。
Configure の plibpth にこれらのディレクトリを加えてやれば解決できる。
ライブラリのパスは環境に依存してるので
$ gcc -print-search-dirs | grep libraries | cut -f2 - -d= | tr ':' '\n'
などして確認できる。
make test の時に
../lib/User/pwent.t ............................................... ok ../lib/feature/unicode_strings.t .................................. ok x2p/s2p.t ......................................................... ok Test Summary Report ------------------- op/stash.t (Wstat: 0 Tests: 31 Failed: 0) TODO passed: 26 ../cpan/Memoize/t/errors.t (Wstat: 11 Tests: 6 Failed: 0) Non-zero wait status: 11 Parse errors: Bad plan. You planned 11 tests but ran 6. ../ext/ODBM_File/t/odbm.t (Wstat: 32512 Tests: 0 Failed: 0) Non-zero exit status: 127 Parse errors: No plan found in TAP output ../lib/DBM_Filter/t/01error.t (Wstat: 32512 Tests: 3 Failed: 0) Non-zero exit status: 127 Parse errors: Bad plan. You planned 21 tests but ran 3. Files=1808, Tests=349977, 930 wallclock secs (73.67 usr 14.43 sys + 511.11 cusr 72.13 csys = 671.34 CPU) Result: FAIL make: *** [test_harness] Error 3
のようなエラーが出た場合は libgdbm のリンクに失敗しているため、
$ perlbrew install perl-5.12.3 # もしここで失敗したら $ cd ~/perl5/perlbrew/build/perl-5.12.3/ $ locate libgdbm | grep o$ /usr/lib/i386-linux-gnu/libgdbm.so /usr/lib/i386-linux-gnu/libgdbm_compat.so $ vim ext/ODBM_File/hints/linux.pl
として ext/ODBM_File/hints/linux.pl の条件式をlocate で確認したパスに合わせて次のように書き換える
if (-e '/usr/lib/libgdbm_compat.so' or -e '/usr/lib64/libgdbm_compat.so'
    or -e '/usr/lib/i386-linux-gnu/libgdbm.so' or -e '/usr/lib/i386-linux-gnu/libgdbm_compat.so'
) {
パーミッションが -r--r--r-- なので強制的に上書き。その後、
$ make clean && make && make test $ make install
すればあら不思議インストールされています。
$ perlbrew list * perl-5.12.1 perl-5.12.3 /usr/bin/perl (5.10.1)
ということでライブラリのパスが変わってしまったために起きた問題でした。
みんながhello worldばかりやっているのでちょっとしたアプリつくってみようということで作ったもの。http://middleware.punytan.dotcloud.com/
Plack::Middleware::* なnamespaceを列挙して、モジュール名クリックでPODを読み込んでくれる超便利な仕上がりとなっております。適当な頻度で更新されるはず。(うまく動いていれば)
ソースはここにおいてあります https://github.com/punytan/middleware.punytan.dotcloud.com
dotcloudは簡単すぎでhello worldの解説は日本語でも大量にあるのでそれを読むとすぐに使えるようになってしまうのでそのあとの+αを。
ssh 使うときは info でポートがわかるけれども、
dotcloud ssh foo.bar
みたいにも入れるのでこっちのほうが楽ですね。
はじめからperlの環境とcpanmが入ってるのでdot-filesを管理している人はモジュールを一気に入れたほうがモジュール不足でイラッとしなくて済むと思う。(とくにモジュール周りやテスト周り)
必要なものをいちいち選んでインストールするのが面倒なので富豪的に普段使っているものをどんどん入れた。
https://github.com/punytan/dot-files/blob/master/build-env/06_perl-modules.list
この中で入らなかったのは
の二つだけなのでXML関連をはじめ、諸々ライブラリはOSにインストール済みな雰囲気。これは大変便利。
これは多分個人の趣味よるかもしれないけれども、CPANに上げてないモジュールはextlib使わずにインストールしてみた。手元の環境と全く同じという訳でもなさそうなのでPP以外に依存してる場合は、同梱するとあとでやや面倒になりそうな気がする。
gitが入ってるぽいので富豪的にモジュールを入れたあとは github などから git clone して make dist 後(あるいは$HOME/shipit-distから持ってきて)、cpanmに渡すだけで楽々インストールできます。
VPSだとXMLライブラリのインストールやsshの設定やら諸々手間なことが待っているわけですが、ここまでデプロイするのが楽だと丸投げしたくなってしまいますね!
個人的にはmemcachedが使えるといいなぁという所。
サーバとクライアントがひとつのプロセスにまとまってクローラー的なプロセスが必要なくなり、監視せずに済むので地味に嬉しい
言われてみれば何だ、という程度ではあるけれども、app.psgiで Twiggy::Server を使い、コネクションを AE::timer で監視し、AE::cv->recv; するという方法をつかえば Tatsumaki::MessageQueue も使えて万々歳。これがなかなか思いつかなかった…
Twitter の userstream は時々切れるので再接続の処理をする必要があった。今までずっとクローラーで while ループ回したりしてたけれども、 while を使って接続が切れるごとに ->recv してるとサーバと一緒に動かすことができなくて悶々とした日々を過ごすこと数ヶ月。
サーバでは builder {} を return していたので、当然 while なんて挟めなかった。
必要な部分だけ切り取り
use strict;
package PollHandler;
use parent 'Tatsumaki::Handler';
__PACKAGE__->asynchronous(1);
use Tatsumaki::MessageQueue;
sub get {
    my $self = shift;
    my $mq = Tatsumaki::MessageQueue->instance('tweet');
    my $client_id = $self->request->param('client_id')
        or Tatsumaki::Error::HTTP->throw(500, 'client_id is required');
    $mq->poll_once($client_id, sub { $self->on_new_event(@_) });
}
sub on_new_event {
    my $self = shift;
    $self->write(\@_);
    $self->finish;
}
package main;
use AE;
use Plack::Builder;
use Tatsumaki::Application;
use Twiggy::Server;
use AnyEvent::Twitter::Stream;
my $app = Tatsumaki::Application->new([ '/poll' => 'PollHandler' ]);
my $mapp = builder {
    # enable etc...
    $app;
};
my $server = Twiggy::Server->new(
    host => 'example.com',
    port => 5000,
)->register_service($mapp);
my $STREAM_CONN; # we need $STREAM_CONN to keep the availability of stream connection
my $LISTENER;    # we have to declare $LISTENER here, because variables in the timer callback scope are not visible from here
my $w; $w = AE::timer 1, 10, sub {
    return if $STREAM_CONN;
    my $oauth = { ... };
    say "ALERT: wake up";
    $LISTENER = AnyEvent::Twitter::Stream->new(
        %$oauth,
        method     => 'userstream',
        on_error   => sub { $STREAM_CONN = 0; },
        on_eof     => sub { $STREAM_CONN = 0; },
        on_connect => sub { $STREAM_CONN = 1; },
        on_tweet   => sub {
            my $tweet = shift;
            my $mq = Tatsumaki::MessageQueue->instance('tweet');
            $mq->publish({ type => 'tweet', tweet => $tweet });
            $STREAM_CONN = 1;
        }
    );
};
AE::cv->recv;
AnyEvent::HTTP の変更によって AE::T::Stream 0.20 は動かないが、 AE::T::Stream の DEVELOPER RELEASE (0.20_1) だと chunked encoding 周りが fix されているので上げるべし
dot-files を git で管理してるとついつい vim プラグインも submodule 使って管理してしまいたくなるんですが、 submodule を使わないほうが楽なんじゃないかなと思って dot-files の整理と一緒にこんなシェルスクリプトを書いてみた。
整理した dot-files はこちら https://github.com/punytan/dot-files
#!/usr/bin/env bash
if [ ! -d "$HOME/dot-files/vim/bundle" ]; then
    mkdir -p "$HOME/dot-files/vim/bundle";
fi
cd "$HOME/dot-files/vim/bundle";
case $1 in
    'install')
        git clone git://github.com/thinca/vim-guicolorscheme.git;
        git clone git://github.com/Shougo/neocomplcache.git;
        git clone git://github.com/thinca/vim-quickrun.git;
        git clone git://github.com/Shougo/unite.vim.git;
        git clone git://github.com/thinca/vim-ref.git;
        ;;
    'upgrade')
        dirlista=`find . -maxdepth 1 -type d -print`;
        for d in $dirlista; do
            if [ "$d" != "." ]; then
                cd "$d";
                echo $d;
                git pull;
                cd '..';
            fi
        done;
        ;;
    *)
        cat << EOM
Usage:
    $0 install
    $0 upgrade
EOM
        ;;
esac
 daemontools はめったに設定しないので毎回忘れる!とくにmultilogとか編集中にはダウンさせておくとか。一連の流れをまとめたメモ。
$ sudo apt-get install daemontools-run $ cd $ mkdir daemontools
$ cd ~/daemontools $ mkdir foo $ mkdir foo/log $ chmod +t foo
自動で supervise が起動するのでダウンさせておく
$ cd /etc/service $ sudo ln -s /home/hoge/daemontools/foo foo $ sudo svc -d /etc/service/foo $ sudo svc -d /etc/service/foo/log
$ cd ~/daemontools/foo $ vim run
export PATH=/path/to/library cd /home/hoge/app/foo && exec setuidgid hoge executable-file
$ vim log/run
exec setuidgid hoge multilog t ./main
ログを書き込むためにlogディレクトリの所有ユーザを指定する
$ chmod +x run log/run $ chown hoge log
$ sudo svc -u /etc/service/foo/log $ sudo svc -u /etc/service/foo
$ sudo svstat /etc/service/foo $ sudo svstat /etc/service/foo/log
を実行して up の秒数が増えていれば正常に起動している。10 seconds 以上にならないときは異常終了しているので環境変数やパスが間違っていないか確認する。
Growlしたい!というときは次のようにやってます。
これだけ。growlnotify が必要なので Growl のディスクイメージの Extras フォルダから事前にインストールしておく。
$ echo 'foo' > $HOME/growler.log
このように、ログファイル(通知させたい内容を追記するファイル)を作成しておく。
$ ssh example@example.com tail -n 1 -f '$HOME/growler.log' | perl -e 'system "growlnotify", "-m", $_ while (<STDIN>);'
この例では example.com に ssh でログインし、ログファイルを tail -f で監視する。標準出力が pipe によって perl に流れるので、それを受信して growlnotify を起動する。これによってファイルの最終行が growl され、接続できているか確認できる。
$ echo 'hoge' >> $HOME/growler.log $ ls >> $HOME/growler.log
このように標準出力をログファイルに追記するだけでgrowl通知ができる!
cpan-outdated -p | cpanm; echo 'upgraded' >> $HOME/growler.log
としておくと、処理が終わったときに通知してくれます。
ssh example@example.com tail -n 1 -f '$HOME/.weechat/logs/*/*/*.weechatlog' | perl irc_growl.pl &
としてサーバにつなげて irc_growl.pl で処理を加えた上で growl させることもできる。irc_growl.pl では、--appIcon に LimeChat を指定することで Growl 時のアイコンを変更している。
ssh のコマンドが長いと感じたら、.bashrc に alias を設定しておくと良い。参考に irc_growl.pl を貼りつけておく。
while (<STDIN>) {
    chomp;
    next unless $_;
    next if /^==>/; # ignore tail's output
    my $attr = parse($_);
    if ($attr->{type} =~ /(:?NOTICE|PRIVMSG)/) {
        system("growlnotify", "-m", $attr->{content}, "-t", $attr->{user}, "--appIcon", "LimeChat");
    }
}
sub parse {
    my ($line) = @_;
    my ($time, $cmd, @contents) = split /\t/, $line;
    my $content = join "\t", @contents;
    my $user;
    if (not $cmd) {           # critical error
        $cmd = 'ERROR';
    } elsif ($cmd eq '-->') { # join
        $cmd = 'JOIN';
    } elsif ($cmd eq '<--') { # part
        $cmd = 'PART';
    } elsif ($cmd eq '--') {  # messages from server
        $cmd = 'SERVER';
    } elsif ($cmd eq '*') {   # notice
        $user = $cmd;
        $cmd = 'NOTICE';
    } else {                  # privmsg
        $user = $cmd;
        $cmd = 'PRIVMSG';
    }
    return +{
        type    => $cmd,
        time    => $time,
        content => $content,
        user    => $user,
    };
}
これで困ってないので調べていない。
sugyanさんが週末を犠牲にしてまで調べているのでしばしお待ちを!!
AjaxとjQueryの説明は不要として、pushStateとはなんぞや。
pushStateを使ってブラウザの履歴に対する操作をし、HTMLの一部のみを書き換える動作でもブラウザの戻る/進む機能を実現できる方法のひとつ。Ajaxなページを再現し、かつURLを見慣れた方法で自然にpermalinkを表現できる。有名なところではGitHubで使われてるアレ。
ブラウザの履歴を機能させるため、URL の fragment (#) を使ってAjaxなページを実現する方法。一時期もてはやされた感があるが、 http://d.hatena.ne.jp/karasuyamatengu/20110212/1297465199 など合理的な反論があり、これから導入するのはためらわれるところ。
有名なところではTwitterで使われているあの厄介者。
pjax とは pushState + ajax を合わせた語で、その名のとおり pushState を使いつつ Ajax な処理を行う為の jQuery ライブラリ。 GitHub の defunkt が開発していることもあり、今後 pushState を使ったものでは導入が進んでいく可能性がかなり高いのではないかと読んでいる。
また、最近ではページの見た目の面では非クロスブラウザを許容する風潮があるように見受けられ、この流れともpjaxは親和性が高い。
「高機能なWebブラウザでは見栄えよく、そうでないWebブラウザで“も”それなりに」
http://itpro.nikkeibp.co.jp/article/Watcher/20110329/358885/
pjax の振る舞いは、 Chrome のようなブラウザでは pushState を使い、 IE のようなブラウザでは通常のアクセスと同じように、全く同じ permalink でアクセスできるようにしてくれる。
私もこの勢力のうちのひとりだと内心思っているのでやや傾倒している感はある。
しかし hash fragment を使用したページは curl では取得できない。これはサーバ側に fragment 以降は送信されないためであるが、 public なものに curl や LWP::UserAgent や Google のクローラーといったクライアントでアクセスさせるために URL に細工することは解せない。かと言って自分でゴリゴリと pushState の実装を書くのも骨が折れる。そういった問題を解決してくれるものになると思われる。
ここではざっくりとした使い方を書いておく。
細かい使い方はいろいろあるようなので、GitHubにあるREADMEを参照すると良い。https://github.com/defunkt/jquery-pjax
"js-pjax" クラスのアンカーに対してのみ機能させる場合はこのようにセレクタを書く。
<script src="/jquery.min.js"></script>
<script src="/jquery.pjax.js"></script>
<script type="text/javascript">
  $(function () {
    $('a.js-pjax').pjax('#main');
  })
</script>
pjax を使ったリクエストの場合に、 HTTP ヘッダに X-PJAX: true が付くのでサーバサイドではヘッダを見て返すbodyを変更する。
もし、 X-PJAX がない場合はすべてをレンダリングした HTML を返し、X-PJAX が true の場合には対象のコンテナにロードさせるのに適切な response body を返すよう処理を書く。X-PJAX のリクエストの場合でタイトルを変更したい場合は、<title>タグも含めて応答する。
サーバをPerlで実装したので
$ cd /tmp $ curl https://gist.github.com/raw/901139/c13279a29cfcca8cc75e63fb9eeb65b3ca2785c7/app.psgi -LO $ plackup
で起動し、リクエスト/レスポンスをチェックできる。
use strict;
use warnings;
use feature qw/say switch/;
use Data::Section::Simple;
use Text::Xslate;
use Plack::Request;
my $tx = Text::Xslate->new(
    path => [ Data::Section::Simple->new->get_data_section ],
);
my $app = sub {
    my $req = Plack::Request->new(shift);
    my %data = ( %ENV,
        TIME => scalar localtime,
        PJAX => ($req->header('X-PJAX') ? 1 : 0)
    );
    say "----- X-PJAX is " . ($data{PJAX} ? 'TRUE' : 'FALSE');
    my $type =  $data{PJAX} ? 'pjax' : 'default';
    my $res = $req->new_response(200);
    $res->content_type('text/html; charset=utf-8');
    given ($req->path_info) {
        when ('/') {
            $data{title} = "root";
            $res->body( $tx->render("root-$type.tx", {data => \%data}) );
        }
        when ('/home') {
            $data{title} = "/home";
            $res->body( $tx->render("home-$type.tx", {data => \%data}) );
        }
        when ('/help') {
            $data{title} = "/help";
            $res->body( $tx->render("help-$type.tx", {data => \%data}) );
        }
        when ('/favicon.ico') {
            $res->redirect("http://www.google.com/favicon.ico", 301);
        }
        default {
            $res->status(404);
            $res->body('Not Found');
        }
    }
    return $res->finalize;
};
$app;
__DATA__
@@ home-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <p> Hello, <: $data.USER :> </p>
@@ home-default.tx
    : cascade base;
    : override main -> { include "home-pjax.tx" }
    : override title -> { "/home" }
@@ help-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <pre> <: $data | dump :> </pre>
@@ help-default.tx
    : cascade base;
    : override main -> { include "help-pjax.tx" }
    : override title -> { "/help" }
@@ root-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <p> pjax!! pjax!! pjax!!</p>
@@ root-default.tx
    : cascade base;
    : override main -> { include "root-pjax.tx" }
    : override title -> { "root" }
@@ base.tx
    <!DOCTYPE html>
    <html>
        <head>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
            <script src="http://pjax.heroku.com/jquery.pjax.js"></script>
            <meta charset='utf-8'> 
            <title> <: block title -> { :> hello pjax <: } :> </title>
            <script type="text/javascript">
                $(function () {
                    $('a.js-pjax').pjax('#main');
                })
            </script>
        </head>
        <body>
            : include "nav.tx"
            <div>
            : $data.TIME
            </div>
            <div id="main">
                : block main -> { }
            </div>
        </body>
    </html>
@@ nav.tx
    <ul id="nav">
        <li><a href="/" class="js-pjax">Index</a></li>
        <li><a href="/home" class="js-pjax">Home</a></li>
        <li><a href="/help" class="js-pjax">Help</a></li>
    </ul>
pjax リクエストを飛ばす方法、レスポンスをどう返せば意図したとおりに動くか、レスポンスを受けてタイトルを変更する方法など、手っ取り早く知りたいであろう事柄を埋めこんであるので、多少は参考になると思う。
curl で gist から取ってくる方法が一番楽だと思う。
簡単に pushState による履歴操作と hash fragment (裏側のURLは汚い)に勝る綺麗な URL を実現できるので流行るといいな。
CPAN本の出版おめでとうございます&献本頂きありがとうございます。拙作 (AnyEvent::Twitter 0.52 と AnyEvent::Twitter::Strem のパッチ紹介) も収録されています。

CPANモジュールを32のジャンル(ユーティリティ/データ/クラス/日付/アルゴリズム等々)に分け、目的のモジュールと使い方を説明する形式で、まさに書名の通り「CPAN モジュールガイド」と呼ぶにふさわしい本といえるのではないでしょうか。また、400ページ超のこの分量を書くのは相当な苦労だったのだろうと想像できるほどの力作となっています。
多くのPerl入門書ではCPANの記述は最終章近くで存在に触れている程度でしかなく、どれを使えば良いのかわからない状態で先に進めない人が多いのではないかと思いますが、本書はそういった問題を解決するひとつの手段になりうるのではないかと思います。
また、自分の知らないモジュールがたくさんあることを思い知らされました。さらに、モジュール紹介の中にあるtips (例えば、use constant した定数にコンパイルタイムで最適化が入ること等々) はPerlを使う上で知っておいたほうが良いものが多いです。
モジュールガイドなのにAcme::のnamespaceがないのはうわなにをするやめくぁwせdrftgyふじkl;
ということで、Perl CPANモジュールガイド おすすめです。
http://linknode.net/tepco.html
Access-Control-Allow-Origin: *
をヘッダーに含めているので、Same Origin Policyを気にせず、サイトをまたがってリクエストを飛ばしてJSONを取ってくることができます。
次のようなもので簡単に表を出力することができます。
    <script> 
        $(function () {
            $.ajax({
                url : 'http://api.linknode.net/tepco/v2/demand.json',
                dataType : 'json',
                success  : function (data) {
                    //console.log(data.raw);
                    //console.log(data);
                    $('#raw').append(data.raw);
 
                    $('#last_updated').append(data.last_updated.date + ' ' + data.last_updated.time);
 
                    for (var i in data.demand) {
                        $('#target > tbody').append(
                            $('<tr>').append(
                                $('<td>').append(data.demand[i].date + ' ' + data.demand[i].time),
                                $('<td>').append(data.demand[i].today),
                                $('<td>').append(data.demand[i].yesterday)));
                    }
                }
            });
        });
    </script> 
http://itpro.nikkeibp.co.jp/article/NEWS/20110325/358756/