D-6 [相変わらず根無し]で“perl”タグの付いているブログ記事

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

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

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

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

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

皆様の参加をお待ちしております。
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すれば全部一緒に消えてくれ、あんまり後で問題になる事がないってもグッド。やー、楽ちん!

さあ、これで新しいPerlをインストールできないから使えないとか言ってられませんよ!是非新しいPerlを使ってみてください。
7/31に久しぶりに岡山に行ってOkayama.pmに参加してきました!
途中から僕の独壇場になってしまったっぽいですが、大丈夫だったかなぁ。

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

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

ちなみに資料は来週アップ予定です。
http://xslate.org/

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

もしご意見等ございましたらお気軽にどうぞ。
以前ここで書いた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
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!

あくまで自分はどうしてるか、って話ですが、最近は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とか使えたほうがもっと楽な気はするんだけど、とりあえず依存関係を増やすのもあれなのでこんな感じでやっております。
(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.

Text::Xslate is one of the latest inventions by Goro Fuji. It's yet another template engine -- and since it is yet another template engine, I could easily see everybody going "oh, *another* one....". Yeah, I get the idea. I feel the same for most new template engines.

But I have to say, this time... this one /is/ worth looking at.

First off, it's blazing fast. For trivial tasks, Text::Xslate (ex-slate) is 10 ~ 20 times faster than Template::Toolkit. Check out some of the benchmark scripts in the benchmark directory.

For example, here's demo-tt.pl ran on my Mac OS X:

daisuke@beefcake p5-Text-Xslate$ perl benchmark/demo-tt.pl 
Perl/5.12.1 darwin-2level
Text::Xslate/0.1030
Template/2.22
Template-Toolkit's process() x 1000
1000
Used: 3.457 sec.
Text::Xslate's render() x 1000
1000
Used: 0.207 sec.
In this benchmark, Xslate is about 16.7 times faster than Template-Tookit.

Now usually that kind of speed comes with a significant hit on the template language's usability. 

For example, templates like Mason or Text::MicroTemplate allows you to write raw Perl -- which /is/ good in some situations, but in my line of work, you need a moderately easy language for designers to work on. You can't quite expect designers to be able to decipher Perl code in the templates.

The really cool thing about Text::Xslate is that now it supports a 80 ~ 90% Template-Toolkit compatible template language, with all the variable methods and what not. I just switched my Template::Toolkit based Catalyst app to Text::Xslate with only minor changes in the template -- it was an easy task. And now the template rendering is blazing fast!

To use Text::Xslate in a sort-of-TT-compatible mode, do the following:
use strict;
use Text::Xslate;

my $xslate = Text::Xslate->new(
    syntax => "TTerse",
    module => [
        'Text::Xslate::Bridge::TT2Like',
        # or 'Text::Xslate::Bridge::TT2'
    ]
);
print $xslate->render_string( <<EOT, { foo=> "bar" } );
The value of foo is '[% foo %]'
Its length is [% foo.length() %]
If I perform s/foo/bar/, it becomes [% foo.replace('foo', 'bar') %]
EOT

The use of "TTerse" enables the use of TT compatible template tokens ('[%', '%]'), and enables TT like syntax like WRAPPER, INCLUDE, FOREACH, WHILE, SET, etc. DEFAULT and PROCESS are not supported, but from what I can tell you can live with it.

If you suspect you might have some performance bottleneck in your template rendering, I suggest you seriously consider this template engine. If you are a Catalyst-head like me, you can use my Catalyst::View::Xslate to easily integrate Text::Xslate and Catalyst.


Happy Xslate-ing!
Web+DB Press vol.56でAnyEvent入門記事を書きました。

いつも使うわけではないだろうけれども、これからはイベント駆動、非同期処理はサーバーサイドのエンジニアには必須なツールの一つになっていくと思います。

AnyEventを使うと簡単に非同期処理が手軽に書けるので、興味のあるかたは是非確認してみてください!
perl-casual #02 でライブコーディングしてきましたよ

伝わったかどうか自信がないけど、あの中で分かって欲しかったのは

  • CPANに色々あります
  • 無限の組み合わせこそがPerlの強み
  • 自分の環境にperldocっていうすげぇツールがついてるからうまく利用!
  • 英語のドキュメントにびびらないで!ほとんどのモジュールはかなり質の高いドキュメントがついているから賢く検索してみて!(関数名、キーワード、"return")

というあたりかな。Perlは怖くないよ!





あとはこちらは半分冗談ですが「IDEなんていらねぇよ。vimで十分」というのも昨日垣間見えたのではないかな!w
*追記有り。

今Markdownで紙用の原稿書いているんだけど・・・

Markdownって楽だけど、実際にフォーマッティングがどんな感じになるかを見るには1回パースして処理する必要があるじゃないですか。いちいちMarkdown.pl呼び出すのも面倒くさいからPlackでディレクトリ直下のファイルをパースしてはき出すようにした。

Plackはなにげにこういう使い方が異常に便利な気がする。まさにSwiss Army Knifeって感じ。
use strict;
use File::Spec;
use Text::MultiMarkdown 'markdown';

sub {
    my $env = shift;

    my $file = File::Spec->catfile(
         File::Spec->curdir, $env->{PATH_INFO});
    open my $fh, '<', $file or die "Failed to open $file: $!";

    [ 
        200, 
        [ "Content-Type" => 'text/html; charset=utf-8' ],
        [
            <<EOM,
<html>
<head>
    <style type="text/css">
    <!--
        body {
            font-family: Halvetica, sans-serif;
            padding-left: 2em;
        }
        pre { padding-left: 2em }
    -->
    </style>
</head>
<body>
EOM
            markdown(do { local $/; <$fh> }), 
            <<EOM,
</body>
</html>
EOM
        ]   
    ]   
};

*追記

全く持ってめんどくせぇけど、PHPのほうがいいじゃないか、ってのはすげぇ違うので一応書いておく。


まず誤解を生まないように書くと、CGI環境で動くようなのもText::MultiMarkdownさえ動く環境ならどこでもすぐできますし、Plackがあろうとなかろうとら昔からこれくらいできますね。

でもCGIだとこれをサーバーにアップロードしないといけないじゃない。俺はローカルディスクに入ってるファイルをちょこちょこ見ながら編集したいだけなんだよね。だから書いたの。というわけでPHPだから云々は話が違う。「RoRだったら・・・」だったら分かるけど。


ついでなのでPlack版も組み替えましたよ。別に汚くてもリリースするわけじゃないし、いずれにせよ5分で実用的なものがかけたからいいと思うけどね。ちなみにpsgiじゃなくてもPlack::App::WrapCGI使えばさっきのCGIファイル使えるよ!(自分もそれでテストしたのに忘れてた)

Test::More 0.94からsubtest()ってのが使えるので、Any::Mooseみたいな根本的な切り替えが入っちゃうコードの検証に、こんなテストが書けることに今日気づいた。

use strict;
use Test::More;

local $ENV{ANY_MOOSE} = 'Moose';

my @files = ;
plan tests => scalar @files + 1;

while (my $file = shift @files) {
    subtest $file => sub { do $file };
}

ok( Any::Moose::moose_is_preferred() );

Data::Localize 0.00013 に入れた。
まだちゃんと使ってないから問題点とかわからないけど、とりあえずStarmanを使うとかで結構スケールできる気がする。そのあたりを自由に変えられるのもPlack++だね。

使用想定としては、これをplackupしておいて、クライアント AnyEvent::HTTPで http_postして非同期で応答待ちする感じ。

package Lyra::Server::Worker;
use Moose;
use Router::Simple;
use JSON::XS;
use namespace::autoclean;

use constant NO_SUCH_WORKER =>
    [ 404, [ "Content-Type" => "application/json" ],
        [ q|{ "status" => 0, "message" => "no such worker" }| ] ];
use constant BAD_PAYLOAD =>
    [ 500, [ "Content-Type" => "application/json" ],
        [ q|{ "status" => 0, "message" => "bad payload" }| ] ];
use constant WORKER_ERROR =>
    [ 500, [ "Content-Type" => "application/json" ],
        [ q|{ "status" => 0, "message" => "worker error" }| ] ];
use constant WORKER_SUCCESS =>
    [ 200, [ "Content-Type" => "application/json" ],
        [ q|{ "status" => 1 }| ] ];

has router => (
    is => 'ro',
    isa => 'Router::Simple',
    default => sub { Router::Simple->new() }
);

sub register {
    my ($self, $path, $worker, $method) = @_;
    $method ||= 'process';
    $self->router->connect( $path, { controller => $worker, action => $method } );
}

sub psgi_app {
    my $self = shift;
    return sub {
        $self->process(@_);
    }
}

sub process {
    my ($self, $env) = @_;

    my $matched = $self->router->match( $env );
    if (! $matched) {
        return NO_SUCH_WORKER;
    }

    my $payload;
    eval {
        $payload = $self->fetch_payload( $env );
    };
    if ($@) {
        warn $@;
        return BAD_PAYLOAD;
    }

    my $response;
    eval {
        my $worker = $matched->{controller};
        my $method = $matched->{action};
        $response = $worker->$method( $payload );
    };
    if ($@) {
        warn $@;
        return WORKER_ERROR;
    }

    if (! $response ) {
        $response = WORKER_SUCCESS;
    }

    return $response;
}

sub fetch_payload {
    my ($self, $env) = @_;

    if ($env->{REQUEST_METHOD} ne 'POST') {
        return;
    }

    my $cl = $env->{ CONTENT_LENGTH };
    my $ct = $env->{ CONTENT_TYPE };

    # $ct should be application/json, but we're not checking this right now

    my $input = $env->{ 'psgi.input' };

    # Just in case if input is read by middleware/apps beforehand
    $input->seek(0, 0);

    my $buffer = '';
    my $spin = 0;
    while ($cl > 0) {
        $input->read(my $chunk, $cl < 8192 ? $cl : 8192);
        my $read = length $chunk;
        $cl -= $read;
        $buffer .= $chunk;
        if ($read == 0 && $spin++ > 2000) {
            Carp::croak "Bad Content-Length: maybe client disconnect? ($cl bytes remaining)";
        }
    }

    decode_json $buffer;
}

__PACKAGE__->meta->make_immutable();

1;
JPAでやんなきゃいけない事、手伝って欲しいことがいっぱいあるのでとりあえずメモ

  • 2010年度マニフェスト的なものを書かないといけないかなぁ。
  • JPAの日常業務をボランティアで手伝ってもらえる人を募集したい。メールへの反応、YAPC準備、研修準備。毎回お願いしてると結局俺がボトルネックになるので、ボランティアチームがいたらすごい嬉しい。特にYAPCは俺は前線に*出てはいけない*。YAPCで目立ちたい!人が前線に出るべき。LLにならってブレストをやったら人来るかな?
  • JPA PressがGoogle 検索に表れないので、どうにかする必要あり。この間のヤツからAdobeなんちゃらに文字データを入れ込んだはずなんだけど、検索にでてこないんだよな。どうすればいいの?
  • TPF本家ともっと緊密に行動する。特にGrantは使えると思う。ドキュメンテーション回りを特にどうにかしたい。

こっから下はまだ希望の段階。

  • 希望: どこかの大学・専門学校でPerlを教えているところと連携。ツテがあまりありませんので、連絡してもらえると嬉しい。
  • 希望: 全然声を掛けてないけど、なんらかの枠でDBI/Devel::NYTProfの作者のTim Bunce氏をYAPCに呼べたらなーと思ってるんだけど、ぶっちゃけ予算がありません。旅費をスポンサーしてもいいという企業・個人の方はいませんか。
  • 希望: 北海道・東北エリアでの Perl Mongerグループの創設。MLとかそういうのは別口でやってもらうにしても、PMのページをJPAで持つのはありかなぁ。
  • 希望: JPA会員だよ!ってな感じのブログパーツを作成したい。これもぶっちゃけ今は予算がないので、できればボランティアでお願いしたい。
まだこう、細かいpros/consがわからないのでなんとも言えないんだけど、とりあえずaio_open/aio_writeと普通のopen/print/closeで同じ事した場合とでベンチマークとか取ってみた。これでいいのかなー

環境はMac OS X 10.5.8, 2.4 GHz Intel Core 2 Duo, 4GM RAM.

Comparing with buffer size 10...
         Rate normal    aio
normal 80.0/s     --   -19%
aio    99.0/s    24%     --
Comparing with buffer size 100...
         Rate normal    aio
normal 80.0/s     --   -18%
aio    97.1/s    21%     --
Comparing with buffer size 1000...
         Rate normal    aio
normal 76.9/s     --   -13%
aio    88.5/s    15%     --
Comparing with buffer size 10000...
         Rate normal    aio
normal 52.4/s     --   -27%
aio    71.9/s    37%     --
Comparing with buffer size 100000...
         Rate normal    aio
normal 15.9/s     --   -63%
aio    42.7/s   169%     --

コードはこちら、githubで。
前のエントリ書いたら宮川さんにオススメ方法を教えてもらった

http://twitter.com/miyagawa/status/10271128928
plackup \
      -L Shotgun
      -MPlack::App::WrapCGI
      -e 'Plack::App::WrapCGI->new(script => "/path/to/cgiscript.cgi")'

だそうです!これなら全ての要件を満たせるもよう!そしてapp.psgiを作る必要ありません。

今これやってみて一つだけ問題点。多分POD入りのCGIは動かない。それと、__DATA__は見てくれるけど、__END__があると動かない。__END__さっき簡単なパッチのpull requestを送っておきました!

なお、-L Shotgunを使うと CGIファイルの中身は実行時までコンパイルされないのでご注意(つまり、plackup -rもいらないってことですね)
ちょっと案件で簡単なCGIスクリプトをを開発することになった。StarmanとかTwiggyでほとんど全て書いているのに今更CGIとかそういう突っ込みはとりあえずおいておいて・・・環境を作るのも面倒くさいし、簡単にサーバーを立ち上げたり落としたりしたい。

そこでPlackです!

追記:その後もらったコメントによると、こちらのほうがよさそうです







具体的にはplackupとCGI::Emulate::PSGIを使います。

何も考えずにとりあえず開発したいので、以下のようにapp.psgiにコードを書いていく。
use strict;
use CGI::Emulate::PSGI;
return CGI::Emulate::PSGI->handler(sub {
    CGI::initialize_globals(); # 重要
# ここから
#!/usr/bin/env perl
use strict;
use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser);

sub main {
    my $q = CGI->new();
    ....
}

main();

# ここまでがCGIのコード
});
あとで本当に単純にコピペしたいのでshebangもそのまま書いちゃうし、インデントもその部分だけファイルに書いたかのように記述していっちゃいます。

これを以下のように-rオプションをつけて起動。app.psgiのCGIコードに変更を加える度にサーバーも再起動して新しいコードを適用してくれる。
plackup -r -a app.psgi


これはいいわー。
(はいはい、まず言語論争するつもりないから、そういうコメント禁止ね)

1. 論旨

新たな就職氷河期到来が叫ばれる昨今、就職に困っている学生さんもたくさんおろう。「技術があればチャンスはあるかも」って思う人もたくさんおろう。その際にプログラミングでもいっちょやってみっか、って思う人もたくさんおろう。

そこでとりあえず言語を選ぶよね。「就職を有利にするため」という条件をクリアするためにあなたなら何を選ぶ?Java? C#? Ruby? PHP? (ちなみに自分は大学を卒業した時点で一番得意だったのはJavaですよ)

色々選択肢はあるのは当たり前なので他の言語も排除はしないけど、僕としては自分の今持っている情報と経験から、Perlは覚えておいて損は全くない!と言い切れる。それは至極単純な理由で、需要より供給が圧倒的に少ないから。

就職情報サイトで一番多く当たる言語とかはもちろんそれだけ需要があるという事でもあるのは当然だけれども、それはつまり供給もほうも当然多いのです。競争も激しいし、当然ながら1人当たりの価値は下がります(この場合の価値というのは給料という意味ではなく、採用する企業側からみたあなたの価値.... 採用するときにどれだけハングリーな目で見てもらえるか、です)

JPAの会員になってくれてる企業のうちアクティブに採用を検討している会社は(採用基準も高いところも多いけれど)、それはそれは切実にPerlエンジニアをほしがってます。今から少しでも多くPerlの知識を身につけておけば、競争が比較的少ない分野で輝くことができる可能性が高いわけですよ。

あなたのゴールはなんですか?クールな言語を覚えること?流行物やバズワードに乗ること?それとも・・・仕事を見つけること?重要なのは採用してもらい、社会に出て、そこで経験を積むことなはず。

株式相場でもそうだけれども、勝ちたいなら基本的には逆張りしないといけないですよ。そういう意味で全く持って Perlは良いオプションだと思います。

ちなみに名の通ってるPerlを使用している企業の中ではWeb系の企業が多いのでPerlを選ぶ=Web系のキャリアに就くと思っている人もいるかもしれないが、Perlはもともと面倒なシステム管理タスクなどを簡単に包括的に行うためのツールとして作られているので、Web系に限らずその後もっと広いシステム領域に自分の仕事を広げていくつもりだったとしてPerlという言語は悪いチョイスではないと思います。

2. ただし書き

・・・とここまで書いておいてなんだけど、だからと言ってPerl勉強すればいいってもんじゃない。色々なただし書きがついてきます。

ここからはまとまりにくいのだけど、とりあえず羅列してみる:

  • 今回僕はPerlを便利なツールで、覚えておいて損はない、そして今ならある程度のPerl力があれば就職できる可能性が高いよ!って言いたいだけ。
  • Perlを覚える事にしたとしても、それだけでは何にもならない。優秀なプログラマーは第1言語以外のどの言語を与えても資料さえあればそれなりのものが作れる。重要なのは言語の仕様ではなくて、プログラムを書く時の考え方だから。だから他の言語も一緒に覚えればいいと思う。
  • Perlに関してはバッシングが結構ひどいけど、少なくとも「言語仕様」を叩く人の言うことは8割ほど引いて聞いておけ。他人のコードを読めないとのたまう人は自分が得意とする言語・環境以外のデバッグができないだけだし、まずいコードを書く人はどの言語で書かせてもまずいコードを書く。

3. ということで

今はPerlは戦略的に就活を行うなら、早い内から手に入れておいて役に立つスキルのひとつだと思う。ただしYMMV

4. ついでに

あくまで個人的な意見ですが、今から新たにPerlの何かを覚える目標としてはPlack/PSGIともっと進んだところでは非同期プログラミングはいい題材だと思います。技術的なレベルはちょっと高いけど、その辺りを目指すと外れはないかな〜。まぁこれは本当に個人的意見なので初心者には辛いかもしれません。

あとこれからPerlを覚えようって方には(もっと他にもあるだろうけど)とりあえずPerl関連書籍Perl関連資料のページも作りましたよ。

あと、Perlメインでなくてもいいので、Perlで開発を受注できる!という会社の方は是非こちらに書き込んでください!数が揃えばJPAサイトのほうに移します。
松濤であった爆発の件について、そういえばtwitter使ってたなーと思って探したら全然見あたらない。なにせ4年近く前のことなのでそもそもそこにたどり着くラインがないのだ。

なので、一個だけブクマされてたtweetから超ローテクな方法で残りのヤツを捜した。その方法とは・・・一個ずつIDを増やして、HTTP GETが通ったらOKというだけ。一旦書いてから走らせてそのまま放置して一晩おいておいたら大体とれてたくさい。

というわけで、松濤温泉施設爆発時の俺のtweet → ここ


use strict;
use LWP::UserAgent;

my $base = 110764722;
my $ua = LWP::UserAgent->new();
my $i = $base;
while(1) {
    my $res = $ua->head("http://twitter.com/lestrrat/statuses/$i");
    if ($res->is_success) {
        print "http://twitter.com/lestrrat/statuses/$i\n";
    }
    $i++;
}
やー、ローテクですね。あまりオススメしませんw
Perlを使うことのできる中小企業(地方企業含む)って結構いると思うんだけど、そういうのをとりまとめたリストを作って公表するとか、もしくは弊社(endeworks)と取引のあるところと今後の仕事の際にそういう会社に仕事を流して関係を築いていくとか、そういうのって需要あるのかなぁ?

発注したい側、受注したい側、両方とも意見が聞けると嬉しい。

追記1: 見切り発車でこんなwikiページも作ってみました。よろしかったら書き込んでください!

追記2: リンク先間違ってました!直しました!
「だからどうだ」ってことですけど、(perlだとreverseあるので本来なら1オペで終わっちゃうけど)とりあえずループを使わずに与えた配列を逆にするコードの自己最短コードはこうなりました:

sub r{@_?(pop,r(@_)):()};
もう少し読みやすく展開するとこんな感じですね。ただの再帰処理です:

sub myreverse {
   if (@_ > 0) {
      return (pop @_, myreverse(@_));
   } else {
      return ();
   }
}
popで配列の最後の要素を最初に回して、残りを再帰処理してるだけー
  • 注1:まだ本番にはデプロイしてませんが、確認テストでは使いました(単位テストでは使わない。あくまで本番さながらの形で動いているのを確認したい時だけ)。
  • 注2:以下スクリプトは開発者の労力を減らすためのスクリプトで、万全なデプロイ方法だとか言うわけではありません。
  • 注3:正直シェルスクリプトは素人です。

  • 追記:envdirはどうか、と言われた件。最初それを思い出せなくてcat ENVなんてしてたんだけど、設定値をもらって後からフックしたいところがあるからenvdirではそれを実現できない・・・気がする。ので、今回は以下で。
  • hirose31 さんが美しい スクリプトを提供してくれたので、それをベースにしてgithubにあげておけました。最新版はここです

ここ最近のPlackだとかStarmanとかを使ったWebアプリケーションのバンドル・デプロイについてちょっと固まりつつあるので、書いてみる

まず アプリケーションと、その依存関係。デプロイ側のサーバーにはlocal::libと必要なModule::Install系のモジュール、それにModule::Install::Bundle::LocalLibがインストールされている前提です。アプリケーションの依存関係は全部Makefile.PLに書きます。

use inc::Module::Install;

name 'MyApp';

..... # 必要な事色々 ....

# Plack stuff
requires 'Plack' => '0.9910'; # or 1.00 when the time comes
requires 'Starman';
requires 'Server::Starter';
requires 'Net::Server::SS::PreFork';

bundle_local_lib;

WriteAll;

こんな風にしておく。で、デプロイする際には
make bundle_local_lib
する。するとextlibというディレクトリに依存関係が全部入ります。このextlibは一旦安定稼働するのを確認したら、依存関係のアップグレード時には一旦別のところに待避させるとかコピーするとかしてからアップグレードを行ってみるという事ができる。万が一動かない場合はすぐ元のextlibに戻せば良いってわけだ。

ってなわけで、Plackを含めた全ての依存関係をextlib以下にある。

で、これをdaemontoolsで動かすのでrunファイルが欲しい。作る。今までdaemontoolsで運用してて開発→デプロイで面倒くさいなーと思ってたのがこのrunファイルの細かい設定を変えていくことなんだけど、今回はこれを分離してみた:
#!/bin/sh

# ENVというファイルが このファイルと同じディレクトリか
# pwdにあれば、それを読み込んで、そこから環境変数を広う。
# 例えば後で出てくる PSGI_FILEの場所を明示的に指定したければ
# PSGI_FILE=/path/to/app.psgi とかENVファイルに書いておく。
if [ -f `dirname $0`/ENV ]; then
    ENV_FILE=`dirname $0`/ENV
    export `cat $ENV_FILE`
elif [ -f ENV ]; then
    export `cat ENV`
fi

if [ -z $APP_HOME ]; then
    APP_HOME=`pwd`;
fi
export APP_HOME
if [ -z $CATALYST_HOME ]; then
    CATALYST_HOME=$APP_HOME
    export CATALYST_HOME
fi

if [ -z $CATALYST_CONFIG ]; then
    if [ -f "catalyst.yaml" ]; then
        CATALYST_CONFIG=catalyst.yaml
        export CATALYST_CONFIG
    fi
fi

if [ ! -z $DEBUG ]; then
    DBIC_TRACE=2
    export DBIC_TRACE
fi

if [ -z $PLACK_SERVER ]; then
    PLACK_SERVER=Starman
fi

if [ -z $PSGI_FILE ]; then
    PSGI_FILE=app.psgi
fi

if [ -z $PORT ]; then
    PORT=5000
fi

if [ -z $USER ]; then
    USER=www
fi

if [ -z $PERL ]; then
    PERL=`which perl`
fi

EXTLIB=$APP_HOME/extlib

# start_serverを通して plackupを実行するのだが、こいつらはextlibの中に
# あるので、それを指定して実行する(local::lib設定もつけないと依存関係が
# みつからない状態になってしまう)
exec setuidgid $USER \
    $PERL -Mlocal::lib=$EXTLIB \
    $EXTLIB/bin/start_server --port $PORT -- \
    $PERL -Mlocal::lib=$EXTLIB \
    $EXTLIB/bin/plackup -s $PLACK_SERVER -a $PSGI_FILE -p $PORT 2>&1

こんな感じ。これでENVの中身を変えてsvc -tするとその設定が反映される、と。

自分の場合はPSGIファイルの中にももう少し仕掛けを入れていく必要があったので、ENVから可変な情報をもらっていくようにした:
use strict;
use lib "$ENV{CATALYST_HOME}/lib"; # パスを通す
use local::lib "$ENV{CATALYST_HOME}/extlib"; # ついでにlocal::libのパスも通す

use MyApp;
use Plack::Builder;

MyApp->setup_engine('PSGI');

builder {
    # リバースプロキシ使う場合はenableしちゃう
    if ($ENV{USE_PROXY}) {
        enable "Plack::Middleware::ReverseProxy";
    }
    return sub { MyApp->run(@_) };
};
これをレポジトリに入れておき、デプロイするときにはdaemontoolsあたりからsymlinkしておいて動かす。開発時にもrunファイルを実行すれば本番さながらの感じでサーバーが立ち上がる(ただし、実際にはテストは全然違う方法で動かしてる)

誰かの役に立つなら幸い。なんか突っ込みがあればお願いします。
forでは$.はカウントされない

ちょっと違うので横から。

これはforの動作というより、式の評価コンテキストの問題。

forはその性質上、操作対象は配列になる:
for (<>) {
     ....
}

# 上記はこれと同等
my @lines = <>;
for(@lines) {
    ....
}
なので、<>も配列として評価されるべく*まず最初に*<>を最後まで読み切って、行区切りの配列を作ってからforに渡す。このときファイルハンドルを読み切っているので、当然 $.はファイル全体の行数になる。

これに対して、whiileは対象を毎ループ評価するので、1行ずつの読み込みになる。だから$.は行毎の値になる。B::Deparseを使うとちょっとわかるかも。
# こっちはちょっと正直わかりにくいとは思うけど・・・
daisuke@beefcake $ perl -MO=Deparse -e 'print $. . $_ for <>'
;
print $. . $_ foreach (<ARGV>);
-e syntax OK

# こちらでは $_ = という操作になっているので、1行毎読み込んでるのが
# はっきりわかる。
daisuke@beefcake $ perl -MO=Deparse -e 'print $. . $_ while <>'
print $. . $_ while defined($_ = <ARGV>);
-e syntax OK
というわけで、forが違うというよりはファイルハンドルをリストコンテキストで読み込んだ場合とスカラーコンテキストで読み込んだ場合の動作が違う、っていうのが正解。
ちょっとずれてるので横からコメント

unshift、push で変更を加えた @INC(モジュール検索PATH)が使用されない
起こってる現象とこの認識はちょっと違う。

具体的に何が起こっているか説明する前に、Perlにはコンパイル(BEGIN) フェーズとランタイムフェーズという、2回コードが実行されるタイミングがある(まぁ厳密にはもう少し色々フックすることが可能だけど、とりあえず一般的にはその二つで大丈夫)。

これがどういうことかというと、順番にコードを書いているつもりでもコンパイルフェーズで評価される部分とランタイムフェーズで評価される部分があるということ。わかりやすい例としてはこんな感じか:

#!/usr/bin/perl
use strict;
print "Runtime!\n";

BEGIN {
    print "Compile time!\n";
}

BEGINで囲まれた部分はコンパイルフェーズで実行され、BEGINの前にあるprint文より前に実行されるはず。

で、一般的にモジュールを読み込む"use"宣言はどうかというと、これはコンパイルフェーズで実行される。"use"宣言というのは実は以下の構文と同等なのだ:
# use MyClass;
BEGIN {
    require MyClass;
    MyClass->import;
}

つまり、use宣言で扱うクラス名が発見されるためには、コンパイルフェーズ時に@INCが変更されてなくてはならない。mod_perlだろうとなんだろうとこれは一緒なので、mod_perlじゃなくても以下のコードではMyClassは見つからない:
unshift @INC, "/path/to/MyClass";
use MyClass;
つまり、"/path/to/MyClass"をuse宣言に認識させるためには以下のようにする必要がある:
BEGIN {
    unshift @INC, "/path/to/MyClass";
}
use MyClass;
これなら、どちらもコンパイルフェーズで動き、あとは読み込まれた順番に処理されるから問題無し!

まぁでもmod_perlを使うのなら、なにか制限がない限りstartup.plで@INCを普通にunshift/pushで変更しておくのが一番いいと思う。
検索エンジンの皆様こんにちは。2010年以降のPerlウェブサーバーといえばPlack/PSGIですね。そんなSEOな記事です。

私がPerlでWebアプリ的な物を初めて書いたのは1999~2000年にかけての事なのでそれ以来ほぼ10年たっているわけです。その間に色々さわりました。ウェブサーバーとアプリをつなぐアダプターのレイヤーとしてはCGI.pm, mod_perl, FastCGIなどを使ってきました。フレームワークもMason, Sledge, Catalyst, AxKitなんかをさわってきました。

どれもこれもいいところもあり悪いところもあり、それぞれコメントしたい部分はあるのですが、とにかくデプロイ方式が全然違うのでどうにもできん、という問題がずっとありました。

しかし PSGIというPerlウェブアプリケーション用の規格ができたことにより、これは全く変わってきました。PSGI規格に準じていれば、フレームワークがなんだろうとサーバーがなんだろうと同じ方式でがっちゃんこできるのです。これはいままで「Perlをレンタルサーバーでデプロイできねぇよ」とか言ってた層には素晴らしい事で、ホスティングプロバイダのほうがそのPSGI形式さえサポートしてくれれば、がんがんデプロイできちゃったりするんですね。

これは是非みなさん、まわりのPerlの事をちょっと遠くから見ていた人たちにも是非教えてあげて下さいね!

日本語での記事が足りない気がしたので書いてみました。
dve_deprecated.png

I'll support it for a while should there be any problems , but in general, please consider using Data::Recursive::Encode instead.

Data::Visitor::Encodeの開発を停止します。もうしばらくの間は問題等あればサポートしますが、基本的にはData::Recursive::Encodeを使って下さい。
企画が出ただけの段階なので、まだなんとも言えないのですが、2010年5月〜7月くらいに東京以外の某場所でYet another Perl UNconference (YPUN)を開催しようかという話が出ました。

アンカンファレンスに関してはこちらを参照

まだこれから諸々調整があるのでぼかした言い方ばかりで申し訳ないのですが、一応考えているのはこんな感じ:

  • 某地方都市で泊まりがけ
  • キーノートだけこちらで設定
  • 地元企業もからめてBOF
  • あとはアンカンファレンスで話したい内容を思い思いに
  • Perlとはあるけど、ゆる〜〜〜〜〜い縛りで
  • なるたけうまく方々と調整して参加者は基本的に交通費だけで済ませることができるようにしたい!
  • 今候補に挙がっている場所は某中部地方エリア
  • うまくいくようなら定期的に地方都市で巡回開催したい!
ちなみに直接の発案者は僕じゃないので、この企画が本当に通るかどうかは Emersonさんに頼るところが大きいです!メアドをさらすわけにはいかないので・・・twitter で@okaihatuしちゃえ!w


例えば、AnyEvent::DBIで Q4Mを使って、*常に*なんらかのイベントをqueue_wait()する状態にしたいとする。するとexec()が終わったらまた同じexec()を呼ぶ事になるので、例えばこんなコードを書くかもしれない。

    use strict;
    use AnyEvent::DBI;

    my $sql = "SELECT ....  FROM queue_table WHERE queue_wait('queue_table', 10)";
    my $dispatch;
    $dispatch = sub {
         $dbh->exec( $sql, sub {
              # 返ってきた値でなんかする
              $dispatch->();
         });
    };

    $dispatch->();

    # まぁ他の事してるプログラムで使うだろうから、本来はいらないけど・・・
    AE::cv->recv;
でもこれだと再帰的に exec()を呼んで、さらにまた$dispatchからexecを呼んで・・・って事になってしまうよね。これを無くしたい。検証のためにもっとも単純なクロージャを使った再帰呼び出しをまず書いてみる。ここではスタックの深さをついでに表示しておく:
use strict;
 
my $dispatch; $dispatch = sub {
    my $i = 0;
    while(1) {
        my @caller = caller($i++);
        # noop
        last if ! scalar @caller;
    }
    print "$i\n";
    $dispatch->();
};

$dispatch->();
このコードを実行すると、エラーを起こすまで、少しずつ表示される数が大きくなっていく。これは再帰的に関数を呼び出し続けている数なわけで、どこかの時点でdeep recursion云々言われる。当たり前だよね。

さて、ではこれをAnyEventを使ってる場合はどうするか。まずひとつ簡単な方法としては idleウォッチャーを設定する事ができる:
use strict;
use AnyEvent;

my $idle;
my $dispatch; $dispatch = sub {
    my $i = 0;
    while(1) {
        my @caller = caller($i++);
        # noop
        last if ! scalar @caller;
    }
    print "$i\n";
    $idle = AE::idle $dispatch;

};

$idle = AE::idle $dispatch;

AE::cv->recv;
このコードを実行した場合、自分の環境では"6"が繰り返し表示されるだけ。idle()はイベントループが「次にビジーでない状態」に与えられた関数を実行するウォッチャーなので、続々と実行してくれるわけです。

「次にビジーでない状態」という曖昧な状態ではなく、確実にすぐ実行したい場合はタイマーを使ってもよいかも。この場合最初の引数に0を渡しておいてやれば、とにかく次の機会にすぐクロージャを実行してくれる。この場合も自分の環境では"6"が繰り返し表示される:
use strict;
use AnyEvent;

my $count = 1;
my $w;
my $dispatch; $dispatch = sub {
    my $i = 0;
    while(1) {
        my @caller = caller($i++);
        # noop
        last if ! scalar @caller;
    }
    print "$i\n";
    $w = AE::timer 0, 0, $dispatch;
};

$w = AE::timer 0, 0, $dispatch;

AE::cv->recv;
というわけで、直接Perlの関数を呼び出すのではなく、イベントループに一旦預ける事によって再帰的呼び出しを続けるのを減らすことができるのです。
なんとなくLDRでtwitterのPerlに関する発言を検索した結果を購読してたんだけど、面倒くさくなってきたので、@perlismに投稿するボットを作った。単純にPerl関連の発言を持ってくるボットです。とりあえず日本語限定。よかったらフォローどうぞ。
最近、miyagawaさんが書いたこちらののツールを多少改変したものを使ってlocal::lib環境にCatalystアプリの全依存関係を突っ込んでからデプロイ、ということをしていました。

が、このツール自体をコピペするのに疲れました。なのでApp::BundleDepsModule::Install::Bundle::LocalLibというものを作りましたよ!M::I::Bundle::LocalLibのほうはMakefile.PLに1行だけ記述を入れておくとmake bundle_local_libと書くだけで./extlibにlocal::lib環境を作ってくれるというものです。

俺的には超便利!

想定している使い方はApp::BundleDepsのPOD(cpan github)を見てもらうのが一番よいかもしれません。

これを使うとCatalystアプリのバージョン違いなんかのデプロイを比較的気軽に行う事ができるようになるので、是非さわってみて感想なんかを教えていただけると幸いです。

あと、自分ではそんなに使わないんだけど折角Platypus対応コードをmiyagawaさんが書いてたので、それも同様にあげといたです

追記: Platypusのほうは make platypusってできるように0.00002で変更しておきました
某所での会話:
13:14 <#perl6> t****: y'know, one thing that got me into Ruby was that the CGI library would auto-detect that I was testing it in a browser and take arguments as if I was working in a browser. Made troubleshooting a breeze... 13:14 <#perl6> t****: I mean, testing from the command line... 13:15 <#perl6> t****: I always wished the perl CGI module would do that... 13:16 <#perl6> P*******: eh? 13:17 <#perl6> t****: When you write a ruby cgi program, and run it from the command line, it detects that you are in a command line and allows you to input stuff that it would normally get from the URL string. 13:17 <#perl6> t****: Maybe there is a way to do this with the perl cgi module, but I never learned how. 13:17 <#perl6> P*******: perl's CGI module has always done that. 13:18 <#perl6> P*******: you simply pass name=val on the command line 13:18 <#perl6> P*******: (or you could set some environment variables) 13:18 <#perl6> t****: hm... Wish I had known that then...
ええええええええええ。この機能ってすごい有名だと思ってた。
普通にCGI.pmのスクリプトはコマンドラインから実行できるお>< デバッグとかにはとても便利です!
#!/usr/bin/perl use strict; use CGI; my $q = CGI->new(); print $q->header(-content_type => 'text/plain', -charset => 'utf-8'), "Hello, ", $q->param('name') || "NoName", "!\n"
こんなスクリプトを、以下のように実行すると、"Hello, lestrrat!"と本文に表示されます
% perl test.cgi name=lestrrat Content-Type: text/plain; charset=utf-8 Hello, lestrrat!
簡単!・・・まぁ、最近は生CGIでなんか書くってのもなくなってきたけどね
スクリプトからサブルーチンだけをインポートする
Script::Sub::Importというモジュールを作成しました
http://d.hatena.ne.jp/perlcodesample/20091130/1258979624

これは大変いただけない。perlcodesampleというIDがさらに初心者に信用させがちだからさらにいただけない。なので苦言を呈させてもらう。

命題はスクリプト内で使っている関数のテストをしたい、なんだよね。
なら、いくつかこんなひどいハックじゃないやり方がある。

まず第一の、そして一番まともな方法としては「モジュール化」を考えるべき。元のスクリプトがこんな感じだとする:
#!/usr/bin/perl use strict; main(); sub main { # pseudocode... foo(@ARGV); bar(); } sub foo { ... } sub bar { ... }
だったら、こんなlib/MyApp.pmを作って、そこにこんなコードを書いておけばいい:
package MyApp; use strict; sub foo { ... } sub bar { ... }
で、以下のようにスクリプト内で呼び出せばいい。
#!/usr/bin/perl use strict; use MyApp; main(); sub main { MyApp::foo(@ARGV); MyApp::bar(); }
これならテストはそれこそ普通のモジュールのテストを書くときのようにすればいい。
でもモジュール化したくない時というのもわかる。例えばlibディレクトリを使わずにスクリプト単体で完結させたい、なんて状況も考えられる。なのでそれは理解できるのだが、それなら以下のように caller()を確認すればメイン関数は走らないので、スクリプトの実行はせずに関数群をrequireできるようになる。
#!/usr/bin/perl use strict; main() unless caller(); # perl script.plで呼び出された時は空配列が返るのでmain()実行 # テストスクリプトやその他の場所から呼び出された場合は # なんらかの値が返るので、実行されない sub main { ... } sub foo { ... } sub bar { ... }
これなら、普通にテストスクリプトでrequireなりなんなりして、スクリプト内の関数をぼこっと使える:
# テストファイル require 'script.pl'; ok( foo( ... ) ); is( bar(...) );
caller()の手法の代わりに$ENV{TEST}みたいなフラグを使うのもありだろう。

追記:そういえば書こうとして忘れてた。

@tokuhirom: .pl にはほとんど何も書かないのが最近のトレンドなんだけどな。

そうそう。ということで、こういうのでもいいわけです:
package App::MyApp; use Moose; # or Mouse, or Class::Accessor::Fast, whatever sub run { my $self = shift; $self->foo(@args); $self->bar(); } sub foo { ... } sub bar { ... } # in script.pl use strict; use App::MyApp; App::MyApp->new()->run(@ARGV); # MooseX::Getoptならもっと簡単
追記終了。

いずれにせよ、いきなりCPANに登録する前に#shibya.pmで聞くとか、ブログでまずつぶやくとかしてみて、一旦回りの評価を聞いてからやってもよかったのではないか。

初心者に道を示すなら特にその辺りには気を遣って欲しいと思う。個人的にはCPANからも削除したほうがいいと思うな。
perl 5.8.8 で GuardScope::Guardを比較してみた。関数が終了したタイミングでコールバックが走るようにした状態で比較。Guardはスコープ終了時に走るscope_guard()とダイナミックガードであるguard()両方で試してみた。
use strict; use Benchmark qw(cmpthese); use Scope::Guard; use Guard; cmpthese(500_000, { scope_guard => sub { my $guard = Scope::Guard->new(sub { 1 }); }, guard_dynamic => sub { my $guard = guard { 1 }; }, guard => sub { scope_guard { 1 }; } });
結果はGuard.pmの*圧勝*
Rate scope_guard guard_dynamic guard scope_guard 204918/s -- -61% -71% guard_dynamic 531915/s 160% -- -24% guard 704225/s 244% 32% --
mlehman恐るべし。

DBIx::Classもこれ使えばいいのに。 Scope::Guard使ってなかった><
思い立ったので適当に書いてみる
Perlのシグナルハンドラは%SIGグローバル変数にシグナル毎のハンドラを設定することで実現できる。例えばCtrl-CはSIGINTを発生させるので、INT用のハンドラを設定する:
$SIG{ INT } = sub { warn "got SIGINT!" };
%SIGに限った事ではないのだけれども、ここでまず注意しなくてはいけないのが、%SIGはグローバル変数であること。ある関数foo()内で%SIGをいじったあとにreturnすると、foo()を呼び出したスコープでもその値は有効になってしまう。これだと自分はよくても他のモジュール等に影響を与える可能性がある。そこでまず癖としてlocal修飾子を使う癖をつけてほしい
local $SIG{INT} = sub { warn "got SIGINT (OUTSIDE)" }; sub foo { warn "inside foo()"; local $SIG{INT} = sub { warn "got SIGINT (INSIDE foo)" }; sleep 10; } warn "sleeping..."; sleep 10; foo(); warn "outside foo()"; sleep 10;
こんなコードを書いて、foo()を呼ぶ前、foo()の中、foo()を呼んだ後、と3回Ctrl-Cを押すとハンドラの効果がfoo()内で局所化されているのがわかると思う(ちなみにsleep中にCtrl-Cするとsleepが邪魔されるので10秒待つことはなくなる)

ちなみに、この局所化はfork()した時にも是非同じ処理をするようにする癖をつけたほうがいい。fork()するような場合では親プロセスでSIGCHLD用の処理を入れてたりするのだが、それをうっかりそのまま継承するとopen("|-")系のコマンドをうっかり使ってしまった時などに問題が起こることがある。ちなみに、この場合は%SIG全体を局所化すればいいだろう:
my $pid = fork(); if (! defined $pid) { die "Could not fork: $!"; } if ( $pid ) { # 親プロセス } else { local %SIG; # これだけでOK ..... exit 1; }
シグナルはどの言語でもそうだけれども、普通のプログラム処理の流れをぶったぎって処理が入るものなので(厳密にはPerlの1OP毎にシグナルのチェックがはいるので、OP中にシグナルが入ることは基本的にはない)、とにかくシグナルハンドラ内で時間を食う処理をしない、に限る。

基本はフラグをたてるだけでさっとハンドラを終わらせるのが吉。例えばCtrl-Cした時に メインループを終わらせるなら、こんな感じ:
my $loop = 1; local $SIG{INT} = sub { $loop = 0; }; while ( $loop ) { .... }
自分がやりたいタイミングより1回余計にループが走るかもしれないけれど、逆にそこは基本の動作に任せてきれいにループを終えるほうが後々頭痛の種を作らない。さらに確実に停止したい動作に関してもうまくフラグをチェックしたりなんだりでやる方法が大概ある。

それでもやっぱりある程度の処理をシグナルハンドラ内でしなければいけない場合は、まずまっさきにハンドラ内でシグナルを無視するようにしたほうが良い:
local $SIG{ INT } = sub { local $SIG{ INT } = 'IGNORE'; # このハンドラ内ではSIGINTは無視 .... }

こうしないとSIGINTで受け付けた処理中にもう一度同じ処理が入ってしまうような妙な状態になってしまうことがる。

ただし、この方法はひとつだけ危険な問題があって、もし万が一ハンドラ内でブロックするような処理をしてしまった場合にはもうシグナルを無視しているのでCtrl-C等が効かなくなる。そういう意味でもやはりシグナルハンドラからは最小限の処理をしてとっととコントロールを元の状態に戻すのがおすすめ。開発当初はIGNOREをつけるのはおすすめしない。俺は何回もそれで失敗してる

ちなみに綺麗に全ての子プロセスを待つためのSIGCHLDなんかは結構面倒くさい。AnyEvent->child() ウォッチャーはその辺りがとても美しいので、便利ですね。ただしfork()の直後に子プロセスが失敗すると、運が悪いとハンドラー設定の前にSIGCHLDが発生してしまうことがあるらしいので注意。
今のところJPA的な活動で個人的に一番手応えを感じているのが地方行脚活動なんだけど、関西系と九州系以外でPerlな人たちっていないのかしら。なんか東北とか、北海道はpmグループが存在しないくさいんだけど・・・

もしいるなら少人数でも行きたいのでご連絡をっ。

ちなみに近いうちにまた関西にでもいくかなぁ。特に最新のトピック、を話すより現実的な悩み相談レベルの話をさせてもらえる機会が欲しいと思っております。なにかあったら声をかけてください!
# 追記: 2010 19 Feb - このコード何回も書くの面倒くさくなったので、
# CPANにモジュールアップロードしてしまいました
# http://search.cpan.org/dist/AnyEvent-FIFO/

まぁ考えてみれば単純な話ですけど、いわゆるGuardを使うとよいです。参考はAnyEvent::HTTP

コールバック$cbの最初の引数をguard変数にしておき、キューの1スロットを使用している間はこのguard変数をundefしないようにしておく感じです。
   use strict;
   use AnyEvent;
   use AnyEvent::Util;

   my @q; # 実際にコールバックを入れておくところ
   my $ACTIVE = 0; # 現在の使用中スロット
   my $MAX_ACTIVE = 1; # 最大何個のコールバックを「同時」に行うか(もちろん本当に同時じゃないよ!)

  sub drain_queue {
      while ( @q && $ACTIVE < $MAX_ACTIVE ) {
          if (my $cb = shift @q) {
              $ACTIVE++;
              $cb->( AnyEvent::Util::guard {
                  $ACTIVE--;
                  drain_queue();
              });
          }
      }
  }
デモ程度に 1から10までの数字を(最低)0.5秒あけてから表示するコード:
my $cv = AE::cv;

# XXX - 12/1: よくよく考えたらここはbegin()/end()使うところだったので
# 修正しておいた
for my $x (1..10) {
    $cv->begin;
    push @q, sub {
        my $guard = shift;
        my $w; $w = AE::timer 0.5, 0, sub {
            undef $guard;
            undef $w;
            warn $x;
            $cv->end;
        };
    };
}

drain_queue();

$cv->recv;
これ以外の実装の仕方もあるんだけれども、これだと次のqueue popとかを自前で呼ぶ必要がないのが素敵です。
11/14、ようやく杉山さんとの去年?からの約束を果たすべく福岡に行って、Fukuoka Perl Workshop #14 に参加してきました。

都内でやっているイベントでは比較的最先端技術とかを話す人が多いですが、地方にいくと必ずと言っていいほど「実務的な話」のリクエストがあがります。今回もご多分に漏れずまっさきにそういうリクエストが出たので、JPAの肩書きで参加はしたものの、弊社endeworksの実例を出して色々どういう開発手法を採っているのかを話させていただきました。


発表の時はJPAのロゴが入ったスライドだったんですが、内容がJPA公式とかじゃなくてendeworksの物なので味気ない真っ白背景に修正しました。内容はあくまでendeworksの話ですよ!!


その後の懇親会ももつ鍋をいただきながら地元の雇用・仕事事情をたくさん聞く事ができました。やはり地方だとお役所の方針とかもあってPerl以外の言語を強要されることも多いそうで、なかなか弊社みたいに全面的にPerlで開発をするチャンスには恵まれないとの事。でも正直もっとチャンスさえあればぐいぐいPerlの事をやってくれそうな予感がする、良い技術者の人たちにたくさん会えた気がします。

こういう言い方はちょっと失礼かもしれないけれども、これだけのパワーがあるなら関東でPerlの仕事に恵まれている弊社のような会社はもっと地方に仕事をオフショアするべきなんじゃないのかなぁ、と夢想しております。せっかくネットリテラシーがあるんだから遠隔作業をもっとうまくやれるべきだよねぇ・・・。まぁまだまだ夢想の段階ですが、なにかうまくやれたらと思います。

さて、JPAで各地のPerlイベントに顔を出すというのもやっと2回目ですが、もし他の地方でイベントがある際は是非呼んでください!どこへでもいきます!

Morris君の中身を書き換えてAnyEvent::DBIにしてみたわけだが、一個だけはまった部分があったので、メモっておく。

例えば以下のようなコードをAnyEvent::DBIを使うように変更したいとする。
use strict; use DBI; my $dbh = DBI->connect('dbi:SQLite:dbname=test.db'); $dbh->do( "CREATE TABLE IF NOT EXIST t1 (c1 int not null, c2 int not null)"); $dbh->do( "DELETE FROM t1"); for my $i (0..10) { $dbh->do( "INSERT INTO t1 (c1, c2) VALUES (?, ?)", $i, $i * 2); } my $sth = $dbh->prepare("SELECT * FROM t1"); # まぁ本当ならbind_columns使うけど while ( my $row = $sth->fetchrow_arrayref ) ) { print "c1 = $row->[0], c2 = $row->[1]\n"; }
これをまずさくっと以下のようにすると動かない
use strict; use AnyEvent::DBI; my $dbh = AnyEvent::DBI->new( "dbi:SQLite:dbname=test.db", undef, undef, undef, exec_server => 1, ); $dbh->exec( "CREATE TABLE IF NOT EXIST t1 (c1 int not null, c2 int not null)"); $dbh->exec( "DELETE FROM t1"); for my $i (0..10) { $dbh->exec( "INSERT INTO t1 (c1, c2) VALUES (?, ?)", $i, $i * 2); } my $cv = AnyEvent->condvar; $dbh->exec("SELECT * FROM t1", sub { my ($dbh, $rows, $rv) = @_; foreach my $row (@$rows) { print "c1 = $row->[0], c2 = $row->[1]\n"; } $cv->send; }); $cv->recv;

理由はシンプル。exec()の最後の引数としてコールバックを指定していないから。もちろん、本来であれば一個一個戻り値とかを確認すべきなんだけど、とりあえず動かしたい場合とかあるじゃない。そういう場合に結構はまりがちなので、ここは何もしない空のコールバックを用意する
use strict; use AnyEvent::DBI; my $noop = sub {}; my $dbh = AnyEvent::DBI->new( "dbi:SQLite:dbname=test.db", undef, undef, undef, exec_server => 1, ); $dbh->exec( "CREATE TABLE IF NOT EXIST t1 (c1 int not null, c2 int not null)", $noop); $dbh->exec( "DELETE FROM t1", $noop); for my $i (0..10) { $dbh->exec( "INSERT INTO t1 (c1, c2) VALUES (?, ?)", $i, $i * 2, $noop); } my $cv = AnyEvent->condvar; $dbh->exec("SELECT * FROM t1", sub { my ($dbh, $rows, $rv) = @_; foreach my $row (@$rows) { print "c1 = $row->[0], c2 = $row->[1]\n"; } $cv->send; }); $cv->recv;
ちょっぴり面倒くさいね!
この間はまったのでメモ
package MyObject; use Moose; use overload # overloadの色々... ; use namespace::clean;
これだと、overload.pmが適用してくれる関数も削除してしまうので、オーバーロードが効かない。なので、overloadを適用するMooseオブジェクトはこうやって書かないといけない:
package MyObject; use Moose; use overload ....; no Moose;
それだけ。

Moose + POEなIRCボット作ろうと前に思い立って、結構長いこと弊社内で使ってたんですが、このたびAnyEvent化して色々モダナイズしました。やっぱり非同期なhttp_getしてる部分とかは抜群に速いなぁ。

AnyEvent したついでにAnyEvent::MPにも対応したですよ。POE::IKCと違って、一旦セットアップしちゃえば、Morrisの外から何かをポストするのにコマンドラインから一発でできる;
aemp morris privmsg "#channel" "Your comment"
ただMPってセットアップが結構面倒くさいのね。以下はまったり考え込んだりしてしまったところ:

  • 最初動かない時になにやってんんだかわからないので、PERL_ANYEVENT_MP_TRACEとPERL_ANYEVENT_MP_WARNLEVELを設定したほうがいい。後者は10くらいにしないとなんも有用な事を言ってくれない。
  • seedの役割をするプロセスが必要。Morris自身がseedになれないかなーと思ってやってみたけど、トレースで「refuses to talks to myself」みたいなこと言われた。
  • .perl-anyevent-mpファイルに受け側のbind情報を書いておいたほうがデバッグは楽かも。実際に動かす段階ではどのポートでも使えるようにしたほうが簡単かもしれない。
  • rcv/sndとportの使い方、シンタックスエラーにならない限り間違った使い方をしていてもなんの注意もしてくれないので、よくわかってない間はAnyEvent::MPディストリビューションの eg/内を見たほうがいいかもしれない。
  • MPやAnyEvent::IRCのようなネットワーク接続型のAnyEventモジュールを使っていると環境によっては"Out of memory!"エラーが出てくる事がある。もしバックエンドを特に指定しないでAnyEventを動かしている場合は、素直にEV.pmをアップグレードしてみるとよい。なぜか直る。
以上。Morris君は結構お気に入りなのであった。
ミニブログサービスで先行するTwitterと差別化を図った上で新たなサービスを出すのは面倒くさいので、id:miyagawa氏のコードを丸々パクった上でTwitter支援ツール、Hamakiを昨日の夜からちょこちょこと開発しました(本当はTwitter以上に色々できるんですが、そのあたりはまださわってない)

もう自分がやりたい事はだいたいできるようになったのでとりあえず公開です。

まず前提としてTwitterを使ってていくつか不満な点があったわけです:

  • Twitterで特定のアプリの発言がちょっぴり気になるので自分のTLに表示したくない
  • かといって上記アプリの発言している人たちの他の発言は読みたいので、それらの人をremoveするのもしたくない。
  • あと、全般的にtwitterをリロードするような事をしていると非常に効率が悪い
  • かといって、今のところアプリを気に入ったアプリも存在しないし、第一おれは色々カスタマイズしたいんじゃ、ゴルァ。
ってことで、Plackベースの非同期エンジンTatsumakiを使用したHamakiです。こんな感じで見れます。

hamaki.png

ほしかった機能としては、まず自動ロード。一回ページを読み込んでしまえば、その後は置いておけばJSが勝手に新規発言をロードしてページをリフレッシュしてくれます。

これはAnyEvent::Twitter::StreamでTwitter Stream APIを使うことによって実現してます。ちょっと仕組み上、普通のTwitter TLとは表示内容が違ってきますが、僕はこっちのほうが好きだったりします。

あと、フィルタリングができます。例えば、発言内容によって更新を無視したりできるわけです。

ちなみに今回使った設定はこれですね。ごめんね、radiotube。
で、最後に、元々のパクったソースコードにはなかった機能、Twitterに自分の新発言を送信する部分を追加したので、このUIだけでTwitter的な事はほとんどできてしまうことになります。まぁ、DMとか@自分とかはみれないけど、そんなんたまにしかみないし・・・

設定ファイルを書いたらあとは./script/hamaki.pl --configfile=config.yamlってするだけ!サーバーが立ち上がるのでそこにブラウザで /chat/twitterとかにアクセスすれば上記画面がでて勝手に更新してくれますお。

とまぁ色々書きましたが、実際の構造のほとんどはid:miyagawa氏のものですね。元々Tatsumakiに入っているデモ用のスクリプトdemo.plが素晴らしかったので、僕はこれをががっとファイルごとにわけて整理して、スコープが違ってもデータを共有できるようにして、Twitterにポストする機能と、それを実現化するためのTatsumakiフレームワークの拡張を子クラスで行った程度です。なので、miyagawa++, Tatsumaki++, Plack++ なのでした。
PSGIだPlackだって某IRCチャンネルでお祭りをやっている最中金策に走っていたので、まださわっていなかったのですが、とりあえず練習でさわってみました。

http://github.com/lestrrat/GitPlack

結論からまず言います:WAFを作りたいんじゃなければとりあえずCatalystとか使っておけ。

私の見解は以下の通り:

PSGI/Plackは統一された、しかも非同期エンジンを念頭に置いて考えられた仕様・実装なのでその点は素晴らしい。だけど、Plackはフレームワークではないし、とりあえずアプリを作る分には我々凡人は普通にフレームワークを使っておけばいい。・・・という感じ。

結果的には2時間くらいでWAFもどきとgitの情報を表示するアプリを作れたし、こういう仕様があるのは素晴らしい事です。

細かいところだけど難点はリスタートサーバーが動かないところくらいかなぁ。でもこれもそのうちなおるんじゃなかろうか。

追記: 10/21の時点でgit レポジトリ版はplackup -r とすることで自動的に再起動がちゃんと動くようになっています
Test::mysqldを使うとクールにMySQLを起動させられるので、それを使おうとしたんだ。でもおいらのローカルにあるmysqlをMacPortsのmysqlで、ファイルレイアウトがメタメタなんだ。だからまずこんな感じで、Test::mysqldを継承するMyApp::Test::mysqldを書いたわけさ!

必要とあればMacPortsとかの環境じゃなくてもMYSQL_INSTALL_DBとMYSQLDを設定すればテスト時にTest::mysqldが見るバイナリを変更できるのがミソだね!

さて、これを使っても、何個もテストスクリプトがある時に一回一回mysqlを立ち上げ直してちゃ意味がない。遅いし、毎回DBの設定をしなくちゃいけないじゃないか!

だもんで、まずmake testが走るときに前もってTest::mysqldを起動させ、make test 終了とともにDBが停止するような仕組みをMakefile.PLに書いてみたわけさ!


Oh, how very unpleasantly hack-ish!

汚いけど、これでMakefileをいじっちゃうわけだね!ちなみにModule::Installも同じような力業でMakefileを編集してるよ!

ともあれこれでmake testとした時に自動的にMyApp::Test::mysqldが立ち上がるってわけさ。$SIG{INT} = \&exitってしておくのは、ユーザーがCtrl-Cした時にTest::mysqld::DESTROY()が走るようにするためのおまじないだよ!

mysqldが立ち上がったら、そこへの接続情報を$ENV{TEST_DSN}につっこんでおくわけさ。これでテスト側からこの値を参照するだけでDBに接続できるわけだね。

ここまで来たらもう一歩!

  • DBIx::Classを使ってるんだから、DBスキーマも自動的にdeployしたいじゃない!
  • それぞれのテストでTEST_DSN呼んだりするの面倒くさいよね!
  • 単体のファイルを実行した時は、mysqld起動+deployを各ファイルでやってほしいけど、make testの場合は1回だけ実行したい!

こんなワガママなキミはそれ用のラッパーを作ると便利かもね!そこでこんなMyApp::Testを書いたわけさ(3回目)!

ここでは僕の自家製DIコンテナOrochiってのを使ってるのでわからないことだらけかもしれないけど、重要なのは以下のことさ!

  • MyApp::Schemaは$self->assembler->get('schema/master')とすることにより、すでに生成されているならその値を、そうでなければあらたに生成した値を得ることができる。
  • MyApp::Schemaの生成には 'schema/master/connect_info'というデータが必要
  • connect_infoのDSN部分は、$ENV{TEST_DSN}が設定されているなら、それを利用する(mysqldはMakefileによって起動している)が、そうでないなら、自分でmysqldを立ち上げる。
  • その際、空のデータベースを立ち上げただけでは意味がないので、deployフラグもつけておく
  • deployフラグがたっている場合は、DIコンテナからschemaを取り出す際にdeployもしてしまう
こんなことをしておけば、make test時に一回一番最初だけに実行されるt/000_setup.tみたいなファイルを用意してMyApp::Test->new()->deploy()すればmake test中には一回しか起動+deployはされないし、各テスト実行時にもMyApp::Test->new()しておけばMyApp::Schemaを使用する時に自動的に同じ事が起こるわけさ!

じゃあもう眠いから、アディオス!

11/14のFukuoka Perl Workshopにおじゃまする予定です。

登録等はこちらから:http://atnd.org/events/1834

東京以外のPerlの利用状況とか、使用者の声を聞きに行くのと、なんか一席お話をさせていただきます。話す内容が決まらないのでリクエストとかあったら是非お願いします!

相方からは明太子を買ってこいとの厳命を受けております。
I'm just going to wear my Japan Perl Association Head Director's hat for a bit and add a small, but what I consider to be something very important about the whole Moose or no Moose argument:

"Moose Developers, please act like you care (more) about startup cost, use of RAM, and other efficiency issues."

Here's what I mean by the above:

I'm an engineer. I like Moose. I understand the whole Moore's law thing, I understand the "buy more RAM" thing.

However, most of the world is made up of non-engineers. This group is made up of  people who make decisions, or they are your regular joe programmer who don't give a rat's ass if they use Moose or plain hash or worse, some other language.

What these people see are things like numbers. If they see a benchmark touting Moose to be slower, they'll ONLY remember that Moose was lower ranked. They won't remember the context in which the benchmark was taken, nor Moose's advantages.

Furthermore, if a Moose developer responds to that kind of benchmark with a smart-ass comment, or a phrases like "dude, buy more RAM", it just doesn't do anything to convert people's minds. I'd really like to avoid that.

I know Moose is great. I know something like it was long overdue. I'd like to keep using it on all of my projects -- which I try to do now.

But if a client sees these articles and get the wrong idea, I'm going to have to spend another 2 weeks writing up evidence claiming Moose's superiority before I can install it in their systems. And if that happens, I'm just going to whip my Perl-fu and say "bless this hash, and let there be no Moose" -- cause it costs money to prove otherwise.

So, here's my $0.02.

I know that the Moose developers are doing a great job. I'm not even going to say that they need to change their attitude or goals. However, I'd really like them to make a more thought-out response, and act like you actually care about the parties involved that have a slightly different view than yours (in this case, people who really care about speed, memory efficiency, etc). I actually don't care if they actually care. I know they are good enough that they won't completely ignore it. And I/we will contribute where possible. Just... act like you care :)

That will make my life easier. I can point people to the Moose roadmap or whatever and say, "look, they care. they /will/ address your concerns. let's just install Moose in your system for now, yeah?" and go on hacking with Moose.

 And you know, you can always mention/encourage more activities like JPA was doing with gfx to shove some startup costs ;) 

(update: fixed typo and such)
今年のYAPCの後、Moose入門 (9/14)、Perl/Ajax/Unicode 9/15、DBIx::Class x MySQL x スケーラビリティ(9/16) の3コースが提供されるわけですが。正直に言おう、もうちょっと人が来ないとちょっと辛い感じ。公平に考えて、かなり価値の高いコース内容になると思います。僕も普通に行きたい感じ。

逐次通訳もする予定なので言語の壁は特に問題ないはずです。あと当日は僕とかが現場にいるので講師の人に聞きにくい事があった場合とか、その場でフォローします。怖がらずにきていただいて、僕とかにでもいいのでガンガン質問してください。

なんだったらその後俺と飯を食いながら復習するのでもいいので、是非是非どうぞ。皆様のご来場をお待ちしております!!
http://github.com/lestrrat/PicoEV-Perl/tree/master

事務仕事ばっかりだと気が詰まるよね。ってことで突然PicoEVをPerlから使うためのバインディングを書き、コールバックが設定できるところまでやったところで大分飽きた。

現状としては
  • picoev_select.cしか使えない(Makefile.PLで選択できれば良し)
  • 本当にとりあえずコールバックが呼び出せるところまでしか作ってない

このまま僕のモチベーションに任せてても多分これ以上出てこないので、是非どなたか改良していただくか、僕のモチベーションをあげるよーにし向けていただけると幸い。できれば改良してください!
Pixisががらっと変わろうとしている。最初は単純に継承ではなくRoleでその機能を提供しようと思ってあれこれ考えてたんだけど、その際にCatalyst.pmの中身を見たらsetup_plugins()が・・・
sub setup_plugins { my ( $class, $plugins ) = @_; $class->_plugins( {} ) unless $class->_plugins; $plugins ||= []; my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins); for my $plugin ( reverse @plugins ) { Class::MOP::load_class($plugin); my $meta = find_meta($plugin); next if $meta && $meta->isa('Moose::Meta::Role'); $class->_register_plugin($plugin); } my @roles = map { $_->name } grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') } map { find_meta($_) } @plugins; Moose::Util::apply_all_roles( $class => @roles ) if @roles; }
おわああ!Roleが適用できるじゃないか!どうもこれは将来的にPlugin機構をすべてRoleに取り替えるための伏線な気がするぞ。しかしともあれ、これがあるということは今までCatalyst Pluginを指定していた所でRoleを指定すればそのまま動くということ。

ってことでpixis/tree/rollerブランチで主立った機能をすべてRoleに移した。今後の使い方はこんな感じ:
package MyApp; use Pixis::Web; __PACKAGE__->config( ... ); __PACKAGE__->setup(); 1;
このファイル一個と、myapp_server.plさえあればいきなりある程度動くCatalystアプリができあがるという寸法。myapp_server.plが作れるようにしないとなあ。
昨日id:zigorouさんと話してたら、Matt Trout氏が主催しているPerl Ironman ブログコンテストのフィードの事を知らないって話題になったので、ちょっと書いておく。

Perl Ironmanは基本的にPerlな人たちに向けて「メーリングリストやIRCに引きこもってないで、ブログにエントリを書け!」という運動。ブログを書いている連中の中にはDBIx::Class、Catalyst、Mooseの開発者達もいるけれど、それ以上にいわゆる中堅プログラマの人たちのエントリがあがるようになってきてて、最近購読しているフィードの中では一番おもしろい。知らない事もたくさん出てくるしすでに知っている事でも、他人がそれを容認しているという状況は頼もしい。

フィードはAtomRSSがある。サイトは http://ironman.enlightenedperl.org/ にあるので←のリンクが動かない場合は正式なURLはそちらからどうぞ。

今日もすでにnothingmuchがPerlの内部データの紹介をしていたり、chromaticがPerl本体のバグの改修の仕方を紹介したりと読み応えのある記事が色々あっておもしろい。あと、これは紹介記事だったけど、今年のOSCONでのキーノートの一つであったSkud女史の「Standing Out in the Crowd」などの紹介も読めて非常によかった。

というわけで、海外のPerlの動向に興味のある方は購読してみる勝ちのあるフィードだと思いますよ!
先週末、僕が相方とのんびりとしているとメールが入ってきた。「アイデアがあるんだけど」とメール始まるが、僕はピンと来た。これは「やってくれ」という催促だ。僕は身を固くして続きを読み始めた。

JPAのターゲットのひとつにクラウドコンピューティング

サポートってのが入っていて,ec2とかで使うことを考えて

ました.が,よく考えるとperl-appengineってのがありますね.


ああ、ありますね。そうですね。

誰かYAPCでしゃべってくれるといいんですが〜


うは!くっ。僕は肩書きこそ「長」がつくけど、そんなに偉くないので年上&目上のこの人に「君がやりたまえよぉ」とは言えないので「隙を見つけてやってみよう」と思った。

月曜、きっとやらないとなんか言われるだろうなぁ、と思いつつも忙しくてなにもできなかった。そして今日。メールを山のようにさばき、オフィスへの来客とも話をし、僕の目の前に座っている人に申し訳ないなぁと思いつつさらに仕事を振ったあと・・・ふと、一瞬時間が空いた事に気づいた。

「今だ!」

そこでとりあえずperl-appengineのサイトに行き、すべての準備を整え始めた。素っ気ない文章だけど このGetting Startedページに書いてある手順通りにやればだいたい大丈夫なようだ。モジュール類はほとんどすでにインストール済みだったので、普段使わないモジュールだけ入れておいた。Sys::ProtectやProtobufあたりはちゃんとsvnから入れておいたほうがいい。DateTime::Calendar::Mayanは全く必要ない。

Protobufをmakeしているときに問題にぶちあたった。問題というか、大量の警告・・・ひどい。これは最近のMooseが親切になった結果、通常使う必要のない形の呼び出しをすると警告文を発するようになったからだ。これについてはバグ報告をしておいた。気になる人はlib/Protobuf/Meta/Message.pmに is => 'ro'っていうのを一カ所付け加えればいいだけだ。よくわかんない人は特に気にしないでインストールすればいい。

次の問題はpython。僕の環境はMac OS Xだ。/usr/bin/pythonでとりあえず以下のコードを実行してみた:

cd perl-appengine
perl dev_appserver.pl demos/guestbook/

そしたらHTTPSHandlerがないとかよくわかんないエラーが出た。orz

Pythonのエラーはあんまり見慣れてないので途方に暮れた僕はとりあえずGoogle先生に聞いてみる。

すると、「sudo port install python25-socket-sslしろよタコ」って言われたので、これはそうかMacPortsを使えばいいんだな?と思い、とりあえずpythonを入れ替えた:

mv /usr/bin/python /usr/bin/python.mac
ln -s /opt/local/bin/python25 /usr/bin/python

そしてportsからpython25-socket-sslをインストール。

わくわくしてまた実行してみた。同じエラーが出た・・・。orz

色々やってみたが、なんかよくわからないので、「ついでにportsのpythonもアップグレードしてみようか」という安易な考えでsudo port install python25ってしたら、なぜかtclとかtkとか入れ始めたので、wassrで毒づいた。

そろそろインストール終わるか、と思ったら「すでにactiveなpython25があるからactivateできねぇよ、タコ」って言われたので、deactivateしてから今度こそ新しいのをactivate。
そしてついに実行・・・動いた!Pythonに関してはMacPortsでは新しいpython25とpython25-socket-sslがあればよいみたい。

今度こそ、と思ってhttp://localhost:9000/ にアクセスしたら、「Class::C3::XSはロードできねぇよ、タコ」って言われた。ドキュメントには確かに書いてあったね、うん。でもSys::Protectなんて結局なんちゃってセキュリティのくせに、なにやってんだよ、ちきしょう。

Class::C3::XSを消すのは面倒くさいので、local::libで使ってるディレクトリの中からClass::C3を探して、require Class::C3::XSしてる部分を

eval "die 'Cannot locate shit'; require Class::C3::XS";

に変更した。重要なのは"locate"の文字列を入れることだよ!

これでようやく完成。guestbookアプリが無事に動いた。

その後、ちょこちょこと試してみたが、とりあえずXS系のモジュールは全部駄目くさい。

TemplateもデフォルトでTemplate::Stash::XSを使うようになっている場合は、以下のように明示的にStashを指定しないと静かにundefを返す:

use Template;
use Template::Stash;

my $t = Template->new( STASH => Template::Stash->new() );

でもそれ以外はわりとまんま。app.yamlにURL→ハンドラスクリプトのマッピングを書いて、HTML出力とかすればいいんですね。わかります。

ってことで、なんかPerl AppEngineでも色々書けそうな予感、という報告でした。
Algorithm::BIT - http://d.hatena.ne.jp/naoya/20090606/1244284915

以下、全然そうする必要はなかったけど、敢えてMoose化をしてみた。なんとなく、例としてご参照ください。


ppackage Algorithm::BIT; use Moose; use MooseX::AttributeHelpers; use namespace::clean -except => qw(meta); has size => ( is => 'ro', isa => 'Int', required => 1, ); has data => ( metaclass => 'Collection::Array', is => 'ro', isa => 'ArrayRef', lazy_build => 1, provides => { get => 'data_get', set => 'data_set', } ); sub _build_data { my $self = shift; return [ map { 0 } 1 .. $self->size ] } sub BUILDARGS { my ($self, $n) = @_; return { size => $n }; } sub read { my ($self, $idx) = @_; if ($idx > $self->size) { confess 'assert: index overflow'; } my $sum = 0; while ($idx > 0) { use integer; $sum += $self->data_get($idx); $idx = $idx & $idx - 1; } return $sum; } sub update { my ($self, $idx, $val) = @_; my $max = $self->size; while ($idx <= $max) { use integer; $self->data_set($idx, $self->data_get($idx) + $val); $idx += ($idx & -$idx); } } __PACKAGE__->meta->make_immutable; 1;
注:まだ本当にレンタルサーバーでは試してないです。

思い立ってすっげぇ簡単なスクリプトを書いてみた。local::libを同梱する必要があるのと、~/perl5と.cpanがスクリプト実行ユーザー権限で書き込み・読み込みできる必要がある。
#!/usr/local/bin/perl use local::lib qw(/home/daisuke/perl5); use CGI; use CGI::Carp qw(fatalsToBrowser); use CPAN; sub main { local $| = 1; my $q = CGI->new; if ($q->param('mode') eq 'install') { install($q); } else { control($q); } } sub install { my $q = shift; print $q->header('text/plain'); CPAN::Shell->install($q->param('module')); } sub control { my $q = shift; print $q->header(), $q->start_html(), ; print $q->start_form(-action => $ENV{SCRIPT_NAME}, -method => 'GET'); print $q->textfield( -name => 'module', -size => 30, -value => $q->param('module') ); if ($q->param('module')) { my $mod; { local *STDOUT; close(STDOUT); $mod = CPAN::Shell->expandany($q->param('module')); } if (! $mod || ! $mod->inst_version) { print $q->div($q->param('module') . ": Not installed"); } else { print $q->div( ($q->param('module')) . ": Installed version is " . $mod->inst_v ersion); } } print $q->submit(-name => 'mode', -value => 'check'), $q->submit(-name => 'mode', -value => 'install'), ; print $q->end_form(), $q->end_html(); } main();
Catalystはたいへんすばらしいフレームワークですが、新しいプロジェクトを始める、という時にcatalyst.plでスケルトンから作り直していつものプラグインを設定して・・・みたいな面倒な手間がいろいろあります。

Pixisはなるたけ簡単に新しいアプリを作れるようにしたかったので最初からプラグイン機構を念頭に置いて書き始めましたが、それはあくまで機能の追加にしか使えず、JPAサイトのようにPixisというフレームワークを使って、JPAというサイトがPixis機能を乗っ取るというような場合はそれだけではうまく設計ができませんでした。

これについては悶々と考えていたのですがCatalyst 5.8になり、Mooseベースのオブジェクト指向ができるようになったことでひとつひらめきました。たとえばJPA::Webというアプリを作るとして、基本的にPixisがすでに提供しているすべてのコントローラーやモデルをそのまま使いたい、と思うならこれまでは自前でPixisを継承したコントローラーやモデルを作ることが必要でした。ですがMooseが前提ならClass::MOPの機能を使って、イン・メモリでクラス作っちゃえるじゃないですか。

ってことでやってみました。今のPixisは以下のように継承を宣言するだけで、とりあえずさくっと動くようになりました:
package JPA::Web; use Moose; BEGIN { extends 'Pixis::Web' } __PACKAGE__->setup_config(); __PACKAGE__->setup(); 1;

これは便利!

実装方法まだハックっぽい面がありますが、基本的な方向性はこれで正しいと思います。setup_components()とconfig()をオーバーライドして、子クラスはconfig()に引数を渡さずに設定できるように、setup_components()はPixis::Web::名前空間以下のすべてのモジュールをMoose::Meta::Classのcreate()を使ってインメモリで子クラスを作れるようにしてみました。

もうちょいしたらJPAサイトもこの方式に切り替えようと考えています。
これから新幹線の中で書きます。GunghoPixisで色々Moose::RoleとTest::FITesqueでやってみた結果についてぼちぼち。ちなみにTest::FITesqueはJay Shirleyさんに言われて、えーと思ってたんだけどid:lopnorががっちり基本を書いてくれて、それでようやく理解した。lopnor++
(5/13 追記): メソッドモディファイヤーの件、Catalyst開発チームにテストパッチを送ろうと思って色々書いたら理由がわかったので、追記しました

最初からMooseベースでアプリケーションを作るというのは、実務ではなかなか難しいのはわかります。なので使うとしたら既存のシステムにMooseを組み込む事のほうが多いとは思います。

それについての一般論は JPA #02で話すのでおいておきますが参加申し込みは今日5/12までですよ!)、5.8 からMoose化したCatalystにポートを行う際に実際にあった問題・注意点をちょっと書き出してみます。

1. use Catalyst

Catalyst::Upgradingを読んでいると
package MyApp; use Moose; extends 'Catalyst'; __PACKAGE__->setup(qw/ ConfigLoader /);
という表記が見られるが、これは気をつけないと駄目。

自分が直面した問題は、path_to()等を使った時に起こった。path_to() は現アプリのルートディレクトリからのパスを指定したい時に使う。例えばTTテンプレートのコンパイルされた物をMyApp/tt2 以下に格納したければ、
my $path = MyApp->path_to("tt2");
というようにする。このpath_to() というのは $c->config->{home}という値を使用するのだが、この値は通常Catalystによって自動的にMyApp/ 以下に設定されるので、デフォルト状態のファイルレイアウトでCatalystを使用する分には特に問題にならない。だが、上記のextends記法を使っているとなぜか home変数が設定されず、path_to()の挙動がおかしくなる。

これを回避するには、home変数を設定する必要があるのだが、homeの自動設定はCatalyst::import()で行われており、通常extends() (use baseでも一緒)を使うとimport()は実行はされないのだ(importが自動的に実行されるのは、「use Module」とした時だけ)。それはすなわちhomeの初期化が行われない、ということである。

ということで、実際はこうする必要がある:
package MyApp; use Moose; use Catalyst; extends 'Catalyst';

2. :Index, :Private, :Local, :Chained等

CatalystでコントローラーメソッドをURIパスに結びつけるのが:Indexや:Chained等のメソッドアトリビュートと呼ばれる物だが、これらはPerlのコンパイルフェーズで処理される。いわゆるBEGIN {} ブロックが走るタイミングと考えて良い。

コンパイルフェーズというのは特殊で、use() 宣言やBEGIN{}ブロック等、Perlのネイティブレベルで定義されたものだけが(注:割愛するが、実はそれ以外の黒魔術的なやりかたもある)、それ以外のコードを走らせる前に全部動く。本当のコードが走る前の一種の初期化フェーズと考えてもいいかもしれない。

通常Mooseマニュアルには継承を行う際はextendsを使えと書いており、同じノリでMyApp::Controller::RootをCatalyst::Controllerから継承する、というようにMooseで書こうとすると以下のようになる:
package MyApp::Controller::Root; use Moose; extends 'Catalyst::Controller'; sub index :Index { .... }
が、ここが落とし穴。これだと :Indexを見た瞬間にPerlがエラーを吐く。なぜか。

:IndexをPerlが最初に見るのはコンパイルフェーズだ。前述の通り、use宣言やBEGINブロック以外はその段階では実行されない。:Indexを理解するためにはMyApp::Controller::Rootはその時点ですでにCatalyst::Controllerを継承していなければならないのだが、継承を指定するextends宣言は普通の関数なので、コンパイルフェーズでは走らない。よって、コンパイルフェーズでは MyApp::Controller::Root->isa('Catalyst::Controller')にはなっておらず、メソッドアトリビュートも理解されないのだ。

これを回避するのには単純にBEGINブロックで囲ってやればいいのだが、正直一瞬意味がわからなくて困る
package MyApp::Controller::Root; use Moose; BEGIN { extends 'Catalyst::Controller' } sub index :Index { .... }

3. プラグインが実行されない

(5/13 追記) 以下の理由がわかった。単純。Catalyst->setupはMooseのメソッドモディファイヤーを使用する前に呼び出されなければならない。
package MyApp; .... before finalize => sub { ... }; __PACKAGE__->setup(...); # これは駄目
package MyApp; .... __PACKAGE__->setup(...); # これならOK before finalize => sub { ... };
Catalystは過去の経緯から、setup()時にクラスの継承関係を変更するのだが、それをやる前にメソッドモディファイヤーを使ってしまうと親クラスをきちんと認識してくれない、ということっぽい。setup()の中で親クラスのリセットを行ってくれればそれでいいんじゃねと思うが、それまでは単純にMyApp.pmの上のほうでCatalyst関係の初期化を行えば良い、ってことですな。

プラグインの多くは継承のメカニズムを使ってCatalyst内のメソッドの前か後に実行されるように書かれている。

Catalyst 5.8からはMooseなので、プラグインを書くほどでもないちょっとした事ならMyApp内でメソッドモディファイヤー(afterやbefore)を使ってちょこちょこと自前のロジックを足したりもしたいのだが、それをすると突然プラグインが動かなくなったりする。

自分がはまったのはCatalyst::Plugin::Unicode。それまで動いていたのに、以下のようにbeforeメソッドモディファイヤーで自前のエラー処理をつけようとしたら、"Wide character in syswrite"というエラーが出始めた。
package MyApp; ... before finalize => sub { my $c = shift; $c->handle_exception if @{ $c->error }; };
色々と試してみたところ、これはメソッドモディファイヤーが適用されるタイミングに左右されることがわかった。モディファイヤーがCatalystの継承によるディスパッチを邪魔して、Catalyst.pm以外のメソッドを呼び出してくれない(ちなみにこれでより一層Moose::Roleが素晴らしい、ということがわかった。継承を意識しながらメソッドディスパッチとかうざすぎる)

ちょっとまだ一般解まではわかってないのだが、とにかく、上記のようにMyApp内からfinalizeにフックしたいなら、overrideとnext::methodを組み合わせるのが吉
package MyApp; ... override finalize => sub { my $c = shift; $c->handle_exception if @{ $c->error }; $c->next::method(@_); };
このようにすると正しくプラグインへのディスパッチを行いつつ、フックもできる。

4. エラーがよくUnknown Errorになる。

Perl 5.10.x ではUnknown Errorがよく出る、というのは聞いていたが、5.8.9でもなぜかなる。あんまりちゃんと調べてないので、そもそもこれは既知の事だったらあれなのだが、わかっているのはこれはとにかくコンパイルフェーズやその他セットアップしている時に起こるということ。

なので、このエラーが出たら、まずモジュール類がちゃんとコンパイルすることから調べ始めるのを薦める。それとちょうど良い機会なので、エラーを吐きそうなブロックは明示的に囲ったりして自前でエラーを出力するようにすればよい。warn && confessみたいな単純な処理でも以外と役立つ。
eval { Class::MOP::load_class($pkg); # 個人的にはこういうのとか、DBICのconnect()とかでよくなった }; if ($@) { warn && confess; }
いずれにしろ、エラーケースはちゃんと自前で処理したほうが良いので、これを機に怪しいところは全部やっつけるといいのではないだろうか。




とりあえず、今回Catalyst 5.7 -> 5.8に移行した際に直面した問題はだいたい以上のようなもの。それまでのCatalystでは出ない類のエラーが多いので、困る人も多かろう。なんらかの手助けになれば幸い。
JPA #02用「Moose入門」スライドを書いた。長かった・・・。

盛りだくさんな内容になると思いますので、是非参加してください!懇親会というか、飲みもある予定です
社長業の傍ら理事長業を行いつつ、時々プログラマーをしています。今は来週JPAセミナー#02@大阪用のスライドの調整に飽きたのでブログを書いているわけですが。

さて、来週のJPAセミナー、私はまた泊まりで行くわけですが、今度こそ、今度こそ、誰か飲みませんか!懇親会!懇親会!

5/14 18:30からセミナー、9時前くらいに体が空く予定です。まだ空きがあるっぽいので、未登録な方は是非参加ください。かなりお値打ちなセミナーになるはずです :)

よろしくお願いしますー
おりょ?動くよ?
use strict; my $stop = 0; $SIG{"INT"} = sub { $stop = 1; print "sigint received\n"; }; while (1) { print "loop start\n"; sleep 3; if ($stop) { print "stop flag is true\n"; exit 1; } print "loop end\n"; }
こんな感じの出力:
daisuke@beefcake-7 ~$ perl hoge.pl loop start ^Csigint received stop flag is true daisuke@beefcake-7 ~$
JPAセミナー#02をやります。登録開始は5/1から。

内容は#01でお話したJay Shirley氏のCatalyst Talkと、私の「Moose入門」を予定しています。Moose入門に関しては、JPAが提供する有料セミナーのひとつとして捉えている内容のセミナーなのですが、今回は特別プレビューということで早送りバージョン(本来なら4時間を超える内容をかいつまんで話すので、「早送り」)を話させていただきます。

なお、参加費についてはJPA活動開始キャンペーン?中なので破格の1000円です(学生は無料!)。正直大赤字。なんで、皆さん是非来てください!
本当はJPAブログを開設するべきなので、このエントリはまた後でそちらに移動します。多分。

さて、先日はJPA #01にお越しの皆様、大変ありがとうございました。講演資料や動画はWikiに後日準備が出来次第公開しようと考えています。スライドのほうは多分今日中になんとかできるのではないかな、と思っております。

少なくとも2,3ヶ月に一回こういうイベントを開催したいと考えていますので、今後とも何卒よろしくお願いいたします!
5/14 18:00 から JPA セミナー #02 in 大阪(梅田)を計画中です。内容はJay Shirleyさんのセミナー#01 での内容のうち一本と、多分もう一本自分か他の誰かに話してもらう予定です。

関西の皆様、興味ありますか?
タイミングの悪い事に本日より数日間ネットにあんまり接続する時間がなさそうなのですが、取り急ぎ:

コメントリストを見ていたら未承認のコメントがあったので先ほど承認しておいた

地方在住の方に関してはJapan Perl Associationは最初から地方での活動を視野に入れて団体を作った、というのは断言しておきます。ちゃんとこれから東京以外の地域でも活動はしていく予定です。

ちなみにまだ未確定ですが第一回セミナーと同時期に関西地区でのセミナーも視野に入れて調整中です。その後も、ニーズに合わせて少しずつ活動を広げていくつもりです。とりあえず考えているのは広島、仙台、北海道あたり。それ以外は行かない、というより、個人的になんの予備知識もないため、これから調査をする必要があります。情報及びヘルプは随時募集いたしますので是非ご連絡ください。ただ、もうしばらくは地盤固めのほうが先に来てしまいがちになるのはどうか理解いただきたい。

なお録画に関しては正直他のことにかまけていて忘れていたのですが、確約はできませんが調整できるかどうか調べてみます。

あと、これはコメントの方はそう思っていないのかもしれないのでひょっとしたら斜めに行ってしまう事柄かもしれませんが・・・一応JPAのスタンスについて捕捉させていただきます。

JPAは「勉強会」を提供する団体ではありませんし、Shibuya.pmのようなPerlギークの今興味あることの発表の場でもありません。それらはそれらで意義はありますが、JPAが提供するセミナーは事業であり、広義のPerlへの利益を考えたビジネスでもあります。これはPerl文化やユーザーに利益があるようにする「事業」です。お金が動きます。人も動きます。

別にShibuya.pmや、それらに関わっている人たちの活動にケチをつけるつもりはありません。あれはあれで必要な事ですが、ハッカーの集まりだけではできない活動をしたかったのがそもそも「社団法人」という法人格を作った理由です。ハッカーの集まり、っていうだけではコードは書けてもお金と企業は動きません。

JPAの直近の活動内容はひょっとしたらこのコメントをくれた方が直接利益を感じられる類の物ではないかもしれません。複数の企業の皆様と事業を行うのであれば、それなりに水面下で色々な動きをしないといけませんので、直接エンドユーザーの人々の目にふれない事も色々あるかもしれません。ですが、JPAの活動内容はPerlを使う技術者と、Perlを使う企業にとって有意義な事をであることは保証いたします。

エンドユーザーに見える内容に関しての方針としては「実践的」な路線を計画しています。たとえそれが少しベーシックな事であっても、Perlをビジネスで使うために必要な技術や情報を教えていったり、次に来る技術をどうPerlから扱うのか、これから新しくPerlを覚える人たちはどのようなリソースを参考にすればよいのか、どのような企業でのキャリアを考えればいいのか、といった極めて実践的な路線を進んでいこうと考えています。

ま、というような中で、手伝っていただける方は大歓迎です。地方にも行きますので是非セミナー等の希望も出してください。全部が全部無料というわけにはいきませんので、複数の企業で招待していただいて研修とか、そういう感じのアクティビティとうまく搦めていければ幸いです。

是非Perl文化と技術の明日を一緒に考えてください。我々の活動が物足りない時はそちらからも手を伸ばしてください。応えられるよう、精一杯努力します。

以上、日曜の真夜中ですが、とりあえずマニフェストもどきでした。
Japan Perl Associationセミナー#1 を4/21に開催します。

記念すべき第一回旗揚げセミナーです!

秋葉原UDXでCatalystを使い倒す話とMoose等の最新ベストプラクティスに関しての話になる予定で、講師はJay Shirley氏です。日本ではまだあまり名前が売れてないかもしれないですが、トップレベルの技術者で、CatalystやDBIx::Classなどを現場でどう生かすのかというノウハウについては絶大なるものを持っています。また、最近ではMatt Trout氏らとEnlightened Perl Origanisation (EPO)という、エンタープライズ分野でPerlを使うためのノウハウやサポートの提供を行う団体を立ち上げています(EPOはThe Perl FoundationなどよりもJPAにかぶるかもしれませんね)。

内容的にはJay Shirley氏のこれまでのWebアプリケーション等の開発から、Catalyst, Moose, DBIx::Classなどをどう効率的に、速く、フレキシブルに開発に使うのかを話していただく予定です。現場でこれらを使っていない方も、どのような設計方針で開発を進めているのかという点や、2009年現在現場の第一線でPerlを書くというのはどういうことなのかを知るのにはとても貴重な体験になること請け合いです。

第一回ということでこれは無料セミナーです。開場は200人ほどのキャパシティで、無線LAN完備。確定ではありませんが、希望者先着40名ほどにはスタッフが同時通訳を行ってくれる予定です。本当は全員、とかやりたかったけど、予算的に無理です。すみません。

参加申し込みに関しては追って発表します。ブクマとかで前もってどれくらいの人が興味あるのか教えてくれると嬉しいかもです。

皆さんのご来場をお待ちしております!

前のエントリでData::LocalizeをMouse化したと書いたが、それぞれこんな感じ:

daisuke@beefcake $ env ANY_MOOSE=Moose perl -Mblib tools/benchmark/benchmark.pl Rate locale_maketext data_localize locale_maketext 9615/s -- -64% data_localize 26786/s 179% -- daisuke@beefcake $ env ANY_MOOSE=Mouse perl -Mblib tools/benchmark/benchmark.pl Rate locale_maketext data_localize locale_maketext 9615/s -- -66% data_localize 28037/s 192% --
Data::LocalizeMooseベースで、Moose嫌いなtokuhiromがMouseじゃねーから使わないって言われたのが発端。おお、んじゃあAny::Mooseにすべかぁ、と思ったらParameterized typesが実装されていない。ないからことごとくエラー。「実装されてないじゃん!」って言ったらtokuhiromとYappoに「え〜、俺ら必要ないし」的な発言をされて正直Mouse Mouse言うならちゃんと最後まで面倒みてやれよ!と思った。

...というような事を書いたが、このエントリは別に彼らに文句を言いたいわけではない。

オープンソースの世界は参加者一人一人が自分の技術を少しずつ世間に提供する世界だ。彼らにMooseとの完全なる互換性を求めるのは大間違いだ。ここまでちゃんと使える物にしてくれたのはそもそも彼らなんだから、それはそれで充分とするべきだし、彼らがその開発のための犠牲になるのはおかしい。

そもそも俺がMouseのコード書けるかもしれない。面倒くさがってるのはこの場合おれのほうだよね。

というわけで、まず俺のできること第一弾はAny::Moose化はしつつ、Parameterized typesを使わない方法で実装してしまうこと。んで、しました。その後は家を掃除したり、ポトフを作ってみたり。正直この時点でまだMouseのコードを1行も見てない。

それが終わって、PCの前に戻ってきたら、やっぱり互換性がないと新規参入者達にMouseもMooseもすすめられねぇや、JPA的にまずい、と思って改めてMouseのコードを確認。・・・30分後、なんとなくわかってきた。あと、Parameterized typesってのは基本的に再帰的にデータの内容を確認する事だから、再帰呼び出しをする関数を書けばいいのもなんとなくわかってきた。Mouseを見ると基本的にisaという宣言がある部分は基本的にはクロージャを作っている:

# ちょっぴり実際のコードとは違います my $code = $optimized_constraints->{ $type } || sub { Scalar::Util::blessed($_) && $_->isa($type) };
これをベタ書きしてた所を、ジェネレータにすればいいのだ。そして毎回isaが呼ばれると作ってたsub{}も、キャッシュしてしまえ。というようなパッチを書き、Parameterized typesが動くようになった。コミット。Data::Localizeも無事Paarameterized typesを使えるようになった。

というわけで、MouseでParameterized typesが使えるようになったあらまし。問題がなければ次の0.19に入ると思う。
結局ユーザー管理だとか、お金を振り込む事だとか、なんか色々やらざるを得ないと言うことから、自分で書いたほうが速いという決断でJPA用のサイトを一からCatalystで書いてます。

今回はこれをgithubで晒しているわけですが、今ふと思ったのは、普段から我々PerlプログラマはCPANでいろんなCatalystのプラグインだとかを見ていたり、もしくはあちこちで細かいティップスみたいなものを聞きかじってるわけだけど、ひょっとすると俺たちほとんど他人のCatalystアプリケーションって見たことない?

よくよく考えたら自分でさえも2,3回しかなかった。もちろん、社内のものは除く。完全に外部の奴ね。MojoMojoみたいな完全にできあがってるアプリケーションってなかなか外に出てこないし、そもそも一回作ってそのままインスコしておしまい、みたいなの多いもんな。

まぁそんな事を踏まえると、githubにソースコードを一通り晒しておくというのは、JPA的にもいいたたき台になってよいんじゃないかとか思ってる。やりかたの賛否両論はあるにしろ、ね。

とりあえずこのままがりがり書き続けて、近いうちにhttp://japan.perlassociation.orgにアップするのでもしご意見等々ありましたら是非お知らせください。
おお、指摘ありがとうございます

DATA_LOCALIZE_DEBUG=1 を使ってデバッグしていただく、というそもそもの目論見の部分がうまく活用できたようでよかったよかった。

ってことでさっき0.00002をアップしました。
Data::Localize 0.00001をリリースした。やってることはLocale::Maketextと一緒。主な違い:

  • オブジェクトベース。
  • Locale::Maketextは名前空間をいじってどうのこうのするんですが、これはしない。基本的には登録されているハンドラを全て試す、っていうだけ。
  • デバッグ用のメッセージを一杯仕込んだので、なぜか国際化がされない場合は多分Locale::Maketextより問題箇所の判断が大分楽
  • Locale::Maketextより速い
速さに関しては、以下の通り:
daisuke@beefcake-7 Data-Localize$ perl -Mblib tools/benchmark/benchmark.pl Rate locale_maketext data_localize locale_maketext 9615/s -- -64% data_localize 26786/s 179% --

で、これをCatalystから使うためのCatalyst::Model::Data::Localizeを作ってみた。こいつはCatalyst::Plugin::I18Nを使わずに国際化するためのツール。といっても、Catalyst::Model::Data::Localizeは単純にCatalystの設定からData::Localizeを設定するためのレイヤーで、同梱のCatalyst::Plugin::Data::LocalizeというモジュールでCatalyst::Plugin::I18Nのようにlocalize()関数を生やすためのものです。Catalyst::Plugin::I18Nを使っていた方ならほぼそのまま使えるかと。

というわけで数週間前にLocale::Maketextの中味を解説しだした私は、結局国際化モジュールを自分で作ってしまった、という落ちでした。
以下、まぁ書き散らかしです。あんまり推敲してません。すまそ。ちなみに、下記記事に対するブクマはDISも多いけど、素直な反応もちらほらあるようで興味深い。




僕にとってのJavaは2001年に終わってますが・・・。同じ事何回も書かなくちゃいけない言語なんて死んだも同然ですよ。ライブラリもちらばってて何がどこにあるのかわかんないし。

って、書くのは簡単です。多分元記事をテンプレ化してほぼ同じ事をどの言語に対しても僕は書けます。

ただ、エンジニアという職種の人がそんなことしてるのはどうかなぁ、と。エンジニアの使命を問題を解くことです。何でつまづいたかとか、なにがむずかしかったとか、何ができなかったとかそういう事をちゃんと書いて欲しいなと思う。CPANのアップロードとかも状況に対しての認識もなく、「回数」という一面だけで判断をばっさりしてていいのでしょうか。

正直Perlという技術が関わっていなかったとしても基本的にツールの有用性を一面でしか判断できないのは危険信号だと感じます。FUDは言い放つのは簡単ですが、なんらかしらのツールが出ていて、それが使われ続けているということはすなわちそこに「何か」がある、ということだと考えが及ばないのはちょっと想像力が足りないんじゃないかと(きっと古い言語でライブラリがアップデートされないCなんて最悪の言語なんでしょうね。うんうん)。

もうちょっとマジレスすると、言語仕様として、下位レイヤーのライブラリはアップデートがないのもあれですが、回数が多すぎるのもどうかと思います。っていうか、CPANモジュールはアップデートに追いつけない!っていうクレームのほうがどう考えてもおおいんだけど、本当に一年に5回も6回もアップデート(しかもその一回一回の中味が多い)とかしてるライブラリを使いたいのかな。そういうライブラリがないわけじゃないけど、きっと今回話に出ていたXML::RSSやXML::Parserがそれに該当したらそしたらそれで文句たらたらになるんじゃないかな。

XML::Parserに関しては機能に限定的な部分がそれなりにあるものの、下位ライブラリとしてはとてもよくできており、これ以上の更新が必要ないと判断されているので更新されていません。実際使ってみて問題があったのでしょうか。そして、さらにXML関係で高機能なものを求めるのであれば、libxml2のインターフェースであるXML::LibXMLなども存在し、それらもかなり安定しているためそれほど更新がありません。XML::LibXMLなどは僕も完全に安心して使い続けられているモジュールのひとつです。

で、それが他の言語だとどう違うのでしょうか?Rubyにしろ、PHPにしろ、それぞれの良いところがあり、使いどころがありますし、それぞれの運用や開発の癖もあるはずです。ひょっとして・・・そのあたりをよくわからないで書いてませんか?

それならそれで構いません。それはこれまでのPerl業界のマーケティングやトレーニングの失敗だからです。でもツールの失敗じゃないですよね?事実関係を見失いがちな放言は同業者としては気をつけていただきたいと感じます。

なお、マーケティングに関しては(←ブクマで指摘されたので修正)、日本国内では僕らが去年から進めているJapan Perl Association (JPA)という団体が責任を持って行います。僕は個人的にはPerlが最強!とはさすがに言いません。僕はとても性にあった言語だと感じてますし、実際この言語のおかげでいままで大変たすかってきていますが、それなりに使いどころがあると感じているので全部のシチュエーションで使えるとはさすがに考えないからです。

JPAは技術者の皆さんに正しい知識とノウハウを持ってPerlの生かし方を判断してもらえるように、そしてPerl及びその後ろに控える優秀なハッカー達の頭脳をうまく皆さんのビジネスに役立てる方法を啓蒙していくつもりです。Javaだって、僕は4年くらいずっと先生として教えてきたから、良いところも知ってます。でもJavaじゃあ工数かかってしょうがない!とか、そういう場所だってあるわけです。そこで色んなチョイスがあるなかで、今回の記事のようなFUDに踊らされず、正しい知識を持ってPerlという言語を判断してもらえるよう、精一杯活動していくつもりです。

JPAは4月の本格活動に向けて、ただいま色々裏で調整してます。もうちょっと待って!





下記ですが、Moose 0.65で直ってますよ!


MooseでRoleを作ります
package Role; use Moose::Role; requires 'foo'; no Moose;
で、このRoleをChildで実装しますが、こいつに親クラスParentがあると仮定します。こんな感じ。
# Parent package Parent; use Moose; has 'foo' => ( is => 'rw', isa => 'Int' ); no Moose; # Child package Child; use Moose; extends 'Parent'; with 'Role'; no Moose;
で、foo()ってattributeがついているんだから、当然Roleのrequiresも満たしてると思いますよね? 満たしてません。

実はMoose::Roleのrequiresによって作成される制限はMoose自身のhasをrequiresの条件を満たす物として認識できないようです。これは既知。なので、以下のようにoverrideしてやる必要があります:
package Child; use Moose; extends 'Parent'; override 'foo' => sub { super() }; with 'Role'; no Moose;
ちなみにnew()で色々と魔法を追加したい場合はaroundを使うといいわけですが、ここにも落とし穴があります。先ほどのParentでaround newをしてみます
package Parent; use Moose; has 'foo' => ( is => 'rw', isa => 'Int' ); around 'new' => sub { my ($next, $class, @args) = @_; # なんかごにょごにょ・・・ return $next->($class, @args); }; no Moose;
ChildはParentから継承しているんだから、Parent::new()およびそのaroundで指定された無名関数が呼ばれると思いますよね?ところがこのままではこのnew()は呼ばれません。

呼ばれるのはnew()から*明示的に*指定されたメソッドと、BUILDだけです。なので、このChildのnew()が呼ばれた時にParentのnew()を呼び出すには、こちらもまたoverrideを使う必要があります
package Child; use Moose; extends 'Parent'; override 'foo' => sub { super() }; override 'new' => sub { super() }; with 'Role'; no Moose;
というわけで皆さん気をつけてくださいね