だるろぐ跡地

2009-03-26

catalystでモデル分離が落ち着いた。その上でUser->find_by(id => 2)とかしてみた

| 23:27 | catalystでモデル分離が落ち着いた。その上でUser->find_by(id => 2)とかしてみたを含むブックマーク

いつまでモデル分離やってんだよって感じですが。

色々問題も解決していい感じに落ち着いた。


APIDBICCRUDなメソッドが呼べなかった件

  • 流れ
    • モデルを分離するためにAPIを作ろう
    • $c->model('API::User')でAPIが呼ばれるようにしよう
    • モデルを呼ぶのにwebのコンテキストから切り離せた!
    • でも当然DBICのsearchとかのメソッドはAPIから呼べないよね
    • searchとかしたい場合はresultsetオブジェクト用意してね
    • 毎回用意するの面倒です
  • 解決策

DBICCRUDなメソッドが使えるクラスを用意して、全APIがそれ継承すればいい


この考え自体は思いついてたし、pixisでもそうやってたから間違っては無いはず。

が、自分で実装ができなかったのと、pixis丸パクりは自分のためにならないのでやらなかったので、実現できなかった。


pixisのソースちゃんと読んだらやってること分かったのでさくっと実装。

  • 用意するクラスの必要条件
    • CRUDメソッドを持っている
    • テーブルの名前に依存しない

とりあえず、こうなる。

package AutoTest::API::Base::DBIC;
use strict;
use warnings;

use String::CamelCase qw/decamelize/;

sub new {
    my $class = shift;

    $class = ref $class || $class;
    bless { @_ }, $class;
}

sub class {
    my $self = shift;
    (split(/::/, ref $self))[-1];
}

sub resultset {
    my $self = shift;

    my $class = $self->class();
    my $schema = $AutoTest::Web::schema;

    return $schema->resultset($class);
}

### DBIC CRUD

sub create {
    my ($self, $args) = @_;
    my $rs = $self->resultset();
    $rs->create($args);
}

# different from the original
sub find {
    my ($self, $id) = @_;
    my $rs = $self->resultset();
    $rs->find($id);
}

sub search {
    my ($self, $cond, $attrs) = @_;

    my $rs = $self->resultset();
    $rs->search_rs($cond, $attrs);
}

sub update {
    my ($self, $args) = @_;
    my $rs = $self->resultset();
    $rs->create($args);
}

sub delete {
    my $self = shift;
    my $rs = $self->resultset();
    $rs->delete;
}

  • 解説
    • クラス名は$selfの末尾から取得
      • 完全にpixisのパクりである。でもこれは俺も思いついてたYO!
      • 毎回正規表現ってのがリソース気になるっちゃなる
    • スキーマはクラス変数に置いたので、そこから取得
    • newは使わないけど、無いとCatalyst::Model::MultiAdaptorが怒る
    • searchはアレイコンテキストで使うことがまずないので、search_rsでラップ
    • findも正直primary keyでしか検索しないので、そういうラップに。オレオレ仕様ですよ

これで全APIで、

$self->search({hoge => 'huga});

とかできる。

populateとかtxn_doも必要なら追加すればいい。


  • use String::CamelCase qw/decamelize/; は使ってないけど、何なの?
    • まぁ先を読んでください

コントローラで$c->model('API::User')じゃなくてUserで呼べるようにする

railsかぶれの小手先だけのせこい手法である。

オレオレActiveRecord以前のをちと修正しただけ。


package AutoTest::ActiveRecord;
use strict;
use warnings;

use DirHandle;
use base qw/Exporter/;

my @schemas;

use subs @schemas;
our @EXPORT = @schemas;

BEGIN {
    my $app_name = (split (/::/, __PACKAGE__))[0];
    my $path = $ENV{APP_HOME} || '';
    $path .= "./lib/$app_name/Schema";

    my $d = DirHandle->new($path);
    while (defined (my $f = $d->read)) {
        next if $f =~ /^\./;
        $f =~ s/\.pm//;
        push @schemas, $f;
    }
}

sub setup {
    my $c = shift;

    $c->NEXT::setup(@_);

    foreach my $F (@schemas) {
        *{$F} = sub { $c->model("API::$F") };
    }
}

1;

以前はここで find_by と find_all_by を名前空間を侵して実装してたけど、それはAPI::Base::DBICに移動。

no strict 'refs'; が消えた。

これで User と $c->model('API::User') が等価になったので、これをコントローラでuseすればいいと。

グローバル変数使ったり、色々酷いコードだ。


find_by(id => 3) とか find_all_by(name => 'foo') とかを実装

戻って AutoTest::API::Base::DBIC へ。上で書いたのは途中までだったのだ。


### original

sub find_by {
    my ($self, $column, $value) = @_;

    $self->find_all_by($column, $value)->next;
}

sub find_all_by {
    my ($self, $column, $value) = @_;

    my $class = decamelize $self->class();
    $self->search({"$class.$column" => $value});
};

1;

perlは => の左辺にはBarewordが使える。hoge(huga => 'foo') とすると、hogeの中ではkeyがhugaで、valueがfooのハッシュとして受け取れる。

だから

p 'find';
p User->find(4)->name;
p 'find_by';
p User->find_by(name => "ore")->name;
p 'find_all_by';
p User->find_all_by(age => 9)->count;

とできる。実行結果は

$VAR1 = 'find';
SELECT user.id, user.name, user.age, user.sex, user.created_at, user.updated_at FROM user user WHERE ( ( user.id = ? ) ): '4'
$VAR1 = 'user_ok';
$VAR1 = 'find_by';
SELECT user.id, user.name, user.age, user.sex, user.created_at, user.updated_at FROM user user WHERE ( user.name = ? ): 'ore'
$VAR1 = 'ore';
$VAR1 = 'find_all_by';
SELECT COUNT( * ) FROM user user WHERE ( user.age = ? ): '9'
$VAR1 = 8;

となる。

トラックバック - http://d.hatena.ne.jp/foosin/20090326/1238077646