時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

#yapcasia 2012 に参加してきました

YAPCはおわっちゃいねえ。俺達のYAPCはまだ始まったばかりだ!

と言ってるわけにもいかないので、いい加減書きます

今年のYAPC

今年は東京大学のなんちゃら記念館というところで開催されました。
今年出来たばかりみたいで新しく、綺麗な建物でした。
運営もいろいろ進化していて、英語のセッションで質問をするときにlestrratさんなど英語の話せる方が翻訳してくださったり、受付も素早く、ホールで飲み物も提供されていたりして、運営の方が去年以上に細かいところまで気を配って運営していてすごいと思いました。
運営の皆さんお疲れ様でした。ありがとうございました。
ぼくも来年は運営手伝ってみたいなと思いました。

聞いたトーク

What Does Your Code Smell Like? (Larry Wall)

Perl6の紹介をライブコーディングでやっててすごかった。
Perl6、多機能なのは良いのだけど、whatever starはどうも受け付けない。。。
あとvimの操作が達人過ぎてやばかった。

Nana/Tora - Perl5 から見える未来。Perl5 と共にあゆむ Perl6 ではないプログラミング言語、それは。 (id:tokuhirom)

toraとnode-perlとnpmの話とかいろいろ聞けて面白かった。
あとトークタイトルがやたらかっこよかった。

Perlと出会い、Perlを作る (id:goccy)

C++によるperlの高速な処理系を作って、どういうところを工夫したのか聞けて面白かった。
まだまだ作っている途中の模様だけど、今後どうなっていくかに期待。

Distributed Job System. Clutch (id:nekokak)

Cluntchの紹介。
前々から興味はあったけど、思ってた以上に使いやすそうで、
Dainamo::Profile::Cluntchとか書いて使ってみようかなと思った。

続・Mobage を支える技術(id:xaicron)

Perlでシグナルを扱うときにXSモジュールが絡む場合にどう扱うか、
何故そのように扱わなければいけないかが知れて良かった。
リアルタイムに茶々が入るのも見てて面白かった。

Profiling memory usage of Perl applications (Tim Bunes)

perlのメモリ管理の解説とDevel::Size(?:Me)?の紹介。
自分の英語力の無さを最も呪ったトークだった。
内容的に凄く知りたい内容なのに、言ってる事を節々しか聞き取れないのは痛かった。
でも、スライドやデモが充実してたので、なんとなく理解出来た気がした。
ブログになんかいろいろ書いてくれていたりするので頑張って読みつつ英語を勉強しようと思いました。
そしてトーク中にデモで使ってたWebAppがすごく便利そうなので、使ってみたくなった。(Devel::SizeMeに同梱されてる模様)
英語、勉強するぞ!!11111っておもった。

Perlアプリケーションのベンチマークとプロファイリング (id:sfujiwara)

アプリケーションの高速化のためには計測が欠かせないというところで計測の手法をいろいろ紹介していた。
本番環境で一部のアクセスだけでNYTProfを動かす方法で、POSIX::AtForkを使って一部のプロセスだけでプロファイリングしているのが面白かった。

Perl 今昔物語(Tomihisa Fuon)

いまのYAPCに至るまでPerlコミュニティはどのように変わってきたのか、
Perlをいま使い続ける理由は何があるのか、
これからperlperlコミュニティはどのように変わっていくべきなのか考えさせられて面白かった。

Perl as a Foreign language(id:dankogai)

まだまだperlを使い続ける理由はあるよという事でperlの良い所として、
perlはサーバーサイドでは今でもどこでも動くし、多少変な書き方してても動く。
まともなUnicodeのサポートもあるし、起動速度が速い。という点を挙げていたりして面白かった。

Skyarc Systems presents 「遠方よりの参加者」発表

遠方からの参加者によるLT。id:meru-akimbo がLTやるという事で聴きに来た。
卵かけご飯とこわい人*1のF#の話がインパクトありすぎた。

ウェブアプリケーション開発の現状・課題とJSX (id:kazuhooku)

ビジネス的な視点も入れて、ウェブアプリケーション開発の歴史と現状について説明して、
JSXが何故必要でどういう機能を持っていて、どのような周辺ツールがあるのかなどを説明していて、
内容盛りだくさんで面白かった。特に、僕はビジネス的な考え方がまだまだ下手なので、
その辺りから説明していたのが聞けて勉強になった。

How Perl Changed My Life (id:mizzy)

mizzyさんがperlコミュニティが如何に好きなのかがすごい伝わってきてじーんとした。
(なぜか)ぼくもPerlで初めて触ったWAFはSledgeで、WAF使うとWebAppが簡単に書けて万能感が手軽に味わえるので、
Sledgeでプログラミングが楽しくなったというところはすごく共感出来たりした。
ぼくがプログラミング楽しいなーと思ったきっかけは中3の頃に罪と罰というCGIゲームを改造していたときで、
コードはメッチャクチャ汚い上にすごくバグってたけど、こんな機能入れちゃえとかいうのが簡単に出来て、
俺SUGEEEEEEって出来たのがまるでゲームみたいで楽しかった。
もっとコード書くの楽しめばもっと良いものが作れる気がしてきた。

ぼくのLTについて

2日目にLTをやらせて頂きました。
説明がめっちゃ駆け足で言葉が足りなかったなと猛反しているのですが。
結局何やってたのかよくわかんない感じになってしまって、
デモってむつかしいなーって思いました。

http://yapcasia.org/2012/talk/show/e9030f38-0637-11e2-a778-779e6aeab6a4

このLTで一番伝えたかった事は「こういうアプローチを試してみたけど、あまりうまくいかなかったので、いい方法あれば教えてください!」という事なんですが、
ただ、「『こういうアプローチ』ってどういうアプローチや!!!」って感じになってしまったので、
簡単にですが補足してみたいと思います。

簡単に言いたかった事を補足

ダミーデータはData::RuledFactoryとか使えばすごく楽に作れるし、使わなくてもそんなに苦労せずに作れるんですが、
ダミーデータの型を変えたり、カラムを減らしたり増やしたりする度にDBのテーブル定義を書き換えたりしなきゃいけないかったりします。

アプリケーションの機能開発をしていると、途中でDBのテーブル定義を書き換えたくなったり、ダミーデータの生成規則を変えたくなる事が多くあったりするので、
このArrayRef[HashRef]なダミーデータをDBにわざわざ毎度入れるのが面倒くさくなってきて、
じゃあArrayRef[HashRef]なダミーデータをgrep { $_ eq "hoge" } @$dummy_data;するか?ってなったけど
わざわざTeng使ってるコードをgrepに書き換えるのはナンセンスだなーってなった。


じゃあTengと同じインターフェースでgrepする(ArrayRef[HashRef]なデータ構造をDBにINSERTせずにそこから直接データを検索する)事が出来たら、
毎度毎度DBにINSERTする手間を省けて楽出来るんじゃないかなーと思ったので、
Teng::Plugin::Staticとか書いてみたという感じでした。


やってる事的にはKawaii DBみたいな事をやっていて、

$teng->search(table_name => +{ hoge => +{ '!=' => "hoge" } });

とかやると裏で

grep { $_->{hoge} ne "hoge" } @$table_data;

みたいなコードが走るイメージです。(実際は!=以外も対応してるのですこし複雑です)


利点は、前述した問題(ダミーデータの生成ルールを書き換えたりするたびにDBにINSERTする必要がある)が解消されていることです
また、Tengのインターフェースをそのまま使えるので殆どのコードを書き換えずともロジックがうまく動きます。
欠点は、、、沢山あります!!!
通常の場合と比べて、ダミーデータの生成*2+フルテーブルスキャン的に検索条件の解釈とマッチ+deep clone*3というのをやっている為非常に遅いです。
Storableを用いてcacheする事によりダミーデータの生成をある程度skipすることには成功しましたが、
それでもやはり結構遅いです。
更にもちろん、SQLを解釈しているわけではないので、dbhを直接触っているコードはもちろん、
search_by_sqlなどsql系のメソッドも動作しません。(利用環境にはそういうコードが無い前提でした*4
また、当然ですがRDBMSを使っていないのでベンチマークなど計測目的では使えません。*5


LTでやるには重すぎるネタかなと思いつつ、自分的に一番アツいネタだったのでこれを話してみましたが、
いろいろ省きすぎたなあと思って反省。
でもポジティブに考えれば以前より考えてコード書くようにはなってきたので、
来年は、問題とそのバックグラウンドも含めて解決策とコードを提案する普通のトークが出来たらいいなと思ってます。

*1:@kyon_mm

*2:ただし初回読み込み時のみ

*3:親データに影響を与えない為

*4:Tengの良い所を潰してしまってる感あるけどやむなしと考えた

*5:スライドのダミーデータの用途でベンチマークを挙げたのは誤解を招いたなと反省しました。

LWP::Protocol::Hostsが話題になってたので

Socket::inet_atonをoverrideするとLWPに特化せずにより汎用的になるのではと思って、
それらしきものを書いてみた。
ただし、Socket::inet_atonがexportされる前にuseしなければいけないので、
実用性はあまり高くなさげ。

https://github.com/karupanerura/p5-Net-DNS-Hosts

以下のコードは$scopeが生きている間、Socket::inet_atonをoverrideしてwww.cpan.orgの向き先を127.0.0.1に変えている。

use Net::DNS::Hosts;
use LWP::UserAgent;

my $scope = Net::DNS::Hosts->scope(
    'www.cpan.org' => '127.0.0.1'
);

# override request hosts with peer addr defined above
my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://www.cpan.org/");
print $res->content; # is same as "http://127.0.0.1/" content

Data::Validator::Managerなるものを書いてみた。

メソッドや関数の引数をバリデーションするときにData::Validatorを使わせて貰っているのですが、
低レベルAPIと高レベルAPIで同じバリデーションルールを適用したい事があって、
そういうときに複数箇所にコピペして使ったりしていると、
低レベルAPIのインターフェースを変えたくなったときにそれに依存している高レベルAPIのバリデーションルールも変える必要があってだるかったり、
そもそもクローンとも言えるオブジェクトが複数ヶ所で静的変数に保持されているのは無駄なのではないか。
あと、そもそも5.10未満では静的変数は使えないので5.8しか使えない環境とかで使いたくなったとき困るのではないか*1とか、そんな事を思ったので、
そんな問題を解消するモジュールを書いてみました。

PrePAN: http://prepan.org/module/429En4oFcf
Github: https://github.com/karupanerura/p5-Data-Validator-Manager

使い方は簡単で、以下のようにDSLでルールを定義して、

package Example::Validator;
use strict;
use warnings;
use utf8;

use Data::Validator::Manager::Declare;

rule model_get => +{
    key => 'Str'
} => with(qw/Method/);

1;

自分用のバリデーションモジュールからvalidateをexportしてきて使う。

package Example::Model;
use strict;
use warnings;
use utf8;

use Example::Validator qw/validate/;

sub get_with_cache {
    my($self, $args) = validate(model_get => @_);

    my $val = $self->cache->get($args->{key});
    return $val if defined $val;

    $val = $self->get($args);
    $self->cache->set($args->{key} => $val);

    return $val;
}

sub get {
    my($self, $args) = validate(model_get => @_);

    return $self->backend->get($args->{key});
}

sub delete_cache {
    my($self, $args) = validate(model_get => @_);

    return $self->cache->delete($args->{key});
}

1;

ね、簡単でしょ?


ちなむと、
NoThrow使いたいときとかはget_rule('rule_name')でData::Validatorのオブジェクトを取ってこれるので、それが使えます。


個人的にはこういうのあると凄く嬉しいんですが、他の人はどうなのかなと思ったので、
とりあえずPrePANにあげてみました。
良かったらgoodするなりlikeするなりしてください!

*1:静的変数無くても使えるは使えますが、毎度オブジェクトを生成するのは嫌だったり、オブジェクトを使いまわすために汚い書き方をするのは嫌だったり

Perlで1つのファイルに定義されてるpackageの一覧を得るの法


[追記]
@bokutin さんから Devel::InnerPackage が使えると教えていただきました。
シンボルテーブルを走査して取得してるみたいです。こっちのほうが賢いですね!


同僚に聞かれたので実装してみた。
PPI使えば出来るっちゃ出来るけどもっと上手い方法ないんですかね。

PerlまたはJavaScriptでObjectを文字列として評価したときの挙動をコントロールする方法。

例えば、Perlではオブジェクトを文字列として評価したときの挙動をコントロールしたい場合は、
以下のようにoverloadプラグマを利用して文字列化のためのメソッドをcoderefとして登録すると良い。

package MyURI;
use strict;
use warnings;
use utf8;

use Class::Accessor::Lite (
    rw  => [qw/scheme host path query/],
    new => 1,
);

use overload (
    q{""}    => \&as_string,
    fallback => 1,
);

sub as_string {
    my $self = shift;

    my $uri_str = sprintf '%s://%s%s', $self->scheme, $self->host, $self->path;
    $uri_str .= '?' . $self->query if $self->query;
    return $uri_str;
}

1;

このMyURIのObjectを以下のコードのように文字列として評価させようとすると、
内部的に上のoverloadプラグマで指定したas_stringメソッド*1が実行される。

use strict;
use warnings;
use utf8;

use MyURI;

my $uri = MyURI->new(
    scheme => 'http',
    host   => 'www.example.co.jp',
    path   => '/',
);

print $uri, "\n"; # http://www.example.co.jp/

$uri->query('aaa=bbb');

print $uri, "\n"; # http://www.example.co.jp/?aaa=bbb

Perlではこの方法は比較的よく知られている。*2


先日、業務でJavaScriptで同様の事をやりたくなり、調べたところ、
以下のようにtoStringメソッドをoverrideすると出来るらしい事が分かった。

var MyURI = (function () {
  var Constructor = function (opt) {
    this.scheme = opt.scheme;
    this.host   = opt.host;
    this.path   = opt.path;
    this.query  = opt.query;
  };
  Constructor.prototype.toString = function () {
    var uriStr = this.scheme + '://' + this.host + this.path;
    if (this.query)
      uriStr += '?' + this.query;

    return uriStr;
  };

  return Constructor;
})();

以下のように呼び出してみると、ちゃんと文字列化出来ているのが分かる。

var uri = new MyURI({
  scheme: 'http',
  host:   'www.example.co.jp',
  path:   '/'
});

console.log(uri + ""); // http://www.example.co.jp/

uri.query = 'aaa=bbb';

console.log(uri + ""); // http://www.example.co.jp/?aaa=bbb

参考記事
http://blog.livedoor.jp/dankogai/archives/50851804.html
http://blog.anselmbradford.com/2009/04/05/object-oriented-javascript-tip-overriding-tostring-for-readable-object-imprints/

*1:厳密には、overloadプラグマに渡すcoderefはメソッドとして呼び出される事を想定されたcoderefであればなんでも良いが、メソッドとしても呼び出せるようにしておく事を僕はオススメします。

*2:と僕は思っているが、もしかしたらそんな事は無いのかもしれない。

Kyoto.pmに行って話してきたの巻

勢い余ってKyoto.pmのトークに申し込んだらぎりぎりにも関わらずやらせてもらえる事について発表してきました。
運営のshiba_yu36さんありがとうございました。また、会場を提供していただいたはてなさんありがとうございました。

トーク内容について

正月発火村とかXOClockとかについて話しました。
始まる前からだいぶ緊張してしまっていた事もあり、
発表自体はだいぶぐだぐだになってしまいました。
もっともっとちょっとプレゼンスキル磨きたいナー。


XOClockは便利に使える場面は多そうだけど、
まだまだ足りない機能が多すぎたり、
設計的にアレな部分があるので、
意見言ってくれる人とか、一緒に開発してくれる人とか募集中です!

atコマンドに対する優位性

yappoさんから指摘されたXOClockのatコマンドに対する優位性については、現状では

  • 同時に実行する数を制限出来る
  • ネットワーク越しにenqueue出来る
  • Job毎にタイムゾーンを一緒に指定してenqueue出来る

の3点です。


configでmax_workersを指定する事により同時に実行される数を制限出来ます。
これによって、年越しの瞬間にJobの発火が集中するといった場合に一気に大量にプロセスが生成されるという事態を防ぐ事が出来ます。


また、atコマンドでは間に何かMiddleware的なものを挟まなければネットワーク越しにJobを登録する事が出来ません。
これが出来ない限り、WebApplication等、他のサーバーに対してenqueue出来るようにしなければならない用途では使えません。
sshすれば良いという話もあるかもしれませんが、個人的には嫌です。


また、atコマンドでは時刻の指定は実行環境のタイムゾーンに依存します。*1
よって、様々なタイムゾーンの時刻でenqueueされた場合に、一度何かを通してシステムと同じタイムゾーンの時刻に変換してやらなければいけないという手間が発生します。
XOClockではenqueue時にtime_zoneを指定すれば、指定されたタイムゾーンで時刻を解釈します。


また、将来的にはDBにJobを保存するように出来たりするようにしたいなーと思っているので、
すけーらびりてぃとかもよくなるかもしれません。

Kyoto.pmについて

お菓子食べながらLTしたり、肉に厳しい人が居たりして面白かったです!
OR Mapperのまわりの話とJob Queueについての話が多かったかなーと思います。
特にOR Mapperまわりの話はアツかった。
終わった後鴨川で餃子カンファレンスが開催されたり楽しかったです!


また、観光地なので次の日も色々回れて面白かった。
適当にぶらぶら散歩してたけど、商店街の中にいきなりお寺が現れたり、
適当に裏路地入ってみたらまたお寺が現れたりして京都!京都!ってなりました!
京都タワーも初めて昇りました!
なんか緩いsinカーブしてる感じの鏡があって面白かった!


面白かったのでまた行きたいなーと思いました。

*1:そういえば、atコマンドでenqueue時のタイムゾーンとJobの発火時のタイムゾーンが違ったときってどうなるんでしょう?

最近書いているプロジェクトまとめ

とりあえずプロジェクトを列挙

いろいろなところに手を出しすぎて自分でも混乱してきたのでまとめてみる。

XOClock

https://github.com/karupanerura/XOClock
時刻を指定してJobを実行出来るJobQueue Server。
最近はJobの保存先を変えられるようにしたり、
コマンドをWorkerのように指定する事が出来るようにしたりしている。
perlで書いている。

AnyEvent::ForkManager

https://github.com/karupanerura/p5-AnyEvent-ForkManager
Parallel::ForkManagerのAnyEventのイベントループをブロックせずにwait出来るようにした奴。
ブロックしてwaitする事も出来る。(デフォルトではノンブロック)
IFは少し違うが、だいたいParallel::ForkManagerと同じ感覚で使える。
明日あたりshipitされるのではという噂があるが真偽は定かでは無い。
XOClockでも使っている。

AnyBot

https://github.com/karupanerura/p5-AnyBot
AnySanは便利なのだけど、
AnySanで書くにはちょっと大規模過ぎるかなという規模のbotを書くときに
ロジックを整理しやすいようなIFでbotを書けるというような事を目指している、
bot framework。
ほぼ自己満足のためだけに書いているという説も濃厚。
最近飽きてきたという噂も。

Atelier

https://github.com/karupanerura/p5-Atelier
作った本人も忘れていたかもしれない俺々Web Application Framework。

  • Sledge互換
    • Plugin使えば完全互換
    • Sledge::Pluginも使える
  • Pluginを手軽に書ける

という事を意識して作っているらしい。
ほぼ自己満足のためだけに書いているという説がかなり濃厚。
最近飽きてきたという噂も。

Tako

https://github.com/karupanerura/Tako
Webからユーザー登録/設定して使えるtiarra的な機能を持ったIRC Proxy Serverが欲しいと思って作り始めたらしい。
弊社内 #発火村 ではじめてコードを書いたと思わせつつ、実は同じ思想で作っていたPirocyというプロジェクトの後継。(Pirocyは公開していません)
Pirocyは1からがむしゃらに実装してコードがぐちゃぐちゃになったという反省点を生かして、Amon2+AnyEventベースで、なるべくロジックを分離出来るような設計にして開発しているらしい。
ついでにAnyEvent::DBI用の簡単なO/R Mapperができそうな気配なのでそこは分離する可能性があるらしい。

優先順位

さて、これだけ謎プロジェクトが増えすぎると「あれも開発したい」「だけどこれも開発したい」となって1つのプロジェクトに集中する事が出来ない。
そこで、優先順位を付けることにした。

  • 実用性
    • 業務で活用出来るか
    • これを利用する事でメリットを得られる場面は多いか
  • 汎用性
    • さまざまな場面で活用出来るか
    • これを利用する事により無駄に冗長なコードや仕組みが減らせるか

を重視し、優先順位をつけるとしたら以下のようになっただ。


XOClock > Tako > AnyEvent::ForkManager > AnyBot > Atelier


というわけでこの優先順位で今後は開発を進めていくとおもう。たぶん。