オレオレモジュールの進捗報告なんかしてどうする

SQLを書くプロジェクトのためのDBIx::Sunnyってのを書いている」で紹介した DBIx::Sunny はしばらく放置中だったが、最近思い出していじってる。

github: https://github.com/kazeburo/DBIx-Sunny

まず、DBIx::Sunny は DBIx::Sunny::Schema へ rename。基本的には機能変わってない。

package NoNoPaste::Data;

use strict;
use warnings;
use utf8;
use parent qw/DBIx::Sunny::Schema/;
use Mouse::Util::TypeConstraints;

subtype 'Uint'
    => as 'Int'
    => where { $_ >= 0 };

no Mouse::Util::TypeConstraints;

__PACKAGE__->query(
    'add_entry',
    id => 'Str',
    nick => { isa => 'Str', default => 'anonymouse' },
    body => 'Str',
    q{INSERT INTO entries ( id, nick, body, ctime ) values ( ?, ?, ?, DATETIME('now') )},
);

__PACKAGE__->select_all(
    'entry_list',
    'offset' => { isa => 'Uint', default => 0 },
    q{SELECT id,nick,body,ctime FROM entries ORDER BY ctime DESC LIMIT ?,11}
);

__PACKAGE__->select_row(
    'retrieve_entry',
    'id' => 'Str',
    q{SELECT id,nick,body,ctime FROM entries WHERE id = ?}
);

1;

Schemaを継承したクラスを作り、SQLとbind値を定義する。そして

my $dbh = DBI->connect(...);
my $data = NoNoPaste::Data->new( dbh => $dbh );

# エントリ追加
my $row = $data->add_entry(
    id => $id,
    nick => $nick,
    body => $body,
);

# 最近のエントリ
my $rows = $data->entry_list( offset => $offset );

# エントリ取得
my $entry = $data->retrieve_entry( id => $id );

このようにSQLを隠蔽して使える。ちなみにインスタンスを作る際にreadonlyオプションを渡すと、queryメソッドが使えなくなる。slaveサーバなどの場合に使えるのかも。Schemaクラスは継承するだけの普通のモジュールなので好きにメソッドを足しても問題ない

package NoNoPaste::Data;
use parent qw/DBIx::Sunny::Schema/;
...
sub recent_entries {
    state $rule = Data::Validator->new(
        'limit'  => { isa => 'Natural', default => '10' },
        'offset' => { isa => 'Uint', default => 0 },
    )->with('Method');
    my($self, $args) = $rule->validate(@_);

    $self->select_row(
        q{SELECT id,nick,body,ctime FROM entries ORDER BY ctime DESC LIMIT ?,?},
        $args->{offset},
        $args->{limit}+1,
    );
    my $next;
    $next = pop @$rows if @$rows > $args->{limit};
    return $rows, $next;
}

package main;

my ($rows, $has_next) = $data->recent_entries( ... );

selectone, selectrow, selectall, queryはオブジェクトメソッドでも使えるようになっている(多分わかりにくい)。オブジェクトメソッド時はmethod(query,bindvalue)で使える。このようにSchemaクラスはシンプルなので、SQL::Makerや他のSQL genaratorと組み合わせても使えると思う。

んで、renameしたあとの DBIx::Sunny は Amon2::DBIインスパイアの DBI wrapperになっている。Scope::Containerと組み合わせるのに Amon2::DBI を試していたんだけどオレオレにするために fork させてもらった。

使い方は

my $dbh = DBIx::Sunny->connect(...);

# or

use DBI;

my $dbh = DBI->connect(.., {
    RootClass => 'DBIx::Sunny',
    PrintError => 0,
    RaiseError => 1,
});

上のDBIx::Sunnyを直接呼び出す場合は、PrintError,RaiseErrorが自動で設定されるのでオプションの設定は基本的にいらない。RootClass を使う場合は、connectionが確立するまでは DBIx::Sunny が呼ばれないので Print、RaiseError等は接続前に指定しておく必要がある。BKですね。

DBIx::Sunny の主な機能ですが、まず設定まわりで

  • AutoInactiveDestroy を有効に
  • sqliteunicode と mysqlenable_utf8 を有効に
  • mysqlautoreconnect は無効
  • RaiseError => 1, PrintError => 0、ShowErrorStatement =>1 に設定

このあたりが強制的に変更されます。そして追加機能ですが、

  • DBIx::TransactionManager 組み込み
  • SQL にコメントでstatementを読んだファイルの名前と行数を埋め込み
  • Schemaと同じく、selectone,selectrow,select_all,query のメソッド追加

が追加されています。

Scope::Container::DBI、DBIx::Sunny、DBIx::Sunny::Schema を組み合わせて使うと多分便利。Scope::Container::DBIのv0.03で、$Scope::Container::DBI::DBI_CLASS を変更することで利用するDBIのクラスを変更できるようになっている。

local $Scope::Container::DBI::DBI_CLASS = 'DBIx::Sunny';
my $dbh = Scope::Container::DBI->connect(
    "dbi:SQLite:dbname=$db_path", '', '', {...});
my $data = NoNoPaste::Data->new( dbh => $dbh );

# 最近のエントリ
my $rows = $data->entry_list( offset => $offset );

# エントリ取得
my $entry = $data->retrieve_entry( id => $id );

この組み合わせのサンプルとして、nopasteのコードがありますので参考になれば〜。

このブログ記事について

このページは、Masahiro Naganoが2011年4月12日 15:17に書いたブログ記事です。

ひとつ前のブログ記事は「Perl CPAN モジュールガイド」です。

次のブログ記事は「Webサーバ勉強会#3に参加してきました」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja