今、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に変更するかも。
読みにくそうな例
1 2 3 4 5 6 7 8 9 10 11 12
| use strict;
use warnings;
# 連発
my $hoge = 1;
chinko() if $hoge == 1;
unko() if $hoge == 2;
# 一行に収まってない
do {
...;
} if $hoge; |
読みやすそうな例
1 2 3 4 5 6
| use strict;
use warnings;
my $hoge = 1;
my $fuga = 1;
return $hoge if not defined $fuga; |
returnあると読みやすそうだと思った。
return — if not defined —;は結構すきです。
結論
結局は好みだけど、使い方ミスったら読みにくいと俺は思う。
蛇足
あと一行に収まってない時は後ろに置かないようにした方が見やすいんじゃないかなーと思った。orとかandとかも。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use strict;
use warnings;
# ぎゅっと詰まってて読みにくそう。
my $chinko = $hisa->chinko(
name => 'hisaichi5518 no musuko',
size => 'small',
) or die 'no chinchin!';
# 読みやすそう。
my $chinko = $hisa->chinko(
name => 'hisaichi5518 no musuko',
size => 'small',
);
if (!$chinko) {
die "no chinchin!";
} |
と思ったけど、案外普通ですね。
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
| package t::Util;
use strict;
use warnings;
use lib qw(
./local/perl5
);
use Plack::Test qw(test_psgi);
use Plack::Util ();
use File::Spec ();
use Exporter 'import';
our @EXPORT = qw/apptest/;
my $psgi_file = File::Spec->catfile('script', 'app.psgi');
my $app = Plack::Util::load_psgi($psgi_file);
sub apptest {
my ($req, $test_code) = @_;
test_psgi $app, sub {
my $cb = shift;
my $res = $cb->($req);
$test_code->($res);
};
}
1; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #!perl -w
use strict;
use Test::More;
use t::Util;
use HTTP::Request::Common;
subtest 'GET /' => sub {
my $req = GET '/';
apptest $req, sub {
my $res = shift;
is $res->code, 200;
};
};
done_testing; |
Authorization加えたいとかopensocial_viewer_id加えたいとかはt::Util#apptest変えればオッケーすね。
戦略的設計、戦術的設計があるけど、それはとりあえず置いといておいらは三つあると思うでござる。
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のみになると楽。
そもそも設計って言葉が自分の中でふわふわしてて、よくわからないので間違ってるかもー。ツッコミ待ってます。
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}});
} |
みたいなの。分かりやすいと思うし便利。
更に追記
自分自身これめっちゃ便利だなーっていうのが分からないのとそんな中途半端な状態でショートカットを増やすと読むコードが増えるだけなので実装しない事にしました。
ツッコミモロタ。ウレシス。
@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もそんな感じで生やせる。
同期のPCでテストを実行するとテストが多いわけでもないのに遅い。会社から支給されてるPCなので低スペックってわけでもないと思う。
僕のPCでも結構遅いのだけど、それ以上に遅い。待ってるのつらい。
・テストを書く→実行する→遅い→イライラする→実行しなくなる→つらい
テストは書くべき、テストは早く終わるべき、テストは実行されるべき、と思う。
でも早く終わるテストって形が決まってないとむずいとも思う。そんで僕はその形がまだよくわかってない。
Test::mysqldは便利!と思って使ってた時期があったのだけど、上に書いたような事もあって最近は使ってると遅くなるなーと思うようになった。あとCSVを毎回読み込んだりするのも遅くなるなーって。うーん、良いテストの回し方?形?みたいなの全然知らないから知りたいなぁ。
そういえばtokuhiromさんが、テストの時は”DBは開発環境のものをピーコしてつかう”って言ってたの思い出した。
Testing Web Application 2011秋 – ”><xmp>TokuLog 改メ tokuhirom’s blog
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 });
}; |
んー結論はない。