# NAME Dwarf - Web Application Framework (Perl5) # SYNOPSIS package App::Controller::Web; use Dwarf::Pragma; use parent 'App::Controller::WebBase'; use Dwarf::DSL; sub get { render 'index.html'; } 1; # DESCRIPTION Dwarf ã¯å°è¦æ¨¡ã‚°ãƒ«ãƒ¼ãƒ—(1〜5人)å‘ã‘ Plack ベースã®ã‚¦ã‚§ãƒ–アプリケーションフレームワークã§ã™ã€‚<br /> - ã‚る程度ã®ä½œæ¥å˜ä½ (モジュールå˜ä½) ã§åˆ†æ¥ãŒã—易ㄠ- è¨è¨ˆã®ç¾Žã—ã•ã‚ˆã‚Šã€ç°¡æ½”性ã¨åˆ©ä¾¿æ€§ã‚’é‡è¦– ã¨ã„ã£ãŸç‰¹å¾´ãŒã‚ã‚Šã¾ã™ã€‚<br /> <br /> Catalyst ã«æ¯”ã¹ã‚‹ã¨ã‹ãªã‚Šè»½é‡ã€‚多ãã® Sinatraish 㪠WAF ã¨ç™ºæƒ³ã‚„è¦æ¨¡ã¯è¿‘ã„ãŒã‚¹ã‚¿ã‚¤ãƒ«ãŒç•°ãªã‚Šã¾ã™ã€‚ ## プãƒã‚¸ã‚§ã‚¯ãƒˆåˆæœŸåŒ– % dwarf hello_world ã‚‚ã—ã㯠% dir=${dwarf_cloned_dir} % mkdir hello_world % perl ${dir}/bin/dwarf --share_dir=${dir}/share --output=. hello_world created ./hello_world/app created ./hello_world/htdocs ## èµ·å‹• デフォルトã§ã¯ plackup ã§èµ·å‹•ã—ã¾ã™ã€‚<br /> オプション -m ã« production ã¨æŒ‡å®šã™ã‚‹ã“ã¨ã§ starman ã§èµ·å‹•ã—ã¾ã™ã€‚<br /> ã“ã®èµ·å‹•ã‚¹ã‚¯ãƒªãƒ—トã¯è‡ªç”±ã«ç·¨é›†ã—ã¦ä½¿ã‚れるã“ã¨ã‚’想定ã—ã¦ã„ã¾ã™ã€‚ % cd hello_world/app % ./script/start_searver.sh ## プãƒã‚¸ã‚§ã‚¯ãƒˆæ§‹é€ Dwarf ã¯ã€Œãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆæ¯Žã«ä½¿ã„æ¨ã¦ã‚‹ã€ã¨ã„ã†æ€æƒ³ã§ä½œã‚‰ã‚Œã¦ã„ã¾ã™ã€‚<br /> よã£ã¦ãƒ•ãƒ¬ãƒ¼ãƒ ワーク本体もãƒãƒ¼ã‚«ãƒ«ã«ç½®ã‹ã‚Œã‚‹ã®ãŒç‰¹å¾´ã§ã™ã€‚ app/ app.psgi ... PSGI ファイル cli.psgi ... コマンドラインツール用 PSGI ファイル cpanfile ... cpanfile Makefile ... Make ファイル lib/ ... プãƒã‚°ãƒ©ãƒ 本体 App.pm ... アプリケーションクラス App/ Config/ ... è¨å®šãƒ•ã‚¡ã‚¤ãƒ« Constant.pm ... 定数定義 DB.pm ... Teng ã®ã‚µãƒ–クラス DB/ Schema.pm ... スã‚ーマクラス Controller/ ... コントãƒãƒ¼ãƒ© Api/ ... JSON ã‚„ XML を返㙠API 用コントãƒãƒ¼ãƒ© ApiBase.pm ... API 用コントãƒãƒ¼ãƒ©ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ Cli/ ... コマンドラインツール用コントãƒãƒ¼ãƒ© CliBase.pm ... コマンドラインツール用コントãƒãƒ¼ãƒ©ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ Web/ ... HTML を返㙠Web ページ用コントãƒãƒ¼ãƒ© WebBase.pm ... Web ページ用コントãƒãƒ¼ãƒ©ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ Model/ ... モデル Test.pm ... テストクラス Util/ ... ユーティリティクラス Dwarf.pm ... Dwarf 本体 Dwarf/ script/ ... コマンドラインツール sql/ ... SQL t/ ... テスト tmpl/ ... HTML ã®ãƒ†ãƒ³ãƒ—レート htdocs/ ... ドã‚ュメントルート ## è¨å®šãƒ•ã‚¡ã‚¤ãƒ« è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã¯ Perl オブジェクトã§è¨˜è¿°ã—ã¾ã™ã€‚<br /> デフォルトã§è¨˜è¿°ã•ã‚Œã¦ã„ã‚‹é …ç›®ä»¥å¤–ã«ã¤ã„ã¦ã¯è‡ªç”±ã«ç·¨é›†ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚<br /> package App::Config::Production; use Dwarf::Pragma; use parent 'Dwarf::Config'; sub setup { my $self = shift; return ( db => { master => { dsn => 'dbi:Pg:dbname=hello_world', username => 'www', password => '', opts => { pg_enable_utf8 => 1 }, }, }, ssl => 1, # SSL をサãƒãƒ¼ãƒˆã™ã‚‹ã‹ã©ã†ã‹ url => { base => 'http://hello_world.com', ssl_base => 'https://hello_world.com', }, dir => { }, filestore => { private_dir => $self->c->base_dir . "/../data", public_dir => $self->c->base_dir . "/../htdocs/data", public_uri => "/data", }, app => { facebook => { id => '', secret => '', }, twitter => { id => '', secret => '', } }, ); } 1; ## ルーティング Dwarf 㯠Router::Simple を使ã£ã¦ãƒ«ãƒ¼ãƒ†ã‚£ãƒ³ã‚°ã‚’実装ã—ã¦ã„ã¾ã™ã€‚ デフォルトã®ãƒ«ãƒ¼ãƒ†ã‚£ãƒ³ã‚°ã¯ Dwarf.pm ã«å®Ÿè£…ã•ã‚Œã¦ã„ã¾ã™ã€‚ sub add_routes { my $self = shift; $self->router->connect("/api/*", { controller => "Api" }); $self->router->connect("/cli/*", { controller => "Cli" }); $self->router->connect("*", { controller => "Web" }); } å¤‰æ›´ã‚„è¿½åŠ ã‚‚å‡ºæ¥ã¾ã™ã€‚App.pm ã«å®Ÿè£…ã—ã¾ã™ã€‚ before add_routes => sub { my $self = shift; $self->router->connect("/images/{id:-?[0-9]+}", { controller => "Web::Images::Detail" }); }; ### splat ã«ã¤ã„㦠Router::Simple ã® match メソッドã«ã‚ˆã£ã¦å¾—られãŸã‚ªãƒ–ジェクトã®è¦ç´ ã« splat ãŒå˜åœ¨ã™ã‚‹å ´åˆã€Dwarf 㯠Controller åã®æœ«å°¾ã« splat ã‚’è¿½åŠ ã—ã¾ã™ã€‚ 例) /api/ping ã¸ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆ App::Controller::Api::Ping ã¸ãƒ«ãƒ¼ãƒ†ã‚£ãƒ³ã‚°ã•ã‚Œã¾ã™ã€‚ ``` { controller => 'Api', splat => ['ping'] } ``` ## コントãƒãƒ¼ãƒ© Dwarf ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã¯ãƒ‡ã‚£ã‚¹ãƒ‘ッãƒã•ã‚Œã¦ããŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å‘¼å¿œã™ã‚‹ãŸã‚ã®ãƒã‚¸ãƒƒã‚¯ã‚’実装ã™ã‚‹ã‚¯ãƒ©ã‚¹ã§ã™ã€‚<br /> 一般的㪠MVC フレームワークã®ã‚ˆã†ã«ãƒ¢ãƒ‡ãƒ«ã‚„ビューをæ“作ã™ã‚‹ã“ã¨ã«çµ‚æ¢ã™ã‚‹ã‚¯ãƒ©ã‚¹ã¨ã¯å°‘ã—é•ã„ã¾ã™ã€‚<br /> <br /> 例ãˆã°ã€WEB ページを表示ã™ã‚‹ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã®å ´åˆã€DB ã‹ã‚‰æƒ…å ±ã‚’å–ã£ã¦ãã¦åŠ å·¥ã™ã‚‹æ“作やビューã«æ¸¡ã™ãƒ‡ãƒ¼ã‚¿ã®åŠ å·¥ãªã©ã®ãƒã‚¸ãƒƒã‚¯ã¯å…¨ã¦ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã«å®Ÿè£…ã—ã¾ã™ã€‚<br /> ### ä½œæˆ /login ã§ã‚¢ã‚¯ã‚»ã‚¹ã•ã‚Œã‚‹ WEB ページ用ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã‚’作æˆã™ã‚‹ % ./script/generate.pl Controller::Web::Login ### 実装 GET ã§ãƒã‚°ã‚¤ãƒ³ãƒ•ã‚©ãƒ¼ãƒ を表示ã—ã€POST ã§èªè¨¼ãƒã‚¸ãƒƒã‚¯ã‚’実装ã™ã‚‹ package App::Controller::Web::Login; use Dwarf::Pragma; use parent 'App::Controller::WebBase'; use Dwarf::DSL; use Class::Method::Modifiers; # ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã®å®Ÿè£…例。validate ã¯ä½•åº¦ã§ã‚‚呼ã¹ã‚‹ã€‚ # will_dispatch 終了時ã«ã‚¨ãƒ©ãƒ¼ãŒã‚れ㰠receive_error ãŒå‘¼ã³å‡ºã•ã‚Œã‚‹ã€‚ after will_dispatch => sub { if (method eq 'POST') { self->validate( username => [qw/NOT_NULL/], password => [qw/NOT_NULL/], ); } }; sub get { render 'login.html'; } sub post { model('Auth')->authenticate(param('username'), param('password')) or unauthorized; redirect '/'; } ### REST çš„ãªãƒªã‚¯ã‚¨ã‚¹ãƒˆã®ç½®ãå ´æ‰€ å˜ãªã‚‹ç¿’æ…£ã§ã‚ã£ã¦ãƒ«ãƒ¼ãƒ«ã§ã¯ã‚ã‚Šã¾ã›ã‚“。 `Resources` コントãƒãƒ¼ãƒ©ã«ã¯ä»¥ä¸‹ã‚’é…置。 ``` GET /api/resources => 一覧 POST /api/resources => ä½œæˆ ``` `Resoures::Detail` コントãƒãƒ¼ãƒ©ã«ã¯ä»¥ä¸‹ã‚’é…置。 ``` GET /api/resources/{id} => 詳細 PUT /api/resources/{id} => ç½®ãæ›ãˆ PATCH /api/resources/{id} => 部分置ãæ›ãˆ DELETE /api/resources/{id} => 削除 ``` `App.pm` ã«ãƒ«ãƒ¼ãƒ†ã‚£ãƒ³ã‚°ã‚’è¿½åŠ ã™ã‚‹ ``` before add_routes => sub { my $self = shift; $self->router->connect("/api/resources/{id:[0-9]+}", { controller => "Api::Resources::Detail" }); }; ``` ## モデル Dwarf ã®ãƒ¢ãƒ‡ãƒ«ã¯è¤‡æ•°ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã§å…±ç”¨ã•ã‚Œã‚‹ã‚ˆã†ãªãƒã‚¸ãƒƒã‚¯ã‚’汎用化ã—ã¦å®Ÿè£…ã™ã‚‹ãŸã‚ã®ã‚¯ãƒ©ã‚¹ã§ã™ã€‚ ### ä½œæˆ model('Auth') ã§å‘¼ã°ã‚Œã‚‹ãƒ¢ãƒ‡ãƒ«ã‚’作æˆã™ã‚‹ % ./script/generate.pl Model::Auth ### 実装 package App::Model::Auth; use Dwarf::Pragma; use parent 'Dwarf::Module'; use Dwarf::DSL; use App::Constant; use Dwarf::Accessor qw/member/; sub is_login { session->param('member') or return false; return true; } sub authenticate { my ($self, $username, $password) = @_; if (my $member = db->single('members', { username => $username, password => $password }) { self->member($member); self->login; return TRUE; } return false; } sub login { c->refresh_session; session->param(member => { id => self->member->id, email => self->member->email, nickname => self->member->nickname, }); session->flush; } sub logout { session->param(member => {}); session->flush; return true; } 1; ## アプリケーションクラス App (based on Dwarf) = アプリケーションクラス + コンテã‚ストクラス + ディスパッãƒãƒ£ãƒ¼ã‚¯ãƒ©ã‚¹<br /> <br /> コントãƒãƒ¼ãƒ©ã‚„モデルã«æ¸¡ã•ã‚Œã‚‹ $c ã¯ã‚³ãƒ³ãƒ†ã‚ストオブジェクトã§ã‚ã‚‹ãŒã€Dwarf ã®å ´åˆã¯ã‚¢ãƒ—リケーションクラスã§ã‚‚ã‚る。è¨è¨ˆçš„ã«ã¯ã‚ã¾ã‚Šç¾Žã—ããªã„ãŒã€ãƒ•ãƒ¬ãƒ¼ãƒ ワークã®å®Ÿè£…をシンプルã«ã™ã‚‹ãŸã‚ã«ã“ã®ã‚ˆã†ã«ãªã£ã¦ã„る。<br /> ### è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã®èªã¿è¾¼ã¿ 手元ã®é–‹ç™ºç’°å¢ƒã§å‹•ã‹ã™å ´åˆãªã©è¤‡æ•°ã®ç’°å¢ƒã§å‹•ã‹ã™ã“ã¨ã‚’想定ã—ã¦ã€ç’°å¢ƒæ¯Žã«é•ã†è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’èªã¿è¾¼ã‚€ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚<br /> - production ã¨ã„ã†ã‚ーã«æœ¬ç•ªç”¨ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«åを渡ã—ã¾ã™ã€‚ - development ã¨ã„ã†ã‚ーã«é–‹ç™ºç”¨ã®è¨å®šã¤ã„ã¦ã®é…列リファレンスを渡ã—ã¾ã™ã€‚ - é…列リファレンスã«ã¯ã€è¨å®šãƒ•ã‚¡ã‚¤ãƒ«åã‚’ã‚ーã«ã€ç’°å¢ƒã®å®šç¾©ã‚’値ã«ã—ãŸãƒãƒƒã‚·ãƒ¥ã‚’渡ã—ã¾ã™ã€‚ - 上ã‹ã‚‰é †ã«æ“作ã—ã¦ã„ãã€æœ€åˆã«ãƒžãƒƒãƒã—ãŸç’°å¢ƒã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ãŒé©ç”¨ã•ã‚Œã¾ã™ã€‚ 環境ã®å®šç¾©ã«ã¯ãƒ›ã‚¹ãƒˆåã«ãƒžãƒƒãƒã•ã›ãŸã„æ–‡å—列ã‹ã€ç’°å¢ƒã‚’定義ã—ãŸãƒãƒƒã‚·ãƒ¥ãƒªãƒ•ã‚¡ãƒ¬ãƒ³ã‚¹ã‚’指定ã—ã¾ã™ã€‚<br /> $self->load_plugins( 'MultiConfig' => { production => 'Production', development => [ 'Staging' => { host => 'hello_world.s2factory.co.jp', # ホストå dir => '/proj/www/hello_world_stg' # アプリケーションディレクトリã®ä½ç½® }, 'Development' => 'hello_world.s2factory.co.jp', 'Seagirl' => 'seagirl.local', ], }, ); ### 処ç†ã®æµã‚Œ - 1. BEFORE\_DISPATCH トリガーã®å®Ÿè¡Œ (Dwarf ã¯ãªã«ã‚‚ã—ãªã„) - 2. Router::Simple を使ã£ã¦ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã¨ãƒ¡ã‚½ãƒƒãƒ‰ã‚’探索 - 3. コントãƒãƒ¼ãƒ©ã®ç”Ÿæˆ - 4. メソッドを実行 - 5. AFTER\_DISPATCH トリガーã®å®Ÿè¡Œ (decode\_json ãªã©ãŒè¡Œã‚れる) - 6. ãƒ•ã‚¡ã‚¤ãƒŠãƒ©ã‚¤ã‚ºå‡¦ç† ($self->response->finalize) ### プãƒãƒ‘ティ ro => [qw/namespace base_dir env config error request response router handler handler_class models state is_production is_cli/], rw => [qw/stash request_handler_prefix request_handler_method/], ### ショートカット param (= $self->request->param) conf (= $self->config->get / $self->config->set) req (= $self->request) method (= $self->request->method) res (= $self->response) status (= $self->response->status) type (= $self->response->content_type) body (= $self->response->body) ### メソッド #### dump Data::Dumper->Dump([@_]) ã®ãƒ©ãƒƒãƒ‘ー #### to\_psgi PSGI アプリケーションを返ã—ã¾ã™ã€‚ #### finish ($self, $body) ç›´ã¡ã«ãƒ¬ã‚¹ãƒãƒ³ã‚¹ã‚’è¿”ã—ã¾ã™ã€‚ #### redirect ç›´ã¡ã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã—ã¾ã™ã€‚ #### not\_found ç›´ã¡ã« 404 Not Found ã‚’è¿”ã—ã¾ã™ã€‚ #### unauthorized ç›´ã¡ã« 401 UNAUTHORIZED ã‚’è¿”ã—ã¾ã™ã€‚ #### load\_plugins ($self, %args) プラグインをèªã¿è¾¼ã¿ã¾ã™ã€‚ ## リクエストã¨ãƒ¬ã‚¹ãƒãƒ³ã‚¹ ### Dwarf::Request Dwarf::Request 㯠Plack::Request ã®ãƒ©ãƒƒãƒ‘ーã§ã™ã€‚<br/> Plack::Request ã¨ã®é•ã„ã¯ä»¥ä¸‹ã®é€šã‚Šã§ã™ã€‚ - リストコンテã‚スト㧠param メソッドを呼んã§ã‚‚å¿…ãšã‚¹ã‚«ãƒ©ãƒ¼å€¤ãŒè¿”ã‚‹ - å…¨ã¦ã®ãƒ‘ラメータ㌠decode_utf8 ã•ã‚Œã‚‹ - é…列型ã®ã‚µãƒãƒ¼ãƒˆ (param メソッドãŒé…列リファレンスを返ã™) ### é…列型ã®ã‚µãƒãƒ¼ãƒˆ \[\] を使ã£ãŸãƒ‘ラメーターåを用ã„ã‚‹ã“ã¨ã§ã€é…列型をサãƒãƒ¼ãƒˆã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ # hoge[]=1&hoge[]=2&hoge[]=3 my $array = param('hoge[]'); # $array is [1, 2, 3] ### Dwarf::Response Dwarf::Response 㯠Plack::Response ã®ãƒ©ãƒƒãƒ‘ーã§ã™ã€‚ ## フォームãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ Dwarf::Validator 㯠FormValidator::Lite ã«ä¼¼ãŸãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã§ã™ã€‚<br /> FormValidator::Lite ã¨ã®é•ã„ã¯ä»¥ä¸‹ã®é€šã‚Šã§ã™ã€‚ - エラーメッセージ周りã®æ©Ÿèƒ½ã‚’削除 - NOT_NULL 㨠NOT_BLANK ã®å·®åˆ¥åŒ– - フィルター機能ã®å¼·åŒ– ### エラーメッセージ周りã®æ©Ÿèƒ½ã‚’削除 Dwarf ã§ã¯ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ HTML テンプレートã«åŸ‹ã‚込むスタイルをå–ã‚‹ãŸã‚ã€ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸é–¢é€£ã®æ©Ÿèƒ½ã¯ã‚ã‚Šã¾ã›ã‚“。 ### NOT_NULL 㨠NOT_BLANK ã®å·®åˆ¥åŒ– - NOT_NULL … undef ã®ã¿ä¸å¯ (空文å—を許å¯) - NOT_BLANK … undef も空文å—ã‚‚ä¸å¯ ### フィルター機能ã®å¼·åŒ– デフォルトã§ä»¥ä¸‹ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ãŒå®Ÿè£…ã•ã‚Œã¦ã„ã¾ã™ã€‚ - DEFAULT - TRIM - DECODE_UTF8 - ENCODE_UTF8 - NLE (Normalize Line Encodings) ``` self->validate( 'offset' => [[DEFAULT => 0], qw/NOT_NULL UINT/], 'limit' => [[DEFAULT => 100], qw/NOT_NULL UINT/, [qw/BETWEEN 0 5000/]], ); db->search(‘posts’, {}, { offset => param(‘offset’), limit => param(‘limit') }); ``` ã¾ãŸã€FormValidator::Lite::Constraints ã® rule 関数ã¨åŒã˜æ„Ÿã˜ã§ filter 関数を使ã£ã¦ç°¡å˜ã«ã‚«ã‚¹ã‚¿ãƒ フィルターを定義出æ¥ã¾ã™ã€‚ filter DEFAULT => sub { my ($value, $args, $opts) = @_; unless ($value) { $value = $args->[0]; } $value; }; ## メソッドã®å¼•æ•°ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ Dwarf::Module ã§ã¯ Data::Validator を使ã£ãŸå¼•æ•°ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’ç°¡å˜ã«è¨˜è¿°ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ Data::Validator ã®æ›¸å¼ã«åŠ ãˆã€æ–‡å—列ã§åž‹ã‚’書ãスタイルもサãƒãƒ¼ãƒˆã•ã‚Œã¾ã™ã€‚ æ–‡å—列 | ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãƒ«ãƒ¼ãƒ« ---------- | -------------------------------------------------- 'Str' | { isa => 'Str' } 'Int? = 0' | { isa => 'Int', optional => 1, default => '0' } ``` sub create_member { my $args = args { name => 'Str', age => 'Int', gender => 'Int? = 0' }, @_; ... } self->create_member({ name => 'Taro Yamada', age => 35 }); ``` ## JSON ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ validate_json_body を使ã£ã¦ POST ã•ã‚ŒãŸ JSON データã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãŒè¡Œãˆã¾ã™ã€‚ ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãƒ«ãƒ¼ãƒ«ã®å®šç¾©ã®ä»•æ–¹ã¯ãƒ¡ã‚½ãƒƒãƒ‰ã®å¼•æ•°ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã¨åŒã˜ã§ã™ã€‚ ``` after will_dispatch => sub { self->validate( id => [qw/NOT_BLANK UINT/], ); self->validate_json_body( name => 'Str', tel => 'JTel', ); }; ``` #### 複雑ãªã‚ªãƒ–ジェクトをãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã™ã‚‹ä¾‹ rules ã¨ã„ã†ã‚ーã«ãƒ«ãƒ¼ãƒ«ã‚’渡ã™ã“ã¨ã§ã€HashRef ã‚„ ArrayRef[HashRef] ã«é–¢ã™ã‚‹ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãƒ«ãƒ¼ãƒ«ã‚’記述ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ ã“ã‚Œã«ã‚ˆã£ã¦ã€è¤‡é›‘ãªã‚ªãƒ–ジェクトをãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ ``` sub ITEM_TYPE { return { id => 'Int', code => 'Int', name => 'Str', name_kana => 'Str', }; } sub ITEM_TYPE_TYPE { return { id => 'Int', name => 'Str', gender => 'Int', }; } sub TEXTURE_TYPE { return { id => 'Int', code => 'Str', maker => 'Int', price1 => 'Int?', price2 => 'Int?', price3 => 'Int?', }; } sub ORDER_ITEM_TYPE { return { id => 'Int', quantity => 'Int', item => { isa => 'HashRef', rules => ITEM_TYPE() }, item_type => { isa => 'HashRef', rules => ITEM_TYPE_TYPE() }, texture => { isa => 'HashRef', rules => TEXTURE_TYPE() }, }; } after will_dispatch => sub { self->validate_json_body( order_items => { isa => 'ArrayRef[HashRef]', rules => ORDER_ITEM_TYPE() } ); }; ``` #### 独自型ã®å®Ÿè£…例㯠Dwarf::Plugin::MouseX::Types::Common ``` subtype URL => as 'Str' => where { $_ =~ /($RE{URI}{HTTP}{-scheme =>'(https|http)'})/o }; subtype Email => as 'Str' => where { Email::Valid::Loose->address(encode_utf8 $_) }; ``` ## モジュール Dwarf::Module ã¯ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã‚„モデルã®æ ¹åº•ã‚¯ãƒ©ã‚¹ã€‚<br /> <br /> Dwarf ã§ã¯ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«å˜ä½ã§ä½œæ¥ã‚’切り分ã‘ã‚‹ã¨ã„ã†æ–¹é‡ã§è¨è¨ˆã•ã‚Œã¦ã„る。ã¾ãŸãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’実装ã™ã‚‹ã“ã¨ãŒå³ã¡ã‚¢ãƒ—リケーションを実装ã™ã‚‹ã“ã¨ã«ãªã‚‹ã®ã§ã€ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã§ã‚ã‚ã†ãŒãƒ¢ãƒ‡ãƒ«ã§ã‚ã‚ã†ãŒãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‹ã‚‰ã¯å…¨ã¦åŒã˜ã‚„ã‚Šæ–¹ã§ãƒ•ãƒ¬ãƒ¼ãƒ ワークã®æƒ…å ±ã‚’å‚照出æ¥ã‚‹ã‚ˆã†ã«ãªã£ã¦ã„る。 ### プãƒãƒ‘ティ #### context App.pm ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ ### ショートカット self (= $self) app (= $self->context) c (= $self->context) m (= $self->model) conf (= $self->context->config->get / $self->context->config->set) db (= $self->context->db) error (= $self->context->error) e (= $self->context->error) env (= $self->content->env) log (= $self->context->log) debug (= $self->context->log->debug) session (= $self->context->session) param (= $self->context->param) parameters (= $self->context->request->parameters) request (= $self->context->request) req (= $self->context->request) method (= $self->context->request->method) response (= $self->context->response) res (= $self->context->response) status (= $self->context->response->status) type (= $self->context->response->content_type) header (= $self->context->response->header) headers (= $self->context->response->headers) body (= $self->context->response->body) not_found (= $self->context->not_found) unauthorized (= $self->context->unauthorized) finish (= $self->context->finish) redirect (= $self->context->redirect) is_cli (= $self->context->is_cli) is_production (= $self->context->is_production) load_plugin (= $self->context->load_plugin) load_plugins (= $self->context->load_plugins) render (= $self->context->render) dump (= $self->context->dump) use Dwarf::DSL ã™ã‚‹ã“ã¨ã§ä¸Šè¨˜ã®ã‚·ãƒ³ã‚¿ãƒƒã‚¯ã‚¹ã‚·ãƒ¥ã‚¬ãƒ¼ã‚’ DSL ã¨ã—ã¦å‘¼ã¶ã“ã¨ãŒã§ãã¾ã™ã€‚ ### メソッド #### init ($self, $c) モジュール作æˆæ™‚ã«å‘¼ã³å‡ºã•ã‚Œã‚‹åˆæœŸå‡¦ç†ç”¨ã®ãƒ†ãƒ³ãƒ—レートメソッド #### model ($self, $package, @\_) $self->c->models ã«ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒå˜åœ¨ã—ãªã‘れ㰠create\_model を呼んã§ãƒ¢ãƒ‡ãƒ«ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’作æˆã—ã¾ã™ã€‚ #### create\_model ($self, $package, @\_) モデルã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’作æˆã—ã€ãƒ¢ãƒ‡ãƒ«ã‚¯ãƒ©ã‚¹ã® init メソッドを呼ã³ã¾ã™ã€‚ 残りã®å¼•æ•°ã¯ãƒ¢ãƒ‡ãƒ«ã‚¯ãƒ©ã‚¹ã® new ã«æ¸¡ã•ã‚Œã¾ã™ã€‚ 返り値ã«ã¯ä½œæˆã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒè¿”ã‚Šã¾ã™ã€‚ ## Dwarf モジュール ### Dwarf::Module::APIBase API 用ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã‚’実装ã™ã‚‹ãŸã‚ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ - validate - will\_dispatch - will\_render - did\_render - receive\_error - receive\_server\_error ### Dwarf::Module::HTMLBase Web ページ用ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã‚’実装ã™ã‚‹ãŸã‚ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ - validate - will\_dispatch - will\_render - did\_render - receive\_error - receive\_server\_error ### Dwarf::Module::CLIBase CLI 用ã®ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã‚’実装ã™ã‚‹ãŸã‚ã®ãƒ™ãƒ¼ã‚¹ã‚¯ãƒ©ã‚¹ - receive\_error - receive\_server\_error ### Dwarf::Module::SocialMedia::Twitter ### Dwarf::Module::SocialMedia::Faceboo ### Dwarf::Module::SocialMedia::Mixi ### Dwarf::Module::SocialMedia::Weibo Twitter/Facebook/Mixi/Weibo å„種 API を扱ã†ãŸã‚ã®ã‚¯ãƒ©ã‚¹ ## テスト App::Test を使ã£ã¦ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ãƒ¼ã®ãƒ†ã‚¹ãƒˆã‚’書ãã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ ``` use App::Test; my $t = App::Test->new(will_decode_content => 1); my $c = $t->context; my ($req, $res); ($req, $res) = $t->req_ok(GET => "http://localhost/api/posts"); ($req, $res) = $t->req_ok(POST => "http://localhost/api/posts", { name => "Takuho Yoshizu", }); ($req, $res) = $t->req_ok( POST => "http://localhost/api/images", Content_Type => 'form-data', Content => { 'image[]' => [ $c->base_dir . '/t/03_app/file/image.jpg' ] } ); ``` ## エラー Dwarf ã§ã¯ 2 種類ã®ã‚¨ãƒ©ãƒ¼ã‚’扱ã†ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ - Dwarf ã®ã‚¨ãƒ©ãƒ¼ (ERROR) - Perl ã®ã‚¨ãƒ©ãƒ¼ (SERVER\_ERROR) ## Dwarf::Error Dwarf::Error 㯠Dwarf ã®ã‚¨ãƒ©ãƒ¼ã‚’å–り扱ã†ãŸã‚ã®ã‚¯ãƒ©ã‚¹ã§ã™ã€‚ Dwarf::Error ã¯è¤‡æ•°ã® Dwarf::Message::Error ã‚’ä¿æŒã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ ### プãƒãƒ‘ティ #### autoflush ã“ã®ãƒ•ãƒ©ã‚°ã‚’ true ã«ã™ã‚‹ã¨ throw ãŒå‘¼ã°ã‚ŒãŸæ™‚ã«è‡ªå‹•çš„ã« flush ãŒå‘¼ã°ã‚Œã¾ã™ã€‚ デフォルト㯠false。 #### messages Dwarf::Message::Error オブジェクトã®é…列ã§ã™ã€‚ ### メソッド #### throw エラーメッセージを作æˆã—ã€ã‚¨ãƒ©ãƒ¼ã‚’é€å‡ºã—ã¾ã™ã€‚ autoflush ㌠true ãªå ´åˆã¯ã€flush を呼ã³å‡ºã—ã¾ã™ã€‚ #### flush é€å‡ºã•ã‚ŒãŸã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’実際ã«ãƒ•ãƒ¬ãƒ¼ãƒ ワークã«å‡ºåŠ›ã—ã¾ã™ã€‚ ## Dwarf::Message::Error Dwarf ã®ã‚¨ãƒ©ãƒ¼å€‹ã€…ã®å†…容を示ã™ã‚¯ãƒ©ã‚¹ã§ã™ã€‚ ### プãƒãƒ‘ティ #### data ã‚¨ãƒ©ãƒ¼ãƒ‡ãƒ¼ã‚¿ã‚’æ ¼ç´ã™ã‚‹é…列リファレンスã§ã™ã€‚ Dwarf::Error ã® flush メソッドã«æ¸¡ã•ã‚ŒãŸå¼•æ•°ãŒãã®ã¾ã¾ data ã«æ¸¡ã•ã‚Œã¾ã™ã€‚ my $m = Dwarf::Message::Error->new; $m->data([@_]); ## エラーã®é€å‡º Dwarf ã®ã‚¨ãƒ©ãƒ¼ã‚’出力ã™ã‚‹ã«ã¯ã€Error クラス㮠throw メソッドを使用ã—ã¾ã™ã€‚ $c->error->throw(400, "Something wrong."); Dwarf::Plubin::Error ã‚’èªã¿è¾¼ã‚€ã“ã¨ã§ã‚¨ãƒ©ãƒ¼ã‚¯ãƒ©ã‚¹ã«ã‚·ãƒ§ãƒ¼ãƒˆã‚«ãƒƒãƒˆã‚’作æˆã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ $c->load_plugins( 'Error' => { LACK_OF_PARAM => sub { shift->throw(1001, sprintf("missing mandatory parameters: %s", $_[0] || "")) }, INVALID_PARAM => sub { shift->throw(1002, sprintf("illegal parameter: %s", $_[0] || "")) }, NEED_TO_LOGIN => sub { shift->throw(1003, sprintf("You must login.")) }, SNS_LIMIT_ERROR => sub { shift->throw(2001, sprintf("SNS Limit Error: reset at %s", $_[0] || "")) }, SNS_ERROR => sub { shift->throw(2002, sprintf("SNS Error: %s", $_[0] || "SNS Error.")) }, ERROR => sub { shift->throw(400, sprintf("%s", $_[0] || "Unknown Error.")) }, } ); モジュールã®ä¸ã§å®Ÿéš›ã«å‘¼ã³å‡ºã™å ´åˆã«ã¯ã€æ›¸ãã®ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ e->LACK_OF_PARAM('user_id'); # $c->error->LACK_OF_PARAM('user_id'); ## エラーãƒãƒ³ãƒ‰ãƒªãƒ³ã‚° 二ã¤ã®ã‚¨ãƒ©ãƒ¼ã«å¯¾å¿œã™ã‚‹ãƒˆãƒªã‚¬ãƒ¼ã‚’登録ã™ã‚‹ã“ã¨ã§ã‚’エラーをãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ $c->add_trigger(ERROR => sub { warn @_ }); $c->add_trigger(SERVER_ERROR => sub { warn @_ }; トリガーãŒä¸€ã¤ã‚‚登録ã•ã‚Œã¦ã„ãªã„å ´åˆã¯ã€Dwarf.pm ã® receive\_error メソッドãŠã‚ˆã³ receive\_server\_error メソッドãŒå‘¼ã³å‡ºã•ã‚Œã¾ã™ã€‚ sub receive_error { die $_[1] } sub receive_server_error { die $_[1] } ## APIBase.pm ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã¨ã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚° APIBase ã® validate メソッド㯠FormValidator::Lite ã® check メソッドã®ãƒ©ãƒƒãƒ‘ーã«ãªã£ã¦ãŠã‚Šã€ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã‚¨ãƒ©ãƒ¼ã‚’検知ã—ãŸå ´åˆã« Dwarf ã®ã‚¨ãƒ©ãƒ¼ã‚’é€å‡ºã—ã¾ã™ã€‚ã¾ãŸã€APIBase ã§ã¯ Dwarf::Error ã® autoflush ã‚’ true ã«ã‚»ãƒƒãƒˆã™ã‚‹ãŸã‚ã€ã‚¨ãƒ©ãƒ¼ãŒé€å‡ºã•ã‚Œã‚‹ã¨ãŸã ã¡ã« receive\_error メソッドã«å‡¦ç†ãŒç§»ã‚Šã¾ã™ã€‚ sub validate { my ($self, @rules) = @_; return unless @rules; my $validator = S2Factory::Validator->new($self->c->req)->check(@rules); if ($validator->has_error) { while (my ($param, $detail) = each %{ $validator->errors }) { $self->c->error->LACK_OF_PARAM($param) if $detail->{NOT_NULL}; $self->c->error->INVALID_PARAM($param); } } } APIBase ã§ã¯ã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ç”¨ã®ãƒˆãƒªã‚¬ãƒ¼ãŒã‚らã‹ã˜ã‚登録ã•ã‚Œã¦ã„ã¾ã™ã€‚サブクラスã§ä¸‹è¨˜ã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’オーãƒãƒ©ã‚¤ãƒ‰ã™ã‚‹ã“ã¨ã§æŒ¯ã‚‹èˆžã„を変ãˆã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ # 400 ç³»ã®ã‚¨ãƒ©ãƒ¼ sub receive_error { my ($self, $c, $error) = @_; my (@codes, @messages); for my $m (@{ $error->messages }) { warn sprintf "API Error: code = %s, message = %s", $m->data->[0], $m->data->[1]; push @codes, $m->data->[0]; push @messages, $m->data->[1]; } my $data = { error_code => @codes == 1 ? $codes[0] : \@codes, error_message => @messages == 1 ? $messages[0] : \@messages, }; return $data; } # 500 ç³»ã®ã‚¨ãƒ©ãƒ¼ sub receive_server_error { my ($self, $c, $error) = @_; $error ||= 'Internal Server Error'; my $data = { error_code => 500, error_message => $error, }; return $data; } ## HTMLBase.pm ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã¨ã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚° HTMLBase ã® validate メソッド㯠FormValidator::Lite ã® check メソッドã®ãƒ©ãƒƒãƒ‘ーã«ãªã£ã¦ãŠã‚Šã€ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã‚¨ãƒ©ãƒ¼ã‚’検知ã—ãŸå ´åˆã« Dwarf ã®ã‚¨ãƒ©ãƒ¼ã‚’é€å‡ºã—ã¾ã™ã€‚ã¾ãŸã€HTMLBase ã§ã¯ Dwarf::Error ã® autoflush ã‚’ false ã«ã‚»ãƒƒãƒˆã™ã‚‹ãŸã‚ã€ã‚¨ãƒ©ãƒ¼ãŒé€å‡ºã•ã‚Œã¦ã‚‚ flush メソッドãŒå‘¼ã°ã‚Œã‚‹ã¾ã§ receive\_error メソッドã«å‡¦ç†ãŒç§»ã‚Šã¾ã›ã‚“。HTMLBase ã§ã¯ will\_dispatch メソッドã®å®Ÿè¡Œå¾Œã« flush メソッドを呼ã³å‡ºã—ã¾ã™ã€‚ãã®ãŸã‚ã€ã‚³ãƒ³ãƒˆãƒãƒ¼ãƒ©ã®å®Ÿè£…時ã«ã¯ will\_dispatch メソッドã®ä¸ã§ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’è¡Œã„ã¾ã™ã€‚ sub validate { my ($self, @rules) = @_; return unless @rules; my $validator = S2Factory::Validator->new($self->req)->check(@rules); if ($validator->has_error) { while (my ($param, $detail) = each %{ $validator->errors }) { $self->error->LACK_OF_PARAM($param, $detail) if $detail->{NOT_NULL}; $self->error->INVALID_PARAM($param, $detail); } } } HTMLBase ã§ã¯ã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ç”¨ã®ãƒˆãƒªã‚¬ãƒ¼ãŒã‚らã‹ã˜ã‚登録ã•ã‚Œã¦ã„ã¾ã™ã€‚サブクラスã§ä¸‹è¨˜ã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’オーãƒãƒ©ã‚¤ãƒ‰ã™ã‚‹ã“ã¨ã§æŒ¯ã‚‹èˆžã„を変ãˆã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ # 400 ç³»ã®ã‚¨ãƒ©ãƒ¼ sub receive_error { my ($self, $c, $error) = @_; $self->{error_template} ||= '400.html'; $self->{error_vars} ||= $self->req->parameters->as_hashref; for my $message (@{ $error->messages }) { my $code = $message->data->[0]; my $param = $message->data->[1]; my $detail = $message->data->[2]; $self->{error_vars}->{error}->{$param} = merge_hash( $self->{error_vars}->{error}->{$param}, $detail ); } return $c->render($self->error_template, $self->error_vars); } # 500 ç³»ã®ã‚¨ãƒ©ãƒ¼ sub receive_server_error { my ($self, $c, $error) = @_; $self->{server_error_template} ||= '500.html'; $self->{server_error_vars} ||= { error => $error }; return $c->render($self->server_error_template, $self->server_error_vars); } WEB ページ実装時ã®ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã¨ã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ã®ä¾‹ package App::Controller::Web::Login; use Dwarf::Pragma; use parent 'App::Controller::WebBase'; use Dwarf::DSL; use Class::Method::Modifiers; # ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã®å®Ÿè£…例。validate ã¯ä½•åº¦ã§ã‚‚呼ã¹ã‚‹ã€‚ # will_dispatch 終了時ã«ã‚¨ãƒ©ãƒ¼ãŒã‚れ㰠receive_error ãŒå‘¼ã³å‡ºã•ã‚Œã‚‹ã€‚ sub will_dispatch { if (method eq 'POST') { self->validate( user_id => [qw/NOT_NULL UINT/, [qw/BETWEEN 1 8/]], password => [qw/NOT_NULL UINT/, [qw/BETWEEN 1 8/]], ); } }; # ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ãŒã‚¨ãƒ©ãƒ¼ã«ãªã£ãŸæ™‚ã«å‘¼ã³å‡ºã•ã‚Œã‚‹ï¼ˆå®šç¾©å…ƒ: Dwarf::Module::HTMLBase) # エラー表示ã«ä½¿ã†ãƒ†ãƒ³ãƒ—レートã¨å€¤ã‚’変更ã—ãŸã„時ã¯ã“ã®ãƒ¡ã‚½ãƒƒãƒ‰ã§å®Ÿè£…ã™ã‚‹ # ãƒãƒªãƒ‡ãƒ¼ã‚·ãƒ§ãƒ³ã®ã‚¨ãƒ©ãƒ¼ç†ç”±ã¯ã€self->error_vars->{error}->{PARAM_NAME} ã«ãƒãƒƒã‚·ãƒ¥ãƒªãƒ•ã‚¡ãƒ¬ãƒ³ã‚¹ã§æ ¼ç´ã•ã‚Œã‚‹ before receive_error => sub { self->{error_template} = 'login.html'; self->{error_vars} = parameters->as_hashref; }; sub get { render('login.html'); } sub post { my $user_id = param('user_id'); my $password = param('password') if (model('Auth')->authenticate($user_id, $password)) { model('Auth')->login; redirect '/'; } e->INVALID_PARAM(user_id => "INVALID"); e->INVALID_PARAM(password => "INVALID"); e->flush; } 1; エラー画é¢ã®ä¾‹ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>400 Bad Request</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content=""> <meta name="author" content=""> <!-- Le styles --> <link href="/dwarf/bootstrap/css/bootstrap.css" rel="stylesheet"> <style> body { padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */ } </style> <link href="/dwarf/bootstrap/css/bootstrap-responsive.css" rel="stylesheet"> </head> <body> <div class="container"> <h1>400 Bad Request</h1> : if $error { <!-- error messages --> <dl class="alert"> <dt>入力内容ã«ä¸å‚™ãŒã‚ã‚Šã¾ã—ãŸã€‚</dt> <dd> <ul> : if $error.category.UINT { <li>種別ã¯å¿…é ˆé …ç›®ã§ã™ã€‚é¸æŠžã—ã¦ãã ã•ã„。</li> : } : if $error.name.NOT_NULL { <li>商å“å・邸åã¯å¿…é ˆé …ç›®ã§ã™ã€‚入力ã—ã¦ãã ã•ã„。</li> : } : if $error.introduction.NOT_NULL { <li>紹介文ã¯å¿…é ˆé …ç›®ã§ã™ã€‚入力ã—ã¦ãã ã•ã„。</li> : } </ul> </dd> </dl> : } </div> <!-- /container --> </body> </html> ## Dwarf::Pragma use ã™ã‚‹ã¨åŸºæœ¬çš„ãªãƒ—ラグマをã¾ã¨ã‚ã¦ã‚»ãƒƒãƒˆã™ã‚‹ã‚·ãƒ§ãƒ¼ãƒˆã‚«ãƒƒãƒˆã®å½¹å‰²ã‚’ã™ã‚‹ã‚¯ãƒ©ã‚¹ã§ã™ã€‚ use strict; use warnings; use utf8; use feature '5.10'; use boolean; オプション㧠utf8 㨠feature ã®æŒ™å‹•ã¯å¤‰æ›´ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ sub import { my ($class, %args) = @_; $utf8 = 1 unless defined $args{utf8}; $feature = "5.10" unless defined $args{feature}; warnings->import; strict->import; boolean->import; boolean->export_to_level(1); if ($utf8) { utf8->import; } if ($feature ne 'legacy') { require 'feature.pm'; feature->import(":" . $feature); } } ## Dwarf::Accessor アクセサを作æˆã™ã‚‹ãŸã‚ã®ã‚¯ãƒ©ã‚¹ã§ã™ã€‚ ### Lazy Initialization 「\_build\_ + プãƒãƒ‘ティåã€ã¨ã„ã†ãƒ¡ã‚½ãƒƒãƒ‰ã‚’実装ã™ã‚‹ã“ã¨ã§ã€åˆæœŸå€¤ã‚’é…延生æˆã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ use Dwarf::Accessor qw/json/; sub _build_json { my $json = JSON->new(); $json->pretty(1); $json->utf8; return $json; } ## Dwarf::Message ディスパッãƒå‡¦ç†ã®ä¸ã§é€å‡ºå¯èƒ½ãªãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚¯ãƒ©ã‚¹ã€‚主ã«ãƒ•ãƒ¬ãƒ¼ãƒ ワークãŒã‚¨ãƒ©ãƒ¼ãƒãƒ³ãƒ‰ãƒªãƒ³ã‚°ãªã©ã«åˆ©ç”¨ã—ã¦ã„る。not\_found メソッドや redirect メソッドãŒåˆ©ç”¨ã—ã¦ã„ã‚‹ finish メソッドã®å®Ÿè£…ã«ã‚‚ディスパッãƒãƒ‘ッãƒã‚’ç›´ã¡ã«çµ‚了ã™ã‚‹ç›®çš„ã§ä½¿ã‚ã‚Œã¦ã„る。 ## Dwarf::Trigger トリガークラス。Dwarf ãŒæä¾›ã—ã¦ã„るトリガー㯠BEFORE\_DISPATCH / AFTER\_DISPATCH / ERROR / SERVER\_ERROR ã®å››ç¨®é¡žã€‚ã¾ãŸã€Dwarf::Plugin::Text::Xslate ãªã©ã®ãƒ—ラグインã¯èªã¿è¾¼ã¾ã‚Œã‚‹ã¨ BEFORE\_RENDER / AFTER\_RENDER ã®äºŒç¨®é¡žã®ãƒˆãƒªã‚¬ãƒ¼ã‚’æä¾›ã™ã‚‹ã€‚APIBase.pm ã‚„ HTMLBase.pm ã¯ã“れらã®ãƒˆãƒªã‚¬ãƒ¼ã‚’実装ã™ã‚‹ãŸã‚ã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’ã‚らã‹ã˜ã‚用æ„ã—ã¦ã‚ã‚Šã€ã‚µãƒ–クラスã§å®Ÿéš›ã«ãƒ¡ã‚½ãƒƒãƒ‰ãŒå®Ÿè£…ã•ã‚Œã‚‹ã¨ã‚³ãƒ¼ãƒ«ã•ã‚Œã‚‹ä»•çµ„ã¿ã«ãªã£ã¦ã„る。 $c->add_trigger(BEFORE_RENDER => $self->can('will_render')); $c->add_trigger(AFTER_RENDER => $self->can('did_render')); $c->add_trigger(ERROR => $self->can('receive_error')); $c->add_trigger(SERVER_ERROR => $self->can('receive_server_error')); ## Dwarf::Util ユーティリティクラス。以下ã®ãƒ¡ã‚½ãƒƒãƒ‰ãŒ @EXPORT\_OK ã§ã‚る。 ### メソッド #### add\_method #### load\_class #### installed #### capitalize #### shuffle\_array #### filename #### read\_file #### write\_file #### get\_suffix #### safe\_join #### merge\_hash #### encode\_utf8 #### decode\_utf8 #### encode\_utf8\_recursively #### decode\_utf8\_recursively # LICENSE Copyright (C) Takuho Yoshizu. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. # AUTHOR Takuho Yoshizu <yoshizu@s2factory.co.jp>