新しいblogに移行しました

新ブログ "All Yout Bugs Are Belong To Ass" に移行しました!

ラベル Data::Model の投稿を表示しています。 すべての投稿を表示
ラベル Data::Model の投稿を表示しています。 すべての投稿を表示

2011-01-05

[Perl]Data::ModelでつくったスキーマクラスをMouseでくるんでdriver食わせたりしてみた

明けましておめでとうございます。Hachioji.pm#1のLTで何か話したいけど、何を話すか決めきれていないytnobodyです。

かなり前に[Perl]Data::ModelをMouseでextendsしたけどあんまり意味なかった。というメモ。という記事を書いたのですが、2年の時を経てもう一度同じ事をしてみました。
今回もご多分に漏れず備忘録的な記事ですが、今回はちゃんと「意味あるんじゃね?」という風に思わせているはずです。。。

スキーマクラス

ユーザに関する情報(=userスキーマ)をtokyotyrantに食わせ、ユーザーIDと登録タイムスタンプをSQLiteに食わせる、というデータ構造になってます。

package MySchema;

use Mouse;
use MouseX::Foreign qw/ Data::Model /;
use Data::Model::Schema;

has dbi => ( is => 'rw' );
has tyrant => ( is => 'rw' );

sub BUILD {
    my $self = shift;

    install_model user => schema {
        schema_options model_name_realname => 'u';
        key 'id';
        columns qw/ id name sex place favorite /;
        schema_options column_name_rename => {
               id => 1,
               name => 2,
               sex => 3,
               place => 4,
               favorite => 5,
        };
    };

    install_model user_list => schema {
        key 'user';
        driver $self->dbi;
        column user => varchar => {
            required => 1,
        };
        column datein => integer => {
            default => sub { time() },
        };
    };

}

no Mouse;
1;
Data::Modelを継承するために、MouseX::Foreignを使っています。これは、Mouse-0.71から非MouseなクラスをMouseで継承する上で、MouseX::Foreignというモジュールを使う必要があるためです。Mouse作者のブログでも明記されてますので、ぜひご一読を。
hasで宣言されているアクセサはそれぞれ、Data::Model::Driver::MemcachedとData::Model::Driver::DBIのオブジェクトを受け取る為の窓口となります。
本来のスキーマ定義はBUILDメソッド内に納めることになりますが、当然アクセサをそのまま使えますので、$self->dbi の様な記述もまかり通ります。

モデルクラス

スキーマクラスとアプリケーションの間を取り持つクラス。
このクラスを通して、スキーマオブジェクトを生成させるのですが、その時にD::M::D::MemcachedやD::M::D::DBIの設定をある程度やってくれるようにしてみました。

package MyModel;

use warnings;
use strict;

use Mouse;
use MySchema;
use Cache::Memcached::Fast;
use Data::Model::Driver::Memcached;
use Data::Model::Driver::DBI;

has dbi_options => ( is => 'ro', isa => 'HashRef' );
has memcached_options => ( is => 'ro', isa => 'HashRef' );
has schema => ( is => 'rw' );

sub BUILD {
    my $self = shift;
    my $tyrant = Data::Model::Driver::Memcached->new(
        memcached => Cache::Memcached::Fast->new( $self->memcached_options ),
        namespace => 'hoge',
        serializer => 'Default',
        strip_keys => 1,
    );
    my $dbi = Data::Model::Driver::DBI->new( %{ $self->dbi_options } );
    my $schema = MySchema->new( tyrant => $tyrant, dbi => $dbi );
    $schema->set_base_driver( $schema->tyrant );
    $self->schema( $schema );
}

around new => sub {
    my $super = shift;
    my $class = shift;
    my $self = $class->$super( @_ );
    return $self->schema;
};

no Mouse;
1;

アプリケーション

ただレコードをsetしてData::Dumperで吐かせるだけの簡単なお仕事です。

use warnings;
use strict;

use MyModel;
use Data::Dumper;

my $model = MyModel->new(
    memcached_options => {
        servers => [qw[ 127.0.0.1:11211 ]],
        namespace => 'hoge',
    },
    dbi_options => {
        dsn => 'dbi:SQLite:dbname=mymodel.db'
    },
);

$model->set(
    user => 'user001' => {
        name => 'ytnobody',
        place => 'sagamihara',
        sex => 'male',
        favorite => 'soba',
    }
);

$model->set(
    user_list => 'user001'
);

print Dumper( $model->lookup( user => 'user001' )->get_columns );
print Dumper( $model->lookup( user_list => 'user001' )->get_columns );

実行結果は以下の様になり、レコードのsetおよびlookupが出来ていることがわかります。
(2回目以降の実行では、レコードのsetで失敗しますので、エラーとなります。)

$VAR1 = {
          'favorite' => 'soba',
          'place' => 'sagamihara',
          'name' => 'ytnobody',
          'id' => 'user001',
          'sex' => 'male'
        };
$VAR1 = {
          'user' => 'user001',
          'datein' => '1294213651'
        };

モデルクラスの記述次第では、D::M::D::Memcachedのようなインスタンスをパラメータに求めるタイプのドライバすらも、インスタンスではなく接続先アドレスの指定のみで動作します。当然、モデルクラスとスキーマクラスを1つにまとめることも可能です。yamlなどの設定ファイルにモデルクラスの設定項目を書き出しておくことが容易になり、他のサーバへのアプリケーション移行時にも役に立ちそうです。

2010-04-28

[Perl]Data::Modelの継承クラス使ってて"extra bytes"言われたら

例えば、

my $model = MyDataModel->new;
my $name = $model->lookup( member => $member_id );

みたいなコードがあったとして、lookupメソッドが走るときに"extra bytes"ってエラーが出ることがあります。
そんな時は、テーブルに格納されているデータを疑ってみてください。もしかすると、decodeされっぱなしのデータが格納されているかもしれません。

2010-04-16

[Perl]Data::ModelをインターフェースとしてTokyoTyrantへデータの出し入れをする際にハマった

YappoLogs: KVSでORマッパーを使うという事を参考に、スキーマクラスを構築して、データの出し入れをしました…が、

Can't use string ("k… ÎKÇ
» Ú") as a HASH ref while "strict refs" in use at /usr/lib/perl5/site_perl/5.8.8/Data/Model/Driver/Memcached.pm line 188.

というエラーを吐いてしまい、データの出し入れどころじゃない状態になってしまいました。どうやらデータのシリアライズ処理に問題が発生しているようです…
で、念のためData::Modelのテストコード[t/060_driver/memcached/serializer.t]を読んでみたわけですが、シリアライズ処理のテストの冒頭で、テスト数が分岐していました。条件は、$Data::MessagePack::VERSIONが0.05以上か否か。0.05未満ならテスト数は20…。 ええ?Data::MessagePackってrequiredじゃないん?
もしや…早速このテストコードを実行。

# perl t/060_driver/memcached/serializer.t
1..20
ok 1
ok 2
ok 3
:
(中略)
:
ok 18
ok 19
ok 20

はわわわ…やはりData::MessagePackが(0.05未満|入っていない)って事ですねorz
んじゃ、とっとと入れましょうかということで

# cpanm Data::MessagePack
Fetching http://search.cpan.org/CPAN/authors/id/T/TO/TOKUHIROM/Data-MessagePack-0.10.tar.gz ... OK
Configuring Data-MessagePack-0.10 ... OK
Building and testing Data-MessagePack-0.10 for Data::MessagePack ... OK
Successfully installed Data-MessagePack-0.10

はい、Data::MessagePack 0.10が入りました!

では気を取り直して、再度テストコードを実行します。

# perl t/060_driver/memcached/serializer.t
1..100
ok 1
ok 2 - pack
ok 3 - unpack
ok 4
ok 5 - NOT INT: pack with Data::MessagePack
:
(中略)
:
ok 99
ok 100 - INT: pack with Data::MessagePack

おほー!来たよ来たわよ来ましたよ!!
この後、TokyoTyrantのデータをvanishしてから、データの出し入れが可能になりました!

2010-06-01 追記
当たり前といえばそれまでですが、TokyoTyrantをデータストアとして扱う場合、Data::Model::Driver::Memcachedのインスタンス生成時には、以下のように必ずSerializerを"Default"に設定する必要があります。

my $tokyo_tyrant = Data::Model::Driver::Memcached->new(
memcached => Cache::Memcached::Fast->new( {
servers => [qw/ 127.0.0.1:1978 /],
} ),
serializer => 'Default',
);