about sitehisaichi5518がPerlを書いて、つついて、イチャイチャするブログ。最近はnode.jsもやってる。

hisaichi5518プロフィール/ twitter管理人twitter/ rss feedRSS feed

今、Maltsを見なおしてる。get ‘/’ => sub {};みたいなの欲しいって言われてたので追加した。

1
2
3
4
5
6
7
8
9
10
use Malts::Web::Router::Simple::Declare;
# いままで
get '/' => 'Root#index';
get '' => {controller => 'Root', action => 'index'};

# 新しく追加
get '/' => sub {
    my ($c) = @_;
    $c->res_200();
};

もちろん、get, post, put, delの全部で使える。

例を書いてたのだけど、Malts::Liteみたいなの作る必要なさそう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use parent qw(Malts Malts::Web);
use Malts::Web::Router::Simple::Declare qw(!dispatch);

get '/' => sub {
    shift->res_200;
};

get '/foo' => sub {
    shift->res_200;
};

get '/foo/:user_id' => sub {
    my $c = shift;
    $c->res_200($c->args->{user_id});
};

sub res_200 {
    my ($c, $text) = @_;
    $c->create_response(200, [], $text || 'ok');
}

sub dispatch {
    Malts::Web::Router::Simple::Declare->dispatch(@_)
        or $_[0]->create_response(404, [], ['404 Not Found!']);
}

__PACKAGE__->to_app;

実際に利用するかどうかは置いといて、こういう事も出来るようにはなった。
Controllerにルーティング書いてみる。Catalystを分かりやすくした感じ。
さっきのに比べるとごちゃごちゃしてるように見える。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use strict;
use warnings;

package MyApp::Web;
use parent qw(Malts Malts::Web);

sub res_200 {
    my ($c, $text) = @_;
    $c->create_response(200, [], $text || 'ok');
}

sub dispatch {
    MyApp::Web::Dispatcher->dispatch(@_)
        or $_[0]->create_response(404, [], ['404 Not Found!']);
}

package MyApp::Web::Dispatcher;
use Malts::Web::Router::Simple::Declare;
# ファイルを分割した時のため
# use Module::Find qw(useall);
# useall 'MyApp::Web::Controller';

package MyApp::Web::Controller::Root;
use Malts::Web::Router::Simple::Declare;

get '/' => sub {
    shift->res_200;
};

package MyApp::Web::Controller::Foo;
use Malts::Web::Router::Simple::Declare;

get '/foo' => sub {
    shift->res_200;
};

get '/foo/:user_id' => sub {
    my $c = shift;
    $c->res_200($c->args->{user_id});
};

package main;
MyApp::Web->to_app;

Malts::Web::Router::Simple::Declareって書くのだるいし、Malts::Web::Router::Simpleに変更するかも。

2012年1月27日 金曜日 perl (No comments) Tags:


戦略的設計、戦術的設計があるけど、それはとりあえず置いといておいらは三つあると思うでござる。

1. アプリケーション固有の部分
2. 人によって変えたいけど、他のアプリケーションと共通にしておくと楽な部分
3. 大体どのアプリケーションでも同じ部分

多分、戦略的設計が2,3で戦術的設計が1だと思う。

フレームワークで必要なのは2, 3

1はフレームワークで実装される事はない。
2はフレームワークで実装されてる場合は多い。
3はフレームワークで実装されてる。

1, 2, 3の例

1はアプリのコード
2はコンテナとか
3はアプリケーションの流れとかencodingとか

ひさいちが作ってるフレームワーク”Malts”ではどうか

1は実装されてない
2はMaltsでは実装されていない
3はMaltsで実装されている

Maltsの拡張”Malts::Style::Premium”ではどうか

1は実装されていない
2はMalts::Style::Premiumでは実装されている
3はMalts::Style::Premiumでは実装されていない

Catalystではどうか?

1は実装されていない
2はCatalystでは実装されている
3はCatalystでは実装されている

フレームワークを作る上で設計が必要な部分は2つ

1はアプリ側の設計なのでフレームワークが関わるべきではない。(どれだけアプリの設計をしやすくするかとかはあるとは思う(例: renderについて))
2をフレームワークで一切実装しないってのもあるけど、結局自分のスタイルは大体同じなので毎回書くとなるとめんどい。
しかしながらフレームワーク本体に実装すると後々不便になると考えられるので、Malts::Style::*っていうのを用意して、そこにコンテナとか作って他のアプリケーションを作る時にそれを使えば自分が良いと思えるスタイルで素早くコードが書けるようにした。Malts::Style::Premiumはそれの参考実装。
3はすべてのフレームワークで実装されるべき部分だと思う。なのでMaltsで実装する。

ウェブアプリケーションを作る上で必要な設計とは

フレームワークを作る人が2と3を実装して、アプリ作る人が実装するのは1のみになると楽。

そもそも設計って言葉が自分の中でふわふわしてて、よくわからないので間違ってるかもー。ツッコミ待ってます。

2011年12月4日 日曜日 perl (No comments) Tags:


renderはstatusが必須になった。
res_404とかアプリケーション毎に結構違うので、フレームワークでは一切実装しないでアプリ毎に実装させようと思った。
でもres_*って結構同じような事を書かないといけない。めんどくさい。なので、( render, render_string )+statusで使えるようにするのが一番良いと思った。

もちろん、Controllerの中に直書きもできる。

1
2
$c->render(200, 'root/index.tx', {hoge => 1});
$c->render_string(404, "not found!");
1
2
3
4
5
6
7
package MyApp::Web;
sub res_200 {
    shift->render(200, @_);
}
sub res_404 {
    shift->render(404, 'error/404.tx', @_);
}

便利。
また、render_stringも同じように使える。
404の時だけrender_string使うとかあるある。

1
2
3
4
5
6
7
package MyApp::Web;
sub res_200 {
    shift->render(200, @_):
}
sub res_404 {
    shift->render_string(404, 'Not Found!');
}

便利。

またアプリによってはJSONを返したい場合もある。そんな時のためにrender_jsonも使えるようにした。これはMalts::Web::View::JSON;をuseすれば使える。
これも直で使えるけど、アプリ毎にres_*を作る方がわかりやすくて良いと思う。

1
2
3
4
5
6
7
8
9
10
11
package MyApp::Web;
use Malts::Web::VIew::JSON qw(render_json);

sub res_200 {
    my ($c, $data) = @_;
    $c->render_json(200, {success => 1, data => $data});
}
sub res_404 {
    my ($c, $errstr) = @_;
    $c->render_json(404, {success => 0, data => {message => $errstr}});
}

みたいなの。分かりやすいと思うし便利。

2011年11月26日 土曜日 perl 1 comment Tags:


更に追記

自分自身これめっちゃ便利だなーっていうのが分からないのとそんな中途半端な状態でショートカットを増やすと読むコードが増えるだけなので実装しない事にしました。

ツッコミモロタ。ウレシス。

@songmuさんにツッコミもらった!ありがとうございます!

別に$req->param(‘*_id’)とかで取れるから、$reqを太らせる必要ないと思う。$cはショートカットメソッド生やしたりする場所だからやり過ぎは良くないけど、多少は太らせても良いという認識。APIは別モジュールで叩くべきなのは同意
Twitter / @songmu: @hisaichi5518 別に$req->p …

$cに生やすかはともかく、割とアクセスするから、どこかでアクセスしやすくしておくと楽かと。本来はopensocialを扱うためのプラギンクラスを作って、それを$cに生やすのが良いと思う。
Twitter / @songmu: @hisaichi5518 $cに生やすかはともかく …

$c->mobile_agentも同じような感じ。$reqをあまり太らせる必要はないと思うし、ある程度生のまま保ったほうが良いと思う
Twitter / @songmu: @hisaichi5518 $c->mobil …

opensocial APIはローカルからopensocial_idセットしてコマンドラインで呼んだりもするから、req依存はやっぱ筋悪かな。
Twitter / @songmu: @hisaichi5518 opensocial A …

requestに生やそうとしたのはrequest関係なのでショートカットもrequestに突っ込んだ方がいいかなと思ったからです。$req->content_lengthとかよくあるじゃんみたいな。
よく考えてみたら、headersへのショートカットがrequestにきてるので、requestへのショートカットがrequestにくるのはちょっとおかしくて、$cくるのはおかしくない!はず!

1
2
3
4
5
6
7
8
9
10
11
package MyApp::Web;
use Malts::Web::OpenSocial qw(opensocial);
use Malts::Web::MobileAgent qw(mobile_agent);

sub startup {
    my ($c) = @_;
    $c->opensocial->owner_id; # 今まで: $c->request->owner_id;
    $c->request->param('openscocial_owner_id');

    $c->mobile_agent->is_non_mobile; # 今まで: $c->request->mobile_agent->is_non_mobile;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package MyApp::Web;
use Malts::Web::OpenSocial qw(opensocial_owner_id opensocial_view_id opensocial_app_id);
use Malts::Web::MobileAgent qw(mobile_agent);

sub startup {
    my ($c) = @_;
    $c->opensocial_owner_id;
    $c->opensocial_view_id;
    $c->opensocial_app_id;
    $c->request->param('openscocial_owner_id');
    $c->request->param('opensocial_viewer_id');
    $c->request->param('opensocial_app_id');
}

三つくらいならそのまま生やしてもいいかなーと思ったけど…ショートカットは必要ないかなあと思えてきた。
あと今回はガジェットサーバからこちらのサーバにリクエストが投げられて、それをどうするかって話なので、CLIは関係ない気がする。
やっぱり一回アプリ書いてみるのが一番だなと思った。間違ってたらツッコミください。

元の本文

Gadget サーバーからリクエストされた時にうんたらかんたら。

1
2
3
4
5
use Malts::Web::Request::OpenSocial;
my $req = $c->request;
$req->app_id;
$req->viewer_id;
$req->owner_id;

まだ何が必要か考えてる途中なんだけど、作る必要ないかなーと思えてきた。もっと必要なのないかな。
OAuth Signatureのチェックは、Plack::Middleware::Auth::OAuthでやればいいかなー。プラットフォームのAPIを叩くのはモデルの中でモジュールでやればいいよねー。
$cに生やすのはおかしいかなーと思ったのでrequestに生やす。HTTP::MobileAgentもそんな感じで生やせる。

2011年11月20日 日曜日 perl (No comments) Tags:


render, render_jsonで悩んでいる。
res_404とかは大体アプリケーションによって違うので実装しない方がいい気がしてる。

あと今の感じだとアプリでres_404とか作るとしても、エラー毎にテンプレート使いたいとかそういうのがあると最初から書かなくちゃいけなくてめんどいかな。
encodeとviewとレスポンス作るのをまとめた部分だけ欲しい場合が結構あるかなーとオモタ。

1
2
3
$c->_render(404, 'root/index.tx', {hoge => 1});
$c->_render_json(404, {hoge => 1});
$c->_render_string(404, '404!', {});

とか作ろうかね。_なしは今まで通り200で。

1
2
3
4
5
6
7
8
use Malts::Web::View::JSON;

around _render_json => sub {
    my $orig = shift;
    my ($self, $status, $data) = @_;
    my $sucess = $status == 200 ? 1 : 0;
    $orig->({ success => $success, data => $data });
};

んー結論はない。

2011年11月14日 月曜日 perl (No comments) Tags:


というかついさっきpushした。互換性残せた部分もあったけど、使ってる人いないと思うのと邪魔になるのでがっつり変えた。ちょっとまだふわふわしてる。

Malts

1. routesの廃止

1
2
3
4
5
# 今まで
sub startup {
    my $r = $self->routes('RSimple');
    $r->connect('/' => {...});
}

Mojoliciousみたいな感じにしてたけど、メリットが一切感じられなかったので変更。
Amon2っぽい感じになりました。

1
2
3
4
5
6
7
8
9
10
11
package MyApp::Web;
use MyApp::Web::Dispatcher;
sub dispatch {
    my $self = shift;
    MyApp::Web::Dispaycher->dispatch($self) or $self->create_response(404, [], ['not not found!']);
}

package MyApp::Web::Dispatcher;
use Malts::Web::Router::Simple;

get '/' => 'Root#index';

orで色々変更出来て便利ですね。Amon2とほぼ一緒だけど動作は若干違う。

2. app_base_classは指定が必須に

別に使わないならいいのだけど、これがundefだとエラーが出るメソッドとかあります。
そういう時はエラーメッセージを出すようにしてるけど、忘れてるところがあったらすんまへん。

1
2
3
package MyApp;
use parent 'Malts';
sub app_base_class { 'MyApp' }

変更した理由としては、自動で作ったやつをキャッシュしてたんだけどそれが無駄だなーって思ったのと、Controllerでapp_base_classを初めて呼ぶと仕様上おかしい感じになるのでそれがわかりづらいなと思った。わざわざ指定するのはちょっとめんどいけど、どうせスケルトン作成で出来るし、アプリ側で指定してしまえば多分早いし分かりやすいのでそれでいいやってなった。

3. app_classの削除

ref($self);するだけとか必要ねえなと思った。

4. ok, not_foundメソッドの削除

結構便利でがっつり使ってたんだけど、okとかにすると他のエラーの時とか分かりにくいなって思った。
res_200とかres_404とか別の形で提供するのではないかと思われる。

5. responseメソッド削除

dispatchがresponseを必ず返せばいいだけなので不必要になった。

6. new_responseメソッド削除

responseをキャッシュしないので、不必要になった。

7. 5.10.1以上で動作する

state使い始めてる。5.8.xはレガシー!って事でいいのではないでしょうか。

8. Malts->context, Malts->set_contextの削除

使わないようにしたので削除。モデルで使われたらやだもん。

9. encodingをMalts::Utilでするようにした

Malts->encodingはただのショートカットになりました。

10. configはScope::Containerを使う

Config::ENVを使おうか悩んだのだけど、設定ファイルを動的に変更可能なら1リクエストに1回設定ファイルを読み込んだ方がいいだろうって思ってScope::Containerを使うようにした。
Config::ENVの場合、paramではなくlocalを使えやって話なのだけど、ついうっかりやってしまいがちだし、paramで上書きしてもエラーもでないのでやめた。
ただちょっとここはブレそうです…。

11. サンプルアプリのテスト追加

サンプルアプリのテストは書いていなかったのだけど、動いてるか確認するが一々めんどかったのでテストを書いた。

Malts::Style::Premium

1. Controllerのdbメソッド削除

Controllerの中でdbを触らせたくなかった。それでどうしよう…って考えてたんだけど、普通にControllerの中にdbメソッドなければええやんってなったのでそうした。
コードもすっきり。

2. Scope::Container::DBIを使い始めた。

modelではdbメソッドがあったけど、dbメソッドを削除してdbhメソッドを新たに作った。
例えばTengを使いたい場合は、Malts::Style::Premium::ModelをMyApp::Modelとかに継承して適当にdbメソッドを作る感じでいいかなと思う。
DBIをそのまま使いたいならdbhであれしたらおk
しかも、Amon2::DBIとかDBIx::Handlerとかも設定で渡したら多分使えるので便利。

eg/MyTest以下にテストでも使ってるサンプルアプリがある。
あと、$self->{db}とかで自分で持つよりScope::Containerを使うと他のモデルで使いまわせるのでよいと思われる。
Teng::Schema::Loaderとか開発の時は便利だし使いたいとか結構変える事が多いと思うので、dbメソッドは自分で書いた方がいいと思う。
今までのやり方は継承して、設定変えてとかしなきゃいけなくて、クソめんどくさかったし、これでいいわってなった。またコードもすっきりして見やすくなった。

まとめ

コードのテスト・ドキュメントは修正したけど、Githubのドキュメントを書きなおしていない。つらい。

2011年11月13日 日曜日 perl, 雑記 (No comments) Tags:


Maltsというフレームワーク書いてる。開発が進むにつれ、$c->routes;が微妙だとしか思えなくなってきた。

最初は見やすいと思ってたけど、普通に見難い。あと遅いかも。
今はこういう感じでいいかなってなってる。後方互換保ったままいけるんだけど、現時点で使ってる人いないだろうし、ややこいし、ぶち壊してもいい気がする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package MyApp::Web;
use strict;
use warnings;
use parent qw(MyApp Malts::Web);
use MyApp::Web::Dispatcher;

sub startup {
    my ($self) = @_;
    MyApp::Web::Dispatcher->dispatch($self);
}

package MyApp::Web::Dispatcher;
use strict;
use warnings;
use Malts::Web::Routes::RSimple::Declare;

get '/' => 'Root#index';
get '/user/:user_id' => 'User#index';

Amon2もこんな感じ…。

あと最近思うのが、getとかpostとかを指定しないってほぼない気がする。
更にcontrollerとaction以外を指定するのってあんまない気がする。
更に更にconstraintsもっと見やすく指定したい。

で考えたけど、ちょっと微妙だなー。うーん。

1
2
3
4
5
6
7
8
9
use Malts::Web::Routes::RHoge::Declare;
constraints {
    user_id => qr//,
    post_id => qr//,
};

get '/user/:user_id' => 'Root#index';

1;
1
2
3
4
5
6
7
8
9
10
use Malts::Web::Routes::RHoge::Declare;
get '/user/:user_id' => 'Root#index';

package MyApp::Types;
use strict;
use warnings;

use Mouse::Util::TypeConstraints;

subtype 'UserId' => as 'Num';

(・ω・)モルスァ

2011年11月5日 土曜日 perl (No comments) Tags:


追記

Amon2はCLIのネームスペースは持ってないよってtokuhiromさんからツッコミもらいました!スミマセンスミマセン!
Webの方が、Web用で、ルートがCLI用だそうです!スミマセンスミマセン!
ただMaltsはそうだよ、って事で!スミマセンスミマセン!
下の本文はAmon2を抜いて読んでください。スミマセンスミマセン!

本文

Amon2やMaltsがMyApp::WebとかMyApp::CLIに分かれてる理由。

もし、MojoliciousみたいにMyAppにWeb関係のものを突っ込んでしまうと、CLI関係のものを書くときにMyApp::CLIとかにすると実質MyApp::Web::CLIって事になる。
さすがにそれキモすぎやろってなるから、分けた方がよい。

まとめ

Web関係のものはMyApp::Web以下に書くべきだし、CLI関係のものはMyApp::CLI以下に書くべき。
Amon2がそうした本当の理由は知らないけど、最低でもMaltsはそう。
あとスクリプトを書くときに.plにがっつり書きまくるのって微妙だと思っている。(思っているだけでゴメンナサイ)

2011年11月3日 木曜日 perl, 雑記 (No comments) Tags:


と最近思い始めた。
今までControllerで全部バリデートやってModelには全部バリデート済みのデータ渡しとけば間違いないんじゃねとか思ってたけど、他の人(未来の自分を含む)がそれを絶対やるなんてありえないよなーなんて。

1
2
3
4
5
6
7
8
9
10
11
12
package MyTest::Model::User;
use 5.10.1;
use warnings;
use parent 'Malts::Style::Premium::Model';

sub get_user_info {
    my ($self, $user_id) = @_;
    state $v = $self->validator(user_id => 'Num');
    my $args = $v->validate(user_id => $user_id);

    return $self->db->single('user', {id => $args->{user_id}});
}

毎回毎回アプリを作るたびに同じようなコード書くのは苦痛でしかない。なので、共通化できるところはしちゃいましょう。
Maltsではこういう事をしてもらうために、Malts::Style::*っていう考え方がある。Malts::Style::Premiumは実装例みたいなの。

例えば上の例だとvalidatorやdbはMalts::Style::Premium::Modelに入ってて毎回書く必要がない。
ただこれをMaltsのコアに入れてしまうとやたらめったら押し付けがましいフレームワークになるし、方向転換したい!って思った時に簡単に方向転換できない。
そこで、Malts::Styleというのに切り分ける事にした。Malts::Styleは自分で書けるし拡張も容易。

「Maltsはコンテナまでがっつり実装しているわけではなくて、全体に必要な部分のみを実装する。それで更にMaltsを便利にするにはStyleを使いましょうねっていう事」

Malts::Style::Premium::ModelはMaltsがないと動かない!なんて事はなくて単体でも動くはず。そんで多分Amon2でも利用出来るはず。
だって今ローカルにあるファイルはこんな感じだもの。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package Malts::Style::Premium::Model;
use strict;
use warnings;
use Data::Validator;

sub new {
    my $class = shift;
    my %args = @_ == 1 ? %{$_[0]} : @_;
    bless {%args}, $class;
}

sub db {
    shift->{db};
}

sub validator {
    shift;
    Data::Validator->new(@_);
}

1;

めっちゃ簡単。

あとMalts::Style::*はドキュメントがないのはクソだよねーみたいな感じにしたいと思っていて、Malts::Style::Premiumはまだ完璧ではないけど”ControllerでDBを直接触らない。”みたいな当たり前な事を書いてある。というか今書いてる。

そのドキュメントに書いてある事をコードを書く人にそれを守ってもらう。これが結構大事でMalts::Styleの肝なんだぜ!!!

2011年10月19日 水曜日 perl (No comments) Tags:


ウェブアプリを作るとほぼ確実にちょっとしたスクリプトを書く。
という事は、アプリケーションフレームワークはCLIにも対応しておくと良い感じ。例えばAmon2はCLIにも対応してる(boostrap)

僕はMaltsというのを書いていて、CLIに対応する必要があるなと思った。
boostrapだけじゃなくて、App::Cmdっぽいのも実装しちゃうかなと考えた ただのメモ

—-
スクリプトを書くと数が増えてごちゃごちゃしがち。だるい。
.plにだらだら書くと汚くなりがち。だるい。
コマンド一つ自動で作って、MyApp::CLI::Command::*を作っていく形にしたら素敵じゃないかなあ。

1
$ ./script/myapp subcommand --option
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package MyApp::CLI;
use parent qw(Malts Malts::CLI);

package MyApp::CLI::Command::subcommand;
use strict;
use warnings;
use parent 'MyApp::CLI::Command';

sub usage { ... }
sub option_spec { ... }
sub run {
    my ($self, $c) = @_;
    # $c->model('Hoge'); など
}
2011年9月30日 金曜日 perl (No comments) Tags: