More Related Content Similar to Mojoliciousでつくる! Webアプリ入門 (20) More from Yusuke Wada (20) Mojoliciousでつくる! Webアプリ入門3. 自己紹介
• 和田裕介 1981/12/23 生
• 慶應義塾大学制作メディア研究科修了
• 株式会社ワディット代表取締役
• 株式会社オモロキ取締役兼最高技術責任者
• http://yusukebe.com/ or @yusukebe
14. ハッシュ関数
my $name = "yusukebe";
my $max = 3;
my $number = 0;
# 文字列を一文字ずつ分解
for my $char (split //, $name) {
# 文字に対応する数値を得る
$number += ord $char;
}
# 想定される最大値で割った余りを得る
my $result = $number % $max;
print "$resultn";
文字列
数値
固定値で割る
余りが結果
23. 愚直に.cgiを書くと
use CGI qw/param header/;
my $name = param('name');
my @list = qw/良い事が起こるでしょう 出会いがあるかも? 不幸になります/;
my $num = 0;
$num += ord ($_) for split //, $name;
my $index = $num % scalar @list;
my $message = $list[$index];
print header('text/html; charset=utf-8');
print "<html><body>";
print "<center><b>$message</b></center>";
print "</body></html>";
24. HTMLだけでも分離させると...
use CGI qw/param header/;
use Text::MicroTemplate qw/render_mt/;
use Data::Section::Simple qw/get_data_section/;
my $name = param('name');
my @list = qw/良い事が起こるでしょう 出会いがあるかも? 不幸になります/;
my $num = 0; $num += ord ($_) for split //, $name;
my $index = $num % scalar @list;
my $message = $list[$index];
my $template = get_data_section('index.mt');
my $html = render_mt($template, $message)->as_string();
print header('text/html; charset=utf-8');
print $html;
__DATA__
@@ index.mt
<html>
<body>
<center><b><?= $_[0] ?></b></center>
</body>
</html>
28. 切り分け方
1. Router
2. Controller
3. Model / Logic
4. DB層 / OR Mapper
5. View / Static files
ビジネスロジックを扱う
出力や見た目に関する部分
個別のリクエストに基づく処理
データベースにまつわる
URL/METHODに応じて振り分ける
35. 特徴1. VCだけサポート
• Routing / Controller
• デフォルトではMojo::Templateを使うView
• のみサポートし「Model」は対象外
Mojo::Baseその他でクラスつくったりOR Mapper
使ったりして自力で実装してね ♪ っていう...
逆にそれがいい
Model/Logic層はWebから切り離すべき
36. 特徴2. Not full-stack but full-stack
Ruby on Rails
DBを扱うまで全てこれ一つで出来る フルスタック
Mojolicious
HTTPのRequestやUserAgentまで
Mojoliciousで使うライブラリが全て自作
フルスタック
コア以外のCPANモジュールに依存しない!
40. 特徴3. その他
• アップデートが頻繁
$self->render_json( $ref ); # 4.00 で廃止
# =>
$self->render( json => $ref );
ただし依存が無いのでMojoliciousだけ追っていればよい
• Mojolicious::Lite を使えばファイル一つで
• 工夫すればCGIでも動作可能
• Perl 5.10.1 以上必須、5.16 以上を推奨
• 開発用サーバmorbo、本番用hypnotoad
• もちろんPSGI互換
45. 立ち上げ方
$ morbo -l "http://*:5000" myapp.pl
morboを使う
$ plackup -p 5000 myapp.pl
plackupで立ち上げる
46. Hello World から学ぶ
get '/' => sub {};
ルーティングとコントローラ
GET http://localhost:5000/
get '/entry/:id' => sub {
my $entry_id = $self->stash->{id};
};
post '/entry' => sub {};POST http://localhost:5000/entry
GET http://localhost:5000/entry/1
47. Hello World から学ぶ
get '/' => sub {
my $self = shift;
$self->stash->{message} = 'Hello Mojo';
...;
}
テンプレートにデータを渡す
$self->render('index'); # index.html.ep を描画
...;
<p><%= $message %></p>
テンプレートの記述とレンダー
50. #!/usr/bin/env perl
use Mojolicious::Lite;
use utf8;
...;
get '/' => sub {
my $self = shift;
$self->render('index');
};
...;
@@ index.html.ep
<p>名前を入力してください</p>
<form action="/result">
<input type="text" name="name" />
<input type="submit" value="占う" />
</form>
トップページを描画
51. ...;
get '/result' => sub {
my $self = shift;
my $name = $self->req->param('name');
my @list
= qw/良い事が起こるでしょう 出会いがあるかも? 不幸になります/;
my $num = 0;
$num += ord ($_) for split //, $name;
my $index = $num % scalar @list;
$self->stash->{message} = $list[$index];
$self->stash->{name} = $name;
$self->render('result');
};
...;
@@ result.html.ep
<p><%= $name %>さんの結果「<%= $message %>」</p>
結果を出力
53. Mojo::Template周り
• Perl-ish templates => Perl書ける!
% for my $entry (@$entries) {
<h2><%= $entry->{title} %></h2>
% }
Mojo::Template / Mojolicious::Plugin::DefaultHelper
• テンプレート向けhelper
@@ index.html.ep
% layout 'default';
Hi, <%= $name %>
@@ layouts/default.html.ep
<html>
<body><%= content %></body>
</html>
54. Mojolicious::Liteアプリを拡張する
• public/ => 静的ファイルを置く
• templates/ => テンプレートファイルを独立
./
!"" myapp.pl
!"" public/
# !"" css/
# !"" favicon.ico
# !"" images/
# %"" js/
%"" templates/
!"" layouts/
# %"" default.html.ep
%"" root/
%"" index.html.ep
57. Mojoliciousアプリをつくるコツ
• $ mojo generate app MyApp では無く
MyApp::Web みたいなネームスペースで
$ mojo genearate app Uranai::Web
• なぜならUranaiがWeb層だけではないかも
• Uranai::Model / Uranai::Logic
• Uranai::CLI
58. だいたいこんな構成
./
!"" Uranai/
# !"" DB/
# # %"" Schema.pm
# !"" DB.pm
# !"" Model/
# # %"" OneModel.pm
# !"" Web/
# # %"" Controller/
# # %"" Root.pm
# %"" Web.pm
%"" Uranai.pm
DBレイヤー / Teng etc.
ビジネスロジック / Perl Module
Webレイヤー /
主にControllerとルーティング
Uranai.pm では パス解決と設定ロードを実装
59. ::Liteからの移行
#!/usr/bin/env perl
use Mojolicious::Lite;
get '/' => sub {
my $self = shift;
$self->stash->{message} = 'Hello Mojo!';
$self->render('index');
};
app->start;
__DATA__
@@ index.html.ep
<html>
<body>
<p><%= $message %></p>
</body>
</html>
lib/Uranai/Web.pm
lib/Uranai/Web/Controller/*
Mojolicious::Controllerを継承
templates/
Mojo::Templateに準ずる
63. Uranai::Model::Uranai
package Uranai::Model::Uranai;
use Mouse;
has 'list' => (
is => 'ro',
isa => 'ArrayRef[Str]',
default => sub {
[qw/良い事が起こるでしょう
出会いがあるかも?
不幸になります
/]}
);
sub uranau {
my ($self, $name) = @_;
my $num = 0;
$num += ord ($_) for split //, $name;
my $index = $num % scalar @{$self->list()};
return $self->list->[$index];
}
__PACKAGE__->meta->make_immutable();
65. Uranai::Web::Controller::Root
package Uranai::Web::Controller::Root;
use Mojo::Base 'Mojolicious::Controller';
use Uranai::Model::Uranai;
sub index {
my $self = shift;
$self->render('index');
}
sub result {
my $self = shift;
my $name = $self->req->param('name');
return $self->redirect_to('/') unless $name;
my $uranai = Uranai::Model::Uranai->new;
my $message = $uranai->uranau($name);
$self->stash->{message} = $message;
$self->render('result');
}
1;
69. use FormValidator::Lite;
• 入力値の妥当性をチェックする
• 例:メールアドレス、郵便番号、文字数
FormValidator::Lite->load_constraints(qw/Japanese Email/);
my $validator = FormValidator::Lite->new($self->req);
my $res -> $validator->check(
name => [qw/NOT_NULL/],
mail => [qw/EMAIL/],
{ mails => [qw/mail mail_confirm/] } => ['DUPLICATION']
);
if($validator->has_error) {
$self->stash->{messages} = $validator->get_error_messages();
return $self->render;
}
...;
70. use Teng;
• データベースを操作する OR Mapper
• とってもシンプル
• Controllerからは操作せずModelから触る
my $teng = Teng::Schema::Loader->load(
dbh => $dbh,
namespace => 'Uranai::DB',
);
my $row = $teng->insert(result => {
text => 'あなたは結婚出来ません',
});
72. ちなみにやったこと
CREATE TABLE result (
id INT UNSIGNED AUTO_INCREMENT,
text VARCHAR(200) NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (`id`)
);
1. DBをつくって...
lib/Uranai/DB.pm
lib/Uranai/DB/Schema.pm
2. TengのDB/スキーマつくって...
lib/Uranai/Model/Uranai.pm
3. Modelから操作するようにして...
lib/Uranai/Controller/Root.pm
5. Controllerから呼び出す
templates/root/index.html.ep
6. Viewの変更
t/01_model.t
4. 簡単なテスト書いて...
できた!
75. DELETE/PUTを擬似的にサポートさせる
$self->hook(
before_dispatch => sub {
my $c = shift;
if($c->req->method eq 'POST' && $c->req->param('_method')) {
my $methods = [qw/GET POST PUT DELETE/];
if ( grep { $_ eq uc $c->req->param('_method') } @$methods ) {
$c->req->method( $c->req->param('_method') );
}
}
}
);
my $r = $self->routes;
$r->delete('/entry/:id')->
to( controller => 'Entry', action => 'delete' );
76. helperでFillInFormをお手軽に
# Uranai::Web
$self->helper(
render_fill => sub {
my ($self, $name) = @_;
my $html = $self->render(template => $name, partial => 1);
return $self->render(
text => HTML::FillInForm::Lite->fill($html, $self->req->params),
format => 'html'
);
}
);
# Uranai::Web::Controller::*
sub form {
my $self = shift;
...;
if ($validator->error) {
$self->stash->{messages} = $validator->get_error_messages();
return $self->render_fill('form');
}
}
77. Uranai->config();
# Uranai
sub config {
my $mode = $ENV{PLACK_ENV} || 'development';
my $fname = File::Spec->catfile( Uranai->base_dir() , $mode . '.pl' );
my $config = undef;
if( -f $fname ){
$config = do $fname or die "Cannnot load configuration file: $fname";
}else{
die "Cannot find configuration file: $fname";
}
return $config;
}
83. 扱わなかった主な点
• よりツッコんだMojoliciousの使い方
• テスト => Test::More / Test::Mojo
• 生に近いDB/SQL操作
• その他ミドルウェア => memcached / Redis
• デプロイ => サーバ構成 / アプリサーバ
• パフォーマンス計測、チューニング
• Perl以外のこと => JavaScript / CSS / HTML