2011年4月アーカイブ

Log::Minimal をアップデートしました。バージョンは 0.05

http://search.cpan.org/dist/Log-Minimal/

$Log::Minimal::TRACE_LEVEL というフラグを追加しました。 Carpの$CarpLevelと同じように、stacktraceを開始するレベルを変更できます。

package TestLog;

use Log::Minimal;

sub test_log {
    warnf @_;
}

package main;

{
    TestLog::test_log("1st log");
}

{
    local $Log::Minimal::TRACE_LEVEL = $Log::Minimal::TRACE_LEVEL + 1
    TestLog::test_log("2nd log");
}

結果は

 2011-04-28T15:02:19 [WARN] 1st log at /tmp/test.pl line 6
 2011-04-28T15:02:19 [WARN] 2nd log at /tmp/test.pl line 17

1個目は、testlog を定義した行がでていますが、2個目はtestlogを呼び出した行になってます。これを使うと、Log::Minimalの関数をwrapしたようなものも作れます

Webサーバ勉強会#3に参加して発表しました。りーおさんのmod_rewriteの話が興味深かったです。

自分の発表内容はReverse Proxyで使うことを想定した Apache の rpm の紹介です

紹介したrpmのspecファイル、設定ファイル、オレオレpatchなどはすべてgithubにあります。

rpm: https://github.com/kazeburo/rpm/tree/master/httpd_proxy
patch: https://github.com/kazeburo/apache-httpd-patch

あわせて読みたい

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

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のコードがありますので参考になれば〜。

YAPC::Asia の忍者こと、tomi-ruさんの Perl CPAN モジュールガイドがでるということで献本を頂きました。出版おめでとうございます! & ありがとうございます!

DSC02152.jpg

たくさんのCPANモジュールの中からベストプラクティスなモジュールが200個ほどサンプルコードとあわせてリファレンス的に紹介されています。私がリリースしているモジュールも

の2つが載っています。



この本を読んで勉強ができる新人エンジニアが羨ましい!と思うぐらい良いセレクション&内容だと思います。自分もまだまだ知らないことがあるのでじっくり読ませていただきます。

DBIのRootClassを使うとScope::Container::DBIで接続を管理しつつ、Amon2::DBIが使えます。

#!/usr/bin/env perl

use strict;
use warnings;
use 5.12.0;
use Scope::Container::DBI;

my $dbh = Scope::Container::DBI->connect(
     'dbi:SQLite::memory:', '', '',
     { RootClass => 'Amon2::DBI' }
);

$dbh->do(q{CREATE TABLE foo (e)});
$dbh->insert('foo', {e => 3});

$dbh->do_i('INSERT INTO foo ', {e => 4});
say join(',', map { @$_ } @{$dbh->selectall_arrayref('SELECT * FROM foo ORDER BY e')});

eval {
    $dbh->insert('bar', {e => 3});
};
say $@;

Amon2を使うと、utf8の自動設定やエラー処理、Nested Transaction、DBIx::Interpをつかったクエリ生成がサポートされるので便利ですね