D-7 <altijd in beweging>

Day to day life of a Perl/Go/C/C++/whatever hacker. May include anything from tech, food, and family.

タグ:Perl

ZeroMQ.pmをつい相続してしまってから本家libzmqの変更についていけず大分悩んだんだけど、ここのところ直してリクエストが多かったので一念発起してlibzmqのPerlバインディングを大幅リニューアルしました。まだ正式リリースは出してないので、何か文句を言うなら今のうち!英語での解説はこちら

いままでlibzmqの直接バインディング、定数、Perlっぽいシンタックスシュガーのラッパーを全部同じディストリビューションにいれていたのをばらしました。これまでZeroMQ::Raw とされていたのは ZMQ::LibZMQ2 ならびに ZMQ::LibZMQ3 となりました。ZMQはこれらのバインディングをうまいことオブジェクト風味にするPPモジュール。ZMQ::Constantsはlibzmqのバージョン毎にやたらと変わるので別モジュールで切り出しました。あとすごくざっくりですが CZMQのラッパも書きました。こちらはZMQ::CZMQというもの。

なおZeroMQ → ZMQの変更についてはlibzmqのメンテナから名前を変えてくださいという半強制的なお願いが出ていたので名前の変更はもう大分前から予定してました。


というわけでお父ちゃん疲れたよ。
    このエントリーをはてなブックマークに追加 mixiチェック

小ネタ。GETした内容のMD5が欲しかったので。

    use strict;
    use Furl::HTTP;
    use Digest::MD5;

    my $digest = Digest::MD5->new;
    my $furl = Furl::HTTP->new;
    $furl->request(
        method => "GET",
        url => "http://whatever/text.txt",
        write_code => sub {
            my ($code, $msg, $hdrs, $partial) = @_;
            return unless $code ne 200;

            $digest->add( $partial );
        }
    );
    print $digest->hexdigest;
    このエントリーをはてなブックマークに追加 mixiチェック

Devel::Coverって便利な道具なんですが、なんかfork + execするとうまく動きませんでした。ずっと見て見ぬ振りしてたんだけど、そういうわけにもいかなくなったので、二日ほどずっとその挙動をprintデバッグで追いかけていったら大体把握できた。

すごくはしょって言うと、Devel::Coverは裏のXSレベルでEND {} にあたる部分とかにコードを挿入してて、元のコードが

   #!perl
   exec "/bin/ls"
だったとしたら、Devel::Coverをuseすると概念的には

   #!perl
   END { do_interesting_stuff() }

   exec "/bin/ls"
という事をするようになる。PPの場合だとexec時にこのENDブロックはうまく無視されるんだけど、なんせDevel::Coverは無理矢理この動作を行っているので、ENDに当たる部分がexec()の直前に実行される。そしてもしここでdieとかCレベルでのエラーとかあるとしずかーに落ちてしまう。

ということで、自分の環境では以下のレポジトリの変更でこれが回避できた:


ちなみにこのあたりの修正ナシでTest::mysqldを使ったコードをDevel::Coverすると↑の問題にぶち当たる。本体のほうにも修正してもらうように言うつもりだけど、もし今困ってたらこれでなんとかなるはず。
    このエントリーをはてなブックマークに追加 mixiチェック

週末にsupervisorっていうものについて話してる人がいたので、特に理由なくPerlでクローンを書いてみようと思った。というわけで本当に基本の部分まで書いた。名前はBrahman。神様の名前にしたかったのはプロセスの生死を管理するから。

繰り返すけど、特に理由はない。書きたかったから。ちなみに内部的には多分本家と全然互換性はない。設定ファイルがだいたいクローンできればいいや、というくらい。


今のところできることは、例えば今daemontoolsで管理してるplakupサーバーがあったとして、こんな感じの設定を書くと立ち上がってstdout/stderrを一緒のログに保存してくれる、ってところまで。

[program:yourapp]
environment=PORT=50000
directory=/service/yourapp
command=/service/yourapp/run
stdout_logfile=/var/log/yourapp.log
redirect_stderr=true

以上を書いて、brahmand -c config.ini とか書くと勝手にこのプロセスがずっと生きてるように管理してくれるでござる。brachmanctl list とかすると、現在動いているプロセスとかでてくるので、supervisor.processes.pid あたりをkillしてみると生き返るのが見えるかと思います。

一応頑張った点としては、万が一 ログのプロセスが死んじゃったとしても、daemontoolsのsuperviseに相当するプロセスが生き残ってればまたログを取るプロセスが生き返って、もう一度アタッチしてログをとり続けられる事と、ログを取る以外のプロセスもアタッチすることによってイベントを受け付ける事ができる、など。

あと外部からのコマンドうけつけはJSONRPCで行う。今はごめん、list(動いているプロセスとかをリスト)とstop(brahmandと管理してるプロセスをストップ)しかできないけど、しこしこ書いていけばいくらでもAPIは追加できる。

ここまで正味二日間でできた。JSONRPCは以前書いた物の流用、あとはTwiggyとかを以前からバリバリ書いてたおかげで楽だった。Twiggyがそのままエンベッドできるのにも助かった。

とりあえず自分の使ってる開発サーバーはこいつに移行できるようにしてみようかな、と思ってるけど、人柱とか一緒にやってくれる人がいると嬉しいなー、と。

    このエントリーをはてなブックマークに追加 mixiチェック

最近PSGI/Perlをサポートし始めたdotCloudのinvite codeをもらったのでとりあえずHello World的なアプリを作った。

作り方だが、まずdotcloud用のツール類をインストールする必要がある。自分のマシンでpythonを動かした事がなかったのでそこから。まず自分のpythonは homebrew経由で入っているヤツで、全てユーザー権限で入っているのでsudoとか使ってないのに注意。

easy_install dotcloud
docloudではまず"deploy"というものを作る。deployは複数の"service"の集合体。"service"はそれぞれが一個のアプリなり、データベースなりを指す(はず)

なのでまず適当に"lestrrat"というdeployを作り

dotcloud create lestrrat
次に"lestrrat.www"というserviceを作る

dotcloud deploy --type perl lestrrat.www

この段階ではまだインフラがセットアップされているだけでアプリ自体は空。アプリ自体はどこに作ってもよくて、次のステップでそのディレクトリを指定すればOK。ここでは仮に~/git/lestrrat.wwwにあるとする

次にアプリそのものをリモートのserviceにpushしないといけない。

デフォルトの挙動では、以下のようにすると特定のディレクトリの中身を*全て*pushする。

dotcloud push lestrrat.www ~/git/lestrrat.www
ただ、ちゃんとバージョンコントロールをしたい場合は現段階ではhgとgitがサポートされていて、この場合は若干挙動が違う。

もし指定されたディレクトリに.gitディレクトリがあれば、リモートにgit pushしたのと同じような動作になる。つまり、git commitしてないファイルはpushされない。自分はとりあえず作業量のディレクトリを作り、git initした。

つぎ。リモート側で動くアプリは当然PerlにしたいのでPSGIファイルが必要なのだが、dotcloudではserviceディレクトリのルートにあるapp.psgiというファイルがデプロイされる。

追記5/1: 今回は生PSGIファイルを使うけれども、当然CatalystDancerMojoliciousをデプロイすることが可能だ。この記事では動くまでの理屈を追いたいので敢えてフレームワーク等は使ってないけれども、普通は何らかの形でそういうフレームワークを使う事になるだろう。それぞれの使い方はリンクを参照のこと。

dotcloudのチュートリアルではこのファイルはbinに実体を置いて、ルートにはsymlinkをつける、というような形になっていたが、なんせHello Worldで面倒くさいのでそのままぼん、とルートに置いた。

内容は以下のような感じ。

use strict;
return sub {
    my $env = shift;
    return [
        200,
        [ 'Content-Type' => 'text/html' ],
        [
            "<html><head><title>Hello</title></head>",
            "<body><h1>Hello, Stranger</h1><p>Here's a dump of what I know about you</p>",
            (map { "<tr><td>$_</td><td>$env->{$_}</td></tr>\n" }
                grep { /^HTTP_/ }
                keys %$env),
            "</table></body></html>"
        ],
    ]
};

これを以下のコマンドでリモート側に送ると、向こうで色々魔法が起こって、サービスが http://www.lestrrat.dotcloud.com/ であがっている・・・という感じ。

 dotcloud push lestrrat.www ~/git/lestrrat.www

ちなみに最後のコマンドはもう書くのが嫌になったので、Makefile.PLを作って以下のような記述をしておいた。これでmake dotcloudでpushされる。(注意!Module::Installを使う場合はinc/以下もpushしないといけないので、git commitしないといけないようです!)

use inc::Module::Install;

name 'lestrrat-DotCloud-Hello-World';
version '0.01';

postamble(<<EOM);
dotcloud:
     dotcloud push lestrrat.www \$(PWD)
EOM

WriteAll;

あと依存関係の類もMakefile.PLで解決してくれるとのこと。やったね!

ってことで、近々dotCloudでアプリをつくるですよ
    このエントリーをはてなブックマークに追加 mixiチェック

@tomitaさんが書いた「CPANモジュールガイド」の刊行記念ということで池袋のジュンク堂本店で話して・・・もとい、コードを書いてきました。普段は滅多にこういう場に現れない「初めてのPerl」や「プログラミングPerl」の翻訳者である近藤嘉雪氏がもう一人のゲスト。司会がもはや技術界のタモリと化しつつある@yusukebe。あと@941さんがスタッフとして手伝いに来てくれました

近藤さんを見られるというだけでも実はすごいイベントだった気がする。ちなみに近藤さんは出番直前まで頑張ってスライドを書いていて、そのおかげで話にクロージャが含まれたそうです!

こちらは@tomitaさんが書いた店頭POP。「Placクマ」の二重のパクリがひどいですねwww !


イベントの内容自体は・・・なんだかリハーサルというか、ちゃんとした打ち合わせもしてなかったのに、すごく内容が良い感じになった気がしますね。Perlの基本を@tomitaさんと近藤さんが丁寧に説明してくれて、最後に僕がわーーーっとCPANモジュールを使ってコードを書く過程をなぞる、という感じ。

自分の発表はCPAN 30分クッキングということでCPANから色々ダウンロードしてきてFacebook::Graphとか使ったらこうなるよー的なコードをざくざく書きました。

最終的に書いたコードは、@sugyanさんのブログのパクリと、自分の友達のタイムラインからロケタッチのリンクを抜き出してきてHTMLを作る、というようなもの。本番では実はトラブルがあって最後のロケタッチページを作成するためのHTTPリクエストがうまく動かなかったので一瞬焦ったが、「CPAN 30分クッキング」ということで「すでにこちらに出来たものがございます」というものを用意しておいたので助かった。一応その程度のネタは仕込んでおいてよかった・・・

というわけでなかなか楽しかった!みんな、もっと僕らの本を買ってくれると次が出せるようになるからよろしくお願いします!w


さて、僕の発表ですが、全然中身はないですが、スライドは以下のような感じ:


あと一応こちらがコード。最初にconfig.plというファイルの中に自分のFacebookアプリ用のapp_idとsecretを入れておく

return {
    app_id => "XXX your app ID",
    secret  => "XXX your app secret"
};

まずはsugyanさんのパクリのヤツ。アクセストークンをコピペするのが面倒くさいのでコンフィグファイルにダンプするようにしてある:

use strict;
use Data::Dumper;
use Facebook::Graph;
use Plack::Request;

my $config = require "config.pl";
my $fb = Facebook::Graph->new(
    app_id => $config->{app_id},
    secret => $config->{secret},
    postback => "http://localhost:5000/"
);

return sub {
    my $env = shift;
    my $req = Plack::Request->new($env);

    if ( my $code = $req->param('code') ) {
      warn "requesting access_token";
        $fb->request_access_token( $code );
        my $dump = Dumper({
            %$config,
            access_token => $fb->access_token
        });
        $dump =~ s/\$VAR = //;

        open my $fh, '>', "config.pl" or
            die;
        print $fh $dump;
        close $fh;
        return [ 200, [], [ $fb->access_token ] ];
    }
      warn "redirect";

    return [
        302,
        [
            Location => $fb->authorize
                ->extend_permissions( "publish_stream" )

                ->uri_as_string
        ],
        []
    ];
}

で、こっちがFacebookにアクセスしてロケタッチのデータだけ抜き出してくるヤツ。普通にFacebook::GraphのAPI使ってたらおせーな、と思ったのでFurl+Coroしてみた。よくよく考えてみると CoroとCoro::LWPしとけばFurl使わなくても多重化できるから速くなる気がするが、まぁ後悔はしてない

use strict;
use Text::Xslate;
use JSON;
use Coro;
use Coro::Select;
use Furl::HTTP;
use Facebook::Graph;
use Data::Dumper::Concise;

my $config = require "config.pl";
my $fb = Facebook::Graph->new(
    app_id => $config->{app_id},
    secret => $config->{secret},
    access_token => $config->{access_token},
);
my $data = $fb->query->find("me/friends")->request->as_hashref;

my $furl = Furl::HTTP->new;
my @coros;
foreach my $friend ( @{ $data->{data} } ) {
    my $q = $fb->query->find( "$friend->{id}/feed" );
    push @coros, async {
        my (undef, $code, undef, undef, $body ) = $furl->get( $q->uri_as_string 
);
        return unless $code eq 200;
        return decode_json $body;
    } $q->uri_as_string;
}

my @spots;
foreach my $coro (@coros) {
    my $feed = $coro->join;
    next unless $feed;
    foreach my $entry ( @{ $feed->{data} } ) {
        if ( $entry->{caption} eq 'tou.ch' ) {
            push @spots, $entry->{link};
        }
    }

}

my $xslate = Text::Xslate->new(
    path => [ "." ],
    syntax => 'TTerse',
);

print $xslate->render_string( <<'EOM', { spots => \@spots } );
<script type="text/javascript" src="jquery-1.5.2.min.js">
<script type="text/javascript">
var urls = [ [% FOREACH spot IN spots %]"[% spot %]"[% IF ! loop.last %],[% END %][% END %] ];

$(document).ready( function() {
    var delay = 200;
    var f = function() {
        url = urls.pop();
        url = url.replace( /\/$/, "" );
        url = url.replace(/\/spot/, "/widget/spot");
        var iframe = $('<iframe frameborder="0" scrolling="no" allowtransparency="ture" height="321" width="350" src="' + url + '?i=0&m=1&t=blue&width=350"></iframe>');
        $("#container").append( iframe );
        setInterval(f, delay);
    };
    f();
} );

</script>
<div id="container"></div>
EOM
    このエントリーをはてなブックマークに追加 mixiチェック

ちょっとだけ躓いたりしたので、備忘録のため。AnyEventでData::MessagePackを受け取るサーバー
use strict;
use AnyEvent;
use AnyEvent::Socket;
use Data::MessagePack;
use Data::Dumper;

main();

sub main {
    my $host = undef;
    my $port = 8888;
    my $guard = tcp_server $host, $port, sub {
        my ($fh) = @_;
        handle($fh);
    };

    my $cv = AE::cv;
    my $w; $w = AE::signal 'INT' => sub {
        undef $w;
        undef $guard;
        $cv->send;
    };

    $cv->recv;
}


sub handle {
    my $fh = shift;

    my $packer = Data::MessagePack::Unpacker->new;
    my $buf = '';
    my $offset = 0;
    my $w; $w = AE::io $fh, 0, sub {
        my $n = sysread $fh, $buf, 65536, length $buf;
        if ( $n == 0 ) {
            undef $w;
        }

        while (length $buf > 0) {
            $offset = $packer->execute( $buf, $offset );
            if (! $packer->is_finished) {
                last;
            }

            warn Dumper( $packer->data );
            substr( $buf, 0, $offset, '' );
            $offset = 0;
            $packer->reset;
        }
    };
    my $s; $s = AE::signal INT => sub {
        undef $w;
        undef $s;
    };
}
適当なクライアントスクリプト
use strict;
use Data::MessagePack;
use AnyEvent;
use AnyEvent::Handle;
use AnyEvent::Socket;
use AnyEvent::Util;
my $count = shift @ARGV || 100;

my $cv = AE::cv;
my $w; $w = tcp_connect '127.0.0.1' => '8888' => sub {
    my $fh = shift;

    AnyEvent::Util::fh_nonblocking($fh, 1);
    my $i = 0;
    my $h = AnyEvent::Handle->new(fh => $fh);
    my $next = sub {
        $cv->begin;
        $h->push_write( Data::MessagePack->pack({ foo => $i }) );
    };

    $next->();
    $h->on_drain(sub {
        my $h = shift;
        $cv->end;
        if (++$i < $count) {
            $next->();
        } else {
            undef $w;
        }
    });
};

$cv->recv;
    このエントリーをはてなブックマークに追加 mixiチェック

追記:わかりにくかったらしいので若干コードと説明を変えてみた

use strict;

sub foo {
     my $n = shift;
     warn $n;
     next if $n > 5;
}

foreach my $x  ( 1..10 ) {
     foo( $x );
     warn "after foo";
}
これで嵌った。問題は当然 "next"。ループの中で呼ばれるfooの中で"next"を使ってるので"after foo"は5回しか表示されないのだ。

理由が分かってみればなるほど、だったけど、これはしばらく嵌った。ちなみにuse warningsしてると
"Exiting subroutine via next"
っていう警告がでるよ!良い子はnextの変わりにreturnを使ってね!
    このエントリーをはてなブックマークに追加 mixiチェック

さきほどの記事に関してtokuhiromに指摘されたのでRPC::XMLにしてみた。基本的にはここに書いてある事と同じ
で自分がちょっと変更した部分は以下の通り:

  1. XMLRPCで prefix.method_nameって呼んでた名残があるので、prefixをとっぱらっちゃう
  2. $q->argsが返すのは RPC::XML::simple_type とかの値なので、これを直してあげないとXMLRPC::Liteで動いてたコードが動かない
  3. @ret = () で RPX::XML::resposeを作ろうとするとundefを返してくるので、その後の $content->as_stringがこける。レガシーコードを期待しているクライアント側との兼ね合いもあるので@retが空だったら [] を渡してあげる

use strict;
use Plack::Builder;
use Plack::Request;
use RPC::XML;
use RPC::XML::ParserFactory 'XML::LibXML';

my $app = sub {
    my $req = Plack::Request->new(@_);
    my $q = RPC::XML::ParserFactory->new()->parse($req->content);
    my $method_name = $q->name;

    # (1) 
    $method_name =~ s/^prefix\.//; 

    my $code = $webapp->can($method_name);
    if (! $code) {
        return [
            404,
            [ "Content-Type" => "text/plain" ],
            [ "RPC method $method_name not found" ]
        ];
    }

    # (2)
    my @ret = $webapp->$code( map { $_->value } @{$q->args} );

    # (3)
    my $content = RPC::XML::response->new( @ret ? @ret : [] );
    return [
        200,
        [ "Content-Type" => "text/xml" ],
        [ $content->as_string ]
    ];
};

builder {
    enable 'ContentLength';
    $app;
};

    このエントリーをはてなブックマークに追加 mixiチェック

諸事情によりレガシーなXMLRPC::LiteベースのプログラムをPlack上にポートすることになったので色々悪戦苦闘してみた。とりあえず普通に動かすのは辛い、ということはなんとなくわかったので色々やった結果、こんな感じのコードを書けばとりあえずディスパッチは動くようになった。もしベターな方法があったら教えて下さい(XMLRPC::Lite以外のベターなライブラリでもOK!)

(追記:指摘を受けたのでRPC::XMLで書き直してみた

use strict;
use Plack::Builder;
use CGI::Emulate::PSGI;
use IO::String;
use XMLRPC::Transport::HTTP;

my $xmlrpc = XMLRPC::Transport::HTTP::CGI->dispatch_to( "MyApp" );

builder {
    my $code = CGI::Emulate::PSGI->handler(sub {
        $xmlrpc->handle;
    });
    sub {
        my $env = shift;
        my $io = $env->{'psgi.input'};
        my $content = do { local $/; <$io> };

        $env->{'psgi.input'} = IO::String->new($content);
        delete $env->{'psgix.io'};
        local %ENV;
        $code->($env);
    };
};
psgi.inputに関してはPOSTデータを読み込むのにSOAP::Lite側で Plack::TempBufferをsysreadしようとしていて、そうすると必ず 0 を返すのですな。open()で作れる疑似ファイルハンドルやなんかでお茶を濁そうとがんばって見たのだけど、結局IO::Stringのようにtieしないと動かないという結論に

なんで local %ENVしてるのかは忘れちゃった。

というわけでこうして動くサーバーは作れたのだけど、なんか他に良い方法あるのかなー
    このエントリーをはてなブックマークに追加 mixiチェック

「オブジェクト指向なパラダイムでプログラムを書くとき」にClass::Data::Inheritableは排除すべきモジュールである。今回激しくそれを痛感しているので、だらだら書いてみたい。

まず、Perlはマルチパラダイムが可能な言語なので、Class::Data::Inheritable自体は否定されるべきものでもないし、あと必ず例外ケースはでてくるのでその際には躊躇なく使えばいいと思う。以下は最初の一文の通り、Perlでオブジェクト指向を使う場合はClass::Data::Inheritableは基本的に使わず、あくまで例外ケースに留めるべきだ、という事を伝えたい。

まずその1: クラスアトリビュートはグローバル変数

クラスアトリビュートはグローバル変数です。異論は認めません。

Singletonもそうだけど・・・使う場所はあるんだけど、グローバル変数は限りなく限定的に使われるべき。基本的に使わない物なので、確実にインスタンス変数では無理、もしくはその方がパフォーマンスが○○%向上する!みたいな箇所以外では原則使うべきものではない。

特にオブジェクト指向とは混ぜるな危険!ざっとググった中で一番簡潔に書いてあったのはこのブログかな

2: オブジェクアトリビュートなのかクラスアトリビュートなのかが激しくわかりにくい。

アトリビュートはどれもアクセッサを通して使うけど、そいつらのうちどれがクラスアトリビュートなのか、どれがインスタンスアトリビュートなのか見ても全然わからない。

結果、グローバルな副作用を常に意識しながらソースコードを改修しなくてはならない。

3:ベタ書きを推奨する→アトリビュート初期化のタイミングが制御しにくい

クラスアトリビュートを使ってるとその指定を.pmファイル内にベタ書きする事が多くなる。Catalystとかでもそうだよね。ある一定の効果はあるので、完全否定じゃないんだけど、アトリビュートとかを全部これで初期化しはじめると・・・微妙。

普通オブジェクトはインスタンス化するタイミングでアトリビュートが初期化されるべきなのに、クラスが*読み込まれた*時点で初期化されちゃう。結果、設定等をBEGIN { } で書く必要があったりする。どうなんだ、これは。

4: アトリビュートのライフサイクルが指定できない→同クラスのオブジェクトインスタンス変数とライフサイクルが違うので困る

クラスアトリビュートはPerlの場合基本的には永続。明示的に ->attr( undef ); や ->attr( $new_value ); とかしないと値がリセットされることはない。どのタイミングでそれをしたらいいのか(していいのか)もわからない。

そのクラスが基本的にデータを貯蔵してるだけのクラスならいいんだけれども、そこでさらに同じクラスでオブジェクトインスタンスを作ろうものなら今度はどの変数が永続して生きてて、どの変数がインスタンスが解放されると解放されて・・・とか考え始めないといけない。

オブジェクト指向で書くなら、オブジェクト本体のライフサイクルに合わせるべきで、これを考え出すと途端に辻褄を合わせるためだけのコードが発生すると思う。

否定ばっかりしていてもしょうがないので、代替策も。

  1. まず基本方針としてクラスデータを持ちたい場合でも極力インスタンス変数で我慢する。
  2. 初期化はそのオブジェクトを初期化するクラスから値を渡す。魔法で勝手に初期化しようとするとすぐグローバル変数に頼りたくなる。
  3. アプリケーション全体を一つのオブジェクトでラップする
  4. クラスデータは例えば*デフォルト値*を指定するのに使う

具体策も書こうと思ったけど、時間が切れたのでとりあえず以下次号!


    このエントリーをはてなブックマークに追加 mixiチェック

YAPCスピーカー絶賛募集中です

今まで参加されてこなかった方達、是非応募してみてください。最先端技術に関して喋る必要はありません。

最先端技術等は比較的「○○の紹介」という感じのトークになることが多いですが、それ以外のトークを発表する際に大事なのはあるテーマを「どうやって」成し遂げたのかを発表する事です。「ほら、みてかっこいいでしょう」ではなく「こういう仕様要求があって、これを達成するための問題はこれで、こうやって解決した」というような内容であれば必ず誰かのためになります。そしてそのテーマは特に難しいものである必要はないのです。「ログ解析」のようにありふれているものでも「ファイルの整理の仕方」とか一件技術っぽくないものでも、知らない人から見たら有用なものって必ずあるはずです。

気張る必要はありません。トーク内容にアドバイスが必要でしたら運営側にもご相談ください。一人でも多く、今までYAPCで登壇されたことの内方に発表していただければと思います。

もちろん、今まで参加してきた方も是非どうぞ!今年はベストスピーカーになるとちょっとした商品がもらえるそうですよ

皆様の参加をお待ちしております。
    このエントリーをはてなブックマークに追加 mixiチェック

2012/5/19 追記:このインストール方法はすでに大分古いです。
現在はhttp://perlbrew.pl に書いてあるように"curl -KL http://install.perlbrew.pl | bash"を実行するのが一番はやいでしょう






Perl 5.6 とか Perl 5.8を今だに使ってるあなた!そうです、これを読んでるそこのあなたです。Perl5の開発チームは後方互換に関しては大変神経をつかっていますので、 Perl 5.6とかで動いてたコードをいきなり 5.12で走らせても基本的には動くはずです(多分警告とかは一杯でるだろうけど)。でもだからといって新しいバージョンで施されている様々な最適化や新しい機能を使えないのはちょっと悲しくないですか?

ということで新しい Perlを使いましょう!最新安定版は5.12.1ですよ!ちなみにどのバージョンを2010年8月時点で使うべきか、はこちらのエントリを参照してください

え?アップグレードできない?システム管理者が許してくれない?前入れてたアプリが壊れる?いやいやいや、全然いけますって。自分用のPerlをインストールすればいいんですよ!

ここではそれを簡単に解説しましょう。

まず短い説明は 「perlbrewを使え」です。以上です。

でもそれだけじゃわかんねーよ!って方は以下をご覧下さい。

まず perlbrewをインストールします。こんな感じでスクリプトを一個ダウンロードするだけです:
    > curl -L http://xrl.us/perlbrew > perlbrew

curlってなってるところはwgetでもlwp-downloadでも、なんでも好きなツールを使って下さいね。

そしたらこのツールを「インストール」します。/usr/localとかじゃなくて自分のホームディレクトリに入れてくれますのでご安心を。
    > perl perlbrew install

一瞬で終わるので、今落としてきたファイルを消しておきます。もう使わないし。
    > rm  perlbrew

で、まずperlbrewを認識できるようにちょっと調整をします:
    # パスやシェルの種類は適時自分のものに変えて下さい
    > echo 'source /home/daisuke/perl5/perlbrew/etc/bashrc' >> ~/.bashrc
    > source ~/.bashrc
    > perlbrew init

これで環境設定はOK。でもこのままではなんにもできないので、ここで使いたいperlのバージョンを指定してperlbrewにインストールさせます。こんな感じでやるだけで5.12.1をダウンロードし、コンパイルをしてくれます
    > perlbrew install perl-5.12.1

ここでトイレに行くとか、軽く掃除をするとか、用事を済ませてきて下さい。perlほど大きなプログラムをコンパイルするにはそれなりに時間がかかります。

コンパイルが失敗した場合はインストールログを見るといいでしょう。多分落ちてるとしてもどっかテストが一個こけてるとかだけだと思うので「まぁいいや」と思えるなら(コンパイルはやりなおしになってしまいますが)、-fをつけてもう一回インストールすると無理やりインストールしてくれます
    > perlbrew install -f perl-5.12.1

できたらこんな感じでスイッチ!次から"perl"って打つとperl 5.12.1を使えてます!
    > perlbrew switch perl-5.12.1
    > perl -v
    This is perl 5, version 12, subversion 1 (v5.12.1) built for darwin-2level

この後、違うバージョンのperlを入れたければ同じようにインストールしてからswitchすればおk!
    > perlbrew install perl-5.13.3
    > perlbrew switch perl-5.13.3 # 開発版!

普段作業するときにはperl hoge.plとやれば良きにはからってもらえます。

スクリプトのshebangに書く場合は「どれでもいいからperlbrewの指定するperlを使ってくれよ!」という時と「確実にこのバージョンのperlを使ってくれ!」という場合がありますが、前者の場合はenvでこんな感じにすればいいですし、
    #/usr/bin/env perl

後者の場合は絶対パスを指定すればおkです。
    #!/home/username/perl5/perlbrew/perls/perl-5.12.1/bin/perl

パワーユーザー的にはこの仕組みの良い点はコンパイルオプションが違う、同一バージョンのPerlを混在させる、とかもできるところですね。例えばスレッド込みでコンパイルされたPerlを扱いたいなら以下のように-D(コンパイル時に渡すフラグ)を渡してやって、インストール時の名前を変えてやればOK!
    > perlbrew install perl-5.12.1 -as perl-5.12.1-threaded -D=useithreads
    > perlbrew switch perl-5.12.1-threaded

お気軽ユーザーにとっては、perl本体とモジュール群がそれぞれ別のディレクトリで管理されるので最後に「もういらね」状態になったらそのperlをrm -rfすれば全部一緒に消えてくれ、あんまり後で問題になる事がないってもグッド。やー、楽ちん!

追記(2011/4/30):最近のperlbrewではinstall-cpanmというコマンドがあり、これを使うとperlbrewと同じ場所にcpanmがインストールされます。これもやっておくと、インストールしたPerlごとにcpanmを入れる必要がなくなるのでさらに楽ちんです

   > perlbrew install-cpanm

さあ、これで新しいPerlをインストールできないから使えないとか言ってられませんよ!是非新しいPerlを使ってみてください。
    このエントリーをはてなブックマークに追加 mixiチェック

7/31に久しぶりに岡山に行ってOkayama.pmに参加してきました!
途中から僕の独壇場になってしまったっぽいですが、大丈夫だったかなぁ。

やはり東京のような都市との情報の格差があるんだなぁ、というのが正直な感想。XslateやCatalystの情報は結構あるはずだ、と思っていてもやはりそれの方向に興味を向けてくれる方法がないとなかなか広まらないんでしょうね。

今はtwitterやらなにやらあるから、うまく活用して情報を拾ってくるといいと思います! twitterで #perlをつけて発言するとか、 IRCで#perl-casualとかに参加するといいですね :)

ちなみに資料は来週アップ予定です。
    このエントリーをはてなブックマークに追加 mixiチェック

http://xslate.org/

tokuhiromがざーっと雛形作って、あとはどかどかみんなでコミットしてたらいつのまにかできてたよ。
    このエントリーをはてなブックマークに追加 mixiチェック

geohash値を計算するためのモジュール、Geo::HashってのがすでにCPANにあるんだけれども、それのXS版Geo::Hash::XSを作ってアップしておきました。実は特に使うアテもないんだけど、目の前で開発が進んでたロケタッチでいざ使おうと思った時にあったらいいかなぁ、と思って現実逃避のために作りました。

もしご意見等ございましたらお気軽にどうぞ。
    このエントリーをはてなブックマークに追加 mixiチェック

以前ここで書いたTest::mysqldの仕組み、すこーしずつ毎回変えながら使ってる。今は継承はしてないが、まぁやってることはだいたい一緒。

で、テーブルのインデックスとか使ってるクエリとかを確認したいなーと思って、slow query logと general log をぼこっと出せるようにした。

    if (! $ENV{ TEST_DSN }) {
        my %my_cnf = (
            'skip_networking'     => '',
        );

        if ( $ENV{SLOW_QUERY_LOG} ) {
            $my_cnf{ slow_query_log } = 1;
            $my_cnf{ slow_query_log_file } = $ENV{SLOW_QUERY_LOG};
            $my_cnf{ long_query_time } = $ENV{SLOW_QUERY_TIME} || 0.1;
        }
        if ( $ENV{GENERAL_QUERY_LOG} ) {
            $my_cnf{ general_log } = 1;
            $my_cnf{ general_log_file } = $ENV{GENERAL_QUERY_LOG};
        }
        my $mysql = Test::mysqld->new(
            my_cnf => \%my_cnf,
        );

        if (! $mysql) {
            confess "Could not establish mysqld";
        }

        $ENV{TEST_DSN} = $mysql->dsn();
        $self->{_mysql} = $mysql;
    }
一回もう少しハードコードに近い形にしてみたんだけど、テストなので環境変数が一番楽だという結論にいたった。使うときはこんな感じ。ログの位置を絶対パスにしないと Test::mysqldが閉じると同時に消されるディレクトリに書き込まれるので要注意。

    SLOW_QUERY_LOG=`pwd`/slow_query.log \
    GENERAL_QUERY_LOG=`pwd`/general_query.log \
        make test
    このエントリーをはてなブックマークに追加 mixiチェック

This is a meta-entry about the effort that Gabor Szabo is pushing for right now (grant request, blog entry). I'm writing on my blog because for the life of me blogs.perl.com doesn't accept my OpenID login, and somehow my MT account there was suspended. WTF. 

Anyways.

I'm the head director of Japan Perl Association (JPA), which is kind of similar to What Gabor is proposing, but not quite. I thought this story on how JPA came to be, things we had to do, and other considerations will shed some new light into the conversation.

Without further ado here goes:

OOPS, DID I JUST SAY THAT?

JPA originally came about when during a dinner after YAPC::Asia Tokyo 2008, I (Daisuke Maki a.k.a. lestrrat) blurted out "Hosting YAPC without a real organization sucks, let's make one!"

Of course, when you speak up, you get to do it. So Emmerson Mills and I started planning and executing the efforts to bring this organization to reality.

There was much that the Japanese Perl community could benefit from such and organization, but we had find the right raison d'etre, so that we could bring in enough people and organizations to the new entity.

MISSION STATEMENT

Well, actually the mission statement is on JPA site, but that's kind of a general, beautified-for-people-that-only-came-to-glance-at-it type of statement.

What this organization really aims for is to create an environment / atmosphere where corporations can hire more Perl engineers (easily). Basically, we want to convey this message:

 "Look (you corporations, employers), Perl is a serious language, and you can get stuff done with it. You also don't have to worry about legacy code, because we will make sure there are more Perl programmers"


That statement alone would just be some random guy saying that for the heck of it. We were going to either prove that the above was true, or make an effort to make those things happen.

So to be more convincing, we narrowed that what would be our goals:

(1) We need more exposure about the fact that serious companies uses Perl. A lot of people -- especially those that are learning about Perl -- still think that Perl is a CGI-ish, Web-ish language. Sure sure, we can use it for the Web, but we can also use it for tons of other things. And we need to tell them that there's evidence for it (i.e. that there are companies using it).

(2) We need to reach out to the (relatively) non-geeks and newbies. Let's face it: if you're a geek, you don't need our help. You know what's good for you, and you will probably pick up the right tool/language for the job in hand. Instead, we need to reach out for those people who are just learning about, or are just using Perl to get some daily duties done. We need to tell them that some stories are just FUD, or plain outdated. We need to tell them how to be lazy and productive with the language.

(3) We need a "local" voice. Just translating news, delivering maybe not all, but at least selected news about the language development and such, so that people who don't actively follow foreign tech news hear about what's going on.

(4) ... and finally. We need to have Perl look "official". Official in a sense that it's not just a handful of hackers doing what they want, when they want, how they want. Hey, I know how OSS goes, so I know that part of that statement will always be true, but the important thing here is how the general audience *perceives* Perl is.

For somebody with minimal technological background, having an official backer takes away a lot of uncertainty and doubt about our beloved tool. 

So then we knew who our target audience was: Corporations and non-hackers of Perl (people who just started, people who only use it because it's part of their job, or people who don't currently use Perl). 

Back in winter of 2008, I don't think I could articulate these messages, but still, I believe the gist of the above goals were there already. So based on that, we started on the next mission: $money


OPERATION IN THE REAL WORLD, AND $MONEY

After going back and forth between doing an NPO and a real company or some other choice, we decided to form a 社団法人 (probably called an "incorporated association" in English, but I'm not sure). I don't know how that translates in other countries, but it's somewhere between a company-for-profit and an NPO. 

An NPO in Japan can, but is limited in ways to, make a profit. Also, an NPO is eligible for some tax breaks but in general requires a LOT more paperwork that must be made public. The reasoning for this is that (1) an NPO is something for the general public, so you need to let everybody know what you're doing, and (2) if you get tax breaks, you're using other taxpayer's money, and therefore you need to let everybody know what you're doing (duh). That's a big blocker for us, because we don't have enough man power. More on that later.

On the other hand with a company-for-profit you're allowed to do what you want. It's a self-governing entity, so you can make your own decisions, do whatever you want --within the general rules of the law. However, a company-for-profit's entire reason to exist is to grow. After all, we just want a better environment for Perl. We're not trying to rule the world or anything. So this wasn't a great choice either.

That lead us to 社団法人. You get to be a self governing entity, you can make a profit, and you don't have to aim to be the next Microsoft or anything.

Having made the choice about the structure of the organization, the rest pretty much came as a no brainer.

JPA asks companies for some subscription fee to be a member. We calculated that, to keep hosting YAPC::Asia and our organization, we needed somewhere along USD 30K ~ USD 50K per year, so we used that estimate to calculate back how much we ask for companies. We currently have about 20 members. Some members pay more than the other, etc. You do the math.

Since our organization isn't a company-for-profit, we don't have the basic stated capital to base our initial activities on. So we decided to create a way to accept funds from our members. If you didn't know, fund in this context is sort of a loan without interest. JPA cannot, unless we somehow royally f*cked it up, keep the money as profit -- we must pay back when we can. So we worked with Mixi (one of the major SNS providers in Japan) to give us a base fund of USD 10K. JPA will not use that money actively, but instead we will keep it as a buffer so that our balance doesn't go negative.

That only covers the initial cost. We need to keep this organization running, so we need to have revenue coming in. Unfortunately for the time being we must rely on subscription fee for the bulk of the costs, but we have... yes, YAPC::Asia Tokyo! YAPC used to be a completely non-profit activity, but we're trying to generate somewhere around USD 5K to USD 10K per yea from it. Don't worry, we're not trying to sell you stuff (we learned our lesson form last year ;). We'll just simply cut costs where we can, and make the event a bit more attractive for non-hard-core Perl people, which should translated to more ticket sales. Anyway, that's a different story. Just saying that we do plan to make some money out of YAPC.

Also, we teach courses and charge people for it. This particular business hasn't really taken off, but then again we can't really keep it up if we had offers to teach courses every week. We all have day jobs. It would be nice if we can do this maybe once per month in the future -- that would give us room to grow.

BTW, when we incorporated JPA in the winter of 2008, I personally put in about USD 10K (of which JPA refunded about USD 5K) for this preparation. This is not to brag about it, but to state the fact that, damn it, you need money to make these sort of things become a reality. Dreams without a financial plan is like a building without a ground to stand on. I hate it, but it's a necessity.

THEN THERE WAS JPA

Anyway, so we planned those things out, I talked to a bunch of Japanese Perl Hackers, got in fights, I talked to more corporations, did a lot of legal munbo-jumbo... and there it was, Dec 26, 2008, JPA was born.

We waited until April 2009 (which is when most Japanese companies start their fiscal year) to "officially" announce the creation of JPA. We had a press conference, and made sure at least some people heard of us. I've been to many PM group meetings north and south of Japan, we held conferences, we started some online papers to market Perl, got involved with legal stuff when we needed to... etc. 

I believe currently we're still in "let our presence be known"-state. So I travel around Japan, telling people that JPA exists, and that Perl dying is a FUD. I believe these efforts are gradually paying off.

As for staff, we have 5 directors. Most daily chores are divided up between these directors. We currently don't have any paid staff. Accounting is managed by my real-life partner (I really want to compensate her for her great job, but I sweet talk to her instead of paying her for now). I don't think we can have a paid staff until our yearly revenue reaches somewhere around USD 100K -- but we still have long way to go.

We have YAPC::Asia Tokyo 2010 coming up this October. Our budget's on track, and we should make a slight profit this year. Oh, last year we had a completely dried out balance and the end of the fiscal year (the funds were safe -- I was really glad we decided to have that fund thing).

TODO, AND PLANS

This blog. I initially started writing about JPA in English to compare with Gabor's ideas, but this turned out to be a massive summary, which I really should write in Japanese as well. Japanese readers, yes it's coming. please hold

I personally hate to be the head honcho, as I'd rather be an unknown who do the dirty deeds behind the scenes, but I believe for the past year and half, JPA has been successful so far. There are a lot more things we should do, but just like any other non-$dayjob activity, I believe that doing what you can now, and gradually keep moving forward is the answer for long term success. 

However, I do believe that an actual fiscal plan,  clear targets, goals, and separation of concerns (i.e. we're not doing anything that TPF would do -- we focus on Japanese corporations and engineers) are required to make an organization like this active and successful.


Hope this story wasn't too boring. Happy Marketing Perl!
    このエントリーをはてなブックマークに追加 mixiチェック


あくまで自分はどうしてるか、って話ですが、最近はCatalystでなんか書くときはこんな感じで使ってます。

my_catalyst_model_setup.png
色んな事がこの図に詰まっているので、箇条書きしてみる:

  • Model::APIがAPIオブジェクトを作成して、使用時にはModel::APIに対して`find()`というメソッドを使って実際のAPIオブジェクトを持ってくる
  • Schema等はMyApp::Schemaに定義し、Model::APIのアトリビュートとして持っている。cacheも同等。これらの初期化引数は設定ファイルのModel::APIから取れるようにしておく
  • Catalyst::Model::DBIC::Schemaは*使ってない*
  • Model::APIではACCEPT_CONTEXTが呼ばれた時点で、もしまだ初期化が行われていなければ、SchemaやAPIの初期化を行っている。Catalyst::Model::Adaptorは*使ってない*
蛇足だけど、Catalyst::Plugin::AuthenticationでStore::DBIx::Classを使うときはどうしてもDBIC::Schema的な形で認証データが入っているモデルを要求されるので、DBIC::Schemaを使いたくなってしまう・・・が、実は単純にその認証データが入っているresultsetが欲しいだけなので、こんな感じの小さいモデルを一個作ってやりすごしている:
    package MyApp::Web::Model::DBIC::Member;
    use Moose;
    use namespace::autoclean;
    BEGIN { extends "Catalyst::Model" }

    has schema => (is => 'rw');

    sub ACCEPT_CONTEXT {
          my ($self, $c) = @_;
          if (! $self->schema) {
              $self->schema( $c->model('API')->schema ); # Model::APIからschemaを盗んでくる
          }
          return $self->schema->resultset('Member');
    }
    __PACKAGE__->meta->make_immutable();
    1;

ちなみに MyApp::CLI::Hogeとかを書くときには、以下のような感じのでやっている:

  • 全部のAPIが必要じゃないことのほうが多いので適時スクリプトの中身によって必要なSchemaやらAPIやらを作成している。(Model::APIに相当するものはない)
  • WithDBICっていうRoleを作って、DBスキーマが必要な場合のSchema生成等のコードを一元化している
  • 引数等はCatalystの設定ファイルを流用するようなことは*してない*。MooseX::Getopt(もしくはMooseX::SimpleConfig)を使ってコマンドラインで --connect_info=dbi:mysql:dbname=hoge と指定できるようにしている
Orochiとか使えたほうがもっと楽な気はするんだけど、とりあえず依存関係を増やすのもあれなのでこんな感じでやっております。
    このエントリーをはてなブックマークに追加 mixiチェック

(This post is posted on my personal blog, since it contains personal rants and what not -- JPA will probably issue some statements after we've talked to the specialists handling these cases)

So since it's probably of general interest, I'm here to let you know that Testuya Kitahara of the Apache Foundation fiasco fame has filed (and has been accepted!!!!) for the trademark of "Perl" in Japan. WTF? Although it's in Japanese, a more detailed history of events can be found around here.

Weee. So some dude is trying to claim that the name "Perl", a language that has been around, used and abused for the past 20+ years, is his to claim. 

(.oO Just to give you some context, this same person is claimed to have been involved with pyramid sales scheme, has attempted to squat on and sell the domain twitter.co.jp to twitter, and is also currently applying to register the words "Ruby" and "Opensource" as his trademark as well )

As the head director of Japan Perl Association (JPA), I'm having to deal with this crud. Sigh. I really don't understand the mind twisting that these kind of people go through to persuade themselves that this is a Good Idea -- and I really hate the fact that both JPA and myself have to spend a significant amount of time and resource on this silly issue.


Anyways, unfortunately the trademark is already accepted. We now have to formally work on repealing it or at least make sure that it does not make working with Perl problematic going forward. So we're going work with specialists, and do the legal dance :/



BTW I've already sent emails to The Perl Foundation's trademark mailing list as well as to friends who I know involved in TPF, but currently no official response has been heard. I wonder what The Perl Foundation is willing to do with all of this. 

I'm not trying to blame TPF or anybody here, but a guidance as to what they want to do with this situation would be most welcome, so here's my public plea :)

Also, this is probably going to cost some amount of money -- probably in the thousands of dollars range. We may have to ask for assistance then. oh well.



Anyway, just a heads up for Perl mongers all over the world, so you know the current situation. Stay tuned.

    このエントリーをはてなブックマークに追加 mixiチェック

このページのトップヘ