2009年06月10日

あざーす。循環参照しすぎるとバターになる。。なんでそんなに人の目を気にするのだろうと、マジレス。

早速ですが Data::Model っていう O/Rマッパー 的な物を CPAN にあげました。
Data::Model
http://github.com/yappo/p5-Data-Model/tree/master
元来は MVC モデルで言う所の Model を一括でまかなえるつもりで実装していますが、ロジック処理は普通の Perl のクラスで書いちゃった方が潰しが聞くため、主にストレージを Perl のオブジェクトにマッピングする ORM 的な使い方が主流となっています。
そして、 Data::Model の多くの実装や設計などは Data::ObjectDriver を参考にして開発しました。
他にも後述してる ORM の実装を参考にしています。
あ、あとは tokuhirom 先生による日本語チュートリアルがあります。

現在の所の情報源は CPAN に上がってるドキュメントの他に http://d.hatena.ne.jp/tokuhirom/searchdiary?word=Data%3a%3aModelとかhttp://d.hatena.ne.jp/yappo/searchdiary?word=Data%3a%3aModelぐらいですね。
あと、Hatetterのコードも結構参考になると思います。

Perl の ORM といえば、 Class::DBI をはじめに DBIx::Class, Data::ObjectDriver, Fey::ORM, Rose::DB, Jifty::DBI, DBIx::MoCo など( DBIx::Skinny も github にあるよ )があり、再実装する必要が無いようにも思います。
しかしながら既存の物は、 Inflate/Deflate まわりが貧弱で ForuceUTF8 的な事をやるのもちょっと大変だったり不安定だったり、 cache させるのが面倒だったり、 Moose 使ってたり、 社内の最新リポジトリと CPAN のバージョンが乖離していたりと「こいつと一緒にやりたい!」的な物が見つかりませんでした。確かに DBIC なんかは良いものだとは思いますが、複雑な事が出来てしまうがためにgdgdしてしまう事もあったりしたのです。
Data::ObjectDriver がだいぶ希望に近かったのですが、ドキュメントや利用事例が少なくてユーザになるのに二の足を踏みました。

という現状の ORM に持っていた不満点もあったのですが、それ以外の要因としては RBDMS を kvs 的なインターフェィスで使いたいな。そもそも Web アプリケーションだったら RDBMS の R の要素って使わなくても、やってけてるんじゃないか? だったら kvs 的に使えるように下ほうがさっくり DB の処理書けるんじゃないかな? という観点で作り始めました。
そういったスタンスなので Data::Model::Driver::Memcached という memcached protocol を喋るストレージサーバをバックエンドストレージとして使えるようになってます。

あまり長文はアレなので軽く特徴を

column sugar

user テーブルの id というカラムと同じ役割のカラムを別のテーブルに持つ。それも沢山のテーブルで user id なんかを持ってたいと言うケースは多々あると思います。
そんな時は column sugar を使うと、カラムの詳細定義は一度だけ書いて、後は column sugar を呼ぶだけですみます。

# 定義する
column_sugar 'user.id'
    => int => {
        required => 1,
        unsigned => 1,
    };

# user テーブルでの定義
column 'user.id' => { # ここではカラム名が id になる
    auto_increment => 1, # auto_increment 属性だけ追加する
};

# bookmark テーブルでの定義
column 'user.id'; # ここではカラム名が user_id になる
user id だと当てはまらないですが、 複数のテーブルで定義するカラムで char がた見たいな仕様が代わり安いカラムだと、文字長の仕様変更が入っても一ヶ所だけ size を書き直せば良いので、楽でミスも減ります。

CREATE TABLE 構文を出力する

他の ORM でもよくあるですが、 Data::Model 使って定義したスキーマを CREATE TABLE の SQL に出力します。
そして column sugar が強力にいかせるのは、スキーマ定義に変更が入ったら as_sqls してしまって、そのまま RDBMS 側の DDL も一緒に変えると言う事です。
DB 側のスキーマ情報を自動的に読み込んで ORM のスキーマとしてやるのもありますが、それだとスキーマを二ヶ所で変えなきゃいけなくて面倒なので ORM 側のスキーマのみ変更すれば良いように考えています。
rails のようはマイグレーションも自動的にやりたい所だがまだ未実装です。DB と ORM 側の差分を自動的に反映したいな。

DBI だけでなく kvs にも保存できる

Tokyo Tyrant や groonga や kai などの memcached protocol を使える kvs 等を DBI の代わりに利用する事が出来ます。決定的な制限として primary key でしかデータが引けません。がこれは別の driver と組み合わせる事で対応可能にする方向性です。

透過的なキャッシュ

Data::ObjectDriver インスパイアですが、 cache driver の failback driver を設定した driver object を使う時は、 cache にデータが無ければ、自動的に fallback driver にリクエストする用になります。

cache は Data::Model の Row object その物を渡す感じですが、 DBIC とは違って必要最低限の情報しか入ってないので、あまり問題にならない予定です。

また failback driver には Driver::Memcached を使う事も出来ますが、いわゆる memcached protocol しゃべる kvs は、それ単体での高速性を売りにしているためやる意味が分からんすね。

index に対する検索を簡素化に

Data::Model の get method では index を特別に扱います。
例えば index post (user_id, post_at) の用な index を張っていたら

my $iterator = $model->get( tweet => { index => { post => [ 1, 1281729102 ] } } );
といった形でクエリを引けます。

Q4M 対応

Data::Model では標準で Q4M が扱えます。
以下の例は SELECT queue_wait('smtp', 'pop', 10); を発行して、 queue が帰ってきたらそれぞれのクロージャを呼び出します。
第一引数には dequeue された queue の row object が渡されますので、改めて query を発行せずに、 queue の処理が行えます。

my $retval = $model->queue_running(
    smtp => sub {
        my $row = shift;
        is($row->id, 'foo');
        is($row->data, 1);
    },
    pop => sub {
        my $row = shift;
    },
    timeout => 10,
);

column_aliase

カラムにエイリアスを張ります。
バイナリデータをデータベースに格納するカラムがあるとして、利用する時には文字列形式とバイナリ形式を使いたい場合に、カラムにエイリアスを張ると両方の形式で利用出来ます。

  columns qw( name nickname );
  alias_column name     => 'name_name';
  alias_column nickname => 'nickname_name'
      => {
          inflate => sub {
              my $value = shift;
              Name->new( name => $value );
# Name は name っていうメソッドを持ってるよ
          },
          deflate => sub {
              my $obj = shift;
              $obj->name;
          },
      };
こんな風にしておくと
$row->nickname;      # 普通に文字列が返る
$row->nickname_name; # Name オブジェクトが返る

$row->nickname('test');    # 文字列をセット
$row->nickname_name->name; # test が返る

$row->nickname_name(Name->new( name => 'おうっふ' ));    # オブジェクトをセット
$row->name; # おうっふ が返る
とエイリアス張る前と張った後のメソッドでも保持するデータを相互的に補完し合います。

MySQL の master - slave

当然対応しています。 Driver::DBI::MasterSlave です。
今どきのモダンなウェブアプリは slave の mysql は lvs 噛ましてると思うので、 slave は一個しか指定出来ません。このあたり Data::ObjectDriver 弄った事ある人なら、いかようにも read dbh 分散する仕組み作れると思います。

add_method, mixin

row object に メソッドを生やせる為の add_method って DSL がついてます。
同じ method をいっぱいの table に生やしたい時は mixin 機構があるので、サクサクメソッド生やせます。
DBIx::Class::ResultSet 的な所にメソッド生やしたい場合は、普通に use base 'Data::Model' してるクラスにメソッド生やせばおkです。

transaction

最近実装しました、 DBIx::Class::Storage::TxnScopeGuard インスパイアで以下のように書けます。

  sub foo {
      my $is_die = shift;
 
      my $model = Your::Model->new;
      my $txn = $model->txn_scope; # トランザクション開始
 
      # トランザクション中は $txn からしか DB の操作出来なくなりますよっと
      my $row = $txn->lookup( user => 1 );
      $row->name('transaction name');
      $txn->update( $row ); # update
      return if $is_die; # スコープ抜けて commit されてないのでロールバックされる
      if ($is_die) {
          $txn->rollback; # 明示的にロールバック
          return;
      }

      $txn->commit; # commit する
  }

  foo(1); # rollback されてる
  foo(0); # commit できる
eval {}; if ($@) {} みたいなトランザクションの実行だと、例外があってメソッドをそのままreturnして抜ける事が出来ないので、こっちのが簡潔で気持ちいいと思ってます。
もし die で抜ける事があったとしても DESTROY のタイミングで rollback するので、問題は起こりえない筈ですね。

一つのスキーマクラスに複数のテーブル定義ができる

ORM だと、よく1テーブル/1クラスファイルみたいな感じになっちゃいますが(普通にそうならなく出来るけど)、 Data::Model だと、1クラス=1データベース みたいな感じにする方向性なので、1つのクラスファイルに沢山テーブルの定義をやっちゃいます。

リレーション未実装

なんとリレーション周りを標準でサポートしてません!
add_method 使えば has_a くらいは楽に書けるよ。
よい実装手法があれば作りたい所。

Driver::Pacific

kazuho ware の新作 Pacificに対応予定。
リゾルバとのやり取りを Data::Model 側でやるかどうするかは考えてないけども、結構簡単にラッピング出来るイメージ。

予定つながりだと FriendFeedのアレとかも入れたいですね。

まとめ

まだ特徴はあるですが、面倒なのでまとめる。

作り始めてから半年以上経ってようやくリリースできました。
現在自分がバリバリのユーザですが、使ってみた感じだと徐々に良い感じにもなってるし、テストも充実させている、ドキュメントもそこそこ使い方がわかる程度には書いてあるので是非ともお試しください。

あ、あと重要な事ですが、何でも自分で作りたい病だから作った訳ではないです。

Posted by Yappo at 2009年06月10日 23:09 | TrackBack | Perl
Comments

rs gold | RS money | buy rs money | buy rs gold | cheap rs gold | cheap rs money | Runescape gold | Runescape money | Runescape power leveling | Runescape powerleveling | rs power leveling | rs powerleveling | rs accounts | rs account | runescape accounts | runescape account | rs powerleveling | Runescape powerleveling | rs power leveling | Runescape power leveling | Runescape gold|Runescape Money | Runescape gold|Runescape Money | rs gold|rs Moneyrs gold | RS money | buy rs money | buy rs gold | cheap rs gold | cheap rs money | Runescape gold | Runescape money | Runescape power leveling | Runescape powerleveling | rs power leveling | rs powerleveling | rs accounts | rs account | runescape accounts | runescape account | rs powerleveling | Runescape powerleveling | rs power leveling | Runescape power leveling | Runescape gold|Runescape Money | Runescape gold|Runescape Money | rs gold|rs Moneyrs gold | RS money | buy rs money | buy rs gold | cheap rs gold | cheap rs money | Runescape gold | Runescape money | Runescape power leveling | Runescape powerleveling | rs power leveling | rs powerleveling | rs accounts | rs account | runescape accounts | runescape account | rs powerleveling | Runescape powerleveling | rs power leveling | Runescape power leveling | Runescape gold|Runescape Money | Runescape gold|Runescape Money | rs gold|rs Moneyrs gold | RS money | buy rs money | buy rs gold | cheap rs gold | cheap rs money | Runescape gold | Runescape money | Runescape power leveling | Runescape powerleveling | rs power leveling | rs powerleveling | rs accounts | rs account | runescape accounts | runescape account | rs powerleveling | Runescape powerleveling | rs power leveling | Runescape power leveling | Runescape gold|Runescape Money | Runescape gold|Runescape Money | rs gold|rs Moneyrs gold | RS money | buy rs money | buy rs gold | cheap rs gold | cheap rs money | Runescape gold | Runescape money | Runescape power leveling | Runescape powerleveling | rs power leveling | rs powerleveling | rs accounts | rs account | runescape accounts | runescape account | rs powerleveling | Runescape powerleveling | rs power leveling | Runescape power leveling | Runescape gold|Runescape Money | Runescape gold|Runescape Money | rs gold|rs Money

Posted by: Runescpae power leveling at 2009年06月23日 15:48
Post a comment









Remember personal info?






コメントを投稿する前に↓の場所にnospamと入力してください。