<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xml:lang="ja">
  <channel>
    <title>JPerl Advent Calendar 2009</title>
    <link>http://perl-users.jp/articles/advent-calendar/2009/</link>
    <description>Perl に関するちょっとした Tips をのっけてみるよ。ちゃんと続くかな？</description>
    <item>
      <title>最後の日は最後の一桁を埋めるAlgorithm::CheckDigits</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/casual/25.html</link>
      <description><![CDATA[<div class="section">
<p>メリクリ！メリクリ！piarraです。</p>

<p>Advent Calendar最後の日は、最後の一桁を埋めるCPANモジュール「Algorithm::CheckDigits」をご紹介します。</p>
<p>このモジュールについて、日本語で紹介されているブログ等を見つけられなかったので、敢えて選んでみました。</p>

<p>こんな経験はありませんか？</p>

<ul>
<li>7桁しかないidを8桁にしたいのだけれど、8桁目を0やランダム値にするのはどうよと思ったとき</li>
<li>なんらかの理由で連番ではないidの生成をしたいとき</li>
<li>JANコードを発行したいのに、何故か12桁しか作れなくって、最後の一桁は自分で作れっていわれても…？なとき</li>
<li>クレジットカードの右端が12mm折れてしまって放置していたら、いつの間にかその破片をなくしてしまったけれど、取り急ぎネットで本を買いたいのに、クレジットカードの最後の一桁がどうしても分からないとき</li>
</ul>

<p>そんなときは、Algorithm::CheckDigitsの出番です。</p>
</div>
<div class="section">
<h3>チェックデジットとは</h3>
<p>さて、「CheckDegit(チェックデジット)」という言葉に馴染みのない方もいらっしゃると思うので、まずは基本的なお話から。</p>

<p>Wikipediaによると、</p>
<pre>
「チェックディジット（check digit, 検査数字）とは、
符号の入力誤りなどを検出するために元の符号に付加される数字のこと。」
</pre>
<p>とされています。</p>
<p>馴染みの深いところでは、バーコードなどで読み取りミスを防ぐために、最後の一桁にチェックデジットを付与して、データを誤って読み取ってしまうことを防いでいます。</p>

<p>例えば、「初めてのPerl」のISBN"978-4-87311-126-1"の最後の"1"はチェックデジットです。</p>
</div>
<div class="section">
<h3>計算してみる</h3>
<p>まずは実際に上のISBNコードのチェックデジットを計算してみます。</p>
<p>ISBNコードやJANコードは、「モジュラス10 ウェイト3・1」というアルゴリズムで算出されます。</p>
<p>このアルゴリズムでは、"左から奇数桁の数字の合計"と、"偶数桁の数字の合計を3倍にしたもの"を加え、10からその和の下一桁の数字を引いてチェックデジットを求めます。</p>
<pre>
(9 + 8 + 8 + 3 + 1 + 2) + (7 + 4 + 7 + 1 + 1 + 6) * 3 = 109 ・・・下一桁は9
10 - 9 = 1 ・・・ この1がチェックデジット
</pre>

<p>確かに計算結果と実際の値とが一致しました。比較的簡単な計算のアルゴリズムですね。</p>
</div>
<div class="section">
<h3>Perl登場｜・ω・)ノ</h3>
<p>Perlの苦手な方はチェックデジットくらい暗算をするみたいですが、</p>
<p>小学校を卒業した賢いCasual Perlerの皆さんは当然Perlを使いますよね。</p>

<p>というわけで、Perlで簡単にチェックデジットを付与し、あるいは検証するモジュールの紹介です。</p>
</div>
<div class="section">
<h3>Algorithm::CheckDegits</h3>
<pre>
use Algorithm::CheckDigits;

my $isbn13 = CheckDigits('ISBN13');
if ($isbn13->is_valid('978-4-87311-126-1')) {
    # OK
}

my $lp_isbn = $isbn13->complete('978-4-87311-126');
print $lp_isbn . "\n";
</pre>

<p>上の通り、CheckDigits()関数にて付与したいチェックデジットの種類を指定します。</p>
<p>ここでは、13桁の新ISBNコードを使っていますので、"ISBN13"という指定をしています。</p>
<p>他には、クレジットカードの"VISA"や"AMEX"、"DINERS"、Amazon等でおなじみの"EAN"(JANもこれと同じ)などが指定できます。</p>
<p>指定できるアルゴリズム名の一覧は、</p>
<pre>
my @ml = Algorithm::CheckDigits->method_list();
</pre>
<p>で取得できます。</p>
<p>ちなみに、m10_005などという命名がなされていますが、それは必ずしも名前通りモジュラス10ウェイト5という意味ではないようですので注意が必要です。</p>
<p>※実際にはm10_004がモジュラス10ウェイト3.1に該当、m10-005がモジュラス10ウェイト4.9に該当など</p>

<p>実際のアルゴリズムを確認したい場合は、以下のようにperldocを参照するか、ソースをご覧ください。</p>
<pre>
perldoc Algorithm::CheckDigits::M10_005
</pre>
</div>
<div class="section">
<h3>様々な用途</h3>
<p>チェックデジットというのは、数値の検証に用いるのが本来の用途だと思いますが、それ以外にも私自身は以下のような用途で使っています。</p>
<ul>
<li>IDの桁数を揃えたい場合で、桁数に余裕があり、かつ連番やゼロを避けたいとき(00001,00002,00003...よりも00016,00025,00034...とすることで連番を避けた様に見える)</li>
<li>JANコードやISBN等を保存する際、1文字分の容量を節約したいとき</li>
<li>彼女の誕生日の下一桁を忘れたとき</li>
</ul>
</div>
<div class="section">
<h3>クリスマスチェックデジット</h3>
<p>チェックデジットでクリスマスがcompleteできるか遊んでみました。</p>
<pre>
my $christmas = CheckDigits('EAN');
print $christmas->complete('2009122'); # 結果はご自身でご確認ください:)
</pre>
</div>
<div class="section">
<h3>最後に</h3>
<p>さて、25日間にわたってお楽しみいただいたJperl Advent Calendar - Casual Trackも今日で最後となりました。</p>
<p>毎日欠かさずチェックいただいたcasual perlerやperl hackerの皆さんありがとうございました。</p>
<p>そして、今日まで途切れることなくバトンをつないで頂いたcasual perlerの皆様お疲れ様でした。</p>

<p>また来年のAdvent Calendarに参加できることを楽しみにしています。</p>
<p>それでは、皆様良いお年を。</p>
</div>
<div class="section">
<h3>参考</h3>
<ul>
<li><a href="http://search.cpan.org/~mamawe/Algorithm-CheckDigits-0.53/">http://search.cpan.org/~mamawe/Algorithm-CheckDigits-0.53/</a></li>
<li><a href="http://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%87%E3%82%A3%E3%82%B8%E3%83%83%E3%83%88">http://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%87%E3%82%A3%E3%82%B8%E3%83%83%E3%83%88</a></li>
<li><a href="http://ja.wikipedia.org/wiki/JAN%E3%82%B3%E3%83%BC%E3%83%89">http://ja.wikipedia.org/wiki/JAN%E3%82%B3%E3%83%BC%E3%83%89</a></li>
</ul>
</div>
<div class="section">
<h3>Special Thanks</h3>
<p>id:kimury</p>

</div>
]]></description>
      <dc:creator>piarra</dc:creator>
      <pubDate>Fri, 25 Dec 2009 12:24:15 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>あとがき</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/data-model/25.html</link>
      <description><![CDATA[<div class="section">
<p>ほんとに無謀な一人 advent calendar thone だと思いましたが無事に完走出来ました。</p>

<p>とちゅうは dann さんのありがたくて作り手としても良いところを突いてくれたと思える良記事を出して頂き嬉しかったです。</p>

<p>あと良き競合プロジェクトの nekokak さんの dbix-skinny の track もあったおかげでここまで書けました。</p>

<p>もともとこのチャレンジを始めたきっかけですが Data::Model 自体のドキュメントがあまりにも整備されていなくて折角だから advent calendar という機会に任せてドキュメントを充実させてみようと思って始めました。</p>

<p>完走して終わりではありません。これの成果を元にドキュメントをきっちり書くところまでやりたいと思います。</p>
</div>
<div class="section">
<h3> 今後の Data::Model</h3>

<p>今後の予定ですが、夏に sfujiwara さんからいただいた Pg 対応の取り込みやら、 リレーショナルスキーマのサポートやら細かい fix 項目など TODO はまだまだあります。</p>

<p>Data::Model はリレーショナルしないっていってたじゃん！ってツッコミもありますが、いい実装方法を思いついたのでやってみようと思います。</p>

<p>もし今回の企画で Data::Model に興味をもって使ってみようと思っていただけたら嬉しいです。</p>

<p>不明点やらなんやらがあれば #perl-casual@freenode やら #data-model@irc.perl.org やら #dbix-skinny@irc.perl.org やら適当なところで捕まえてくれればとおもいます。</p>

<p>ではでは、本当に僕の advent calendar 2009 はこれにておわりです。</p>

<p>水着もってバカンスいってきます。</p>
</div>
]]></description>
      <dc:creator>yappo</dc:creator>
      <pubDate>Fri, 25 Dec 2009 06:22:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Module::Requires で依存モジュールをきっちりチェック</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/hacker/25.html</link>
      <description><![CDATA[<div class="section">
<p>今月の advent calendar だけで通算27日分もの記事を書いている Yappo ですみなさんお元気ですか?</p>

<p>なんだか hacker track が人手不足なので今日ネタを書くために昨日思いついたモジュールを CPAN にアップロードした上で二度目の参戦をします。</p>

<p><a href="http://github.com/yappo/p5-Module-Requires">http://github.com/yappo/p5-Module-Requires</a></p>
</div>
<div class="section">
<h3> まえおき</h3>

<p>みなさんがコードを書く上で依存モジュールの管理に悩む。なんてのは誰しもが通る道だと思います。</p>

<p>もうすでに Makefile.PL の中に依存モジュールを書けば、依存モジュールを全部入れてくれるので悩む事は無いと思います。</p>

<p>さらには、テストケースで必要な依存モジュールのチェックなども Test::Requires が登場した事により、気楽にテストする事も出来ます。</p>

<p>だがしかし、プラガブルなモジュールなどが含まれるディストリビューションを作ったときに、このサブモジュールを使う時は、このモジュールも入れてね的に Makefile.PL に以下のような feature 句を入れるかと思います。</p>
<pre>
# Plack の Makefile.PL から拝借
feature 'FastCGI daemon and dispatcher',
    -default => 0,
    'FCGI' => 0.67,
    'FCGI::Client' => 0.02;
</pre>
<p>これは、実際のインストールフェーズでは以下のように依存モジュールを入れるか聞かれます。</p>
<pre>
[FastCGI daemon and dispatcher]
- FCGI                            ...missing.
- FCGI::Client                    ...missing.
==> Auto-install the 2 optional module(s) from CPAN? [n] 
</pre>
<p>ユーザがインストールする時に、ここで提示されたモジュールを入れてくれれば何も問題ないでしょう。</p>

<p>しかしながら、最初は要らないと思っていても後で使う気になった時には当然これら必須モジュールがはいっていないので「Can't Locate ...」などのエラーが出てきます。</p>

<p>不足してる依存モジュールが少数だったら許せるでしょうが、足りないモジュールがいっぱいあると「Can't Locate ...」と言われるたびに install するとかいうめんどくさい事になってしまいます。</p>

<p>簡単に言うと Module::Requires は、この「Can't Locate ...」エラーメッセージを一度に出して依存モジュールで足りてないモジュール群を一度に提示してあげるという事に使えるのです。</p>

<p># もちろん、これを排除するには sub feature 的なモジュールを別ディストリにしちゃうのが一番綺麗でしょう</p>
</div>
<div class="section">
<h3> 簡単な使い方</h3>

<p>使い方はとても簡単です。</p>

<p>例えば Class::Trigger と Class::Accessor に依存してる場合には以下のように書きます。</p>
<pre>
use strict;
use warnings;
use Module::Requires 'Class::Trigger', 'Class::Accessor';
use Class::Trigger;
use Class::Accessor;
</pre>

<p>もし、両方ともインストールされてないときは下記のようなエラーメッセージを出力します。</p>

<pre>
Can't load Class::Trigger
Can't locate Class/Trigger.pm in @INC (@INC contains: ry) at (eval 1) line 2.
BEGIN failed--compilation aborted at (eval 1) line 2.

Can't load Class::Accessor
Can't locate Class/Accessor.pm in @INC (@INC contains: ry) at (eval 2) line 2.
BEGIN failed--compilation aborted at (eval 2) line 2.
 at lib/Module/Requires.pm line 105
        Module::Requires::import('Module::Requires', 'Class::Trigger', 'Class::Accessor') called at ./a.pl line 3
        main::BEGIN() called at lib/Module/Requires.pm line 3
        eval {...} called at lib/Module/Requires.pm line 3
BEGIN failed--compilation aborted at ./a.pl line 3.
</pre>

<p>ちゃんと、両方の「Can't Locate ...」エラーメッセージが同時に出てきます。</p>
<p>もちろん片方がインストールされてれば、片方だけのエラーメッセージを出すし10個くらいのモジュールをチェックしてて全部入ってなければ全部のエラーを出します。</p>
</div>
<div class="section">
<h3> バージョンの指定</h3>

<p>もちろん通常の use と同じようにバージョンの指定もできます。</p>

<pre>
use strict;use warnings;
use Module::Requires
    'Class::Trigger'  => 0.99,
    'Class::Accessor' => 14.22;
use Class::Trigger;
use Class::Accessor;
</pre>

<p>こう書くと以下のようにバージョンがたりねー！と怒ります。</p>

<pre>
Class::Trigger version 0.99 required--this is only version 0.13
Class::Accessor version 14.22 required--this is only version 0.31 at lib/Module/Requires.pm line 105
        Module::Requires::import('Module::Requires', 'Class::Trigger', 0.99, 'Class::Accessor', 14.22) called at ./a.pl line 4
        main::BEGIN() called at lib/Module/Requires.pm line 5
        eval {...} called at lib/Module/Requires.pm line 5
BEGIN failed--compilation aborted at ./a.pl line 5.
</pre>
</div>
<div class="section">
<h3> 細かいバージョンの指定</h3>

<p>例えば、とあるモジュールが 0.10 まで出ていて 0.03 以上が入ってたら良いんだけど、 0.09 だけバグがあるので 0.03 以上で 0.09 以外のモジュールに依存したいとか書く必要が出てくる場合があると思います。</p>

<p>記憶が確かなら他の CPAN モジュールでも上記要求を満たすモジュールが入ってれば load するなんてのもありますが、 Module::Requires の機能としても実装してあります。</p>

<pre>
use strict;
use warnings;
use Module::Requires
    'Foo'  => [ '>' => 0.03, '!=' => 0.09 ],
    'Bar'  => [ '<=' => 0.02, '>=' => 0.01 ],
    'Baz'  => [ '<' => 0.08 ];
use Foo;
</pre>

<p>もしもインストールされている Foo のバージョンがが 0.09 であった場合下記のエラーを吐きます。</p>

<pre>
Foo version > 0.03 AND != 0.09 required--this is only version 0.09 at /lib/Module/Requires.pm line 105
        Module::Requires::import('Module::Requires', 'Foo', 'ARRAY(0x81948c)') called at ./a.pl line 4
        main::BEGIN() called at lib/Module/Requires.pm line 4
        eval {...} called at lib/Module/Requires.pm line 4
BEGIN failed--compilation aborted at ./a.pl line 4.
</pre>
</div>
<div class="section">
<h3> 同時にロードする</h3>

<p>実は上の方法では、 Module::Requires::* 以下の名前空間から各種モジュールを require してるだけなので use Module::Requires してる名前空間から正しく use するには別途 use ModuleName として書かないと正しく use 出来ませんでした。</p>

<p>これでは冗長なケースもあるので -autoload というオプションを付ける事により require チェックと同時に require && module->import を行うようになります。</p>

<pre>
# これは encode_base64 が入ってないのでだめよ
use strict;
use warnings;
use Module::Requires
    'MIME::Base64';
print encode_base64('last day');
</pre>

<pre>
# 正しく encode_base64 が load されてる
use strict;
use warnings;
use Module::Requires -autoload,
    'MIME::Base64';
print encode_base64('last day');
# use MIME::Base64; と同等
</pre>

<pre>
# decode_base64 しか export されてないので動かない
use strict;
use warnings;
use Module::Requires -autoload,
    'MIME::Base64' => {
        import => ['decode_base64'],
    };
print encode_base64('last day');
# use MIME::Base64 'decode_base64'; と同等
</pre>

<pre>
# これは encode_base64 だけを export してるので動く
use strict;
use warnings;
use Module::Requires -autoload,
    'MIME::Base64' => {
        import => ['encode_base64'],
    };
print encode_base64('last day');
# use MIME::Base64 'encode_base64'; と同等
</pre>

<p>このようにして、import メソッドに渡す引数を指定します。</p>
<p>use ModuleName () と同等にするには下記のように書きます。</p>

<pre>
# decode_base64 しか export されてないので動かない
use strict;
use warnings;
use Module::Requires -autoload,
    'MIME::Base64' => {
        import => [],
    };
print encode_base64('last day');
# use MIME::Base64 (); と同等
</pre>

<p>use Module () って空括弧を引数に渡すと import を呼ばなくなる仕様を忘れててすっかりドハマリしてバグ作ってましたが直しました＞＜</p>
</div>
<div class="section">
<h3> autoload しつつ version していする</h3>

<p>これも出来ます。</p>

<pre>
use Module::Requires -autoload,
    'MIME::Base64' => {
        import  => [qw/ encode_base64 /],
        version => 0.03,
    };
# use MIME::Base 0.03 qw( encode_base64 ); と同等
</pre>

<p>こんな風にシンプルなバージョンの指定から</p>

<pre>
use Module::Requires -autoload,
    'MIME::Base64' => {
        import  => [qw/ encode_base64 /],
        version => [ '>=' => 0.03, '!=' => '0.08' ],
    };
</pre>

<p>のような細かいバージョンの制御もできます。</p>

</div>
<div class="section">
<h3> まとめ</h3>

<p>Module::Requires を使ってモジュールの依存を細かく定義して、万が一依存モジュールの条件を満たさない場合はユーザが楽できる用にエラーを出すという事を紹介しました。</p>

<p>バグ出しやらなんやらで賞味一時間強で創り上げました。</p>

<p>また、このモジュールの実際の仕様のネタ出しは lestrrat さんと nekokak さんにして頂きましたありがとうございます。</p>

<p>例によって英語ドキュメントが不足気味なので、興味を持たれた方は是非ともドキュメントを充実させてくれると嬉しいです。</p>


<p>ということで地味なモジュールで今年の JPerl Advent Calendar を締めくくりましたが、そんなんで良いんじゃないかとおもいます。</p>

<p>ではでは、関係者の皆さんお疲れ様でした。こんどは寿司屋でありましょう！</p>

</div>
]]></description>
      <dc:creator>yappo</dc:creator>
      <pubDate>Fri, 25 Dec 2009 06:13:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>総括 #25</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/25.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！こんにちわ！nekokakです！</p>
<p>アドベントカレーンダー達成しました！</p>
<p>ありがとうございます。</p>
<p>これでかんかんに焼肉をおごってもらえます！</p>

<p>本日はアドベントカレンダー最終日ということで、コードレベルの話から離れて</p>
<p>総括的な話をしてみようかと思います。</p>

<p>まず、今回のアドベントカレンダーでDBIx::Skinnyに興味を持った方は</p>
<p>ぜひ一度試してみてください！</p>
<p>わからないことはメールでもircでも可能な限り手厚くフォローします。</p>

<p>irc: dbix-skinny@irc.perl.org</p>
<p>mail: nekokak+skinny _at_ gmail.com</p>

<p>どうぞよろしくです。</p>

<p>いままでDBIx::Skinnyの話しをするときに引き合いでDBIx::Classを出すことが多かったです。</p>
<p>（DBICはパフォーマンスがでないとか）</p>
<p>DBICを比較対象に出すことが多かったのは私がもともとDBICを使い倒していたユーザで、</p>
<p>色々と不満に思うことがおおかったからです。</p>
<p>ただ、DBICは素晴らしいORMだと思っています。それはDBIx::Skinnyを作った今でも思っています。</p>

<p>DBICのドキュメントの充実ぶりや、世界のPerlHackerによるメンテナスが行われており</p>
<p>多くのユーザがいます。これはある種の安心感があります。</p>

<p>もし今あなたが、DBICを仕事でつかっており、仕事上でDBICに問題点を感じないのであれば</p>
<p>無理にDBIx::Skinnyを使う必要はないと思っています。</p>
<p>もちろん試してみては欲しいところですが、無理やり切り替えてやろうとかは考えない方が良いでしょう。</p>
<p>(DBIx::Skinnyがすごすぎてつかいたい！とか思ってる場合は別ですが）</p>
<p>DBICの問題点を感じないのにDBIx::Skinnyに切り替えるメリットはあまりありませんよ！</p>
<p>移行コストが高く付くだけです！</p>

<p>Perlには他にも素晴らしいORMがあります。</p>
<p>Data::Model Rose::DB::Object Class::DBI Data::ObjectDriver Fey::ORM etc...</p>
<p>その他の素晴らしいORMも色々使ってみてください。</p>
<p>それぞれに特徴があります。</p>
<p>その特徴を見極めた上であなたが本当に必要なORMを選択すればよいと思います。</p>
<p>ORMが必要ないと判断する人もいるでしょう。</p>

<p>ORMは必要ないけどSQLジェネレータは必要だから FeyはつかうとかRose::DBはつかうとかSQL::Abstractはつかうとか</p>
<p>そういう選択肢もありえます。</p>

<p>もっともDBI直接でいいじゃないかと言う人もいると思います。</p>
<p>DBI自体がORMだ！と言う人もいます。</p>

<p>CPANをめくればこんなに素晴らしプロダクトが一杯あります。</p>
<p>それをつまみ食いせずに眺めてるだけなんてもったいなくないですか？</p>

<p>ぜひ色々ためして、あなたが本当に必要とするプロダクトを選んでください。</p>

<p>そして、あなたがDBIx::Skinnyを選んでくれたらそれはとても素晴らしい私へのクリスマスプレゼントになるでしょう。</p>

<p>25日間ありがとうございました。</p>

<p>JPerl Advent Calendar 2009の発起人id:tokuhirom</p>
<p>JPerl Advent Calendarのコンテンツを置くサバーを用意してくれたid:yappo</p>
<p>DBIx::Skinnyのアドベントカレンダーを実行するきっかけになったのもyappoさんのおかげです。</p>
<p>また、DBIx::Skinnyのアドベントカレンダーを手伝ってくれた</p>
<p>id:walf443 id:daijirow id:studio-m(nekoya)</p>
<p>のみなさん。</p>
<p>本当にありがとうございました！</p>

<p>Merry Christmas</p>

<p>have a nice skinny days!:)</p>

</div>
]]></description>
      <dc:creator>nekokak</dc:creator>
      <pubDate>Fri, 25 Dec 2009 00:55:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Image::Imlib2でアイコンをクリスマス仕様に</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/casual/24.html</link>
      <description><![CDATA[<div class="section">
<p>ハッピーメリークリスマス！aomushi510です。好きなお寿司のネタはサーモンです！</p>

<p>今日はクリスマスイブということで、Image::Imlib2を使って簡単にアイコンをクリスマス仕様にしちゃいましょう！</p>
<p>Perlで画像加工をするときに使うモジュールは、Image::Imlib2の他にもImage::MagickやImager、GD::Imageなどがあります。</p>
<p><a href="http://yusukebe.com/tech/archives/20070526/011710.html">yusukebeさんのエントリ</a>では、Image::Imlib2が超絶速いそうです。Image::Imlib2すごい！ってことで早速はじめましょう。</p>
</div>
<div class="section">
<h3>はじめに</h3>
<p>まずはImage::Imlib2をインストールしましょう。</p>
<p>インストールをする前に、Cのライブラリと使いたい画像形式のライブラリを入れておきましょう。</p>
<p>今回はjpegとpngを使いたいと思います。</p>
<pre>
$ port install libpng
$ port install imlib2
</pre>

<p>ではImage::Imlib2をインストールします。簡単ですね！</p>
<pre>
cpan> install Image::Imlib2
</pre>
</div>
<div class="section">
<h3>画像の読み込み、書き出し</h3>
<p>インストールできたら、画像を読み込んだり書き出したりしてみましょう。</p>
<p>face.jpgを読み込んで、face2.jpgに書き出してみます。</p>
<pre>
#!/usr/bin/perl

use strict;
use warnings;
use Image::Imlib2;

my $dir = '/path/to/image';

my $image = Image::Imlib2->load("$dir/face.jpg");
$image->save("$dir/face2.jpg");
</pre>

<p>できた画像</p>
<p><a href="http://azu.sh/jperl_advent_calendar_2009/image/face2.jpg"><img src="http://azu.sh/jperl_advent_calendar_2009/image/face2.jpg" alt="http://azu.sh/jperl_advent_calendar_2009/image/face2.jpg" /></a></p>

</div>
<div class="section">
<h3>画像の合成</h3>
<p>これであなたもImage::Imlib2を使うことができました！が、肝心のアイコンがさみしいです＞＜</p>
<p>クリスマスということで、サンタ帽をかぶってみましょう。</p>

<p><a href="http://azu.sh/jperl_advent_calendar_2009/image/santa_hat.png"><img src="http://azu.sh/jperl_advent_calendar_2009/image/santa_hat.png" alt="http://azu.sh/jperl_advent_calendar_2009/image/santa_hat.png" /></a></p>
<p>このようなサンタ帽画像を用意します。それでは合成してみましょう。</p>
<pre>
#!/usr/bin/perl

use strict;
use warnings;
use Image::Imlib2;

my $dir = '/path/to/image';

my $image = Image::Imlib2->load("$dir/face.jpg");
$image->set_quality(100);
my $santa_hat = Image::Imlib2->load("$dir/santa_hat.png");
$image->blend($santa_hat, 1, 0, 0, 300, 300, 0, 0, 300, 300);

$image->save("$dir/with_santa_hat.png");
</pre>

<p>できた画像</p>
<p><a href="http://azu.sh/jperl_advent_calendar_2009/image/with_santa_hat.png"><img src="http://azu.sh/jperl_advent_calendar_2009/image/with_santa_hat.png" alt="http://azu.sh/jperl_advent_calendar_2009/image/with_santa_hat.png" /></a></p>
<p>サンタ帽をかぶることができました！簡単ですね！</p>

</div>
<div class="section">
<h3>画像を作成する</h3>
<p>サンタ帽をかぶってクリスマスっぽくなりました。もうひといき、今度は星画像を作ってみましょう。</p>
<p>Image::Imlib2::Polygonというモジュールを使って多角形を作ります。</p>
<p>星形は正五角形の頂点を結ぶので、先にShape::RegularPolygonを利用して正五角形の頂点を取得しておきます。</p>
<p>そのあと1つ飛びに頂点を描き、まさに手書きで星を書くときのように描いていきます。</p>
<p>これだけでは中の正五角形が塗られないので、中身も後から塗って星形の完成です。</p>

<pre>
#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use Image::Imlib2;
use Shape::RegularPolygon;

my $dir = '/path/to/image';

my $image = Image::Imlib2->new_transparent(100, 100);
$image->set_colour(255, 255, 0, 255);

my $points = &get_points(50);
my $poly = Image::Imlib2::Polygon->new();

for my $point (@$points) {
    $poly->add_point($point->{x}, $point->{y});
}
$poly->fill();

$image->draw_polygon($poly, 1); 
$image->fill(50,50);
$image->save("$dir/star.png");


sub get_points {
    my $center = shift;

    my $polygon = new Shape::RegularPolygon;
    $polygon->center($center, $center);
    $polygon->sides(5);
    $polygon->radius(50);
    my @points = $polygon->points;

    return [$points[0], $points[2], $points[4], $points[1], $points[3], $points[0]];
}
</pre>
<p>できた画像</p>
<p><a href="http://azu.sh/jperl_advent_calendar_2009/image/star.png"><img src="http://azu.sh/jperl_advent_calendar_2009/image/star.png" alt="http://azu.sh/jperl_advent_calendar_2009/image/star.png" /></a></p>


<p>できた画像を合成します。</p>
<p>create_rotate_imageで画像を回転させて貼り付けます。</p>

<pre>
#!/usr/bin/perl

use strict;
use warnings;
use Image::Imlib2;

my $dir = '/path/to/image';

my $image = Image::Imlib2->load("$dir/with_santa_hat.png");
$image->set_quality(100);
my $star = Image::Imlib2->load("$dir/star.png");
$image->blend($star, 1, 0, 0, 100, 100, 0, 200, 100, 100);

my $rotated = $star->create_rotated_image(30 / 360 * 3.141519*2);
$image->blend($rotated, 1, 0, 0, 150, 150, 210, 80, 100, 100);

$image->save("$dir/with_star.png");
</pre>
<p>できた画像</p>
<p><a href="http://azu.sh/jperl_advent_calendar_2009/image/with_star.png"><img src="http://azu.sh/jperl_advent_calendar_2009/image/with_star.png" alt="http://azu.sh/jperl_advent_calendar_2009/image/with_star.png" /></a></p>
<p>できました！これでアイコンがにぎやかになりましたね！</p>

</div>
<div class="section">
<h3>まとめ</h3>
<p>Image::Imlib2を使うと簡単に画像を加工することができます。ちょっとした画像編集にPerlを使ってみるのはいかがですか？</p>

<p>明日はAdvent Calendarの大トリ、piarraさんです。楽しみですね！</p>

</div>
<div class="section">
<h3>参考</h3>
<ul>
<li><a href="http://search.cpan.org/~lbrocard/Image-Imlib2-2.03/lib/Image/Imlib2.pm">http://search.cpan.org/~lbrocard/Image-Imlib2-2.03/lib/Image/Imlib2.pm</a></li>
<li><a href="http://yusukebe.com/tech/archives/20070526/011710.html">http://yusukebe.com/tech/archives/20070526/011710.html</a></li>
<li><a href="http://it.kndb.jp/entry/show/id/2572">http://it.kndb.jp/entry/show/id/2572</a></li>
<li><a href="http://rakasaka.fc2web.com/delphi/star.html">http://rakasaka.fc2web.com/delphi/star.html</a></li>
</ul>
</div>
]]></description>
      <dc:creator>aomushi510</dc:creator>
      <pubDate>Thu, 24 Dec 2009 00:00:00 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>ドライバを作る</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/data-model/24.html</link>
      <description><![CDATA[<div class="section">
<h3> はじめに</h3>

<p>さぁ、何日か立て続けに Data::Model に用意されている各種 Driver の使い方を紹介してきました。</p>

<p>Driver 編最終回は Driver を作る方法について紹介しましょう。</p>
</div>
<div class="section">
<h3> Driver を作るために必要な知識</h3>

<p>Driver を作るためにはある程度規則に従う必要があります。</p>
<p>ゼロから独自の Driver を作るには Data::Model::Driver の中で空定義してあるメソッドを上書きする必要があります。</p>
<p>そして Data::Model::Driver の各メソッドは Data::Model より delegation されているので、 Data::Model のコードも読んでおく必要があります。</p>

<p>このあたりはドキュメントされていません。</p>
<p>また、内部 API が変わってしまうと互換性が無くなる可能性もあり厄介です。</p>

<p>ということで本日は、需要がありそうで簡単そうな Driver を作る方法を軽く紹介します。</p>
</div>
<div class="section">
<h3> Driver::DBI を拡張する</h3>

<p>主に一昨日紹介した Driver::DBI::MasterSlave の挙動が気にくわないと言った事で作りたい欲求が出るでしょう。</p>

<p>Driver::DBI::MasterSlave は Driver::DBI の dbh のやりくりを制御する部分を独自にハンドリングして master, slave の dbh を切り替えるという要求にしたがって作られています。</p>

<p>rw_handle, r_handle が返す値をうまい具合にやりくりすれば、独自のレプリケーション対応の Driver がかけます。</p>

<p>Driver::DBI::MasterSlave のコードを元にして、どのあたりをいじれば良いのかを紹介しましょう。</p>

<h4> 継承するクラス</h4>

<p>なので、しちめんどい set,get,update,delte,lookup などの処理は再実装しないで Driver::DBI にやってもらえばいいので、これを継承します。</p>

<pre>
package Data::Model::Driver::DBI::MasterSlave;
use strict;
use warnings;
use base 'Data::Model::Driver::DBI';
</pre>

<h4> クラス初期化</h4>

<p>Data::Model::Driver の初期化は new メソッドの引数を全て bless { %args }, $class のようにして保存してから、 init メソッドを単純に呼び出しています。</p>
<p>なので、独自 Driver の初期化処理は init メソッドの中で行います。</p>

<pre>
sub init {
    my $self = shift;
    my $master = $self->{master}
        or Carp::croak "'master' configuration is required";
    my $slave  = $self->{slave} || $master;

    if (my($type) = $master->{dsn} =~ /^dbi:(\w*)/i) {
        $self->{dbd} = Data::Model::Driver::DBI::DBD->new($type);
    }
    $self->{dbi_config} = +{
        master => +{ %{ $master } },
        slave  => +{ %{ $slave } },
    };
}
</pre>

<p>今回は rw_handle, r_handle を自由に差し替えたいという要求を設定しました。</p>
<p>Driver::DBI のでは、このあたりも自由に差し替えするコードを書きやすくしてあります。</p>

<p>状況に応じた DBI のインスタンスを複数作れる用になっており、複数のインスタンスを作るためには DBI の設定を複数設定しておく準備が必要です。</p>

<p>具体的には $self->{dbi_config} に config_name => $config という形で HASH リファレンスを指定します。</p>

<p>Driver::DBI::MasterSlave では master と slave という設定名を使って、二つの設定を保存しています。</p>

<p>$self->{dbd} に Data::Model::Driver::DBI::DBD のインスタンスを入れているところは Driver::DBI で各種 DBD に対応した SQL を吐くために必須ですので注意してください。</p>
</div>
<div class="section">
<h3> dbh を使い分ける</h3>

<p>さぁ DBI インスタンスの設定を複数作ったら、あとはそれぞれ使うだけです。</p>

<p>これはrw_handle と r_handle のメソッドをそれぞれ上書きします。</p>

<pre>
sub rw_handle { shift->_get_dbh('master', @_) };
# トランザクション中は master のみを返す
sub r_handle  { my $self = shift;$self->_get_dbh( ($self->{active_transaction} ? 'master' : 'slave'), @_ ) };
</pre>

<p>見れば分かりますが $self->_get_dbh(config_name) といった形で _get_dbh のプライベートメソッドを読んでいます。</p>

<p>通常は、このように $self->{dbi_config} に格納した設定名を引数にして呼び出せば、その設定を引数にして自動的に DBI インスタンスを作ってくれるので、それを戻すだけでやりたい事が出来ます。</p>

<p>r_handle では $self->{active_transaction} が真だったら master を見るようにしていますが、これは txn_scope 下では常に master を見るべきという設計によるものです。</p>
<p>今の Data::Model の Driver::DBI では、このあたりもハンドリングしてあげる必要があります。</p>

<h4> 応用</h4>

<p>例えば複数台の slave の設定を設定してランダムにその slave を使いたい場合は下記の用な Driver を書きます。</p>

<pre>
package Data::Model::Driver::DBI::ManySlave;
use strict;
use warnings;
use base 'Data::Model::Driver::DBI::MasterSlave';

sub init {
    my $self = shift;
    my $master = $self->{master}
        or Carp::croak "'master' configuration is required";
    my $slave  = $self->{slave} ? ref($self->{slave}) eq 'ARRAY'
        ? $self->{slave} : [ $self->{slave} ] : [ $master ];
    $self->SUPER::init(
        master => $master,
        slave  => $slave,
    );
}
</pre>

<p>slave のオプションを ARRAY ref にする感じです。</p>
<p>殆ど Driver::DBI::MasterSlave の実装を使いまわすので、ここは Data::Model::Driver::DBI::MasterSlave をそのまま継承します。</p>

<p>次は rw_handle と r_handle かと思いますが、 Driver::DBI::MasterSlave の物をそのまま使います。</p>

<p>では、 slave の r_handle を複数から選択するのは選択するの?という疑問ですが、新しく紹介するメソッドを上書きして使います。</p>

<p>dbi_config というメソッドを上書きします。</p>

<p>$self->{dbi_config} に DBI への設定を入れていたと思いますが、この設定を取り出すためのメソッドとして定義されています。</p>

<pre>
sub dbi_config {
    my($self, $name) = @_;
    return $self->{dbi_config}->{master} if $name eq 'master';
    my $slave = $self->{dbi_config}->{slave};
    return $slave->[rand(@{ $slave })];
}
</pre>

<p>このようにして master の時は $self->{dbi_config}->{master} を返して、 slave の時は slave の設定をどれかランダムで返すのです。</p>

<p>DBI のインスタンスを作る為のメソッドの中では dbi_config を使って DBI の設定を取得しているので、ここだけを変更すればうまく行きます。</p>

<h4> 使ってみる</h4>

<p>さて、この作った Driver::DBI::ManySlave を使ってみますか。</p>

<pre>
my $many = Data::Model::Driver::DBI::ManySlave->new(
    master => {
        dsn => 'dbi:mysql:host=master.server:database=test',
    },
    slave => [
        { dsn => 'dbi:mysql:host=slave1.server:database=test' },
        { dsn => 'dbi:mysql:host=slave2.server:database=test' },
        { dsn => 'dbi:mysql:host=slave3.server:database=test' },
        { dsn => 'dbi:mysql:host=slave4.server:database=test' },
    ],
);
</pre>

<p>これだけです。</p>

<h4> とりとめのない話</h4>

<p>これだけの為にわざわざコード書くのはちょっと面倒なので Driver::DBI の設定だけでうまく行くようにしようと思います。</p>

<p>現状でも微妙に出来そうなコード片が入ってるのですが、完璧じゃないのでもすこし書き直してから公開しようとおもいます。</p>

</div>
<div class="section">
<h3> Driver::Cache を拡張する</h3>

<p>さて、次は透過的なキャッシュをする Driver を独自の物に書いてみましょう。</p>

<p>標準では Perl 固有の HASH の中にキャッシュするか、 Memcached なオブジェクトへのキャッシュしか選択出来ません。</p>

<p>しかし Driver::DBI と比べてもさらにシンプルなんです。</p>

<p>基本的な Driver としての実装は Data::Model::Driver::Cache の中で実装されており、これを継承して Driver::Cache 用のインターフェイスを満たせば OK なんです。</p>

<p>これも既存の Driver::Cache::HASH のコードを元に説明しましょう。</p>

<h4> データ追加</h4>

<p>データの追加するメソッドを定義します。</p>

<pre>
sub add_to_cache {
    my($self, $key, $data) = @_;

    my $ret = $CACHE{$key} = $data;
    return if !defined $ret;
    return $ret;
}
</pre>

<p>add_to_cache の第一引数に key を、第二引数に value が渡されます。</p>
<p>成功したら value をそのまま返してください。</p>
<p>失敗時は undef を返します。</p>

<h4> データ取得</h4>

<p>データを取得する処理で使われます</p>

<pre>
sub get_from_cache {
    my($self, $key) = @_;

    my $ret = $CACHE{$key};
    return if !defined $ret;
    return $ret;
}
</pre>

<p>get_to_cache の第一引数に key が渡されます。</p>
<p>成功したら key に対応する value をそのまま返してください。</p>
<p>失敗時は undef を返します。</p>

<h4> データ削除</h4>

<p>データを削除する処理で使われます</p>

<pre>
sub remove_from_cache {
    my($self, $key) = @_;
    
    my $ret = delete $CACHE{$key};
    return if !defined $ret;
    return $ret;
}
</pre>

<p>remove_from_cache の第一引数に key が渡されます。</p>
<p>失敗したら undef を返してください。</p>
<p>成功したら undef 以外を返してください。</p>

<p>今現在 0 や '' などを返しても失敗したと誤認識するバグが発見されました。</p>

<h4> その他</h4>

<p>update 処理は、該当する key の削除のみを行うという挙動になっています。</p>

<p>トランザクションとの組み合わせは、現在完全な透過処理が行われません。</p>

<p>lookup_multi 系のクエリは get_multi_from_cache を上書きします。</p>
<p>以下に Memcached で利用してるコードを張り付けます。</p>

<pre>
sub get_multi_from_cache {
    my($self, $keys) = @_;

    my $ret = $self->{memcached}->get_multi($keys);
    return if !defined $ret;
    return $ret;
}
</pre>
</div>
<div class="section">
<h3> まったく新規に Driver を作る</h3>

<p>もうちょっと詳細に書く予定でしたが、基本的に本日紹介した方法を見れば大体の Driver 作成の要求を満たせるかなと思ったので今回は省略させてください。</p>

<p>もし、そのような需要がある場合は Yappo を捕まえて相談してみてください。</p>
</div>
<div class="section">
<h3> 他の DBD 対応</h3>

<p>Driver とは直接関係ないですが mysql や SQLite 以外の DBD 対応へのポインタを示します。</p>

<p>Data::Model::Driver::DBI::DBD 以下の名前空間の実装を見てください。</p>

<p>基本的に SQL generator からの delegation されるコードですので delegation 元の Data::Model::Schema::SQL などを読んでみてください。</p>

<p>DBD::Pg に関しては sfujiwara さんが実装してくださったので僕の merge まちです＞＜</p>
</div>
<div class="section">
<h3> まとめ</h3>

<p>本日は Driver hack についてあれこれ書きました。</p>

<p>さぁ、明日はいよいよ最終回です。</p>
</div>
]]></description>
      <dc:creator>yappo</dc:creator>
      <pubDate>Thu, 24 Dec 2009 11:06:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Win32::APIでPerlの中に直接機械語を書いてるときのデバッグ</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/hacker/24.html</link>
      <description><![CDATA[<div class="section">
<p>はせがわようすけです。こんにちは。今日みたいな日だとリア充揃いのPerl geeksは人手不足なようで、ほとんどPerlを使ったことのない私まで駆り出されましたよ。</p>
</div>
<div class="section">
<h3>まえおき</h3>

<p>というわけで、Perlで機械語を埋め込む技を応用すると、ActivePerl で stdcall な通常のDLL関数だけでなく、MSVCRT.DLL に含まれる sprintf のような可変長引数の cdecl な呼び出し規約の関数も利用できます。</p>

<pre>
#!/c/perl/bin/perl
use strict;
use warnings;
use Win32::API;

#include <windows.h>
my $EnumWindows = Win32::API->new( "user32", "EnumWindows", "NN", "N" );
my $GetProcAddress = Win32::API->new( "kernel32", "GetProcAddress", "NP", "N" );
my $LoadLibrary = Win32::API->new( "kernel32", "LoadLibraryA", "P", "N" );
my $FreeLibrary = Win32::API->new( "kernel32", "FreeLibrary", "N" );

sub my_sprintf{
    if( @_ < 1 ){
        die "argument error";
    }

    my $hDll = $LoadLibrary->Call( "msvcrt" );
    my $sprintf = pack( 'L', $GetProcAddress->Call( $hDll, "sprintf" ) );   # sprintf is cdecl
    my $buf = "\0" x 1024;
    my $x86 ="";
    my $i = @_;

    while( $i-- ){
        $x86 .= "\x68" . $_[ $i ];      # push args
    }
    $x86 .= "\x68" . pack( 'P', $buf ); # push $buf
    my $n = ( @_ + 1 ) * 4;
    $x86 .= ""
.       "\xb8" . $sprintf               # mov eax, func
.       "\xff\xd0"                      # call eax
.       "\x81\xC4"                      # add esp, @_ * 4
.       pack( 'L', $n )
.       "\x33\xc0"                      # xor eax, eax
.       "\xc2\x08\x00"                  # ret
;

    $EnumWindows->Call( unpack( 'L', pack( 'P', $x86 ) ), 0 );
    $FreeLibrary->Call( $hDll );
    $buf =~s/\0.*$//;
    return $buf;
}

my $s = my_sprintf( pack( 'P', "%s, %s" ), pack( 'P', "Hello" ), pack( 'P', "World" ) );
print $s;

</pre>

<p>実行結果</p>
<pre>
C:\>xmax.pl
Hello, World
</pre>

<p>このように、用意しておいたバイト配列を<a href="http://msdn.microsoft.com/en-us/library/ms633497%28VS.85%29.aspx">EnumWindows</a> APIのコールバック関数として渡してやることで、任意の機械語を簡単に実行させることができます。</p>
<p>ただ、熟練したバイナリアンでなければ機械語を一発で動かすことは難しく、たいていの場合は途中でプログラムが強制終了させられてしまいます。</p>

<p>そこで、もう少し効率的にデバッグする方法を紹介します。</p>
</div>
<div class="section">
<h3>printf デバッグ</h3>
<p>まず、もっとも簡単な方法として、みんな大好きな printf デバッグで機械語を確認することにしましょう。</p>

<pre>
    $x86 .= ""
.       "\xb8" . $sprintf               # mov eax, func
.       "\xff\xd0"                      # call eax
.       "\x81\xC4"                      # add esp, @_ * 4
.       pack( 'L', $n )
.       "\x33\xc0"                      # xor eax, eax
.       "\xc2\x08\x00"                  # ret
;
    print unpack( 'H2' x length( $x86 ), $x86 );
    print "\n";
</pre>

<p>実行結果</p>
<pre>
C:\>xmax.pl
68d4764d0268b4764d026894764d02685c8b4c02b831bd9676ffd081c41000000033c0c20800
Hello,World
</pre>

<p>ごらんのとおり、実行される機械語が画面に表示されますので、どこで機械語の指定を間違えたのか一目瞭然になります。</p>
<p>超画期的です。</p>
</div>
<div class="section">
<h3>デバッガでアタッチ</h3>
<p>printfによる目視デバッグでもあまり困らないのですが、あまりやりすぎると</p>
<p><a href="http://d.hatena.ne.jp/hyoshiok/20090322#p1">よしおかさんに怒られちゃう</a>ので、きちんとデバッガを使うようにしましょう。使うデバッガ環境はもちろん Visual Studio です。</p>

<p>Visual Studioは、「C:\Windows\System32\vsjitdebugger.exe -p プロセスID 」とすることで、任意のプロセスをデバッガにアタッチすることができますので、Perlのなかから自身のプロセスIDを指定してこれを呼び出すことで、うまくデバッガにアタッチできそうです。</p>
<p>指定してこれを呼び出すことで、うまくデバッガにアタッチできそうです。</p>
<p>ただし、Perlのsystem関数を使ってそのままvsjitdebugger.exeを呼び出したのでは、デバッガ終了までperl側の処理がブロックされてしまい、デバッガが立ち上がっても継続してデバッグすることができません。</p>
<p>そこで、start コマンド経由で vsjitdebugger.exe を呼び出すことにします。</p>

<pre>
system("start", "vsjitdebugger.exe", "-p", "$$" );
</pre>

<p>こうすることで、startコマンドは vsjitdebugger.exe を起動すると速やかに終了し、Perl側では system は制御を戻すので、デバッガと並行してコードの実行を進めることができます。</p>

<p>ただし、このままではデバッガが起動しデバッグ対象プロセス(ActivePerl)にアタッチしてデバッグの準備ができるのを待つことなく、Perl側はどんどんコードの実行を進めてしまいますので、今度はデバッガの準備ができるまでPerl側のコードの実行を停止させる必要があります。</p>
<p>これには、Windows APIの<a href="http://msdn.microsoft.com/en-us/library/ms680345%28VS.85%29.aspx">IsDebuggerPresent</a>関数を使います。</p>

<pre>
while( $IsDebuggerPresent->Call() == 0 ){
    sleep( 1 );
};
</pre>

<p>これで、デバッガがきちんとアタッチしていない間は有意なコードの実行を停止させることができます。</p>


<p>つぎに、x86バイナリコードにブレークポイントを置くわけですが、これは単純に int 3 を実行することでデバッガにブレークを通知できます。</p>

<pre>
my $x86 = "\xCC.....";
</pre>

<p>CPUがこの0xCCという機械語を実行すると、デバッガ側にはブレークポイントとして通知され、以降のコードを自由にデバッガ上で動かすことができます。</p>

<p>というわけで、さきの Hello, World の機械語部分をデバッガ上で実行するよう書き換えたコードを以下に示します。</p>

<pre>
#!/c/perl/bin/perl
use strict;
use warnings;
use Win32::API;

#include <windows.h>
my $EnumWindows = Win32::API->new( "user32", "EnumWindows", "NN", "N" );
my $GetProcAddress = Win32::API->new( "kernel32", "GetProcAddress", "NP", "N" );
my $LoadLibrary = Win32::API->new( "kernel32", "LoadLibraryA", "P", "N" );
my $FreeLibrary = Win32::API->new( "kernel32", "FreeLibrary", "N" );
my $IsDebuggerPresent = Win32::API->new( "kernel32", "IsDebuggerPresent", "", "N" );

sub my_sprintf{
    if( @_ < 1 ){
        die "argument error";
    }

    my $hDll = $LoadLibrary->Call( "msvcrt" );
    my $sprintf = pack( 'L', $GetProcAddress->Call( $hDll, "sprintf" ) );   # sprintf is cdecl
    my $buf = "\0" x 1024;
    my $x86 ="";
    my $i = @_;

    $x86 = "\xCC";                      # int 3

    while( $i-- ){
        $x86 .= "\x68" . $_[ $i ];      # push args
    }
    $x86 .= "\x68" . pack( 'P', $buf ); # push $buf
    my $n = ( @_ + 1 ) * 4;
    $x86 .= ""
.       "\xb8" . $sprintf               # mov eax, func
.       "\xff\xd0"                      # call eax
.       "\x81\xC4"                      # add esp, @_ * 4
.       pack( 'L', $n )
.       "\x33\xc0"                      # xor eax, eax
.       "\xc2\x08\x00"                  # ret
;

    $EnumWindows->Call( unpack( 'L', pack( 'P', $x86 ) ), 0 );
    $FreeLibrary->Call( $hDll );
    $buf =~s/\0.*$//;
    return $buf;
}

system("start", "vsjitdebugger.exe", "-p", "$$" );
while( $IsDebuggerPresent->Call() == 0 ){
    sleep( 1 );
};

my $s = my_sprintf( pack( 'P', "%s,%s" ), pack( 'P', "Hello" ), pack( 'P', "World" ) );
print $s;
</pre>
</div>
<div class="section">
<h3>まとめ</h3>
<ul>
<li>Perlに機械語埋め込むときでもデバッガ使ったほうが怒られないで済むよ！</li>
<li>WindowsならVisual Studio最強ですよ!</li>
<li>int 3は0xCC。バッドノウハウ万歳!</li>
</ul>

<p>明日の最後のエントリーは・・・Yappoさんが締めくくってくれる予定？どんなネタか今から楽しみです！</p>
</div>
]]></description>
      <dc:creator>hasegawayosuke</dc:creator>
      <pubDate>Thu, 24 Dec 2009 10:13:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DBIx::Skinny::InflateColumn::DateTimeで勝手にinflate/deflate #24</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/24.html</link>
      <description><![CDATA[<div class="section">
<p>昨日に続きましてnekoyaがお送りします。</p>

<p>今日もSkinny関連モジュールの紹介です。DBIx::Skinny::InflateColumn::DateTimeという名前だけで何をするかだいたい理解できそうな小物モジュールとなっております。</p>

<p>インストールは、CPANから</p>
<pre>
cpan DBIx::Skinny::InflateColumn::DateTime
</pre>
<p>するか、<a href="http://github.com/nekoya/p5-dbix-skinny-inflatecolumn-datetime">githubから</a>どうぞ。</p>

<p>これも昨日のSchema::Loaderと同じく、DBIx::Skinny本体のバージョンが古いと動きません。0.05以上にアップデートしておいてください。</p>

<p>利用方法は、Schemaクラスで</p>
<pre>
package Your::DB::Schema;
use DBIx::Skinny::Schema;
use DBIx::Skinny::InflateColumn::DateTime;
</pre>
<p>のように、useするだけです。</p>

<p>useすると、XXXX_at, XXXX_onなカラムが自動的にDateTimeオブジェクトにinflate/deflateされます。</p>

<p>実際にやっていることも、このAdvent Calenderの<a href="http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/04.html">4日目</a>でnekokakさんが紹介したサンプルとほぼ同等のごくシンプルなモジュールです。</p>

<p>と、これだけで終わると寂しいので、もう一つおまけを紹介します。</p>

<pre>
package Your::DB::Schema;
use DBIx::Skinny::Schema;
use DBIx::Skinny::InflateColumn::DateTime::Auto;
</pre>

<p>のように、DBI::Skinny::InflateColumn::DateTime::Auto（Inflate::DateTimeに同梱されています）をuseすると、insert/update時に特定のカラムに操作時刻を自動設定してくれます。</p>

<p>設定されるtriggerと対象カラムは以下になります。</p>

<table>
<tr>
<td> pre_insert: </td>
<td> created_at, created_on, updated_at, updated_on</td>
</tr>
<tr>
<td> pre_update: </td>
<td> updated_at, updated_on</td>
</tr>
</table>

<p>こうしたカラムの操作はDBの機能を使ってすることも可能ですが、DBに依存せずにアプリケーション側で処理したい場合は使ってみてください。</p>

<p>この機能は、hidekさんの</p>
<ul>
<li><a href="http://blog.hide-k.net/archives/2006/08/dbixclassauto_i.php">DBIx::Classでauto insert/update datetime - hide-k.net#blog</a></li>
</ul>
<p>にインスパイアされて盛り込みました。hidek++</p>

<p>実は、<a href="http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/04.html">4日目</a>に紹介されたcommon_triggerの機能は、このモジュールを作るのに欲しかったので追加したという経緯があります。あなたも周辺モジュールを書いていて「本体にこんな機能が欲しい」と思ったら、パッチを送ったり、IRCやブログで話を投げてみてください。</p>

<p>明日はいよいよ最終日。nakokakさんの大団円エンディングにご期待ください。</p>

<p>have a nice skinny days!:)</p>
</div>
]]></description>
      <dc:creator>nekoya</dc:creator>
      <pubDate>Thu, 24 Dec 2009 02:45:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Mojolicious Plackup!</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/casual/23.html</link>
      <description><![CDATA[<div class="section">
<p>Hi, perl-users.</p>

<p>The past few days, Although I couldn't decide what to write day 23, fortunately, <a href="http://twitter.com/kraih/status/6722965569">sri</a> added native PSGI support to Mojolicious at 12/16/2009.  I decided to write this article of this feature.</p>


<p>So, this year, the most interesting thing in YAPC::Asia 2009 is PSGI/Plack from Miyagawa-san. PSGI is Perl Web Server Gateway Interface Specfication, and Plack is PSGI reference implementation and utilities(I think you know it). For details, go to <a href="http://plcackperl.org">plackperl.org</a> and Googling around it if you want.</p>


<p>First of all, You install Mojo and Plack. I think it would be better to use local::lib. As for local::lib, Please refer to <a href="http://perl-users.jp/articles/advent-calendar/2009/casual/02.html">day 2</a> by otsune-san. Mojo installation is very easy. Doesn't have any dependency other than Perl 5.8.1. </p>

<p>A word of caution: Do not use Mojo(0.999914) on CPAN in this article. You should install Mojo(0.999915) from <a href="http://github.com/kraih/mojo">github</a>. Only github includes PSGI feature. Not includes it on CPAN now. Sorry, I don't expalin installation process in detail here.</p>


<p>Once those installs are done, generate a mojolicious application. Mojo has a helper script that creates all the working files that you need get started in the directory.</p>
<pre>
$ ./extlib/bin/mojolicious generate app Centipede
</pre>
<pre>
$ ls -al
drwxr-xr-x  8 kaz kaz 4096 2009-12-20 22:02 centipede/ # Mojo Application
drwxr-xr-x  5 kaz kaz 4096 2009-12-20 21:45 extlib/    # libraries
</pre>


<p>Once you have generated your application, prepare for the ".psgi". Interestingly, Mojo has a ".psgi" generator.</p>
<pre>
$ mv centipede/script/
$ ./centipede generate psgi
</pre>
<p>centipede.psgi</p>
<pre>
use FindBin;

use lib "$FindBin::Bin/lib";
use lib "$FindBin::Bin/../lib";

use Mojo::Server::PSGI;

my $psgi = Mojo::Server::PSGI->new(app_class => 'Centipede');
my $app  = sub { $psgi->run(@_) };
</pre>


<p>Let's plackup!</p>
<pre>
$ perl -I./centipede/lib ./extlib/bin/plackup -app ./centipede/centipede.psgi
</pre>
<p>Now we have our standalone server running on port 5000 so browsing:<br/> <a href="http://localhost:5000">http://localhost:5000</a><br /> And will see a welcome message from Mojolicious.</p>


<p>Specifically, I think Mojo::Server::PSGI is NOT server, enables Mojo applications to adapt PSGI protocole like CGI::PSGI.</p>
<pre>

 .----------------.
 |   Mojolicious  |
 '----------------'
         |
    .---------.
    |   Mojo  |
    '---------'
         |
.----------------------.
|  Mojo::Server::PSGI  |
'----------------------'
         |
    .---------.
    |  Plack  |
    '---------'
         |
    .----------.
    |  Server  |
    '----------'

</pre>


<p>Mojo already supports most server environment. Although I think PSGI might not be direct benefit immediately,  It is going to get a fast server and useful middlewares near future. I'm looking forward to it.</p>


<p>My English Sux. FIXME.</p>
<p>Next day 24 is aomushi510-san. Don't miss it!</p>

<p>#mojoの人にも読んでもらえらたらなーと思い英語が得意なわけでもないのに英語で書いてみました。英語読みたくない方や間違いだらけで恥ずかしくて読んでいられないというかたはブログに日本語で書くつもりなのでそちらをどうぞ:)</p>

</div>
]]></description>
      <dc:creator>ka2u</dc:creator>
      <pubDate>Wed, 23 Dec 2009 14:57:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>データをどのようにキャッシュするか</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/data-model/23.html</link>
      <description><![CDATA[<div class="section">
<p>こんにちわ！ Yappo です！</p>
<p>2十3日目は Data::Model でどのようにデータをキャッシュし、</p>
<p>キャッシュしたデータをどう扱うかについてです。</p>

<p>アプリケーションの負荷軽減策の一つとしてデータを memcached にキャッシュしてしまい、</p>
<p>DB アクセスをすくなくすることで、レスポンスを早くする手法はみなさん良く使われていると思います。</p>


<p>Data::Model では Driver の機能として、データを透過キャッシュするなどの便利機能が存在しています。</p>
<p>そこで、いろいろ工夫擦る必要は特にありません。 Driver::Cache を使うと透過的キャッシュが利用できます。</p>
<p>このあたりの仕組は Data::ObjectDriver から拝借してます。</p>

<p>ただし Data::Model::Driver::Cache の戦略は全てのキャッシュを透過的に行ってしまうので、アプリケーションによっては無駄にキャッシュしすぎてしまうとかありえます。</p>
<p>ユーザの必要なケースに応じてキャッシュしてもらった方が効率が良いケースももちろんあるでしょうし Driver::Cache を使って楽をしたいときもあるでしょう。</p>
<p>幸い Data::Model では、特定のテーブルの Driver だけ変更する事ができるので一番重くて効果的なテーブルだけ memcached で透過的にキャッシュするという戦略も取れるのです。</p>

<p>では、早速データのキャッシュ方法についてみていきましょう。</p>

<pre>
# base driver を設定
my $driver = Data::Model::Driver::Memory->new;
base_driver $driver;

# user テーブルだけ透過キャッシュを指定
my $cache  = Data::Model::Driver::Cache::HASH->new(
    fallback => $driver,
);
install_model user => schema {
    driver $cache;
};
</pre>

<p>これだけです。</p>
<p>これで user というテーブルだけ透過的に on memory な HASH driver にキャッシュされるようになります。</p>
<p>あとはそのデータが DB から取得したものなのか、 DB から取得したものなのかを気にする必要は全くありません。</p>

<p>fallback というオプションは、 get したときに cache がなかったときに利用される Driver です。</p>
<p>きちんと Data::Model の Driver の規格にあったものなら何でも使えます。</p>
<p>Data::Model::Driver::Cache::HASH の fallback に Data::Model::Driver::Cache::HASH を指定するアホな事もできるし、 Data::Model::Driver::Memcached も使えます。</p>
<p>まぁ KVS ストレージを透過的に memcached でキャッシュするなんて無意味すぎるので誰もやらないけど。</p>

<p>もちろん Driver::Cache を base_driver として指定してもいいです。それは自己責任で。</p>

<p>上記の例は単純な Perl の HASH にキャッシュするだけの Driver でしたが memcached にキャッシュする時は Driver::Cache::Memcached を使います。</p>

<pre>
   my $driver = Data::Model::Driver::Memory->new;
   my $cache_driver = Data::Model::Driver::Cache::Memcached->new(
        fallback  => $driver,
        memcached => Cache::Memcached::Fast->new({ servers => [ { address => "localhost:11211" }, ], }),
    );
</pre>

<p>memcached オプションに任意の Cache::Memcached オブジェクトを入れるだけです。</p>

<p>簡単ですね。</p>

<p>明日は、自作 Driver について掘り下げていこうかと思います。</p>

<p>have a nice data-model days!:)</p>
</div>
]]></description>
      <dc:creator>yappo</dc:creator>
      <pubDate>Thu, 24 Dec 2009 09:48:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>Plack::Server::Standalone 系を使ってウェブアプリケーション開発と運用が楽になる話</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/hacker/23.html</link>
      <description><![CDATA[<div class="section">
<p>こんばんわ。Advent Calendar ２回目の登場になります、kazuho です。<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/05.html">前回</a>は hacker トラックなのに標準添付モジュールの紹介でしたが、今回は <a href="http://search.cpan.org/dist/Plack/">Plack</a> 関連の話です。</p>
</div>
<div class="section">
<h3>既存の環境に対する不満</h3>

<p>Perl のウェブアプリケーションを構築するにあたっては、リバースプロキシと mod_perl を組み合わせるか、あるいは FastCGI (ExternalServer) を利用するのが一般的だと思います。しかし、どちらをとっても、環境を構築して設定するのが難しいというのが個人的な不満でした (mod_redirect を設定したり mod_fastcgi にパッチをあててインストールしたり startup.pl を書いたり...)。自分が Plack の開発 (主に <a href="http://search.cpan.org/dist/Plack/lib/Plack/Server/Standalone.pm">Server::Standalone</a> と <a href="http://search.cpan.org/dist/Plack/lib/Plack/Server/Standalone/Prefork.pm">Server::Standalone::Prefork</a>) に関わるようになったのも、そのイライラを解消したかったからです。</p>

<p>その目的は、ほぼ達成することができ、先週、会社で運用しているサービス「<a href="http://pathtraq.com/">パストラック</a>」のバックエンドを Apache + FastCGI から Apache (mod_proxy) + <a href="http://search.cpan.org/dist/Plack-Server-Standalone-Prefork-Server-Starter/">Plack::Standalone::Server::Prefork::Server::Starter</a> へ切り替えました。</p>

<p>そこで今日は、Plack::Server::Standalone 系のモジュールを利用したウェブアプリケーションの開発と運用の実際について、その楽さ加減を宣伝したいと思います。</p>
</div>
<div class="section">
<h3>.psgi と plackup</h3>

<p>Standalone 系のサーバの特徴は、Plack の Middleware と組み合わせて、単独で動作するウェブサーバを構築できるところです。パストラックの .psgi ファイルは、概ね以下のようになっています。/static 以下のファイルを静的コンテンツとして配信し、動的コンテンツは昔ながらの <a href="http://search.cpan.org/dist/CGI-Application-Dispatch/">CGI::Application::Dispatch</a> を用いて生成しています。</p>

<pre>
builder {
    enable 'AccessLog', format => 'combined';
    enable 'ConditionalGET';
    enable_if { $_[0]->{PATH_INFO} =~ q{^/static/\d+/} } 'Header', set=> [ 'Expires' => 'Tue, 31 Dec 2019 23:59:59 GMT' ];
    enable 'Plack::Middleware::Static', path => qw{^/static/}, root => $ROOT;
    enable 'Plack::Middleware::Static', path => qw{^/(?:favicon\.(ico|png)|robots\.txt)$}, root => "$ROOT/static";
    CGI::Application::Emulate::PSGI->handler(
        sub {
            MyApp->dispatch();
        },
    );
};
</pre>

<p>開発環境では、この .psgi ファイルを以下のようにして Plack::Server::Standalone で動かします。httpd を起動する必要も設定ファイルを書く必要もありません。plackup 自身が、上で述べたように静的コンテンツも含む完全なウェブアプリケーションとして動作します。また、-r オプションによって、コードを書き換えると自動的に再起動するようになるので、修正→テストのサイクルを素早く回すことができます。</p>

<pre>
% plackup -r index.psgi
</pre>
</div>
<div class="section">
<h3>運用環境への投入</h3>

<p>運用環境では、上の .psgi ファイルを Plack::Server::Standalone::Prefork::Server::Starter (以下 PSSPSS) を利用して動かしています。サービスの起動スクリプト (daemontoolsで管理) は、以下のとおり。.psgi は開発用のものと全く同一。他に設定ファイルはありません。</p>

<pre>
#! /bin/sh

exec 2>&1
cd /var/webapp || exit 1
exec /usr/bin/start_server --port=80 -- /usr/local/bin/setuidgid www \
  /usr/bin/plackup -E production -s Standalone::Prefork::Server::Starter --max-workers=40 --max-keepalive-reqs=1 index.psgi
</pre>

<p>PSSPSS は、Plack::Server::Standalone ベースのマルチプロセス httpd である Plack::Server::Standalone::Prefork に、完全無停止での更新機能を追加した httpd です。ウェブアプリケーションのプログラムを書き換えた後に SIGHUP を送ることで、サービスを一切停止しないホットデプロイが可能です。この仕組みについては、詳しくは「<a href="http://developer.cybozu.co.jp/kazuho/2009/09/writing-hot-dep.html">Kazuho@Cybozu Labs: Writing Hot-deployable servers (introduction of Server::Starter)</a>」をご覧ください。</p>

<p>自分は daemontools で管理しているので、以下のようにして SIGHUP を送っています。</p>

<pre>
# svc -h /servire/webapp
</pre>

<p>Plack::Server::Standalone::Prefork (とその子クラスであるPSSPSS) では、デフォルトで keep-alive がオンになるのですが、ここではリバースプロキシと組み合わせるために、keep-alive をオフにしています(--max-keepalive-reqs=1)。</p>

<p>ちなみにパストラックでは、この設定で 20〜40 リクエスト/秒ほどの動的コンテンツを XenServer 上で生成しています。</p>
</div>
<div class="section">
<h3>リバースプロキシとの組み合わせ</h3>

<p>PSSPSS (あるいは Plack::Server::Standalone::Prefork) は単独でのウェブサービス提供も十分可能なポテンシャルを持っていますが、パストラックでは Apache (mpm_worker) ベースのリバースプロキシと組み合わせて運用しています。mpm_worker で同時接続数の上限を稼ぐ。それによって keep-alive オンでの運用が可能となり、ユーザーのレスポンスが向上するから、というのが理由です。</p>

<p>この場合の設定も簡単。plackup が単独で全コンテンツをサーブできるので、必要なのはリバースプロキシとキャッシュだけです。だいたい、以下のような設定で運用しています。</p>

<pre>
&lt;VirtualHost _default_:80&gt;
  ServerName webapp.example.com
  ErrorLog /var/log/httpd/webapp.example.com/error_log
  CustomLog /var/log/httpd/webapp.example.com/access_log combined
  CacheEnable disk /static
  &lt;Location /&gt;
    Order allow,deny
    Allow from all
    ProxyPass http://webapp.local/
    ProxyPassReverse http://webapp.local/
    ProxyPreserveHost On
  &lt;/Location&gt;
&lt;/VirtualHost&gt;
</pre>
</div>
<div class="section">
<h3>まとめ</h3>

<p>このように、Plack::Server::Standalone 系のモジュールを使うことで、開発から運用までのコストを低く抑えることが可能です。具体的には、</p>

<ul>
<li>開発環境の構築が容易</li>
<li>開発環境と運用環境の差異が少ない (ので問題が発生しにくい)</li>
<li>設定項目が少ない</li>
<li>運用環境でのホットデプロイが簡単</li>
<li>全て HTTP ベースなので、問題の切り分けが容易</li>
</ul>

<p>といったあたりになるでしょうか。大規模な案件で使うメリットがどれだけあるかは不明ですが、小〜中規模な開発では便利だと思います。一度お試しあれ。</p>

<p>PS. 明日は、ななしさんです。お楽しみに！</p>
</div>
]]></description>
      <dc:creator>kazuho</dc:creator>
      <pubDate>Thu, 24 Dec 2009 03:26:02 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DBIx::Skinny::Schema::Loaderで楽々Schema設定 #23</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/23.html</link>
      <description><![CDATA[<div class="section">
<p>nekokakさんと名前が紛らわしいと評判のnekoyaです。</p>

<p>今日はSkinny本体ではなく、関連モジュールとしてDBIx::Skinny::Schema::Loaderを紹介します。</p>

<p>インストールは、ふつうにCPANから</p>
<pre>
cpan DBIx::Skinny::Schema::Loader
</pre>
<p>するか、<a href="http://github.com/nekoya/p5-dbix-skinny-schema-loader">githubからclone</a>してください。</p>

<p>DBIx::Skinny本体のバージョンが古いと動きません。CPANから0.05を入れるか、あるいは<a href="http://github.com/nekokak/p5-dbix-skinny">githubの最新版</a>を入れてください。</p>

<p>DBIx::Skinny::Schema::Loaderは名前から想像できるように、Skinnyで使用するSchemaを自動設定するモジュールです。</p>

<p>その場でinstall_tableを実行する動的生成の他に、Schemaクラスのファイルを書き出す静的生成にも対応しています。</p>

<p>対応DBはSQLite, MySQL, Postgresqlです。Skinny本体はOracleにも対応していますが、Schema::Loaderでは今のところサポートしていません。</p>
</div>
<div class="section">
<h3> ■動的生成</h3>

<p>Schemaクラスを以下のように書くと、ロード時にDBに合わせたSchema情報を設定します。</p>

<pre>
package Your::DB::Schema;
use base qw/DBIx::Skinny::Schema::Loader/;

__PACKAGE__->load_schema;

1;
</pre>

<p>通常、Schemaクラスでは use DBIx::Skinny::Schema; しますが、Schema::Loaderを使う場合はuse baseしてください。</p>

<p>load_schemaを呼ぶと、DBの中を見て、各テーブルのpkとcolumnsを設定するinstall_tableが実行されます。</p>

<p>それ以外の要素、例えばinflate/deflateやtriggerなどを設定したい場合は、</p>

<pre>
package Your::DB::Schema;
use base qw/DBIx::Skinny::Schema::Loader/;

install_table users => schema {
    trigger pre_insert => sub {
        my ($class, $args) = @_;
        $args->{ status } = 'hooked';
    };
};

__PACKAGE__->load_schema;

1;
</pre>

<p>のように書きます。install_tableは複数回に分けて書いてもいいので、ここに書いたtriggerとload_schemaが設定するpk, columnsの両方の設定が生きます。</p>

<p>load_schemaによる動的生成を使う場合の注意点として、Skinnyクラス（上の例だとYour::DB）にuse DBIx::Skinny setupでDBの接続情報を書いておくことが必要です。</p>

<p>newで接続情報を渡したりする場合でもやってやれなくはないのですが、無理矢理感があるので、そういったケースでは基本的には次の静的生成をお勧めしています。</p>
</div>
<div class="section">
<h3> ■静的生成</h3>

<p>まず、以下のようなスクリプトを書きます。publish_schema.plなど適当な名前で保存しておきましょう。</p>

<pre>
use DBIx::Skinny::Schema::Loader qw/make_schema_at/;
print make_schema_at(
  'Your::DB::Schema',
  {},
  [ 'dbi:SQLite:test.db', '', '' ]
);
</pre>

<p>そして、以下のようにすると、Your::DB::Schemaのファイルが書き出されます。</p>

<pre>
perl publich_schema.pl > lib/You/DB/Schema.pm
</pre>

<p>DBICと違ってSkinnyのSchemaは1ファイルで完結するので、直接ファイルを書き出すのではなく、標準出力に吐き出すようにしています。</p>

<p>make_schema_atは第2引数にオプションを指定して、出力するSchemaクラスのカスタマイズが出来ます。</p>

<p>例えば、先程のload_schemaのようなtrigger設定を入れるには以下のようにします。</p>

<pre>
use DBIx::Skinny::Schema::Loader qw/make_schema_at/;

my $before = << '...';
install_table users => schema {
    trigger pre_insert => sub {
        my ($class, $args) = @_;
        $args->{ status } = 'hooked';
    };
};
...

print make_schema_at(
  'Your::DB::Schema',
  {
    before_template => $before,
  },
  [ 'dbi:SQLite:test.db', '', '' ]
);
</pre>

<p>この他、install_tableブロックの後に指定の文字列を挿入するafter_templateや、install_tableブロックに任意のテンプレートを使用するtable_templateといったオプションがあります。詳細はPODをご覧ください。</p>

<p>PODには他にも動作の詳細やちょっとしたTipsを書いていますので、よかったら目を通してみてください（破滅的な英語ですが）。</p>

<p>このように、Schema::Loaderを使うとSchema定義を書く手間を軽減することが出来ます。個人的には、テーブル設計が流動的な開発初期はload_schemaによる動的生成を使い、ある程度落ち着いてきたらmake_schema_atによる静的生成に切り替えるといいんじゃないかと思ってます。</p>

<p>明日はDBIx::Skinny::InflateColumn::DateTimeを紹介します。</p>

<p>have a nice skinny days!:)</p>
</div>
]]></description>
      <dc:creator>nekoya</dc:creator>
      <pubDate>Thu, 24 Dec 2009 02:45:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>BackPANで失せ物を探す</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/casual/22.html</link>
      <description><![CDATA[<p>今年も残るところ10日をきりました。皆様如何お過ごしでしょうか。今日は<strong>Acme話を自重する</strong>つもりのmakamaka_at_donzokoでございます。</p>

<p>さて、年の瀬ともなりますと大掃除なんかになりまして、無くしたと思っていたものが見つかったとか、あると思っていたものが見当たらない、なんてことがよくあります。見つかる方は良いのですが、気がついたら無くなってた、というのは困ります。</p>

<p>という強引な前振りですが、search.cpan.orgでモジュールを探していて、「あれ？　確か前はあったはずなのに……」なんて経験、ありませんか？</p>

<p>そう、例えば、今年の初めにはCPANにあったAcme::BabyEaterがなくなってたり、2003年には確かにあったAcme::ManekiNekoが見当たらなくなったり（と思ったら後にひょっこり帰ってきましたが）という具合にですね。</p>

<pre><code>% cpan Acme::BabyEater
(中略)
Warning: Cannot install Acme::BabyEater, don't know what it is.
Try the command

    i /Acme::BabyEater/
</code></pre>

<p>むむむ……　search.cpan.orgで調べるてみても<a href="http://search.cpan.org/dist/Acme-BabyEater/">やっぱり見あたらない</a>。</p>

<p>ディストリビューションがまるまる無くなってしまうのも困りものですが、そうでなくても結構やっかいなケースがあります。</p>

<p>例えば、あるモジュールのバージョンが上がったところ、インストールできなくなったり、今まで動いていたものとの互換性がなくなったり（すいません私、前科あります）ということが、ままあります。</p>

<p>特に古いバージョンのPerl（5.005系や5.6系）で動いていたモジュールがある時点で対応しなくなった上に、以前のモジュールがCPANから削除されていたりすると、まあ大変！（え？　Perlのバージョン上げろと？　色々大人の事情でダメな場合がありますので……）</p>

<p>このように、あなたがお探しのモジュールがCPANから消えてしまった時は、BackPANに出かけましょう！</p>

<hr />

<p><a href="http://backpan.perl.org/">BackPAN</a> "A Complete History of CPAN"は、PAUSE(Perl Authors Upload Server)にアップされたファイルを全て保存しています。そしてPAUSEからモジュールを削除しようとも、BackPANからは削除されません。ですので、BackPANはCPANの「歴史」であり、もはや顧みられなくなった多くのモジュールが静かに（？）眠っているのです。BackPANをCPAN墓場と呼ぶ人もいます。<a href="http://www.slideshare.net/brian_d_foy/backpan-archeology-presentation" title="the bone yard of CPAN">例1</a> 
<a href="http://search.cpan.org/dist/Module-ThirdParty/" title="the BackPAN graveyard">例2</a></p>

<p>さて、BackPANに行けば、なんでもそろっています。行ってみましょう。</p>

<p><a href="http://backpan.perl.org/authors/id/">authors/id/....</a>……CPAN ID別にディレクトリが分かれているので、ちょっとモジュールを探すのが面倒そうですね。</p>

<p>そこで<a href="http://search.cpan.org/dist/Parse-BACKPAN-Packages/">Parse::BACKPAN::Packages</a>の登場です。このモジュールは、BackPANのインデックスデータ<a href="http://www.astray.com/tmp/backpan.txt.gz">http://www.astray.com/tmp/backpan.txt.gz</a>を利用して、ディストリビューション名や作者からBackPAN上のアドレスを教えてくれます。</p>

<pre><code>#!/usr/bin/perl

use strict;
use warnings;
use Parse::BACKPAN::Packages;

my $p = Parse::BACKPAN::Packages-&gt;new();

my $distname = $ARGV[0] or die "Distribution name?";
my $check_version;

if ( $distname =~ /^(.+?)-([_.\d]+)$/ ) { # バージョンまで指定
    $check_version = $2;
    $distname = $1;
}
else { # P::B::PはFoo-Bar形式で受け付けるので、Foo::Bar形式にも対応
    $distname =~ s/::/-/g;
}

# リストは日付の古い順
my @dists = $p-&gt;distributions( $distname ) or die "Can't find $distname";
my $file;

if ( defined $check_version ) {
    for my $dist ( @dists ) {
        if ( $dist-&gt;version == $check_version ) {
            $file = $p-&gt;file( $dist-&gt;prefix );
            last;
        }
    }
}
else {
    $file = $p-&gt;file( $dists[ -1 ]-&gt;prefix );
}

die "$distname was found, but not version $check_version." unless $file;

print $file-&gt;url, "\n";
</code></pre>

<p>backpan_url.plという名前をつけて</p>

<pre><code>% perl backpan_url.pl Acme::BabyEater
</code></pre>

<p>とやってみましょう。</p>

<p><code>http://backpan.cpan.org/authors/id/Z/ZO/ZOFFIX/Acme-BabyEater-0.04.tar.gz</code>が出力されました（<a href="http://backpan.cpan.org/">http://backpan.cpan.org/</a>ですが問題ありません）。</p>

<p>最初はインデックスデータを取りに行くため、初期化にかなり時間がかかるかもしれませんが、一度データを取得すると、1時間キャッシュされます。</p>

<p>上記のサンプルでは該当するディストリビューションの最新版を返しますが、<code>Acme-Manekineko-0.01</code>のような形式にすれば、バージョン0.01を探します。</p>

<hr />

<p>さて、これでモジュールは簡単に手に入るようになりました。後はファイルを展開して<code>cpan .</code>とでもすれば良いでしょう。でもどうせなら、コマンド一発でインストールしたいです。</p>

<p>cpanコマンドからBackPANサイトにアクセスしてうまいことやってくれないかなあ、と考えたのですが、これはなかなか難しいようです（ローカルに自分の用のCPANサイトを作る方法がありますが、そこまで大掛かりにしたくない）。色々検討した結果、<a href="http://search.cpan.org/dist/CPAN-Inject/">CPAN::Inject</a>を使うことにします。</p>

<pre><code>use strict;
use warnings;
use CPAN::Shell;
use CPAN::Inject;

my $cpan_inject = CPAN::Inject-&gt;new(
    sources =&gt; "$CPAN_HOME/sources",
    author  =&gt; 'MAKAMAKA',
);

my $install_path = $cpan_inject-&gt;add( file =&gt; 'path/Not-In-CPAN-1.00.tar.gz' );
</code></pre>

<p>こうすると<code>$CPAN_HOME/sources</code>下の適切な位置（例えば /home/makamaka/.cpan/sources/authors/id/M/MA/MAKAMAKA）にNot-In-CPAN-1.00.tar.gzをコピーしてCHECKSUMを生成してくれます（<code>new</code>の代わりに<code>from_cpan_config</code>を使えばCPAN::Configから自動的に<code>$CPAN_HOME/sources</code>を設定）。</p>

<p>ここまでくると、後はCPAN::Shellを使ってinstallも簡単。</p>

<pre><code>CPAN::Shell-&gt;install( $install_path );
</code></pre>

<p>うまくいけばいつものようにインストールされます。先に載せたbackpan_url.plでURLを取得、ファイルをGETして上記のサンプルコードで適宜設定してしまえば、一連の作業を自動化できますね。</p>

<p>というわけで、上記の処理を簡単に行うために<a href="http://github.com/makamaka/perl-backpan-downloader">BackPAN::Downloader</a>を作ってみました。</p>

<pre><code>package BackPAN::Downloader;

use Mouse;
use Parse::BACKPAN::Packages;
use CPAN::Inject;
use LWP::UserAgent;
use CPAN::Debug;
use CPAN::Shell;
use Path::Class;
use Try::Tiny;
use Cwd;
use Data::Dumper;

our $VERSION = '0.01';

has distfile =&gt; ( is =&gt; 'rw', isa =&gt; 'Parse::BACKPAN::Packages::Distribution|Undef' );

has filedata =&gt; ( is =&gt; 'rw', isa =&gt; 'Parse::BACKPAN::Packages::File|Undef' );

has dist_is_found =&gt; ( is =&gt; 'rw', isa =&gt; 'Bool' );

has temp_dir =&gt; ( is =&gt; 'rw', isa =&gt; 'Str', default =&gt; './' );

has ua =&gt; ( is =&gt; 'rw', isa =&gt; 'LWP::UserAgent', default =&gt; sub { LWP::UserAgent-&gt;new     } );

has error =&gt; ( is =&gt; 'rw', isa =&gt; 'Str|Undef' );

no Mouse;


sub reset {
    my ( $self ) = @_;
    $self-&gt;dist_is_found( 0 );
    $self-&gt;distfile( undef );
    $self-&gt;filedata( undef );
    $self-&gt;error( undef );
}


sub lookup {
    my ( $self, $distname ) = @_;
    my $check_version;

    $self-&gt;reset;

    if ( $distname =~ /^(.+?)-([_.\d]+)$/ ) { # バージョン指定なので正式名称
        $check_version = $2;
        $distname = $1;
    }
    else {
        $distname =~ s/::/-/g; # Foo-BarだけでなくFoo::Barでも検索できるように
    }

    my $p = Parse::BACKPAN::Packages-&gt;new();

    # リストは日付の古い順
    my @dists = $p-&gt;distributions( $distname );
    my $found;

    if ( defined $check_version ) {
        for my $dist ( @dists ) {
            if ( $dist-&gt;version == $check_version ) {
                $found = $dist;
                last;
            }
        }
    }
    else {
        $found = $dists[ -1 ] if ( @dists );
    }

    if ( $found ) {
        $self-&gt;distfile( $found );
        $self-&gt;filedata( $p-&gt;file( $found-&gt;prefix ) );
        $self-&gt;dist_is_found( 1 );
    }
    else {
        $self-&gt;error( "Can't find $distname." );
    }

    return $found;
}


sub download {
    my ( $self ) = @_;

    unless ( $self-&gt;dist_is_found ) {
        $self-&gt;error('donwload() must be called after the dist file was found.');
        return;
    }

    my $url  = $self-&gt;filedata-&gt;url;
    my $file = Path::Class::File-&gt;new( $self-&gt;temp_dir, $self-&gt;distfile-&gt;filename );

    return 1 if ( -s $file );

    my $ua   = $self-&gt;ua;
    my $res  = $ua-&gt;get( $url );

    if ( $res-&gt;is_success ) {
        my $fh = $file-&gt;openw();
        unless ( $fh ) {
            $self-&gt;error( "Can't open file." );
            return;
        }
        print $fh $res-&gt;content;
    }
    else {
        $self-&gt;error( "Can't download, status line is " . $res-&gt;status_line );
        return;
    }
}


sub install {
    my ( $self, %opts ) = @_;

    unless ( $self-&gt;dist_is_found ) {
        $self-&gt;error('donwload() must be called after the dist file was found.');
        return;
    }

    my $distfile = $self-&gt;distfile;
    my $cpan_inject = CPAN::Inject-&gt;from_cpan_config( author =&gt; $distfile-&gt;cpanid );

    my $installed;
    my $file = Path::Class::File-&gt;new( $self-&gt;temp_dir, $distfile-&gt;filename );
    my $cwd  = getcwd;

    try {
        my $inst_path = $cpan_inject-&gt;add( file =&gt; $file );
        CPAN::Shell-&gt;install( $inst_path );
        $installed = 1;
    } catch {
        $self-&gt;error( @_ );
    };

    chdir( $cwd );

    if ( $installed and $opts{ delete_saved_file } ) {
        unlink($file) or Carp::carp("Can't delete saved file $file. $!");
    }

    return $installed;
}

#------- backpan_inst.pl
package main;

use strict;
use warnings;

@ARGV or die "Distribution name?";

my $backpan  = BackPAN::Downloader-&gt;new( temp_dir =&gt; './tmp' );

for my $distname ( @ARGV ) {

    $backpan-&gt;reset;

    unless ( $backpan-&gt;lookup( $distname ) ) {
        printf( "%s was not found.\n", $distname );
        next;
    }

    printf( "Found %s in %s\n", $distname, $backpan-&gt;filedata-&gt;url );

    $backpan-&gt;download() or die sprintf("Can't download. (%s)", $backpan-&gt;error);
    $backpan-&gt;install( delete_saved_file =&gt; 1 )  or die "Can't install, " . $backpan-&gt;error;
}
</code></pre>

<p>さあ、試してみましょう。ダウンロードしたファイルを一時的に保存するtmpディレクトリを作成して</p>

<pre><code>% perl backpan_inst.pl Acme::BabyEater
</code></pre>

<p>例によってデータ取得に時間がかかるかもしれませんが、</p>

<pre><code>Found Acme::BabyEater in http://backpan.cpan.org/authors/id/Z/ZO/ZOFFIX/Acme-BabyEater-0.04.tar.gz
Going to read '/home/makamaka/.cpan/Metadata'
  Database was generated on Sat, 19 Dec 2009 01:30:46 GMT
CPAN: YAML loaded ok (v0.68)
Running make for Z/ZO/ZOFFIX/Acme-BabyEater-0.04.tar.gz
Checksum for /home/makamaka/.cpan/sources/authors/id/Z/ZO/ZOFFIX/Acme-BabyEater-0.04.tar.gz ok
(..skip..)
  ZOFFIX/Acme-BabyEater-0.04.tar.gz
  ./Build install  -- OK
</code></pre>

<p>うまくいきました。上のサンプルは<a href="http://github.com/makamaka/perl-backpan-downloader">http://github.com/makamaka/perl-backpan-downloader</a>にあります。</p>

<p>そうそう、副次効果がありました。Acme::Tinyのように、<code>cpan</code>コマンドからインストールするためにはCPAN IDとバージョンを調べて</p>

<pre><code>cpan DMUEY/Acme-Tiny-0.4.tar.gz
</code></pre>

<p>しないといけないディストリビューションでも、</p>

<pre><code>% perl backpan_inst.pl Acme::Tiny
</code></pre>

<p>で、一発インストール可能です。素晴らしい！　これでインデックス化されないAcme7ディストリも怖くない！</p>

<hr />

<p>それから、BackPANの他にも、最近はschwern氏によってCPANの歴史をgithubに移そうという試みがされています（<a href="http://github.com/gitpan/">gitpan</a>）。</p>

<p><br /><br />さて、それでは最後に、冒頭で取り上げたAcme::ManekiNekoについてのちょっといい話をいたしましょう。</p>

<p>2003年の後、作者はこのモジュールをCPANから削除します。ところが、このモジュールを愛する人たちがいることを知ります。しかし、時既に遅し。作者はコードを失っていました。</p>

<p>おお、なんてことでしょう！</p>

<p>作者は自分を責めます。</p>

<p>しかし！</p>

<p>そう、BackPANがあったのです！</p>

<p>こうして2008年に再びAcme::ManekiNekoはCPANに復活するのでありました。クリスマスの季節に相応しい素敵な物語ですね。</p>

<p>明日はka2uさんです。ではでは～</p>

<hr />

<p>参考：</p>

<p>BackPANについて</p>

<ol>
<li><a href="http://www.slideshare.net/brian_d_foy/indexing-backpan">Indexing Backpan</a></li>
<li><a href="http://www.slideshare.net/brian_d_foy/backpan-archeology-presentation">Backpan  Archeology</a></li>
<li><a href="http://www.slideshare.net/brian_d_foy/making-my-own-cpan">Making Your Own CPAN</a></li>
<li><a href="http://backpan.cpan.org/authors/id/H/HF/HFB/grok-cpan.pdf">Grokking The CPAN</a></li>
</ol>

<p>ローカルCPAN関連（DarkPAN面白そう）</p>

<ol>
<li><a href="http://www.naney.org/diki/d/2006-02-b.html#2006-02-12-CPAN-Mini-Inject">野良パッケージと依存PerlモジュールのインストールセットをCPAN::Mini::Injectで</a></li>
<li><a href="http://log.perl.org/2005/09/create_your_own.html">Create your own "BackPAN"</a></li>
<li><a href="http://www.perlmonks.org/?node_id=722831">A preview of DPAN</a></li>
<li><a href="http://use.perl.org/~brian_d_foy/journal/37375">Cataloging BackPAN: MiniCPAN done in 9 hours</a></li>
</ol>

<p>gitpanについて</p>

<ol>
<li><a href="http://use.perl.org/~schwern/journal/39972">gitPAN</a></li>
</ol>

<p>Acme::ManekiNekoについて</p>

<ol>
<li><a href="http://search.cpan.org/~gmccar/Acme-ManekiNeko-0.02/ManekiNeko.pm#HISTORY">Acme::ManekiNeko/HISTORY</a></li>
<li><a href="http://www.runme.org/project/+AcmeManekiNeko/">Acme::ManekiNekoの画像</a></li>
<li><a href="http://www.hyuki.com/yukiwiki/wiki.cgi?Acme%3A%3AManekiNeko">招き猫モジュール</a></li>
<li><a href="http://www.donzoko.net/doc/perlmod/subarasiki.html">素晴らしきPerlモジュールの世界</a></li>
</ol>

<p>その他</p>

<ol>
<li><a href="http://kawa.at.webry.info/200911/article_8.html">Mac OS X 10.6（Snow Leopard）にPerl 5.005をインストールする</a>
↑ここでmiyagawaさんがコメントしている<a href="http://cp5.5.3an.barnyard.co.uk/">http://cp5.5.3an.barnyard.co.uk/</a>、<a href="http://cp5.6.2an.barnyard.co.uk/">http://cp5.6.2an.barnyard.co.uk/</a>などは初めて知りました。</li>
</ol>
]]></description>
      <dc:creator>makamaka_at_donzoko</dc:creator>
      <pubDate>Mon, 21 Dec 2009 23:00:00 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>レプリケーションとQ4Mを使う</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/data-model/22.html</link>
      <description><![CDATA[<div class="section">
<h3> はじめに</h3>

<p>さぁ Driver 集中講義二回目です。</p>

<p>ホントは別々に書こうと思ったのですが MySQL 関連という事でまとめて紹介します。</p>
</div>
<div class="section">
<h3> レプリケーションを使う</h3>

<p>MySQL にはレプリケーション機能があるのはとても知られている事です。</p>

<p>レプリケーションの無い MySQL は、ブラウン管の無いブラウン管テレビくらいの物でしょう。</p>

<p>そして ORM でも切っても切れない物では無いでしょうか。</p>

<p>Data::ObjectDriver でも livedoor のどっかのサービスで自作 Driver 書いて使ってるとか</p>
<p>DBIC の世界でも DBIx::Class::Storage::DBI::Replicated なんて物があるようです。</p>

<p>もちろん Data::Model でも対応しています。</p>

<p>Driver::DBI::MasterSlave です。 Driver::DBI を使った事がある人ならとても簡単な物となっています。</p>

<pre>
  use Data::Model::Driver::DBI::MasterSlave;
  
  my $dbi_connect_options = {};
  my $driver = Data::Model::Driver::DBI::MasterSlave->new(
      # master の接続設定
      master => {
          dsn => 'dbi:mysql:host=master.server:database=test',
          username => 'master',
          password => 'master',
          connect_options => $dbi_connect_options,
      },
      # slave の接続設定
      slave  => {
          dsn => 'dbi:mysql:host=slave.server:database=test',
          username => 'slave',
          password => 'slave',
          connect_options => $dbi_connect_options,
      },
  );

  # base driver の設定をする
  base_driver $driver;
</pre>

<p>上記の用に master => $conf, slave => $conf という形で設定するだけです。 $conf の中身は先日紹介した Driver::DBI の設定と全く同じ物がつかえます。</p>

<h4> 仕組み</h4>

<p>Driver::DBI も 内部的には DBI のインスタンスを使って DBI 接続を行っていて DBI::MasterSlave では DBI のインスタンスを2個作って、それぞれ master, slave 用として使います。</p>

<p>Driver::DBI は SELECT クエリでは r_handle を使い それ以外のクエリでは rw_handle を使っており、それぞれの handle にたいして master, slave の DBI のインスタンスを割り当てるので、うまい具合 master, slave でクエリを振り分ける事ができるのです。</p>

<h4> 注意</h4>

<p>txn_scope を使ったトランザクション中は、全てのクエリが rw_master に向かいますので注意してください。</p>

<h4> 複数の slave を扱うにはどうするの?</h4>

<p>DBIC の世界だと複数台の slave をうまいこと扱ってくれる仕組みがあるのですが Data::Model では、そのような仕組みはありません。</p>

<p>バグ?手抜き? いえいえ、元からこういう設計です。</p>

<p>そういった複数台の slave を持つ場合には lvs などを使って、より低いレイヤーにて分散環境を整えてください。</p>

<p>このような低いレベルでやるべき処理を Perl のレイヤでやるよりかは、それなりに実績がある低レイヤ処理するべきだと思っています。</p>

<p>しかも slave の分散クエリなんて lvs で十分事足りるし Perl のコード書くよりも高度な事ができるので良いです。</p>

<p>まぁ、インテグレーションというのは時と場合によって思うとおりの道具が使えないでしょうから、細かい要求などは Data::Model::Driver の自分向けに書いてくださいという事になります。この辺の詳しい話は後日書きます。</p>
</div>
<div class="section">
<h3> Q4M を使う</h3>

<p>Data::Model の特色の一つとして Q4M 対応が行われているということです。</p>

<p>このあたりのエッセンスは3日目( <a href="http://perl-users.jp/articles/advent-calendar/2009/data-model/03.html">http://perl-users.jp/articles/advent-calendar/2009/data-model/03.html</a> )に紹介してますが、3日目は超応用編だったので今日は Driver::Queue::Q4M の使い方を紹介します。</p>


<h4> Q4M とは</h4>

<p>Q4M とは、サイボウズラボの奥一穂(以下略</p>

<h4> スキーマ定義</h4>

<p>スキーマ定義は、通常の定義と殆どおんなじ感じです。</p>

<pre>
    package TestQueue;
    use base 'Data::Model';
    use Data::Model::Schema;
    # Queue::Q4M の mixin が必須
    use Data::Model::Mixin modules => ['Queue::Q4M'];
    use Data::Model::Driver::Queue::Q4M;

    my $driver = Data::Model::Driver::Queue::Q4M->new(
        dsn => 'dbi:mysql:database=test'
    );
    base_driver $driver;

    install_model queue_test => schema {
        columns qw/ id job_name /;

        # as_sqls の為にも TYPE=Queue を入れとく
        schema_options create_sql_attributes => {
            mysql => 'TYPE=Queue',
        };
    };
</pre>

<p>Q4M 専用のメソッドを追加するために mixin を指定するのと CREATE TABLE 文の為に schema_options create_sql_attributes を指定します。</p>

<p>as_sqls で出来上がる SQL は下記のようになります。</p>

<pre>
CREATE TABLE queue_test (
    id              CHAR(255)      ,
    job_name        CHAR(255)      
) TYPE=Queue;
</pre>

<p>Q4M では index のサポートが行われていないので、 primary key などの事は忘れましょう。</p>
<p>もちろんユニーク制約なんてのも使えませんからね。</p>

<h4> Queue を作る</h4>

<p>Q4M の Queue を作るのはとても簡単です！</p>
<p>なんてったって普通の MySQL のテーブルの用に扱えるのが Q4M の良いところの一つなんで、普通に set メソッドして INSERT すれば良いんです.</p>

<pre>
    # queue を一つ作る
    $queue->set(
        queue_test => {
            id       => 1,
            job_name => 'get http://example.com/',
        },
    );
</pre>

<h4> Queue を読む</h4>

<p>Q4M を使う場合は queue_wait を使って dequeue してからデータを読むことが一般的ですが抜く、一応 Data::Model ごしでも Queue の内容を直接読むことが出来ます。</p>
<p>ただし primary key とかは無いので where とか使って見る必要があるでしょう。</p>

<p>index を使ってないですが、そもそも Q4M に Queue が大量に保存されてることはありえないので問題にならないでしょう。大量にあるんだったらお前のアプリの書き方がとち狂ってる。</p>

<pre>
    # queue を読む
    my($q) = $queue->get('queue_test');
    warn $q->job_name;
</pre>

<h4> Queue の削除</h4>

<p>普通は Q4M が勝手に削除するんですが、どうしても手動で消したい人の為に書いておきます。</p>
<p>基本的にはスキーマオブジェクトの delete メソッドを叩くだけです。</p>

<pre>
    # こういう delete はできない
    $q->delete;

    # 直接 query を吐いて delete する
    $queue->delete('queue_test', {
        where => [
            id => 1,
        ],
    });

    # DELETE FROM queue_test; はこれ
    $queue->delete('queue_test', {});
</pre>

<p>$q->delete のように Row オブジェクトの delete メソッドは primary key が無いテーブルには使えないため上記のコードのようなコメントになっています。</p>

<h4> update</h4>

<p>(略</p>

<h4> dequeue する</h4>

<p>Q4M では queue_wait を使って dequeue をします、細かいことはドキュメント読んでください。</p>

<p>まぁこんな SQL ですね。</p>

<pre>
mysql> SELECT queue_wait('high_priority_table', 'low_priority_table', 10);
</pre>

<p>これに相当する Data::Model の使い方としては queue_running メソッドを使います。</p>

<pre>
  my $queue = TestQueue->new;
  my $retval = $queue->queue_running(
      high_priority_table => sub {
          my $row = shift;
          # 何かの処理
      },
      low_priority_table  => sub {
          my $row = shift;
          # 何かの処理
      },
      timeout => 10,
  );
</pre>
<p>queue_running の引数に table_name => CODE リファレンス という構造の HASH を渡してあげます。</p>
<p>timeout だけは例外的に queue_wait に渡すタイムアウトを指定します。</p>

<p>table_name の queue table に enqueue されると、オーナーモードになった行の Row オブジェクトを引数としてコードを実行します。</p>

<p>簡単ですね。</p>

<h4> queue_abort</h4>

<p>Q4M では queue が何らかの処理で以上になった時に queue の先頭に enqueue してくれる queue_abort という物があります。</p>
<p>もちろん Data::Model でも使えます。</p>

<pre>
    my $ret = $queue->queue_running(
        queue_test => sub {
            my $row = shift;
            warn $row->id;
            warn $row->job_name;
            $queue->queue_abort;
        },
    );
    warn $ret;
</pre>

<p>このようにスキーマオブジェクトから生えている queue_abort メソッドを呼ぶだけです。</p>
<p>$ret の内容は、 queue_running が失敗してるだけ undef になります。</p>

<p>ちなみに CODE リファレンス中で die してしまった時も内部的には queue_abort されます。</p>

<pre>
    eval {
        my $ret = $queue->queue_running(
            queue_test => sub {
                die "abort"; # ここで queue_abort が呼ばれる
            },
        );
    };
    $@ and warn $@; # 'abort' と表示
    warn $ret;
</pre>

<p>ここの $ret も undef です。</p>

<h4> queue_end</h4>

<p>Q4M で queue の正常終了を表す queue_end という物が用意されています。</p>

<p>Data::Model では CODE リファレンスの中だけが Q4M のオーナーモードでいるという設計になっているので、 CODE リファレンスの中で queue_abort が呼ばれないまま return した時に自動的に queue_end が呼ばれます。</p>

<pre>
    my $ret = $queue->queue_running(
        queue_test => sub {
            return; # ここで queue_end が呼ばれる
        },
    );
    warn $ret; # 処理した queue テーブル名を表示
</pre>

<p>queue の処理が成功した場合に queue_running は queue_wait で処理した queue table 名を戻り値として返します。</p>

<h4> queue_running と queue_abort はどこから来たの?</h4>

<p>冒頭に Queue::Q4M mixin を使う設定をすると書いてありますが、この Mixin がスキーマオブジェクトに queue_running と queue_abort メソッドを生やしているんです。</p>

<p>単純に Driver に処理を delegation してるだけです。</p>
</div>
<div class="section">
<h3> まとめ</h3>

<p>今日は、レプリケーションと Q4M を Data::Model で使う為にどうするかといった事を紹介しました。</p>

<p>サックリ紹介するつもりが長文になって DNBK です。</p>

<p>明日はキャッシュ戦略について紹介します。</p>
</div>
]]></description>
      <dc:creator>yappo</dc:creator>
      <pubDate>Thu, 24 Dec 2009 11:06:01 GMT</pubDate>
      <category></category>
    </item>
    <item>
      <title>DBICx::Modeler::Generatorでスキーマクラス群とモデルクラス群を一発生成しよう</title>
      <link>http://perl-users.jp/articles/advent-calendar/2009/hacker/22.html</link>
      <description><![CDATA[<h2>ご挨拶</h2>

<p>はじめまして、<a href="http://blog.eorzea.asia/">gardejo</a>こと守屋と申します。金融ユー子で働いています。<a href="http://conferences.yapcasia.org/ya2009/">YAPC::Asia 2009</a>の<a href="http://conferences.yapcasia.org/ya2009/wiki?node=TrainingHome">特別研修</a>で、（<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/16.html">16日目</a>を執筆された）<a href="http://blog.livedoor.jp/dankogai/">dankogaiさん</a>の<a href="(http://conferences.yapcasia.org/ya2009/talk/2307)">研修</a>の後に、（<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/15.html">15日目</a>を執筆された）<a href="http://mt.endeworks.jp/d-6/">lestrratさん</a>などの特別補講を受ける機会に恵まれたのですが、「業務でCOBOLを使っている人？」という質問にただ独り挙手して、たいそう恥ずかしい思いをしました。</p>

<p>そんな勤め先では定例作業撲滅のためなどにPerlをゲリラ的に活用していますが、現場レベルでの対症療法であるに過ぎません。私にとってのPerlとは、エスペラント日本語翻訳システムの開発など、趣味の言語として楽しむ対象です。従って、このhacker trackの執筆陣として居並ぶハッカーの方々には多分に見劣りしますが、どうかご容赦ください。</p>

<p>本日は<a href="http://search.cpan.org/perldoc?Moose"><code>Moose</code></a>と<a href="http://search.cpan.org/perldoc?DBIx::Class"><code>DBICx::Class</code></a>が大好きな人にお勧めしたいアプリケーション設計方法論を踏まえて、<a href="http://search.cpan.org/perldoc?DBIx::Class::Schema::Loader"><code>DBIx::Class::Schema::Loader</code></a>と<a href="http://search.cpan.org/perldoc?DBICx::Modeler"><code>DBICx::Modeler</code></a>のヘルパーモジュールである<a href="http://search.cpan.org/perldoc?DBICx::Modeler::Generator"><code>DBICx::Modeler::Generator</code></a>をご紹介します。</p>

<h2>目次</h2>

<ol>
<li>モデルをWAFともスキーマとも分離する設計</li>
<li>クラスの山をどう作るか</li>
<li>作業の実際</li>
<li>実用上の留意点</li>
<li>中身の話</li>
<li>まとめ ～ Perlはエンタープライズアプリケーションに向いています！</li>
</ol>

<h2>1. モデルをWAFともスキーマとも分離する設計</h2>

<h3>1.1. エンタープライズアプリケーションで一番大事なビジネスロジック</h3>

<p>いわゆるエンタープライズアプリケーションの開発に際しては、大量のデータにまみれることになります。ここでの大量とは、テーブルの行方向も指しますが、むしろテーブルの列方向や、テーブル自体の数をこそ指すものと思ってください。</p>

<p>しかし扱うべきデータの量に圧倒されていてはいけません。エンタープライズアプリケーションで一番大事なものは何でしょうか。それはlestrratさんの『モダンPerl入門』でも触れられている通り、ビジネスロジックなのです。</p>

<h3>1.2. ビジネスロジックの書き方</h3>

<p>PoEAA（"Patterns of Enterprise Application Architecture", 邦訳『エンタープライズアプリケーションアーキテクチャパターン』）では、ビジネスロジック（PoEAAでは「ドメインロジック」という用語が使われています）を実装するパターンとして、</p>

<ul>
<li>Transaction Scriptパターン</li>
<li>Domain Modelパターン</li>
<li>Table Moduleパターン</li>
</ul>

<p>が挙げられています。このうち、私は特にDomain Modelパターンを強くお勧めしたいところです。これらの違いを簡潔に説明し、かつ、Domain Modelパターンのすばらしさをまとめている資料として、<a href="http://ameblo.jp/ouobpo">ouobpoさん</a>の「<a href="http://www.slideshare.net/ouobpo/ss-326835">ドメインロジックの実装方法とドメイン駆動設計</a>」というスライドが参考になります。</p>

<p>要はせっかくオブジェクト指向で生活しているんだから、データと振る舞いを分けるTransaction Scriptパターンではなく、データと振る舞いをカプセル化した「賢いデータ」を使うDomain Modelパターンを使うと楽しいよ、ということです。モデリングの敷居は多少高いけれども、業務を可視化して綺麗に書ける利点はとても大きいです。</p>

<p>ouobpoさんはまた<a href="http://ameblo.jp/ouobpo/entry-10036477015.html">日本ではTransaction Scriptパターンが優勢</a>とも述べています。しかし動的言語であるPerlであれば、Javaのように「静的言語を動的に扱う道具仕立て」自体が存在し得ないので、「いいところどり」を出来る余地があるのではないでしょうか。</p>

<p>実際にPerlでDomain Modelパターンに基づいて書いてみると、拍子が抜けるくらいに素直に実装出来る物です。食わず嫌いをやめてみて、一度試してみる価値は十分にあると思います。</p>

<h3>1.3. ビジネスロジックを他の層と分離する</h3>

<p>さてそんなビジネスロジックは、ouobpoさんのスライドの中にあるように、</p>

<ul>
<li>プレゼンテーション層</li>
<li>ドメイン層</li>
<li>インテグレーション層</li>
</ul>

<p>のうち、ドメイン層に位置します。</p>

<p>『モダンPerl入門』では、ビジネスロジックは<code>Catalyst</code>などのWAF（ウェブアプリケーションフレームワーク）とは分離されていて然るべきものだ、と言及されています。これはプレゼンテーション層とドメイン層を分離するという意味です。</p>

<p>そしてインテグレーション層には<code>DBIx::Class</code>などが当てはまりますが、これもドメイン層と分離されていて然るべきです。</p>

<p>lestrratさんの「<a href="http://mt.endeworks.jp/d-6/2008/03/db-1.html">ＭＶＣのモデルはDBじゃなくてもいいんだよ</a>」の記事にあるように、モデルはDBとのやりとりとは超然としているべきであって、インテグレーション層を取り扱うクラスを別途設けるのが手堅い方法論です。lestrratさんの記事では、<code>Model::DBIC</code>を別個設けるという方法論が例示されています。</p>

<h3>1.4. Perlに於けるDomain Modelパターン</h3>

<p>Perlのウェブアプリケーション作成の定番として<code>Catalyst</code>と<code>DBIx::Class</code>を利用する場合で、Domain Modelパターンに基づいた設計をするとどうなるでしょうか。これについては、（<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/07.html">7日目</a>を執筆された）<a href="http://dann.g.hatena.ne.jp/dann/">dannさん</a>による「<a href="http://www.slideshare.net/techmemo/catalyst-367905">CatalystからModelを切り離せ - MVCのMのあるべき姿 -</a>」のスライドが一つの答えになります。</p>

<p>これは「<a href="http://catalyst.g.hatena.ne.jp/dann/20080305/1204732094">Catalystを使っていて困ること</a>」から10を超える記事を経て「<a href="http://catalyst.g.hatena.ne.jp/dann/20080307/1204890012">CatalystでのMVCの私的まとめ</a>」へ至って導き出された考え方で、順を追うととてもよく理解が出来ます。</p>

<p>スライド24枚目にあるように、ドメイン層であるモデルクラスが<code>Schema::*</code>などを参照するということで、実際にはPOPO Modelのさらに奥にインテグレーション層であるスキーマクラスがあることになります。</p>

<p>なお、モデルの後ろにスキーマが必ず控えているとも限らないことも留意しておく必要があります。例えばValue Objectパターンに基づくモデルクラスを追加で作ることもあるでしょう。</p>

<h3>1.5. Mooseベースのクラスでモデルクラスを書く</h3>

<p>さてそのモデルクラスをどう書きましょうか。上述のdannさんの例では、POPO（Plain old Perl object）と書かれていますが、これは<code>Catalyst::Model</code>ベースではないという意味だと理解しています。つまり、そこでは古式ゆかしい<a href="http://search.cpan.org/perldoc?Class::Accessor::Fast"><code>Class::Accessor::Fast</code></a>を使うこともあるでしょう。そして、折角ならばみんな大好きな<code>Moose</code>を是非使いたいところです。</p>

<p>そして「<code>Schema::*</code>などを参照する」という作業をやってくれるのが、<code>DBICx::Modeler</code>というモジュールです。これは<code>DBIx::Class</code>スキーマと<code>Moose</code>クラスを仲立ちする薄いレイヤーで、橋渡しとしては申し分のない出来となっています。これにより、モデルクラスからスキーマクラスを透過的に取り扱えるようになります。</p>

<h3>1.6. <code>DBICx::Modeler</code>以外の解</h3>

<p><code>Moose</code>と<code>DBIx::Class</code>とを繋ぐ方法としては、他にスキーマの記述も<code>Moose</code>記法で書いてしまおうという野心的な<a href="http://github.com/stevan/moosex-dbic/"><code>MooseX::DBIC</code></a>というモジュールがあります。</p>

<p>また、<code>DBIx::Class</code>というORMにこだわらなければ、<a href="http://search.cpan.org/perldoc?KiokuDB"><code>KiokuDB</code></a>や<a href="http://search.cpan.org/perldoc?Fey::ORM"><code>Fey::ORM</code></a>などを活用する手法もあるでしょう。</p>

<p>これらはいずれも魅力的な方法ですが、モデリング結果をコードに落とし込むことを自動化したいという要求に焦点を当てたいので、本稿ではこれ以上は触れないこととします。</p>

<h2>2. クラスの山をどう作るか</h2>

<p>上述の通りエンタープライズアプリケーションではデータが膨大になり、込み入った業務を目指すと、途端にテーブルが数十個、場合によっては百個超に膨れあがってしまいます。</p>

<p>モデルクラスという層を新たに設けることにしたとして、スキーマクラスだけでも2桁・3桁になり得るのに、さらにそれに前後する量のモデルクラスを手作りするのは、気の遠くなる話です。</p>

<h3>2.1. DBIx::Class::Schema::Loaderによるスキーマクラス群の一発生成</h3>

<p>ただし、スキーマクラス群の生成には、定石と言える方法があります。<code>DBIx::Class::Schema::Loader</code>を使って、今そこにあるDBを解析して、一発でコードを生成するというものです。</p>

<ul>
<li><a href="http://trac.mizzy.org/public/blog">mizzyさん</a>の<a href="http://blog.mizzy.org/articles/2007/05/06/dbix-class-schema-loader">Re: DBICとDBIx::Class::Schema::Loader 僕のいろいろな勘違い</a></li>
<li>（<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/04.html">4日目</a>を執筆された）<a href="http://unknownplace.org/memo/">typesterさん</a>による<a href="http://unknownplace.org/memo/2007/05/07/#e001">Schema::Loader 使い方</a></li>
<li>（<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/06.html">6日目</a>を執筆された）<a href="http://d.hatena.ne.jp/ZIGOROu">ZIGOROuさん</a>による、<a href="http://d.hatena.ne.jp/ZIGOROu/20080318/1205828357">DBIx::Class::Schema::Loaderの手動スキーマ生成、初心者向けチュートリアル</a></li>
</ul>

<p>そうであれば、<code>DBICx::Modeler</code>を使ったモデルクラス群の生成も何とか自動化し、単純作業は撲滅したいところです。</p>

<p>そもそも、開発者にとって一番信用ならない人間は誰でしょうか。私は、それは自分だと思います。こと他人であれば事細かく確認したであろう内容であっても、自分に対しては「奴（=自分）はよしなにやってくれるだろう」と見つめてしまうからです。</p>

<p>私は上述のスキーマクラスを作る方法でさえ<del>面倒</del>……もとい、自分の犯しがちな間違いを恐れています。ライブラリ探索パスを<code>use lib</code>で切り替えて2回スクリプトを流すという工程では、DBを直してはコードを生成するという作業を何度か繰り返しているうちに、<code>use lib</code>のコメントアウトをトグルし忘れて流してしまう自信があります。</p>

<h3>2.2. DBICx::Modeler::Generatorによる（スキーマクラス群と）モデルクラス群の一発生成</h3>

<p>ということで、そんな自堕落な自分のために、<code>DBICx::Modeler::Generator</code>というヘルパーモジュールを作ってみました。いやはや、ようやく本題に入れました。</p>

<p>これは、<code>DBIx::Class::Schema::Loader</code>の一連の作業をラップした上で、さらに<code>DBICx::Modeler</code>のモデルクラス群をも一発生成するという代物です。</p>

<h2>3. 作業の実際</h2>

<p>それでは、<code>DBICx::Modeler::Generator</code>を使った作業を、順を追って見ていきましょう。</p>

<p>ここでは、<code>DBIx::Class</code>のPODや<code>DBICx::Modeler</code>のテストでも挙げられている、</p>

<ul>
<li>artist</li>
<li>cd</li>
<li>track</li>
</ul>

<p>というテーブルと、それに関連するスキーマクラス群およびモデルクラス群として、以下のようにクラス群を生成することを目的とします。</p>

<pre><code>path/
    to/
        approot/
            lib/
                MyApp/
                    Model/
                        Artist.pm
                        Artist/
                            Rock.pm
                        Cd.pm
                        Track.pm
                    Schema.pm
                    Schema/
                        Artist.pm
                        Cd.pm
                        Track.pm
</code></pre>

<p>なお、DBICx-Modeler-Generatorディストリビューションには、本稿で触れた内容を全て<code>examples/</code>ディレクトリ以下に収めています。</p>

<p>また、本稿は<a href="http://search.cpan.org/perldoc?DBICx::Modeler::Generator">POD</a>（<a href="http://search.cpan.org/perldoc?lib/DBICx/Modeler/Generator.ja.pod">日本語版</a>）を再構成したものです。PODにはこれ以外のちょっとしたtipsも載せていますので、適宜そちらも参照してください。</p>

<h3>3.1. MySQL Workbenchにより、テーブルを設計</h3>

<p>ここでの例として、テーブル設計ツールとして<a href="http://www.mysql.com/products/workbench/">MySQL Workbench</a>を利用します。</p>

<p>GUIツールであるMySQL Workbench自体は直感的な操作が可能ですので、特に迷うことはないと思います。唯一の落とし穴は、関係（relationship）をGUIで定義する際に、関数従属<em>される方</em>から<em>する方</em>へ順にテーブルをクリックしなければならないことです。つまりArtist has many CDという関係を定義する場合、1:Nのボタンを押下してカーソルを1:N選択モードにした後で、<code>cd</code>テーブル→<code>artist</code>テーブルの順に押下する必要があります。</p>

<p>この作業の成果物は、ディストリビューションにも同梱している<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/doc/DBDSC_schemata.mwb">examples/doc/DBDSC_schemata.mwb</a>を参考にしてください。</p>

<h3>3.2. ER図の生成</h3>

<p>MySQL WorkbenchではER図を出力することも出来ます。スキーマクラス群やモデルクラス群を生成するためには必須ではありませんが、文書化という観点では必須と言えるでしょう。</p>

<p>[File] - [Export] - [Export as PNG...]メニューを辿ってPNGで出力した例も、<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/doc/DBDERII_Including_Information.png">examples/doc/DBDERII_Including_Information.png</a>として同梱しています。SVGやPDFでも出力出来るので、用途に合わせて出力しましょう。</p>

<h3>3.3. DBへの反映、またはDDLスクリプトの生成</h3>

<p>[File] - [Export] - [Forward Engineer SQL CREATE Script...]メニューを辿り、DDL（要は<code>CREATE</code>文）のスクリプトを生成します。DDLスクリプトの例は<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/src/myapp.sql">examples/src/myapp.sql</a>です。</p>

<p>なお、一発で完全なスキーマを作れることはまずないでしょうから、既存のDBにあるテーブルを<code>DROP</code>するよう、生成時オプションで記述しておくのが無難です。</p>

<p>後述の作業で、このスクリプトの内容をDBに反映させます。勿論、DDLスクリプトからDBへ反映する際には、RDBMSのCLIを使う方法もあります。</p>

<p>MySQLならば</p>

<pre><code>mysql &lt; foobar.sql
</code></pre>

<p>SQLiteならば</p>

<pre><code>sqlite3 &lt; foobar.sql
</code></pre>

<p>などです。実際のところ、<code>DBICx::Modeler::Generator</code>は内部で上記をやっているに過ぎません。</p>

<p>MySQL Workbenchではさらに、[Database] - [Forward Engineer...]メニューを辿り、設計したスキーマを直接DBに反映することも可能です。</p>

<h3>3.4. 自動生成されない内容をスキーマクラスに記述</h3>

<p>先にご紹介したみなさんの<code>DBIx::Class::Schema::Loader</code>の利用例にあるように、自動生成されない内容を同名クラスで予め記述しておき、生成先とは別のパスに保存しておきます。この例では、<code>path/to/approot/lib/Schema/</code>以下ではなく、<code>path/to/approot/src/lib/Schema/</code>以下に保存することにしましょう。</p>

<pre><code>path/
    to/
        approot/
            lib/
            src/
                lib/
                    MyApp/
                        Schema.pm
                        Schema/
                            Artist.pm
                            Cd.pm
                            Track.pm
</code></pre>

<p>或るクラスについて追加するコードが何もない場合は、そのクラスを<code>path/to/approot/src/lib/MyApp/Schema/</code>以下に用意する必要はありません。</p>

<p>追加するコードとしてよくある内容としては、</p>

<ul>
<li>インフレーション（inflations: データベースから取り出す際にオブジェクト化する処理など）</li>
<li>デフレーション（deflations: データベースへ格納する際にオブジェクトを文字列化する処理など）</li>
<li>追加の関係（relationships）</li>
</ul>

<p>などが挙げられます。</p>

<p>注意しなければならない点として、ここで記述したクラスはそれ単独でPerlクラスになっていなければならいない、ということが挙げられます。例えばパッケージは真を返さなければなりません。また、<code>DBIx::Class::Schema::Loader</code>に拾ってもらうため、<code>package</code>もきちんと書かなければなりません。具体的には、同梱した<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/src/lib/MyApp/Schema/Artist.pm">examples/src/lib/MyApp/Schema/Artist.pm</a>のように書く必要があります。</p>

<h3>3.5. 自動生成されない内容をモデルクラスに記述</h3>

<p>スキーマと同様、モデルについても、自動生成されない内容を記述しておき、生成先とは別のパスに保存しておきます。この例では、<code>path/to/approot/lib/Model/</code>以下ではなく、<code>path/to/approot/src/lib/Model/</code>以下に保存することにしましょう。</p>

<pre><code>path/
    to/
        approot/
            lib/
            src/
                lib/
                    MyApp/
                        Model/
                            Artist.pm
                            Artist/
                                Rock.pm
                            Cd.pm
                            Track.pm
</code></pre>

<p>ここでもスキーマと同様に、或るクラスについて追加するコードが何もない場合は、そのクラスを<code>path/to/approot/src/lib/MyApp/Model/</code>以下に用意する必要はありません。モデルをスキーマの数だけ用意する必要は必ずしもありませんし、或いは逆に、<code>MyApp::Model::Artist::Rock</code>のようにスキーマにないモデルを用意することもあり得ます。</p>

<p>ここではスキーマと違って、<code>Text::MicroTemplate::Extended</code>のテンプレートとしてコードを記述します。具体的には、<code>Base</code>テンプレートを継承し、<code>code</code>ブロックに追加したいコードをそのまま記述します。具体的には、同梱した<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/src/lib/MyApp/Model/Cd.pm">examples/src/lib/MyApp/Model/Cd.pm</a>のように書きます。</p>

<p>「自動生成されない内容」として追加するコードとしては、「スキーマクラスを透過的に取り扱う」以外にモデルクラスでやりたいこと全てです。そもそも「自動生成される内容」とは、<code>use DBICx::Modeler::Model</code>とPODの雛形程度に過ぎません。モデルには沢山のビジネスロジックが記述されるでしょう。</p>

<p>後はlestrratさんが『モダンPerl入門』で提唱するように、<code>MyApp::API</code>などのサービスクラスを用意し、モデルを跨いだロジックや、モデルを用意しない場合のスキーマクラス用のロジックを記述していくことになります。</p>

<p>あくまで例なので、ディストリビューションに同梱したサンプルにはビジネスロジックを何も記述していません。<code>MyApp::Model::Cd</code>では、<code>price</code>アトリビュートを追加したり、<code>play()</code>メソッドを追加している程度です。前者については、この例では<a href="http://search.cpan.org/perldoc?DBIx::Class::VirtualColumns"><code>DBIx::Class::VirtualColumns</code></a>を利用する方が良いでしょうけれども、<code>Moose</code>アトリビュートであれば</p>

<ul>
<li>ロールでのアトリビュートの定義</li>
<li><code>lazy_build</code>による相互依存の合理的表現</li>
<li>各種メソッドモディファイヤーでの柔軟な処理</li>
</ul>

<p>等々の魔法が使えるので、用途に応じて使い分けるのも一つの手かも知れません。</p>

<h3>3.6. コマンド一発でDB反映・スキーマ生成・モデル生成</h3>

<p>さあ、これで必要な材料は揃いました。後述するようにDIを利用したり、<a href="http://search.cpan.org/perldoc?DBICx::Modeler::Generator::CLI"><code>DBICx::Modeler::Generator::CLI</code></a>で調子に乗って<a href="http://search.cpan.org/perldoc?MooseX::Getopt"><code>MooseX::Getopt</code></a>を利用しているので、面倒な設定は全て外に出しておきましょう。</p>

<p>実行用のスクリプトでは<a href="http://cpansearch.perl.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/src/sbin/maintain_models.pl">examples/src/sbin/maintain_models.pl</a>のように、</p>

<pre><code>use DBICx::Modeler::Generator::CLI;
my $generator = DBICx::Modeler::Generator::CLI-&gt;new_with_options-&gt;generator;
$generator-&gt;deploy_database;    # DDLスクリプトをDBに反映
$generator-&gt;update_schemata;    # DBIx::Class::Schema::Loaderでスキーマクラス群を生成
$generator-&gt;update_models;      # Text::MicroTemplate::Extendedでモデルクラス群を生成
</code></pre>

<p>と書きます（上記の3.3.で、DDLスクリプトを介さずにスキーマを直接DBに反映済みであれば、<code>deploy_database()</code>メソッドを呼ばずにコメントアウトしてください）。</p>

<p>必要なオプションを付けて、上記のスクリプトを呼びます。</p>

<pre><code>maintain_models.pl -a MyApp -r path/to/root -d MySQL
</code></pre>

<ul>
<li><code>-a</code>, <code>--application</code>では、アプリケーション名<code>MyApp</code>や<code>My::App</code>などを指定します。</li>
<li><code>-r</code>, <code>--root</code>では、クラス群を生成する先である、アプリケーションルートパス<code>path/to/approot</code>などを指定します。</li>
<li><code>-d</code>, <code>--driver</code>では、お使いのRDBMSに適合したドライバーを指定します。MySQLならば<code>MySQL</code>、SQLiteならば<code>SQLite</code>です。</li>
</ul>

<p>DB接続用のユーザー・パスワードや、DBサーバーのホスト・ポートやら、諸々の指定についても、必要に応じて指定してください。</p>

<p><a href="http://search.cpan.org/perldoc?MooseX::SimpleConfig"><code>MooseX::SimpleConfig</code></a>も利用しているので、設定ファイルに上記を記述しておいて、<code>--configfile</code>で設定ファイルのパスを渡すだけでも良いです。</p>

<p>設定ファイルの例は<a href="http://search.cpan.org/src/MORIYA/DBICx-Modeler-Generator-0.0003/examples/src/myapp.yml">examples/src/myapp.yml</a>にあります。</p>

<h3>3.7. これで完成です！</h3>

<p>以上で、モデルクラス絡みのコード記述は終わりました。ウェブアプリケーションを作る場合には、<a href="http://search.cpan.org/perldoc?Catalyst">Catalyst</a>や<a href="http://github.com/typester/ark-perl">Ark</a>などのお好みのWAFを活用して、素敵なアプリケーションを作ってください。</p>

<p>モデルは一度作って終わりではなく、アプリケーションの一生を通して繰り返し成長し、かつ合理化していくものです。従ってその実装たるコードも変わっていくものですが、そのための作業が楽だとリファクタリングの心理的敷居が下がります。この些細なヘルパーモジュールが、モデル作成・保守作業の一助となれば、と思います。</p>

<p>なお、MySQL Workbenchと<code>DBIx::Class::Schema::Loader</code>の合わせ技は、冒頭に触れたYAPC::Asia 2009の特別研修の補講で教えていただいた内容のほぼそのままです。JPAのみなさん、どうもありがとうございます！</p>

<h2>4. 実用上の留意点</h2>

<p>趣味で開発中のプロジェクトの一つ（某MMORPG向けのERPやグループウェア）は80テーブル超に肥大化して、モデルもそれに関連してメタボ気味になりましたが、<a href="http://japan.cnet.com/interview/story/0,2000055954,20060067,00.htm">ドッグフードを食べて</a>みたところ、モデルの海を自在に泳いで開発出来ている実感があります。</p>

<p>ただし、実用する際には幾つかの点に留意する必要があります。</p>

<h3>4.1. 多対多の関係は未対応</h3>

<p>一番の留意点は、多対多(N:M)の関係のハンドルはこれからサポートされる予定である、ということです。これは<code>DBICx::Modeler</code>側の<a href="http://search.cpan.org/~rkrimen/DBICx-Modeler-0.005/lib/DBICx/Modeler.pm#Many-to-many_is_not_handled">0.005時点での制約</a>です。多対多の関係を透過的に扱いたい場合には、自分でロールを作って皮を被せるか、<code>DBICx::Modeler</code>自体を拡張するか、いずれかの対処が必要となります。</p>

<h3>4.2. スキーマ全てにモデルが必要ではない</h3>

<p>上述のスライドでdannさんが提唱されている通り、必ずしも全てのスキーマをモデルでラップする必要はありません。エンタープライズアプリケーションというのは、処理の少なからぬ割合がDBの単なるCRUD処理であることも珍しくありません。であれば、無駄に一つ層を設けるよりも、素直にそのままスキーマクラスを取り扱った方が、パフォーマンス面で優れるのみならず、開発生産性や保守性も優れることがあります。層を分かつ原理主義に陥ることなく、現実との距離感を適切に保って、妥協して行きましょう。</p>

<p>ただ、あまり早期から最適化を進めるのも考え物です。端から箸にも棒にも掛からない状態は論外ですが、それはまずい実装に起因することが多いはずで、層を分けた事による極端な性能劣化はあまり起き得ません。まずは綺麗な設計を推し進めてみて、それで性能面の顕著な問題が生じた場合にのみ、以降の保守開発に支障を来さない範囲で、最適化を図れば良いでしょう。</p>

<h3>4.3. 独自ツールへの不安？</h3>

<p>こうした開発効率化の錦の御旗は、あっけないほどに容易に切り裂かれることがあります。</p>

<p>真っ先に思い付く「“どこの馬の骨とも知れぬ”ツールを業務に使うことはまかりならん」、という例のアレについては、MySQL公式のツールなので、安心感があります。Oracle, DB2, Symfoware, HiRDBなどではなくMySQLを使える現場であれば、説得は比較的容易でしょう。</p>

<p>次に、「標準外のツールを使うのはまかりならん」という問題。社用PCには間違いなく入っているExcelと違い、遍在していないツールの使用は敷居が高いことがあります。私もチーム内でVisioを使って、良い顔をされなかった経験があります。</p>

<p>私はExcel方眼ドキュメント否定派ですが、基本要件など、顧客(ユー子ならば親会社)と協同する作業工程であればむしろExcelを活用すればいいと思います。一方でER図に顧客が手を入れる場面というのもあまり考えにくいので、ここは開発側のみで閉じた作業と位置付けて、独自ツールを使う論陣を張りたいところです。</p>

<p>いずれにせよ成果物としてER図の画像やテーブル定義書も生成出来るので、それを納品すれば良いことになります。</p>

<p>「うちはどうしてもExcelで納品しなければいけないんだ」という場合であっても、ER図であれば画像をそのままExcel文書に貼り付ければ良いですし、テーブル定義書もXML文書を（<a href="http://search.cpan.org/perldoc?XML::Parser">XML::Parser</a>などで）パースして、その結果を基に（<a href="http://search.cpan.org/perldoc?Spreadsheet::Write">Spreadsheet::Write</a>などで）Excel文書を生成すれば事足ります。</p>

<ins>追記: Standard版ではHTMLで出力出来ます。Community版は`*.mwb`をunzipして`document.mwb.xml`を扱う手があります。</ins>

<p>同じ作業をツールをまたいで何度も行う雇用創出的な仕事に従事することほど辛いことはありませんよね。</p>

<h3>4.4. MySQL Workbenchにこだわる必要はない</h3>

<p>ここまでMySQL Workbenchを猛プッシュして来ましたが、それ以外のツール（たくさんあります！）でも同様のことは出来ます。</p>

<p>DDLスクリプトを出力出来ること、ないしはDBに直接反映出来ることという条件さえ満たせば、<code>DBIx::Class::Schema::Loader</code>によるスキーマクラス群の生成以降の流れが一緒になるからです。</p>

<p>DB定義書やER図を素のExcelで（オートシェイプと表組みを駆使して）定義しているのでない限り、現在お使いのツールと組み合わせて、各現場なりの創意工夫で作業手順をカスタマイズすることも容易だと思います。</p>

<p>ディストリビューションに同梱した例ではSQLite用のDDLスクリプトを手書きしてしまいましたが、これは<a href="http://search.cpan.org/perldoc?DBICx::Deploy"><code>DBICx::Deploy</code></a>を利用して、スキーマクラス群を元にSQLiteのDBを生成する方が自然だと思います。</p>

<ins>追記: [SQLite出力用のプラグイン](http://www.henlich.de/software/sqlite-export-plugin-for-mysql-workbench/)があります。</ins>

<h3>4.5. ドッグフードの範囲は限定的</h3>

<p>私は<code>DBICx::Modeler::Generator</code>をいくつかのプロジェクトで実用していますが、それはあくまで日曜プログラミングでの範囲に過ぎません。エンタープライズアプリケーションと連呼しておきながら恐縮ですが、勤め先ではPerlを使った案件が殆ど全くないので、本当の業務アプリケーションへの適用事例はまだありません。</p>

<p>何事もそうですが、<code>DBICx::Modeler</code>共々、作ろうとしている（もしくは保守しようとしている）アプリケーションの特性を良く考えて、</p>

<ul>
<li>モジュールの利用が開発生産性および保守性に寄与するか</li>
<li>それらの利点が欠点と比べてなお有用か</li>
</ul>

<p>を、冷静に判断することが求められると言えるでしょう。</p>

<h3>4.6. 怪しげな名前空間</h3>

<p><code>DBICx::Modeler::Generator</code>は<code>DBICx::Deploy</code>と同様に間違い易い名前空間にいますが、それには</p>

<ul>
<li><a href="http://search.cpan.org/dist/DBI"><code>DBI</code></a>の拡張（e*X*tension）として<code>DBIx::Class</code>)があり、</li>
<li>それはしばしばDBIC（「でぃびっく」と読むようです）と略され、</li>
<li>さらにその拡張として<a href="http://search.cpan.org/search?query=DBICx%3A%3A&amp;mode=all">DBICx::*</a>という名前空間がある</li>
</ul>

<p>という理由があります。これは<code>DBICx::Modeler</code>のヘルパーモジュールである以上、宿命として諦めました。</p>

<h2>5. 中身の話</h2>

<p>さて、中身の話は無味乾燥になりがちですが、少し補足しておきます。</p>

<h3>5.1. MooseとOrochiで拡張容易性を確保</h3>

<p><a href="http://search.cpan.org/dist/Moose"><code>Moose</code></a>の他、lestrratさんが<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/15.html">15日目に紹介</a>された<a href="http://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5">DI（dependency injection, 依存性注入）</a>フレームワークである<a href="http://search.cpan.org/dist/Orochi"><code>Orochi</code></a>を利用しています。</p>

<p>DIをまだまだ十全に使いこなしているとは言えないのですが、DIのおかげで疎結合な構造に出来たので、レゴブロックのように部品を組み替えることが容易です。このモジュールではごく一般的な要件にのみ対応した枠組みしかご用意していませんが、特殊用途への対応部分を肉付けすることも比較的容易なので、一つの実装参考例としてご覧頂ければ、と思います。</p>

<h3>5.2. <a href="http://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%B5%E3%82%AE#.E6.85.A3.E7.94.A8.E5.8F.A5.E3.80.81.E3.81.93.E3.81.A8.E3.82.8F.E3.81.96.E3.81.AA.E3.81.A9">獅子搏兎</a>気味なオーバーキルモジュール</h3>

<p>上述の通り、既に一発スクリプトが色々あるくらいですので、わざわざモジュール化するまでもないかも知れません。少なくとも、<code>Moose</code>やDIパターンを使わなくても十分書ける単純な処理であることは確かです。</p>

<p>正直なところ、鳩を撃つのに豆鉄砲でなく大砲を持ち出すような感もあります。ですが、このヘルパーモジュールが必要となりうる場面は、テーブルが満載のエンタープライズアプリケーションを作ろうとする場面だと認識しています。</p>

<p>また、そもそもこのモジュールはコードジェネレーターであるため、本質的に開発者向けのものです。生成されるコードと違って、このモジュール自体は本番環境（production environment）には配備（deploy）しません。</p>

<p>従って、依存モジュールについてはある種の開き直りに基づいて、楽に書けるものを利用させていただいています。</p>

<h3>5.3. でもバッドノウハウです</h3>

<p><a href="http://blog.myfinder.jp/">myfinderさん</a>が<a href="http://perl-users.jp/articles/advent-calendar/2009/hacker/18.html">18日目に紹介</a>された<a href="http://search.cpan.org/perldoc?DBIx::Encoding"><code>DBIx::Encoding</code></a>について、ご本人はBK（<a href="http://0xcc.net/misc/bad-knowhow.html">バッドノウハウ</a>）だと謙遜されています。一方、<code>DBICx::Modeler::Generator</code>は正真正銘のBKです。</p>

<p>DBの内容から生成したスキーマクラス(Aとします)に、それ以外の内容のみを記述した同名のクラス(A'とします）の内容を付加して、最終的なスキーマクラス(A''とします）を生成するというのが<code>DBIx::Class::Schema::Loader</code>の一つの流れです（上述のZIGOROuさんの記事の様に、もう一つの流れもあります）。</p>

<p>この流れを実現するために、<a href="http://search.cpan.org/perldoc?Class::Unload"><code>Class::Unload</code></a>でクラスを一旦アンロードした後に<code>@INC</code>を追加し、再度（最初とは別の場所にある）同名クラスをロードしています。</p>

<p><code>use lib</code>指定をし直して2度スクリプトを実行するということと本質的には同じで、ライブラリ探索パスの動的な切り替えのために<a href="http://gist.github.com/217006">別プロセスの<code>perl</code>を使い分けるという方法</a>よりはましとはいえ、これはかなり気持ちが悪い処理です。</p>

<h3>5.4.  モデルクラスは単なるコードジェネレーターとして生成</h3>

<p>モデルクラスのコード生成にあたっては、人気のテンプレートエンジン<a href="http://search.cpan.org/perldoc?Text::MicroTemplate::Extended"><code>Text::MicroTemplate::Extended</code></a>を利用しています。</p>

<p>であればスキーマクラスのコード生成も<code>DBIx::Class::Schema::Loader</code>に頼らず、DB解析結果の情報だけ拝借して生成する手もあるかと思いました。</p>

<p>上述のZIGOROuさんの記事でも、</p>

<blockquote>
  <p>あるいはSchema::Loader自体に手を加えるかですかね。</p>
</blockquote>

<p>という記述があります。しかし、現状で既に満足出来るワークフローになっているので、深追いはしないことにしました。</p>

<h2>6. まとめ ～ Perlはエンタープライズアプリケーションに向いています！</h2>

<p>以上、casual track的な内容で恐縮ですが、<code>DBIx::Class::Schema::Loader</code>のようにスキーマクラス群とモデルクラス群を一発生成する、<code>DBICx::Modeler::Generator</code>のご紹介でした。</p>

<p>Perlの内在思想として最も有名なのは<a href="http://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it">"There's More Than One Way To Do It"（TMTOWTDI, 方法は一つじゃない）</a>ですけれども、<a href="http://japan.cnet.com/interview/story/0,2000055954,20100857,00.htm">"Easy things should be easy, hard things should be possible"（簡単なことは簡単に、難しいことも可能に）</a>というものもあります。</p>

<p>Perlは<a href="http://ja.wikipedia.org/wiki/%E8%BB%BD%E9%87%8F%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E">LL</a>の代表格なので、軽薄短小で小粋なシステム向きだと思われることがあります。しかしPerlは、Javaの専従分野と思われがちな重厚長大なエンタープライズアプリケーションでさえも開発出来るのです。「も出来る」というと、何か痩せ我慢をして無理にPerlを使っている後ろ向きな感触を受けかねませんので、さらに踏み込みましょう。Perlはエンタープライズアプリケーションでこそ開発生産性や保守性が華開くのだと。</p>

<p>例えば本稿でご紹介した要件・モデリング・実装までを乖離なく貫くことが出来る仕事の仕組みは、“エンタープライズアプリケーションをアジャイルに開発する”という新鮮な体験の一端をもたらしてくれます。DBひとつ作るのに、延々と続く会議や、山ほどの依頼書や、実装としばしば噛み合わない仕様書……等々が必要な仕事に閉口している方は、是非一度体験してみてください。</p>

<p>言語とは言語単独で評価すべきものではなく、便利な外部モジュールであるとか、確立された自動化テスト環境基盤の総体として評価すべきものではないでしょうか。『モダンPerl入門』にも記載されていますが、Perlではそれらが豊富に揃っているのです。</p>

<p>日本に於いて、そして世界に於いて、エンタープライズ分野でのPerlの存在感が増すことを願ってやみません。あなたもPerlで日本の情報システム産業を<a href="http://ja.wikipedia.org/wiki/%E8%A1%8C%E6%94%BF%E5%88%B7%E6%96%B0%E4%BC%9A%E8%AD%B0#.E4.BA.8B.E6.A5.AD.E4.BB.95.E5.88.86.E3.81.91">事業仕分け</a>してみませんか？　……ただしあまりに切り込みすぎて不興を買うこと（実話）を避けるためにも、上司や同僚への進言はくれぐれも慎重に！</p>

<h2>to be continued...</h2>

<p>さあ、JPerl Advent Calendar 2009 hacker trackも、遂にゴールが見えてきました。あと3日間です。明日23日目を執筆してくださる方を絶賛募集中です！</p>
]]></description>
      <dc:creator>gardejo</dc:creator>
      <pubDate>Thu, 24 Dec 2009 13:59:01 GMT</pubDate>
      <category></category>
    </item>
  </channel>
</rss>
