Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Perl入学式

Perl入学式 #9

http://www.perl-entrance.org

Perl入学式について

会場について

  • 今回のPerl入学式は「Joe'sオープンソースJelly」の一環ということで、株式会社Joe'sクラウドコンピューティング(http://www.joeswebhosting.net)様より特別に会場をお借りしています
  • この場をお借りしてお礼申し上げます

ロゴについて

Perl入学式

  • 横山陽平さん(http://yokoyamayohei.com/)にデザインしていただきました
  • この場をお借りしてお礼申し上げます

IT勉強会スタンプラリー

IT勉強会スタンプラリー

  • Perl入学式は, IT勉強会スタンプラリー(http://it-stamp.jp/)に参加しています
  • 今回のPerl入学式は, スタンプラリーの対象となる勉強会です
  • 台紙は勉強会終了後にお渡しします

喋ってる人

  • 名前: papix (ぱぴっくす)
  • Twitter: @__papix__
  • 仕事: 大学院生
  • ブログ: Masteries

本日の目標

  • 前回は, 本講義のゴールである「Mojoliciousを使ったウェブアプリケーションの開発」を目指して, Mojoliciousの基礎を学びました.
  • #9では, Mojoliciousを使って小規模なウェブアプリケーションの開発に挑戦しつつ, これまでに学んできたPerlのテクニックを復習していきます.

本日の内容

  • 前回の復習
  • フォームとデータの送信
  • Suspendan
  • 数あてゲーム

準備

  • Perl入学式のgithubアカウントに, 第9回で使用するデータが入ったリポジトリがあります.
    • まず最初に, $ git clone git://github.com/perl-entrance-org/perl-entrance-2012-09.gitでclone(ダウンロード)しましょう.
  • gitがインストールされていない場合, インストールしましょう.
    • Ubuntuの場合, $ sudo apt-get install git-coreでOKです.

前回の復習(15分)

  • hello.plを使います.
  • 既にテンプレートが用意されています. 「# ここを埋めよう!」と書かれた部分に適切なコードを書いて, あなたの名前を表示するページを表示するようにしましょう.
  • 端末でcd perl-entrance-2012-09でcloneしたディレクトリに移動し, $ morbo hello.plを実行します. ブラウザでlocalhosot:3000にアクセスすれば, 挙動を確認することができます.
  • 時間に余裕があれば, 自分でテンプレートを用意して, 複数のページを作ってみましょう.

フォームとデータの送信

  • Mojolicious::Liteを使えば, ウェブアプリケーションの見た目を決めるテンプレート(__DATA__以下の部分)と, ウェブアプリケーションの挙動を決める部分を1つのファイルで記述できる, ということを学びました.
  • しかし, 今まで作ってきたページは全て静的なページ(いつ読み込んでも同じデータが出力される)です.
  • 今回は, フォームを使ってウェブアプリケーションに対してデータを送信することで, 動的なページを作ってみましょう.

hello.plの改造

  • 先程作ったhello.plは, アクセスすると常にあなたの名前を表示します.
  • これを, このように改造してみましょう.
    • アクセスすると, 文字を入力するフォームと, 「送信」ボタンが現れる.
    • フォームに名前を入力して, 「送信」ボタンを押すと, 「こんにちは(フォームに入力した名前)さん」と表示する.

テンプレートの書き換え

  1. @@ index.html.ep
  2. <html>
  3. <head><title>hello!</title></head>
  4. <body style='padding: 30px;'>
  5. <form action="/hello" method="post">
  6. <input type="text" name="name" size="20" />
  7. <input type="submit" name="submit" value="送信" />
  8. </form>
  9. </body>
  10. </html>
  • トップページのテンプレート(__DATA__以下の部分)を, このように書き換えます.

解説(1)

  1. <form action="/hello" method="post">
  2. ...
  3. </form>
  • <form>から</form>で囲まれた範囲が1つのフォームとなります.
  • <form>には, どのページにデータを送信するかをactionで, どんな方法でデータを送信するかをmethodで指定しなければなりません.
    • methodにはpostgetが存在します. 詳細は後述します.

解説(2)

  1. <input type="text" name="name" size="20" />
  • ここがフォームの本体です. <input>で, データを入力する部品を用意することができます.
  • typeは部品の形状を指定します. 例えばtextは, このようなテキストを入力できる部品を用意してくれます.
    • text以外にも, ラジオボタンradio, チェックボックスcheckboxなどがあります.

解説(3)

  1. <input type="submit" name="submit" value="送信" />
  • submitは送信ボタン( )です. ボタンには, valueで指定した文字が表示されます.
  • 送信ボタンを押すと, 入力されたデータは, <input>内部のnameで指定された文字列と紐付られ, actionで指定されたページへ転送されます.

コードの書き換え

  1. use Mojolicious::Lite;
  2. use utf8;
  3. get '/' => 'index';
  4. app->start;
  • コードの部分(__DATA__より上の部分)は特に処理を行う必要がないので, このように変更します.
    • いつもはget '/'=> 'index'の間に=> sub { ... }でコードを書いていましたが, 今回のように, 処理がない場合は省略可能です.
  • とりあえず, この時点で実行してみましょう.

実行結果

  • 文字を入力するフォームと, 送信ボタンはきちんと出てきますが, フォームに文字を入力して送信ボタンを押すと, Page not found... yet!のようなエラー出てくるはずです.
  • これは, 送信されたデータを受け取り, 「こんにちはXXXXさん」と表示する「hello」というページが用意されていない為です.
  • 続いて, 「hello」というページを作りましょう.

helloのテンプレート

  1. @@ hello.html.ep
  2. <html>
  3. <head><title>hello, <%= $name %>!</title></head>
  4. <body style='padding: 30px;'>
  5. こんにちは<%= $name %>さん
  6. </body>
  7. </html>
  • コードの部分から$nameを送り, 表示します.

コードの追加

  1. post '/hello' => sub {
  2. my $self = shift;
  3. my $name = $self->param('name');
  4. $self->stash( name => $name );
  5. $self->render();
  6. } => 'hello';
  • app->start;よりも上に, このようなコードを書きます.

解説(1)

  1. post '/hello' => sub {
  2. ...
  3. }
  • /helloというページに, postメソッドでアクセスした際の処理を書きます.
  • 先程, <form>method="post"と設定したので, 先頭をpostにしています. method="get"と設定した場合はgetにします.

解説(2)

  1. my $name = $self->param('name') || '名無し';
  • paramを使うことで, フォームから送信されたデータを受け取ることができます.
  • 先程, テキストボックスは<input type="text" name="name" size="20">で宣言していた為, テキストボックスに入力された名前はnameで参照することができます.
  • $self->param('name') || '名無し'としているのは, 入力が空だった場合にわかりやすくする為です(「こんにちはnoneさん」と表示される).

実行結果

  • これで正常に動作するはずです.
    • フォームに名前を入れ, 送信ボタンを押せば, 「こんにちはXXXXさん」のように, 入力した名前が表示されます.

練習問題(20分)

  • question01.plを使います.
  • 既にテンプレートが用意されていますので, 名前と身長(cm), 体重(kg)を入力すると, /bmiに名前とBMIが表示されるように, コードを書いてみましょう.
    • BMIは身長(kg) / 身長(m) * 身長(m)で計算できます.

複雑なテンプレート

  1. <table>
  2. <tr>
  3. <td>名前</td>
  4. <td>papix</td>
  5. </tr>
  6. <tr>
  7. <td>身長</td>
  8. <td>180cm</td>
  9. </tr>
  10. :
  11. </table>
  • フォームから名前, 身長, 体重, 年齢, 趣味を入力し, このような表を作るとします.

複雑なテンプレート

  1. <table>
  2. <tr>
  3. <td>名前</td>
  4. <td><%= $name %></td>
  5. </tr>
  6. <tr>
  7. <td>身長</td>
  8. <td><%= $height %></td>
  9. </tr>
  10. :
  11. </table>
  • このように, 1つ1つ書いていくのは面倒です.

複雑なテンプレート

  • 実は, テンプレートにはperlのコードを埋め込むことができます.
  • この機能を使えば, 複雑な(冗長な)テンプレートを, シンプルに書きなおすことができます.
  • では, 冗長なテンプレートで書かれたlist.plを書き換えて行きましょう.

コードの書き換え

  1. post '/data' => sub {
  2. my $self = shift;
  3. $self->stash( data => {
  4. '名前' => $self->param('name'),
  5. '身長' => $self->param('height'),
  6. '体重' => $self->param('weight'),
  7. '年齢' => $self->param('age'),
  8. '趣味' => $self->param('hobby'),
  9. } );
  10. $self->render();
  11. } => 'data';
  • テンプレートへは, このようにハッシュを使ってデータを渡します.

テンプレートの書き換え

  1. @@ data.html.ep
  2. <html>
  3. <head><title>Output</title></head>
  4. <body style='padding: 30px;'>
  5. <table>
  6. % for my $topic (qw/名前 身長 体重 年齢 趣味/) {
  7. <tr>
  8. <td><%= $topic ></td>
  9. <td><%= $data->{$topic} %></td>
  10. </tr>
  11. % }
  12. </table>
  13. </body>
  14. </html>
  • テンプレートでは, 先頭に%がある行はPerlのコードが書かれている, と解釈します.

練習問題(30分)

  • question02.plの, __DATA__以下の部分のみを書き換えて(テンプレートのみを書き換えて)次のような出力を行うページを作ろう.
    • テキストを入力するフォームと, 送信ボタンを持つ.
    • 送信ボタンを押すと, /doというページに, フォームに入力したデータを送信する.

練習問題(30分)

  • /doページでは, 次のような処理を行う.
    • フォームに入力した文字がアルファベットの場合, hello, (入力した文字列)さん!と表示する.
    • フォームに入力した文字が+, -, /, *, %で区切られた2つの数値(例えば123+45)なら, その演算を行い, 結果を出力する(例の場合, 123 + 45 = 168と表示する).
    • それ以外の場合, undefinedと表示する.

レイアウト

  1. <html>
  2. <head><title>... title ...</title></head>
  3. <body style='padding: 30px;'>
  4. ... contents ...
  5. </body>
  6. </html>
  • テンプレートの中で, 常に同じ記述を使うという箇所がいくつかあります.
  • 例えば, 上記のヘッダの部分など.

レイアウト

  • レイアウトを使えば, このような「複数のページで同一の記述」をまとめることができます.
  • それでは早速, 使ってみましょう.
    • 先程使ったlist.plを, レイアウトを使って更に改造します.

レイアウト

  1. @@ layouts/common.html.ep
  2. <html>
  3. <head><title><%= $title %></title></head>
  4. <body style='padding: 30px;'>
  5. <%= content %>
  6. </body>
  7. </html>
  • __DATA__以下の部分にレイアウトを記載します.
  • <%= content %>には, それぞれのページで表示する内容が埋め込まれます.
  • ヘッダは共有でも, ページのタイトルはそれぞれ異なります. <%= $title %>とすることで, その部分だけ変更することができます.

レイアウト

  1. @@ index.html.ep
  2. % layout 'common', title => 'Input'
  3. <form action="/data" method="post">
  4. 名前: <input type="text" name="name" size="20"><br>
  5. 身長: <input type="text" name="height" size="4">cm<br>
  6. 体重: <input type="text" name="weight" size="4">kg<br>
  7. 年齢: <input type="text" name="age" size="4"><br>
  8. 趣味: <input type="text" name="hobby" size="20"><br>
  9. <input type="submit" name="submit" value="送信"><br>
  10. </form>
  • レイアウトを使うと, テンプレートをこのように書くことができます.
  • % layout 'common'で使用するテンプレートの名前を指定します. 更に,title => 'Input'でページのタイトルを変更することができます(テンプレートの<%= $title %>に埋め込まれる).

練習問題(15分)

  • list.plの, data.html.epのテンプレートを, レイアウトを使うように書き換えてみよう.

getとpost

  • 続いて, getとpostの違いを見てみましょう.
  • post.plを使います.

postメソッド

  • このpost.plは, postメソッドを使って書かれています.
  • 早速実行してみましょう.
  • フォームに名前を入力し, 送信ボタンを押すと, フォームに格納されたデータが, /dataに転送されます.

getメソッド

  • 次に, このpost.plを, getメソッドを使うように書き換えます.
  • まず, post.plを, get.plという名前でコピー($ cp post.pl get.pl)します.

getメソッド

  1. <form action="/data" method="get">
  2. 名前: <input type="text" name="name" size="20"><br>
  3. <input type="submit" name="submit" value="send"><br>
  4. </form>
  • テンプレートのフォームの部分を, このように書き換えます.
  • methodを, postからgetに書き換えます.

getメソッド

  1. get '/data' => sub {
  2. my $self = shift;
  3. my $name = $self->param('name');
  4. $self->stash( name => $name );
  5. $self->render();
  6. } => 'data';
  • コードの部分は, このように書き換えます. 先頭を, postからgetに書き換えることで, /dataに対してgetメソッドでアクセスした場合の処理を書くことができます.
  • getもpostも, $self->param()で送られたデータを受け取ることができます.

getメソッド

  • 書き換えはこれで完了です. 早速実行してみましょう.
    • postメソッドと何が異なるでしょうか?

getとpostの違い

  • get
    • 送られるデータは, URLの後ろに付けられる.
    • その書式は, URL?name=value&name=value ...となる.
    • アルファベット以外のnameやvalueは, URLエンコードされる.
  • post
    • 送られるデータは, 標準入力として与えられる(URLに影響はない).

結局のところ...

  • mojoliciousを使っているならば, 「getメソッドは送るデータがURLに付く」, 「postメソッドは送るデータがURLに付かない」という, 単純な認識でいい... のではないかと思います.

ちなみに...

  • getメソッドはURLに送るデータを与えるので, このようなこともできます.
  • get.plを起動し, localhost:3000/data?name=papixにアクセスすると...
    • post.plでも同様の行為を行うとどうなるでしょうか...?

最終問題(30分)

  • 次のような挙動をするページを作ってみよう.
    • /にアクセスすると, 1つのテキストボックスと送信ボタンが表示される.
    • 送信ボタンを押すと, getメソッドで/outputにデータを送る.

最終問題(30分)

  • /outputでは次のような処理を行う.
    • 送られたデータがアルファベットの文字列ならば挨拶する. 例えばperl-entranceが送られたなら, hello, perl-entrance!などのように表示する.
    • 送られたデータが整数なら, それを3乗した値を示す. 例えば-2が送られたなら, -2の3乗は-8などのように表示する.
    • 送られたデータが小数なら, その整数部を示す. 例えば-12.3456が送られたなら, -12.3456の整数部は-12などのように表示する.
    • 送られたデータが空なら, undefinedと表示する.
    • これらの全てに当てはまらない場合, errorと表示する.

アルティメット最終問題(∞分)

  • rev_polish関数を作ろう.
    • rev_polish関数は, 逆ポーランド記法の計算を行う関数である.
    • 引数として, 逆ポーランド記法が書かれた文字列を受け取り, (&rev_polish('3 4 +')など), その計算結果を返す.
    • 使用可能な演算子は加算(+), 減算(-), 乗算(*), 除算(/), 剰余算(%)とする. 使用可能な値は整数のみとする(3 1.2 +などは不可).
    • 計算できない場合(例えば, 1 2 3 +3 1.2 +など)は, undefを返す.

アルティメット最終問題(∞分)

  • 最終問題で作ったページで, 送られたデータの先頭にP:が付く場合, それ以降のテキストを逆ポーランド記法の式として, rev_polish関数で演算するように書き換えよう.
  • 答えが導けた場合, 演算した式と, その答えを表示する. 例えば送られたデータが1 2 +なら, 1 2 + の演算結果は 3 ですなどと表示する.
  • 答えが導けない場合, 演算した式と, 演算できませんでしたという文字を表示する.

質問タイム

お疲れさまでした

Use a spacebar or arrow keys to navigate