週記くらい

  • Profile

Dancer + Xslate + Skinny によるMVC構成のベースを作ってみた

Posted on 2012/04/22 by smeghead
Share on Facebook
Facebook
Share on Tumblr
Tumblr
Share on Google+
Google+
Tweet about this on Twitter
Twitter
Buffer this page
Buffer

仕事でCatalystを使ったことはあるんですが、Perlのフレームワークも様変わりしてるみたいです。

例えば、Catalystだとこう書くところを、

sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
 
    $c->response->body( 'こんにちは!' );
}

sub index :Path :Args(0) { my ( $self, $c ) = @_; $c->response->body( 'こんにちは!' ); }

Dancerだとこんな感じ。

get '/' => sub {
    'こんにちは!';
}

get '/' => sub { 'こんにちは!'; }

これ以上ありえないくらいにシンプルな記述です。rubyのsinarta風だそうです。

また、ログを出力したりパラメータにアクセスする時は、Catalystでは、下のようになるところを、

sub hello :Path :Args(1) {
    my ( $self, $c, $name ) = @_;
 
    $c->log->debug('logging.');
    $c->response->body( "こんにちは! $name" );
}

sub hello :Path :Args(1) { my ( $self, $c, $name ) = @_; $c->log->debug('logging.'); $c->response->body( "こんにちは! $name" ); }

Dancerだとこんな感じ。

get '/hello/:name' => sub {
    debug 'logging.';
    'こんにちは!' . param 'name';
}

get '/hello/:name' => sub { debug 'logging.'; 'こんにちは!' . param 'name'; }

Catalystでは、何でもかんでも、$cからアクセスするところを、 Dancerでは、該当packageにログ出力やパラメータ取得の関数をぶちまけることで、debug とか param とかで、機能にアクセスできるようにしているようです。関数として呼び出せるということは、OOP的なインスタンス経由のアクセス程煩雑な要素を含めることなく記述をシンプルに保ちながら、get ‘/’ => sub {} のsubのようなルーティングの実処理の無名関数からもアクセスできるという、巧妙な方法だと思います。一昔前だと、packageに勝手に関数をimportしちゃうのは行儀が良くないと思われたかもしれないけど、「肉を斬らせて骨を断つ」的な見事な設計だと思いました。

Dancerは、小さいWebアプリを作るのが簡単なのは、すぐわかったんですが、Dancerである程度大きいアプリも作りたい。 そこで、シンプルを保ちながらMVCで書けなるように、テンプレートエンジン(Xslate)とORマッパ(Skinny)を繋げてみました。

├── I18N  多言語化リソース
├── bin   起動スクリプトなど
├── environments 環境別設定
├── lib
│   ├── DancerApp
│   │   ├── Controller
│   │   │   ├── Root.pm Root用コントローラ
│   │   │   └── User.pm Userディレクトリ用コントローラ
│   │   ├── Logic
│   │   │   ├── Base.pm ロジッククラスの基底クラス
│   │   │   └── User.pm ユーザロジック
│   │   ├── Model
│   │   │   └── Schema.pm Skinny用スキーマ
│   │   ├── Model.pm Skinnyモデルクラス
│   │   └── Plugin プラグイン
│   └── dancer_app.pm スタートアップ
├── public  静的ファイルディレクトリ
├── t       テストディレクトリ
├── validation 入力値検証用ルールディレクトリ
└── views Viewディレクトリ

├── I18N 多言語化リソース ├── bin 起動スクリプトなど ├── environments 環境別設定 ├── lib │   ├── DancerApp │   │   ├── Controller │   │   │   ├── Root.pm Root用コントローラ │   │   │   └── User.pm Userディレクトリ用コントローラ │   │   ├── Logic │   │   │   ├── Base.pm ロジッククラスの基底クラス │   │   │   └── User.pm ユーザロジック │   │   ├── Model │   │   │   └── Schema.pm Skinny用スキーマ │   │   ├── Model.pm Skinnyモデルクラス │   │   └── Plugin プラグイン │   └── dancer_app.pm スタートアップ ├── public 静的ファイルディレクトリ ├── t テストディレクトリ ├── validation 入力値検証用ルールディレクトリ └── views Viewディレクトリ

Xslate

高速テンプレートエンジン。

Skinny

シンプルなORマッパー。リレーションはサポートされていない。 とてもバランスがいいと思う。SQL好きにとっては必要なことは全部できる気がする。

まとめたものの雰囲気

Controller lib/DancerApp/Controller/User.pm

コントローラ担当のファイル。logic という関数を使ってますが、ロジッククラスのインスタンスを取得する自作プラグインを用意して、それを使ってます。プラグインを作成するにも、他のプラグインのコードを見様見真似でできてしまった。 validationは、Dancer::Plugin::ValidateTinyを使いました。flashメッセージはDancer::Plugin::FlashMessage を使いました。(ただし、FlashMessageは、Xslateと相性がよくなかったので、ちょっと改造しました。

get '/add' => sub {
    my $User = logic('User');
    my $user_types = $User->user_types;
 
    template 'user/add', {user_types => $user_types};
};
 
post '/add' => sub {
    my $params = params;
    my $User = logic('User');
 
    # validation
    my $validation = validator $params, 'user.pl';
    unless ($validation->{valid}) {
        flash_errors $validation->{result};
        return forward '/user/add', {}, {method => 'GET'};
    }
 
    my $new_user = $User->register({
        name => $validation->{'result'}->{'name'},
        email => $validation->{'result'}->{'email'},
        user_type_id => $validation->{'result'}->{'user_type_id'},
    });
    die 'failed register user.', $@ unless $new_user;
 
    flash message => 'user created.';
    redirect 'user/';
};

get '/add' => sub { my $User = logic('User'); my $user_types = $User->user_types; template 'user/add', {user_types => $user_types}; }; post '/add' => sub { my $params = params; my $User = logic('User'); # validation my $validation = validator $params, 'user.pl'; unless ($validation->{valid}) { flash_errors $validation->{result}; return forward '/user/add', {}, {method => 'GET'}; } my $new_user = $User->register({ name => $validation->{'result'}->{'name'}, email => $validation->{'result'}->{'email'}, user_type_id => $validation->{'result'}->{'user_type_id'}, }); die 'failed register user.', $@ unless $new_user; flash message => 'user created.'; redirect 'user/'; };

Logic lib/DancerApp/Logic/User.pm

ロジッククラス。Skinnyを使ってビジネスロジックを書いてます。SQL使い放題です(涎 ある程度の規模にも耐えられるように、DBアクセスはロジッククラスを行なうのはだけにして、簡易DIのような形でコントローラとの密結合を防ぐのが目的です。

package DancerApp::Logic::User;
use strict;
use warnings;
use base qw( DancerApp::Logic::Base );
 
use Data::Dumper;
 
sub user_types {
    my ($self) = @_;
    $self->log->debug('enter user_types');
    my @rows = $self->schema->search_by_sql(q{select * from user_types order by id asc});
    return \@rows;
}
 
sub all_users {
    my ($self) = @_;
    $self->log->debug('enter all_users');
    my @rows = $self->schema->search_by_sql(q{select u.*, t.name as user_type from users as u inner join user_types as t on t.id = u.user_type_id});
    return \@rows;
}
 
sub register {
    my ($self, $user) = @_;
    $self->log->debug('enter register');
    return $self->schema->insert('users', $user);
}

package DancerApp::Logic::User; use strict; use warnings; use base qw( DancerApp::Logic::Base ); use Data::Dumper; sub user_types { my ($self) = @_; $self->log->debug('enter user_types'); my @rows = $self->schema->search_by_sql(q{select * from user_types order by id asc}); return \@rows; } sub all_users { my ($self) = @_; $self->log->debug('enter all_users'); my @rows = $self->schema->search_by_sql(q{select u.*, t.name as user_type from users as u inner join user_types as t on t.id = u.user_type_id}); return \@rows; } sub register { my ($self, $user) = @_; $self->log->debug('enter register'); return $self->schema->insert('users', $user); }

View views/user/add

ビュー。Xslateを使ってる。fillinformを使いたかったので、Xslateプラグインをちょっと改造しました。 Xslateのデフォルトの記述方法(Kolon)はいいですね。Velocityと同じように、制御文の行では閉じタグを書く必要がなくなってる。

<div id="page">
  <h1>New User</h1>
  : block form | fillinform($flash.has_errors ? $params : $user || {}) -> {
  <form action="" method="post">
    <div>name</div>
    <input type="text" name="name" id="name" />
    <span class="error"><: $flash.err_name :></span>
    <div>email</div>
    <input type="email" name="email" id="email" />
    <span class="error"><: $flash.err_email :></span>
    <div>user type</div>
    <select name="user_type_id" id="user_type_id">
      <option value="">please select...</option>
      : for $user_types -> $type {
        <option value="<: $type.id :>"><: $type.name :></option>
      : }
    </select>
    <span class="error"><: $flash.err_user_type_id :></span>
    <div>
      : if $user {
        <input type="submit" value="update" />
      : } else {
        <input type="submit" value="create" />
      : }
    </div>
  </form>
  : }
  <a href="<: $request.base :>user">back</a>
</div>

<div id="page"> <h1>New User</h1> : block form | fillinform($flash.has_errors ? $params : $user || {}) -> { <form action="" method="post"> <div>name</div> <input type="text" name="name" id="name" /> <span class="error"><: $flash.err_name :></span> <div>email</div> <input type="email" name="email" id="email" /> <span class="error"><: $flash.err_email :></span> <div>user type</div> <select name="user_type_id" id="user_type_id"> <option value="">please select...</option> : for $user_types -> $type { <option value="<: $type.id :>"><: $type.name :></option> : } </select> <span class="error"><: $flash.err_user_type_id :></span> <div> : if $user { <input type="submit" value="update" /> : } else { <input type="submit" value="create" /> : } </div> </form> : } <a href="<: $request.base :>user">back</a> </div>

githubに置いてあります。https://github.com/smeghead/dancer_app

Posted in perl |
« Starbug1 1.6.00-win32-bin をリリースしました
CSSによるフォントサイズの指定方法 font: normal 1.25em/1.5em MuseoSans,sans-serif; »

Leave a comment Cancel reply

Your email address will not be published. Required fields are marked *

Pages

  • Profile

Archives

  • September 2018
  • August 2018
  • March 2018
  • May 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • April 2014
  • November 2013
  • October 2013
  • July 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • February 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • July 2006

Categories

  • android (35)
  • apache (1)
  • bison (1)
  • BTS (18)
  • c# (23)
  • cgi (1)
  • chrome (9)
  • chromeextention (18)
  • clclcl (9)
  • clojure (13)
  • cloudbug1 (2)
  • css (4)
  • cygwin (7)
  • C言語 (21)
  • dart (1)
  • dotnet (11)
    • vb.net (2)
  • e-hash.jp (1)
  • eclipse (2)
  • emacs (10)
  • excel (1)
  • flex (1)
  • framework (11)
  • free (93)
  • gae (4)
  • gcc (4)
  • gimmehash.in (1)
  • glipper (3)
  • golang (7)
  • howm (1)
  • html (3)
  • ikushipe (1)
  • java (47)
  • JavaScript (29)
  • linux (16)
  • lisp (92)
  • lua (34)
  • luatinycgi (2)
  • mba (1)
  • Meadow (4)
  • memo (1)
  • music (5)
  • mysql (2)
  • neta (5)
  • node (2)
  • O/Rマッピングツール (4)
  • obj-c (6)
  • OOP (6)
  • oracle (1)
  • perl (49)
  • php (38)
    • CakePHP2 (2)
  • PostgreSQL (8)
  • PowerShell (1)
  • putty (1)
  • python (14)
  • redmine (2)
  • ruby (7)
  • s3 (1)
  • sakura (5)
  • screen (1)
  • Selenium (1)
  • SF (1)
  • SKK (4)
  • slime (6)
  • sql (8)
  • sqlite3 (4)
  • starbug1 (179)
  • tthttpd (3)
  • twitter (8)
  • ubuntu (14)
  • Uncategorized (4)
  • unix (14)
  • unkode-mania (5)
  • vba (3)
  • vim (24)
  • w3m (1)
  • Windows (17)
  • wordpress (1)
  • zsh (6)
  • 愚痴 (1)

WordPress

  • Log in
  • WordPress

Subscribe

  • Entries (RSS)
  • Comments (RSS)

Pages

  • Profile

Archives

  • September 2018
  • August 2018
  • March 2018
  • May 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • April 2014
  • November 2013
  • October 2013
  • July 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • February 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • July 2006

Categories

  • android (35)
  • apache (1)
  • bison (1)
  • BTS (18)
  • c# (23)
  • cgi (1)
  • chrome (9)
  • chromeextention (18)
  • clclcl (9)
  • clojure (13)
  • cloudbug1 (2)
  • css (4)
  • cygwin (7)
  • C言語 (21)
  • dart (1)
  • dotnet (11)
    • vb.net (2)
  • e-hash.jp (1)
  • eclipse (2)
  • emacs (10)
  • excel (1)
  • flex (1)
  • framework (11)
  • free (93)
  • gae (4)
  • gcc (4)
  • gimmehash.in (1)
  • glipper (3)
  • golang (7)
  • howm (1)
  • html (3)
  • ikushipe (1)
  • java (47)
  • JavaScript (29)
  • linux (16)
  • lisp (92)
  • lua (34)
  • luatinycgi (2)
  • mba (1)
  • Meadow (4)
  • memo (1)
  • music (5)
  • mysql (2)
  • neta (5)
  • node (2)
  • O/Rマッピングツール (4)
  • obj-c (6)
  • OOP (6)
  • oracle (1)
  • perl (49)
  • php (38)
    • CakePHP2 (2)
  • PostgreSQL (8)
  • PowerShell (1)
  • putty (1)
  • python (14)
  • redmine (2)
  • ruby (7)
  • s3 (1)
  • sakura (5)
  • screen (1)
  • Selenium (1)
  • SF (1)
  • SKK (4)
  • slime (6)
  • sql (8)
  • sqlite3 (4)
  • starbug1 (179)
  • tthttpd (3)
  • twitter (8)
  • ubuntu (14)
  • Uncategorized (4)
  • unix (14)
  • unkode-mania (5)
  • vba (3)
  • vim (24)
  • w3m (1)
  • Windows (17)
  • wordpress (1)
  • zsh (6)
  • 愚痴 (1)

WordPress

  • Log in
  • WordPress

CyberChimps WordPress Themes

© 週記くらい@やーづ

With Google+ plugin by Geoff Janes