Hatena::Groupcatalyst

dann@catalyst このページをアンテナに追加 RSSフィード

2008-03-22

TODO

02:11 |  TODO - dann@catalyst を含むブックマーク はてなブックマーク -  TODO - dann@catalyst  TODO - dann@catalyst のブックマークコメント

  • 認証周りのベースコントローラ
  • FormValidation周りのシンプル化
  • remember me
  • Feed部分のロジックの共通化
  • Catalystに依存しないモデル部分の改善
  • DBICのMock化

くらいかなぁ。ここまでができると、Catalystで基本的な仕組みは勉強できたような気がするなぁ。そこまで終わったらなんか作っていこうかなぁと。

Catalystに依存しないモデルのロジックについて

00:22 |  Catalystに依存しないモデルのロジックについて - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないモデルのロジックについて - dann@catalyst  Catalystに依存しないモデルのロジックについて - dann@catalyst のブックマークコメント

  • DBのみに依存したロジックは、全部Schemaにかいていく。MyApp::Base::Model::DBICを継承したクラスは自動生成して、Logicはそこには記述しない。
  • Logicクラスには、複数のModelクラス(Catalyst非依存)を参照するようにする。

という形にしようかなぁと。

まだ、「MyApp::Base::Model::DBICを継承したクラスの自動生成」ができてないけど、ここはschema_dumperに手をいれてやろうかと思ったたり。別のスクリプトでもいいかもしれないけど。

なんで、MyApp::Base::Model::DBICを継承したモデルクラスにロジックを記述しないかっていうと、スクリプトで自動生成したいから。それ以外の理由はなかったりする。

Catalystに依存しないDBICのモデルクラス

00:04 |  Catalystに依存しないDBICのモデルクラス - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystに依存しないDBICのモデルクラス - dann@catalyst  Catalystに依存しないDBICのモデルクラス - dann@catalyst のブックマークコメント

を作ってみた。

Catalystに依存しないでDBICを使うにはどうすればいいんだろうとこの連休は考えていた。Schemaってやつが情報を知りすぎていて、どうもクラスに分割しにくい。全部のテーブル情報知っちゃってるからで、どうしたもんかなぁと思っていた。

resultsetごとにクラスが分割できるようにベースクラスを作ればいいんだと思った。ベースクラスでやってるのはSchema取得。resultsetを取得して、そのresultsetに対して処理ができるようにするということ。

モデルのクラスからは、SchemaやResultsetが直接みえないようになる。けど、DBICのメソッドは直接触りたいから、AUTOLOADで処理をディスパッチする。という感じ。

Logicのクラスで$schema->resultset('Feeds')->search() とかはしたくないなぁと思って、綺麗に扱えないかっていうだけのことなんだけれど。多くの処理は、MangoのProviderからのパクリですが... ただ、Mangoはメソッドをラップして、Domainモデルを作ろうとしているんだけれど、これは自分には必要ないので、AUTOLOADでresultsetに全部ディスパッチするようにしたというのが違いかな。

モデルのベースクラス(今のところ特に何もしてない)

package MyApp::Base::Model;
use strict;
use warnings;

BEGIN {
    use base qw/Class::Accessor::Grouped/;
};

sub new {
    my ($class, $args) = @_;
    my $self = bless {}, $class;

    $self->setup($args);

    return $self;
};

sub setup {
    my ($self, $args) = @_;

    if (ref $args eq 'HASH') {
        map {
            if ($self->can($_)) {
                $self->$_($args->{$_})
            } else {
                $self->{$_}  = $args->{$_};
            }
        } keys %{$args};
    };

    return;
};

1;

Catalystに依存しないDBIC用のモデルのベースクラス

package MyApp::Base::Model::DBIC;
use MyApp::ConfigLoader;
use strict;
use warnings;
BEGIN {
    use base qw/MyApp::Base::Model/;
    use UNIVERSAL;
    use MyApp::Exception qw/:try/;

    __PACKAGE__->mk_group_accessors('component_class', qw/schema_class/);
    __PACKAGE__->mk_group_accessors('inherited', qw/
        config
        source_name
        connection_info
        _resultset
        _schema
    /);
};
__PACKAGE__->schema_class('MyApp::Schema');

sub new {
    my $class = shift;
    my $self  = $class->SUPER::new(@_);
    $self->setup_config;
    $self->setup_connection_info;
    return $self;
}

sub setup_config {
    # FIXME
    # Catalystで使うときは、configloaderがセットされた状態にする。
    my ($self) = @_;
    unless($self->config) {
        my $config = MyApp::ConfigLoader->new->config;
        $self->config($config);
    }
}

sub setup_connection_info {
    my ($self) = @_;
    $self->connection_info(
        $self->config->{'Model::DBIC'}{'connect_info'}
    );
}

sub resultset {
    my ($self, $resultset) = @_;

    if (defined $resultset) {
        $self->_resultset($resultset);
    } elsif (!$self->_resultset) {
        if (!$self->source_name) {
            MyApp::Exception->throw('SCHEMA_SOURCE_NOT_SPECIFIED');
        };

        try {
            $self->_resultset($self->schema->resultset($self->source_name));
        } except {
            MyApp::Exception->throw('SCHEMA_SOURCE_NOT_FOUND', $self->source_name);
        };
    };

    return $self->_resultset;
};

sub schema {
    my ($self, $schema) = @_;

    if ($schema) {
        $self->_schema($schema);
    } elsif (!$self->_schema) {
        if (!$self->schema_class) {
            MyApp::Exception->throw('SCHEMA_CLASS_NOT_SPECIFIED');
        };
        $self->_schema(
            $self->schema_class->connect(@{$self->connection_info || []})
        );
    };

    return $self->_schema;
};

sub DESTROY {}

sub AUTOLOAD {
    my $self = shift;
    my $method = our $AUTOLOAD;
    $method =~ s/.*:://o;
    warn $method;
    $self->resultset->$method(@_);
}

1;

実際のモデルのクラス

package MyApp::Model::Feeds;
use base qw/MyApp::Base::Model::DBIC/;

__PACKAGE__->source_name('Feeds');

1;

使用方法

    my $feed = MyApp::Model::Feeds->new;
    $feed->update_or_create(
        {   feedlink => 'http://hoge.com/',
            link     => 'hoge.com',
            title    => 'hogehoge'
        }
    );

# まじめにここの仕組みは考えたいから、これじゃ使えないよ!とか突っ込み希望です。

改善案

AUTOLOADを1回だけ呼ぶようにする

http://d.hatena.ne.jp/ysano2005/20051229/1135870740

vkgtaro++

全部Resultsetにdispatchするんだったら、newする段階でresultset返しちゃえばいいんじゃないかというtomyheroさんからの意見。ごもっとも!tModelクラスにロジックを書くという案を最初考えていたから、Resultsetにディスパッチしてるんだけど、実はそれだと自動生成が面倒になるので、それもいいかもしれない!

Catalystのディレクトリ構成(続)

21:12 |  Catalystのディレクトリ構成(続) - dann@catalyst を含むブックマーク はてなブックマーク -  Catalystのディレクトリ構成(続) - dann@catalyst  Catalystのディレクトリ構成(続) - dann@catalyst のブックマークコメント

catstarterで生成して、script/myapp_web_schema_dumper.plを実行した後の状態。MyApp::Web::Modelは使わないというところまで、後少し。今は以下のような感じ。後一歩。

  • MyApp::Web::Controller配下にベースクラスを置かないようにBase用のディレクトリを分離したこと。
  • MyApp::Web::Modelは使わずに、MyApp::ModelというCatalystに依存しないようにするためのModel用のパッケージを用意したところ
`-- MyApp
    |-- Base
    |   |-- Model
    |   |   `-- DBIC.pm
    |   |-- Model.pm
    |   `-- Web
    |       |-- Controller
    |       |   `-- API.pm
    |       |-- Model
    |       `-- View
    |-- CLI
    |   |-- Command
    |   `-- Command.pm
    |-- CLI.pm
    |-- ConfigLoader.pm
    |-- Date.pm
    |-- Exception.pm
    |-- I18N
    |-- Logic
    |-- Model
    |-- Schema
    |   |-- CrawlStatuses.pm
    |   |-- Favicons.pm
    |   |-- Feeds.pm
    |   |-- Folders.pm
    |   |-- Items.pm
    |   |-- Members.pm
    |   |-- Pins.pm
    |   |-- SchemaInfo.pm
    |   `-- Subscriptions.pm
    |-- Schema.pm
    |-- Utils.pm
    |-- Web
    |   |-- Controller
    |   |   |-- API
    |   |   |-- API.pm
    |   |   `-- Root.pm
    |   |-- Model
    |   |   `-- DBIC.pm
    |   `-- View
    |       |-- JSON.pm
    |       |-- TT
    |       |   `-- Plugin
    |       `-- TT.pm
    `-- Web.pm
トラックバック - http://catalyst.g.hatena.ne.jp/dann/20080322