2008年01月30日

WWW::HatenaLogin - はてなにログインするだけのモジュール作った

はてなのサービスにログインした状態をスクレイピングして利用したい時にや、そういったモジュール(WWW::HatenaDiary)を作りたい時になると気になるのが、毎回毎回https://www.hatena.ne.jp/loginをスクレイピングするのは面倒だなぁと思ったので、WWW::HatenaLoginというログイン処理に関する事しかしないモジュールを作りました。
作ったとはいっても、殆どがWWW::HatenaDiaryからのコピペです。
WWW::HatenaDiaryのログイン部分だけ抜き出してCPANモジュール化した感じですね。

codereposはhttp://coderepos.org/share/browser/lang/perl/WWW-HatenaLogin/trunkです。
CPANにもupload済みで、予定地はhttp://search.cpan.org/dist/WWW-HatenaLogin/ です。
CPANに上げてるテストには含まれてないけど、codereposの方はConfig::Pitを使ってhatenaアカウント情報を取得してテストするコードにしてみました。

やってくれる事はWWW::Mechanizeを使って、はてなにログインとログアウトを行います。
ログイン後のWWW::Mechanizeのオブジェクトがそのまま利用出来るので、簡単にログイン後のはてなをスクレイプできます。
当然cookie_jarも取れるのでLWP::UserAgentで処理を書く事も出来ます。

naoyaさん謹製のHatena::API::Authとの違いですが、naoyaさんのは「はてな認証API」をの為のモジュールで自分のはWebブラウザから使うログインフォームからPerlでログインする為のモジュールという違いです。

早速WWW::HatenaDiaryがWWW::HatenaLoginを使うようにしたブランチを切っりましたので、サンプル的なコードを見たい方は、そちらでどうぞ。

Posted by Yappo at 00:03 | Comments (0) | TrackBack

2008年01月24日

autobox の実装を調べたのと tokuhirom のパッチを発展させた

Perl界隈で熱いと噂のautoboxの実装を調べたりtokuhiromパッチを発展させてみました。
autoboxはsexy。

このエントリのまとめ

本文が長過ぎるので先にまとめを書く。

autoboxはPerlの内部実装をhackして、とてもスマートに実装されたモジュールだというのがわかりました。
一カ所だけアクロバティックな事してますが、それ以外は無理無く実装されていて普通に使う分には十分使えるものっぽいというのが判った。

tokuhiromのパッチの件

tokuhiromのパッチはINTEGERとかFLOATとかSTRINGを別けて扱える様になって良いんだけど、既存のSCALARを拡張したautobox::Encodeとかが動かなくなってとても困るので更にパッチを書いた。
取得はhttp://tech.yappo.jp/tmp/autobox-tokuhirom-yappo-2.patchから。

やっている事としては、オブジェクトがINTEGER,FLOAT.STRINGの時に、それぞれのパッケージにメソッドや変数の定義をしていない場合にSCALARを利用するようになります。
細かく書くとautoboxをimportした時に利用するパッケージにisaとcanのメソッドを生やすので、例えばINTEGERの場合には%INTEGER::にエントリが2個以下しか無い時は、SCALARを使うようにしています。

@ISAにSCALARを突っ込めば良いじゃんという話もありますが、@ISAに突っ込んでしまうと意図しないメソッドまで継承してしまう可能性があるので、INTEGERパッケージにメソッド等が定義されていなければ、利用するパッケージをSCALARパッケージに切り替えるという事をやっています。
もし継承させたいなら、利用者が明示的に

@INTEGER::ISA = qw( SCALAR )
するというポリシーです。

とりあえず既存のautoboxのテストが動く状態になったので、追加された要素のテストを書いてchocolateboyにパッチを送ってみようと思うのですが、いかがでしょうか。
2個以下の時はSCALARってのがad hoc過ぎる気がするのが気がかりですが。

ここから本編

物凄くざっくりとコードを読んだ時にメモしたりしたので、もったいないので公開しときます。
だいぶザックリとしてるので信憑性に問題があるかもしれません。
それでも問題無いよ、という人だけ読んで下さい。

autobox.pm

autobox.pmは、importでレファレンスタイプをどのパッケージに割り当てるのか、hintsを使ってuse autoboxをしている側のローカルスコープの中だけでautoboxされる様に良きにはらってくれる。
unimportも実装されてるけど使いどころを良く知らない。

ではautobox::importの処理を簡単に追っていきましょう。

use autobox SCALAR => 'MySCALAR'
等でuseされた場合には、"hoge"->foo;された場合にはSCALAR::fooを呼ぶのではなくMySCALAR::fooを呼ぶ。

hintsの操作もimportでやっている。
0x20000 だけでいいんだけど %^H 周りのバグがあるらしくて 0x100000 も追加しておかないといけない。
これをやる事により %^H の中身がスコープの中でしか有効にならない。
もっというと use autobox されたスコープの中でのみ有効になる。

$^Hとか%^Hのいわゆるhint情報は、コンパイラが動作する為のヒント情報なのでBEGINフェーズ中でしか意図どおりに動かない。
下記のコードは意図どおりになるが

use strict;
use warnings;
BEGIN {
    $^H |= 0x120000;
    $^H{hoge} = 'start';
}

BEGIN{warn $^H{hoge}}
{
    BEGIN{$^H{hoge} = 'YYY';warn $^H{hoge}}
    BEGIN{warn $^H{hoge}}
}
BEGIN{warn $^H{hoge}}
下記のコードは意図どおりにならない
use strict;
use warnings;
$^H |= 0x120000;
$^H{hoge} = 'start';
warn $^H{hoge}
{
    $^H{hoge} = 'YYY';warn $^H{hoge};
    warn $^H{hoge};
}
warn $^H{hoge}

最後にautobox.xsの中にあるenterscopeを呼び出す。
enterscopeは後述するが、perl内部のフックをautobox用に入れ替える。
遂になる物としてはleavescopeがあり、これはautoboxのスコープが抜けた時に元に戻す処理をしている。
スコープが抜けたという事は0x20000のhint bitsと%^HとScope::Guadを上手く組み合わせて実装している。

Scope::Guadというのは以下のような事をやります。

if (1) {
    my $sg = Scope::Guad->new(sub { } ); # DESTROYした時に呼ばれるコードを登録
    # ... 処理
}# ここで登録したコードが呼ばれる
インスタンスがDESTROYした時にnew時に設定したコードを実行するというシンプルな物です。

use autoboxされたスコープを抜けるとScope::GuadのインスタンスもDESTROYされる事になり、Autobox::leavescopeが呼ばれる事になる。
なんでDESTROYされるかというと、%^Hの中にScope::Guadのインスタンスを入れとくので、スコープ抜けると%^Hのなかがクリアされるので、Scope::GuadのインスタンスがDESTROYされるのである。

importの中に書かれたらimportのスコープを抜けた時点でDESTROYされるじゃないか。という突っ込みもあるかもしれないが、useされた時はどうやら別物らしい。

autobox.xs

次はいよいよautobox.xsの中身だ!
autobox.xsにはAutoboxというpackageに属する、enterscope,leavescope,ENDという3個のメソッドが実装されている。
ENDは、いわゆるENDフェーズのアレだから省略する。

leavescope

autoboxの影響化を抜けた時に後述するenterscopeで入れ替えたメソッドを元に戻す。

enterscope

Autobox::enterscopeは、use autoboxされた時に呼び出される、autobox::importの最後の処理でenterscopeを呼び出している。
何をやっているかというと、PerlのVMのとあるフェーズで実行される処理を差し替えている。
具体的にPerl本体のコードで言うと、opcode.hの

EXT OP *(CPERLscope(*PL_check)[]) (pTHX_ OP *op) = {
が書かれているPL_checkというリストの中にフックテーブルがあって、そのフックテーブルを入れ替えている。
フックテーブル/VMのオペコードにどんなのがあるかはopcode.hとopnames.hを見ること、これはテストにでるよ。
PL_checkが何なのかは調べてない。何となくコンパイルフェーズでチェックするフックポイントテーブルみたいなのだと思ってる。

何を差し替えているかというと、OP_METHOD_NAMEDというOO的なcodeで指定されたmethodが存在するかといったチェックとかをやるフェーズっぽい。
本来はop.cのPerl_ck_nullが登録されてるので、何をやるべきかわかってない。
で、autobox.xsのautobox_ck_method_namedに差し替えられている。

autobox_ck_method_named

use autoboxされたスコープの影響範囲にいなければ本来の処理と同じ挙動になる。
影響範囲にいる時には、$^H{autobox}の中に入っている handler table をPL_hintgvから取り出しておき、autoboxの胆となる処理が走る。

autobox_method_named

このメソッドがautoboxの肝。
ここで呼ばれたメソッドがどういう形("hoge"->foo, 1->foo, []->foo, {}->foo)で呼ばれるのか、何のリファレンスタイプで呼ばれてるのかを調べている、具体的にはtokuhiromのpatchしてる辺り。
そして、リファレンスタイプに対応するhandler tableに登録されてる実際に使われるpackage nameを取り出す。
gv_stashpvnというのは指定されたパッケージ名に紐づけられたhashを取り出すもの、Perlのコードでいう所の%SCALAR::みたいな物。gv.cを見よ。
そしてごにょごにょ処理をする。

gv_fetchmethod(stash ? stash : (HV*)packsv, name);
の所では、パッケージの中にメソッド定義されているかを調べる感じ。

autobox_method_namedでもっとも大事な部分というのはXPUSHsでgvをpushしてる所。
スタックを操作することにより、

1->foo;
といったpackage nameでもなくオブジェクトリファレンスでも無いコードに対して、1という部分がSCALARというオブジェクトレファレンスなんだよと錯覚させてしまうのである。と思う。スタック型なんだなぁというのがわかる。

メソッドが見つからなければ

return PL_ppaddr[OP_METHOD_NAという所にいく。
これはメソッドがねーぞというエラーを出すことと同等。

autobox_ck_subr

OP_ENTERSUBというフェーズも書き換えている、本来はop.cのPerl_ck_subrというメソッドを呼んでおり、処理内容を見るとprototype宣言のパースをやってる。
何となくだけど、autobox_ck_method_namedで変更したフラグを戻すとかやってるのかな?
これといって大げさな処理は行われていない。

おまけ

以前encoding::sourceを5.8で動かそうとして無理だった件で、%^Hの値がレキシカルスコープ担ってないからってのがあったんだけど、autoboxを見たらhint bitsに0x20000を立てとけば同じような挙動になるのが解った。
それなんでdan.pmとかencoding::sourceをdorからdefined or にcallerの10個目のを%^Hに、import/unimportで0x20000の出し入れをやってみたけど、うまく動かなかった。
callerの件が実装されてないので、もしかしたらBEGINフェーズ以外でも%^Hが必要になってるのかもしれない。
こんど改めて調べてみることにする。

ちなみに、まだinstall autoboxしてない。

Posted by Yappo at 20:20 | Comments (1) | TrackBack

2008年01月22日

1000speakersでXenとか話して来ました

先週土曜日に開催された「1000人のダム好きを集めるプロジェクト」にてXenやらCobblerとかの話をして来ました。
発表の様子は↓のニコニコ動画で。というかうまく再生出来ない人が多いからhttp://www.nicovideo.jp/watch/sm2078669からどうぞ。

弾幕とか貼るならこっちから
yappoタグも増えて来たな。

動画環境はcojiさんが私財をなげだして素晴らしい機材を買ってくれたのでとてもかっこ良いustreamが出来ていました。
そして即座にニコニコへuploadするという神業に直立不動が止まりませんでした。

技術系カンファレンスのプレゼン動画がニコニコに溜まって来てるので、そろそろエンジニアMADってジャンルな動画が出て来て欲しいすね。
たとえば今回の発表は全部CCライセンスなのでhttp://www.nicovideo.jp/mylist/4573744ここのエンジニア素材が使い放題!
amachangは大変な強制終了をしていきました的な動画が見たいので誰か作って!

発表時間の30分前まで延々とプレゼンツールの実装をしていて、ネタを確定したのが本番30分前でした。
最初は言語的な話を考えていたのですが、レジュメみたら言語的な発表の比重が多かったので趣向を変えてみました。
デモンストレーションも特に用意していた訳でなく、個人で所有しているXenとかの実験環境にログインしといてそのまま操作しただけでした。

こんなに直前まで話す事を考えていなくても何とかなるとは思っていなかったんですが、結局は自分が面白いと思った事だったら台本が無くても説明出来ちゃう訳なんで、1000speakers dam project でもプレゼン経験無い人がどんどん出て来て成功出来ると思った。
エンジニアとかそういう人種だったら、人に伝えられる引き出しが10個以上常にストックされてて、即座にデモする環境とかを持っているので、トーク経験無い人でも緊張しても良いから出るといいよ。

さて本題ですが、今度の日曜日に鎌倉でSoozy Conference #4が開催されるのですが、懇談会会場予定のお店が最低30名から貸し切りに出来るとの事で、少なくともあと6名くらいの追加参加者を募集しています。
wikiにも書かれていないスペシャルゲストも参加するので、参加制限を緩くしたこの機会に是非ご参加を

関係無いけど

13:14 < t*kuhir*m> ドアドアってなに?
どんびきした

Posted by Yappo at 13:29 | Comments (0) | TrackBack

2008年01月20日

自宅近隣が劇毒汚染されてるお

ネタで書いたら新日石から訴えられるから真面目に書いてるけど。

すごく近所に昭和36年くらいから操業してる、ガス臭いのを巻き散らしてる新日石のグリセリン工場があって、最近工場閉鎖して環境改善の住民説明会があったのですよね。
自分は参加するの忘れてたんだけど、ついさっきマンション住民の総会が有って参加して来て、住民説明会のレポートが有ったんですよね。

そしたら何と、ヒ素が政府基準値の三倍!鉛が数十倍!ベンジンとか色々な物も基準値オーバーで面白い汚染具合になってるようでした。(新日石作成の資料見た)
そして、新日石の工場の跡地の区画のみの土砂をそう取っ替えする工事を来月から来年初夏までやるんですって奥さん!
土壌汚染ってそういうもんじゃないのにw

あとビックリしたのが、鉛とかは石油を扱っていたので新日石に原因が有るけどヒ素が基準の三倍有るのは、新日石はヒ素とか使ってないので自然由来だと報告書に書いてあった。
自然発生して政府基準の三倍の量なんてすごいすな。
政府基準を変更してもらわないといけないと思う。


何が言いたいかというと、そんな毒素満載な土壌で山芋とかジャガイモをいっぱい作って食べてる俺サイキョウwwwwwwww

林真須美の謎―ヒ素カレー・高額保険金詐取事件を追って
週刊文春特別取材班
ネスコ (1998/12)
売り上げランキング: 573035
Posted by Yappo at 15:53 | Comments (0) | TrackBack

2008年01月11日

LvalueなAccessorのベンチマーク

空前のlvalue期に突入したので、lvalueなアクセサを作れるClass::Accessor::LvalueとClass::Accessor::Lvalue::Fastを比較したベンチマーク取ってみました。

package Tied;
use strict;
use warnings;
use base 'Class::Accessor::Lvalue';
__PACKAGE__->mk_accessors(qw/ a b /);
1;

package Tied;
use strict;
use warnings;
use base 'Class::Accessor::Lvalue::Fast';
__PACKAGE__->mk_accessors(qw/ a b /);
1;

bench.pl

use strict;
use warnings;

use Benchmark qw(:all);

use Tied;
use Fast;

my $inst  = {};
my $codes = {};

for my $pkg qw( Tied Fast ) {
    $inst->{$pkg} = $pkg->new;
    $codes->{$pkg} = sub {
        my $a = $inst->{$pkg}->a;
        $inst->{$pkg}->a++;
        $a = $inst->{$pkg}->a;
        my $b = $inst->{$pkg}->b;
        $inst->{$pkg}->b++;
        $b = $inst->{$pkg}->b;
    };
}
cmpthese(100000, $codes);

以下結果

[yappo@stfuawsc lvalue]$ls
Fast.pm  Tied.pm  bench.pl
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   8285/s    --  -92%
Fast 107527/s 1198%    --
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   9251/s    --  -91%
Fast 108696/s 1075%    --
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   9083/s    --  -91%
Fast 106383/s 1071%    --
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   9066/s    --  -92%
Fast 111111/s 1126%    --
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   9372/s    --  -92%
Fast 114943/s 1126%    --
[yappo@stfuawsc lvalue]$perl ./bench.pl 
         Rate  Tied  Fast
Tied   9259/s    --  -92%
Fast 111111/s 1100%    --
[yappo@stfuawsc lvalue]$

10倍くらいの差がでました、やはりtieを使ってるとこんなん結果になるんですね。

lvalueの挙動というのは、lvalue attributeを付けられたメソッドが最後に返したスカラ(returnでかえしちゃだめ)に対して代入処理を行っているため、lvalue が付いたメソッド自体は

$self->attr() = 'value'
の形で代入された事を感知出来ません。
そこでtieを使って感知も出来る様にした実装がClass::Accessor::Lvalueなのです、しかしtieの機能が要らない人に取っては余計な遅さなので、tie使わないシンプルな実装としてClass::Accessor::Lvalue::Fastが在る訳です。

やっぱり足して2で割ったやつが欲しいな。

Posted by Yappo at 18:03 | Comments (0) | TrackBack

2008年01月04日

Cobbler Koan を CentOS な Xen で動かしたよ

Xenが熱かった今年ですが皆様いかがおすごしでしょうか。
Koan - mizzy.org - Trac

これで Xen ゲスト OS のインストールがはじまる、はずなんだけど、うちの環境では Segmentation Fault になってしまった。VMWare 上で Xen を動かすというのは無理があるのかもしれない。 実機で試したいところだけど、試せるサーバがないので保留。Yappo さんあたりが試してレポートしてくれるに違いない、きっと。
という牙指令を頂いたので試してみましたよ!

今回の環境をざっと説明すると、Cobblerを入れるマシンはCentOS5.1(i386)な環境で(master server)、cobbler importするディストリビューションはCentOS5.1(x86_64)、koanでネットワークする環境はCentOS5.1(x86_64 xen)な環境(client)です。
Cobblerを入れるマシンでは、元々ネットワークインストールする為のdhcpd,tftpd,httpdなど諸々の環境が入っていたので、とてもとても楽でした。

まず問題なのがmizzyさんはFedora 7っぽいので自分のCentOS 5.1な環境だとmizzyさんの例どおりにはできない!
といってもmizzyさんが参考にした元記事はCentOSでやってるので、そっちを参考にした。
サーバの構築を簡単にするためのステップ (その5:Cobber編 Part1 )
予め/var/lib/cobbler/settiongsで使ってるportを空けておきましょう。

Cobblerが入ったので早速import

master # cobbler import --mirror=/centos/5.1 --name=CentOS5.1
時間はかかるけどimportは簡単に終わりました。
cobblerdとかstartしたりcobbler reportでの確認や、細かい設定はmizzyさんの所を見ながらやって下さい。
Cobbler - mizzy.org - Trac

次はXenゲストOSのインストールです。簡単です。

client # koan --server=192.168.10.1 --profile=CentOS5.1-xen-x86_64 --virt --virtname=koan1
- downloading initrd initrd.img to /var/lib/xen/initrd.img
- url=http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/initrd.img
- downloading kernel vmlinuz to /var/lib/xen/vmlinuz
- url=http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/vmlinuz
- kernel saved = /var/lib/xen/vmlinuz
- initrd saved = /var/lib/xen/initrd.img
reconnect with xm console koan1
installが始まりました。
--virtnameオプションでバーチャルマシン名を指定しておいた方が良いです。
xm console koan1すれば、インストールプロセスを見る事が出来ます。

一件落着の様ですが、xenのimageが/var/lib/xen/images以下に置かれてしまうので、別のディレクトリにxenのイメージファイルを置きたい場合にはストレスが溜まってしまいます。
mizzyさんとこに書いてあるcobbler reportだとvirt-pathみたいな設定があるのですが自分所だと設定出来ません。
どうやらrpmforgeのCobbler/Koanのバージョンが古いせいだったようです。
という事で新しめのバージョンにupdateします。

master # wget ftp://mirror.linux.duke.edu/pub/fedora/linux/extras/6/SRPMS/cobbler-0.6.3-2.fc6.src.rpm
master # rpmbuild --rebuild cobbler-0.6.3-2.fc6.src.rpm
master # rpm -U /usr/src/redhat/RPMS/noarch/cobbler-0.6.3-2.noarch.rpm
client # wget ftp://mirror.linux.duke.edu/pub/fedora/linux/extras/6/SRPMS/koan-0.6.3-3.fc6.src.rpm
client # rpmbuild --rebuild koan-0.6.3-3.fc6.src.rpm
client # rpm -U /usr/src/redhat/RPMS/noarch/koan-0.6.3-3.noarch.rpm
これで新しめになったとおもいます。

準備が出来たはずなので、設定を変更します。

master # cobbler profile edit --name=CentOS5.1-xen-x86_64 --virt-path=/iscsi/xen
master # cobbler sync
master # cobbler profile report
profile         : CentOS5.1-x86_64
distro          : CentOS5.1-x86_64
kickstart       : /etc/cobbler/kickstart_fc6.ks
kernel options  : {}
ks metadata     : {}
virt file size  : 5
virt ram        : 512
virt type       : auto
virt path       :
virt bridge     : xenbr0
virt cpus       : 1
repos           : []
dhcp tag        : default
server          : <<inherit>>

profile         : CentOS5.1-xen-x86_64
distro          : CentOS5.1-xen-x86_64
kickstart       : /etc/cobbler/kickstart_fc6.ks
kernel options  : {}
ks metadata     : {}
virt file size  : 5
virt ram        : 512
virt type       : auto
virt path       : /iscsi/xen
virt bridge     : xenbr0
virt cpus       : 1
repos           : []
dhcp tag        : default
server          : <<inherit>>
無事にXenのイメージファイルのパスが/iscsi/xenに変更されました。

さっそく正しいイメージファイルのパスでinstallできるかやってみます。

client # koan --server=192.168.10.1 --profile=CentOS5.1-xen-x86_64 --virt --virt-name=koan2
- using kickstart from cobbler: http://192.168.10.1/cblr/kickstarts/CentOS5.1-xen-x86_64/ks.cfg
- no virt-type specified, auto-selecting xenpv
libvirt_qemud (pid 2870) を実行中...
downloading initrd initrd.img to /var/lib/xen/initrd.img
url=http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/initrd.img
- using kickstart from cobbler: http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/initrd.img
downloading kernel vmlinuz to /var/lib/xen/vmlinuz
url=http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/vmlinuz
- using kickstart from cobbler: http://192.168.10.1/cobbler/images/CentOS5.1-xen-x86_64/vmlinuz
use virt-manager or reconnect with virsh console koan2
Koanのupdateの影響で--virtnameオプションが--virt-nameとオプション名が変わってる事に注意して下さい。
インストールが終わったらlsコマンドで確認です。
client # ls -l /iscsi/xen/koan2-disk0 
-rwxr-xr-x 1 root root 5368709121 Jan  4 19:13 /iscsi/xen/koan2-disk0
ちゃんと/iscsi/xen以下にイメージファイルが出来ました。

これから更に発展させてDSASのようなネットワークブートするイメージの管理をCobblerを使って実現とかしてみたいよね、とか話していたらmizzyさんにStateless Linuxを教えてもらいました。
どっちにしろinitrdとかイメージとかを自分で作ってかなきゃいけないので、もすこし色々やってみようと思います。

Posted by Yappo at 20:12 | Comments (0) | TrackBack

2008年01月01日

CodeReposでShipItを快適に使うモジュールをCPANにうp

年納めという事でShipItを使ってCPANリリースする時にCodeReposのCommit messege rulesにそったコミットメッセージを吐くようや拡張としてShipIt::Step::CommitMessageWrapをリリースしました。
最初のバージョンあたりは、テストしつつうpるというとんでもない事やってたので動きません。
せめてversion 0.03 から動きます。

何をするかと言うとShipItがsvnとかでコミットした時のコミットメッセージに任意のコメントを追加出来たりします。
例は実際http://coderepos.org/share/browser/lang/perl/ShipIt-Step-CommitMessageWrap/trunk/.shipitを見て下さい。

ついでにpmsetupの方も良い感じにsetupしてくれるように変更しました。

Posted by Yappo at 00:03 | Comments (0) | TrackBack