Mojolicious-8.12 > Mojolicious::Guides::Growing
Mojolicious-8.12

名前

Mojolicious::Guides::Growing - Growing Mojolicious applications

Mojolicious::Guides::Growing - Mojoliciousアプリケーションを育てる

説明

This document explains the process of starting a Mojolicious::Lite prototype from scratch and growing it into a well-structured Mojolicious application.

スクラッチからMojolicious::Liteのプロトタイピングをはじめて、よく構成されたMojoliciousアプリケーションに育てる手順を説明します。

概念

Essentials every Mojolicious developer should know.

すべてのMojolicious開発者が知るべき本質

モデル ビュー コントローラー

MVC is a software architectural pattern for graphical user interface programming originating in Smalltalk-80, that separates application logic, presentation and input.

MVCはSmalltalk-80におけるGUI(graphical user interface)プログラミングのソフトウェアアーキテクチャパターンです。アプリケーションロジックと表現と入力とを分離する考え方です。

           +------------+    +-------+    +------+
  Input -> | Controller | -> | Model | -> | View | -> Output
           +------------+    +-------+    +------+

A slightly modified version of the pattern moving some application logic into the controller is the foundation of pretty much every web framework these days, including Mojolicious.

アプリケーションロジックをいくらかcontrollerに移動するようにしてパターンを多少改造したバージョンは、Mojoliciousを含む近年のほとんどすべてのWebフレームワークの基盤となっています。

              +----------------+     +-------+
  Request  -> |                | <-> | Model |
              |                |     +-------+
              |   Controller   |
              |                |     +-------+
  Response <- |                | <-> | View  |
              +----------------+     +-------+
              +----------------+     +-------+
  Request  -> |                | <-> | Model |
              |                |     +-------+
              | コントローラー |
              |                |     +-------+
  Response <- |                | <-> | View  |
              +----------------+     +-------+

The controller receives a request from a user, passes incoming data to the model and retrieves data from it, which then gets turned into an actual response by the view. But note that this pattern is just a guideline that most of the time results in cleaner more maintainable code, not a rule that should be followed at all costs.

コントローラはユーザからのリクエストを受け取り、入ってきたデータをモデルに渡し、モデルからデータを取り出します。このデータはビューによって実際のレスポンスの中に埋め込まれます。ただし、このパターンは、多くの場合にコードをクリーンでメンテナンス性よくするためのガイドラインにすぎません。必ず従うべき規則というわけではないのです。

REST(Representational State Transfer)

REST is a software architectural style for distributed hypermedia systems such as the web. While it can be applied to many protocols it is most commonly used with HTTP these days. In REST terms, when you are opening a URL like http://mojolicious.org/foo with your browser, you are basically asking the web server for the HTML representation of the http://mojolicious.org/foo resource.

RESTはWebのような分散ハイパーメディアシステムのためのソフトウェアアーキテクチャスタイルです。多くのプロトコルに適用できますが、今日ではほとんどがHTTPと利用されます。RESTの用語で言えば、ブラウザでhttp://mojolicious.org/fooのようなURLを開くとき、基本的にはWebサーバにhttp://mojolicious.org/foo resourceリソースのHTML表現を依頼していることになります。

  +--------+                                  +--------+
  |        | -> http://mojolicious.org/foo -> |        |
  | Client |                                  | Server |
  |        | <-  <html>Mojo rocks!</html>  <- |        |
  +--------+                                  +--------+

The fundamental idea here is that all resources are uniquely addressable with URLs and every resource can have different representations such as HTML, RSS or JSON. User interface concerns are separated from data storage concerns and all session state is kept client-side.

ここで基礎となる考え方は、すべてのリソースがURLによって一意に識別され、リソースごとにHTML、RSS、JSONといった異なる表現形式を持てるということです。ユーザインターフェイスの関心はデータストレージの関心から分離されています。すべてのセッションの状態はクライアントサイドで保持されます。

  +---------+                        +------------+
  |         | ->    PUT /foo      -> |            |
  |         | ->    Hello World!  -> |            |
  |         |                        |            |
  |         | <-    201 CREATED   <- |            |
  |         |                        |            |
  |         | ->    GET /foo      -> |            |
  | Browser |                        | Web Server |
  |         | <-    200 OK        <- |            |
  |         | <-    Hello World!  <- |            |
  |         |                        |            |
  |         | ->    DELETE /foo   -> |            |
  |         |                        |            |
  |         | <-    200 OK        <- |            |
  +---------+                        +------------+

While HTTP methods such as PUT, GET and DELETE are not directly part of REST they go very well with it and are commonly used to manipulate resources.

PUTGETDELETEのようなHTTPメソッドは直接的にはRESTの一部ではありませんが、相性がとてもよく、リソースを操作するために広く利用されています。

セッション

HTTP was designed as a stateless protocol, web servers don't know anything about previous requests, which makes user-friendly login systems very tricky. Sessions solve this problem by allowing web applications to keep stateful information across several HTTP requests.

HTTPはステートレスなプロトコルとして設計されています。Webサーバは以前のリクエストについては何も知りません。このため、ユーザフレンドリーなログインシステムはとてもトリッキーなものになります。 セッションは、ウェブアプリケーションに複数のHTTPリクエストをまたいだ状態の情報を保持させることによってこの問題を解決します。

  GET /login?user=sebastian&pass=s3cret HTTP/1.1
  Host: mojolicious.org

  HTTP/1.1 200 OK
  Set-Cookie: sessionid=987654321
  Content-Length: 10
  Hello sebastian.

  GET /protected HTTP/1.1
  Host: mojolicious.org
  Cookie: sessionid=987654321

  HTTP/1.1 200 OK
  Set-Cookie: sessionid=987654321
  Content-Length: 16
  Hello again sebastian.

Traditionally all session data was stored on the server-side and only session ids were exchanged between browser and web server in the form of cookies.

伝統的にセッションデータはすべてサーバサイドに保存され、セッションIDだけがクッキーを通じてブラウザとWebサーバの間で交換されます。

  Set-Cookie: session=hmac-sha1(base64(json($session)))

In Mojolicious however we are taking this concept one step further by storing everything JSON serialized and Base64 encoded in HMAC-SHA1 signed cookies, which is more compatible with the REST philosophy and reduces infrastructure requirements.

しかし、MojoliciousではすべてをJSONでシリアライズしBase64でエンコードして、HMAC-SHA1による署名つきのクッキーに保存することによって、この概念を一歩前に進めています。これは、RESTの哲学により適合していて、インフラの要件を減らします。

テスト駆動開発

TDD is a software development process where the developer starts writing failing test cases that define the desired functionality and then moves on to producing code that passes these tests. There are many advantages such as always having good test coverage and code being designed for testability, which will in turn often prevent future changes from breaking old code. Much of Mojolicious was developed using TDD.

TDDとは、開発者が要求された機能を定義した失敗するテストケースから書き始めて、次にこのテストにパスするコードを書くことに移行するソフトウェア開発プロセスです。この手法の利点はたくさんあります。常にテストカバレッジがよくなったり、コードがテスト可能なように設計されたりします。これは、将来の変更時に古いコードが壊れることを防いでくれるでしょう。MojoliciousのほとんどはTDDを使って開発されています。

プロトタイプ

One of the main differences between Mojolicious and other web frameworks is that it also includes Mojolicious::Lite, a micro web framework optimized for rapid prototyping.

Mojoliciousとその他のウェブフレームワークの主な違いのひとつは、Mojolicious::Liteという、高速プロトタイピングのためのマイクロWebフレームワークを含んでいることです。

違い

You likely know the feeling, you've got a really cool idea and want to try it as quickly as possible, that's exactly why Mojolicious::Lite applications don't need more than a single file.

あなたはきっとこの気持ちを知っているでしょう。本当にクールなアイディアがあって、 できるだけ早くそれ試してみたい。それこそがMojolicious::Liteアプリケーションが たったひとつのファイルだけしか必要としない理由です。

  myapp.pl   # Templates and even static files can be inlined
  myapp.pl   # テンプレートと静的ファイルがインライン化されます

Full Mojolicious applications on the other hand are much closer to a well organized CPAN distribution to maximize maintainability.

一方、フルMojoliciousアプリケーションは保守性を最大化するために、 よく整理されたCPANディストリビューションにとても近いです。

  myapp                      # Application directory
  |- script                  # Script directory
  |  +- my_app               # Application script
  |- lib                     # Library directory
  |  |- MyApp.pm             # Application class
  |  +- MyApp                # Application namespace
  |     +- Controller        # Controller namespace
  |        +- Example.pm     # Controller class
  |- my_app.conf             # Configuration file
  |- t                       # Test directory
  |  +- basic.t              # Random test
  |- log                     # Log directory
  |  +- development.log      # Development mode log file
  |- public                  # Static file directory (served automatically)
  |  +- index.html           # Static HTML file
  +- templates               # Template directory
     |- layouts              # Template directory for layouts
     |  +- default.html.ep   # Layout template
     +- example              # Template directory for "Example" controller
        +- welcome.html.ep   # Template for "welcome" action
  myapp                      # アプリケーションディレクトリ
  |- script                  # スクリプトディレクトリ
  |  +- my_app               # アプリケーションスクリプト
  |- lib                     # ライブラリディレクトリ
  |  |- MyApp.pm             # アプリケーションクラス
  |  +- MyApp                # アプリケーション名前空間
  |     +- Controller        # コントローラーネームスペース
  |        +- Example.pm     # コントローラークラス
  |- my_app.conf             # 設定ファイル
  |- t                       # テストディレクトリ
  |  +- basic.t              # ランダムテスト
  |- log                     # ログディレクトリ
  |  +- development.log      # 開発モードのログファイル
  |- public                  # 静的ファイルのディレクトリ(自動的にサーブされる)
  |  +- index.html           # 静的なHTMLファイル
  +- templates               # テンプレートディレクトリ
     |- layouts              # レイアウトのためのテンプレートディレクトリ
     |  +- default.html.ep   # レイアウトテンプレート
     +- example              # Exampleコントローラーのためのテンプレートディレクトリ
        +- welcome.html.ep   # "welcome"アクションのためのテンプレート

Both application skeletons can be automatically generated with the commands Mojolicious::Command::Author::generate::lite_app and Mojolicious::Command::Author::generate::app.

両方のアプリケーションでスケルトンは、次のコマンドで自動的に生成できます。 Mojolicious::Command::Author::generate::lite_appMojolicious::Command::Author::generate::app

  $ mojo generate lite_app myapp.pl
  $ mojo generate app MyApp

Feature-wise both are almost equal, the only real differences are organizational, so each one can be gradually transformed into the other.

機能的には両者はほぼ同じです。実質的な違いは構成だけなので、それぞれを徐々に他方へと変換できます。

基礎

We start our new application with a single executable Perl script.

わたしたちは新しいアプリケーションをひとつの実行可能なPerlスクリプトからスタートします。

  $ mkdir myapp
  $ cd myapp
  $ touch myapp.pl
  $ chmod 744 myapp.pl

This will be the foundation for our login manager example application.

これはログインマネージャのサンプルアプリケーションの基礎になります。

  #!/usr/bin/env perl
  use Mojolicious::Lite;

  get '/' => sub {
    my $c = shift;
    $c->render(text => 'Hello World!');
  };

  app->start;

The built-in development web server makes working on your application a lot of fun thanks to automatic reloading.

組み込みの開発用Webサーバは自動リロードしてくれるため、楽しくWebアプリケーションを作成できます。

  $ morbo ./myapp.pl
  Server available at http://127.0.0.1:3000

Just save your changes and they will be automatically in effect the next time you refresh your browser.

変更を保存するだけで、ブラウザを次回リフレッシュしたときに自動的に変更が反映されます。

概観

It all starts with an HTTP request like this, sent by your browser.

すべては、ブラウザが送信するこのようなHTTPリクエストからはじまります。

  GET / HTTP/1.1
  Host: localhost:3000

Once the request has been received by the web server through the event loop, it will be passed on to Mojolicious, where it will be handled in a few simple steps.

リクエストがイベントループを通してWebサーバーによって受け取られると、Mojoliciousに渡されていくつかの簡単な手順で処理されます。

  1. Check if a static file exists that would meet the requirements.

    リクエストに合致する静的ファイルが存在するかをチェック

  2. Try to find a route that would meet the requirements.

    リスエストに合致するルートを探す

  3. Dispatch the request to this route, usually reaching one or more actions.

    合致したルートにリクエストをディスパッチする。通常は、ひとつ、あるいは複数のアクションに到達する。

  4. Process the request, maybe generating a response with the renderer.

    リクエストを処理する。レンダラーでレスポンスを描画することが多い。

  5. Return control to the web server, and if no response has been generated yet, wait for a non-blocking operation to do so through the event loop.

    Webサーバーに制御を戻し、レスポンスがまだ生成されていない場合は、イベントループを通じてノンブロック処理の実行を待つ。

With our application the router would have found an action in step 2, and rendered some text in step 4, resulting in an HTTP response like this being sent back to the browser.

アプリケーションでは、ルーターは、ステップ2においてアクションを見つけ、 ステップ4において、何らかのテキストを描画し、以下のようなHTTPレスポンス をブラウザーに返します。

  HTTP/1.1 200 OK
  Content-Length: 12
  Hello World!

モデル

In Mojolicious we consider web applications simple frontends for existing business logic, that means Mojolicious is by design entirely model layer agnostic and you just use whatever Perl modules you like most.

Mojoliciousでは、Webアプリケーションを、存在するビジネスロジックのシンプルなフロントエンドであると考えます。つまりこれは、Mojoliciousは完全にモデルレイヤーと独立して設計されていて、あなたがもっとも好きなPerlモジュールを利用すればよいということです。

  $ mkdir -p lib/MyApp/Model
  $ touch lib/MyApp/Model/Users.pm
  $ chmod 644 lib/MyApp/Model/Users.pm

Our login manager will simply use a plain old Perl module abstracting away all logic related to matching usernames and passwords. The name MyApp::Model::Users is an arbitrary choice, and is simply used to make the separation of concerns more visible.

わたしたちのログインマネージャーでは、ユーザー名とパスワードのマッチングに関連したすべてのロジックを抽象化する昔ながらのPerlモジュールを利用します。名前MyApp::Model::Usersは任意の選択であり、関心の分離をより見やすくするために使用しています。

  package MyApp::Model::Users;

  use strict;
  use warnings;

  use Mojo::Util 'secure_compare';

  my $USERS = {
    joel      => 'las3rs',
    marcus    => 'lulz',
    sebastian => 'secr3t'
  };

  sub new { bless {}, shift }

  sub check {
    my ($self, $user, $pass) = @_;
    # Success
    return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass;
    # 成功
    return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass;
    # Fail
    return undef;
  }
    # 失敗
    return undef;
  }

  1;

A simple helper can be registered with the function "helper" in Mojolicious::Lite to make our model available to all actions and templates.

シンプルなヘルパーは、"helper" in Mojolicious::Lite関数で登録すると、すべてのアクションとテンプレートで利用できるモデルを作成できます。

  #!/usr/bin/env perl
  use Mojolicious::Lite;

  use lib 'lib';
  use MyApp::Model::Users;
  # Helper to lazy initialize and store our model object
  helper users => sub { state $users = MyApp::Model::Users->new };
  # 初期化を遅延させ、モデルオブジェクトを保存するヘルパー
  helper users => sub { state $users = MyApp::Model::Users->new };

  # /?user=sebastian&pass=secr3t
  any '/' => sub {
    my $c = shift;
    # Query parameters
    my $user = $c->param('user') || '';
    my $pass = $c->param('pass') || '';
    # クエリパラメーター
    my $user = $c->param('user') || '';
    my $pass = $c->param('pass') || '';
    # Check password
    return $c->render(text => "Welcome $user.")
      if $c->users->check($user, $pass);
    # パスワードのチェック
    return $c->render(text => "Welcome $user.")
      if $c->users->check($user, $pass);
    # Failed
    $c->render(text => 'Wrong username or password.');
  };
    # 失敗
    $c->render(text => 'Wrong username or password.');
  };

  app->start;

The method "param" in Mojolicious::Controller is used to access query parameters, POST parameters, file uploads and route placeholders, all at once.

"param" in Mojolicious::Controllerメソッドを利用すると、クエリパラメーター、POSTパラメーター、ファイルアップロード、ルーティングプレースホルダのすべてにアクセスできます。

テスト

In Mojolicious we take testing very serious and try to make it a pleasant experience.

Mojoliciousは、テストをとても大切に考えていて、快適にテストを行えるように取り組んでいます。

  $ mkdir t
  $ touch t/login.t
  $ chmod 644 t/login.t

Test::Mojo is a scriptable HTTP user agent designed specifically for testing, with many fun state of the art features such as CSS selectors based on Mojo::DOM.

Test::Mojoはスクリプト化可能なHTTPユーザーエージェントです。テスト用に特別にデザインされていて、Mojo::DOMにもとづくCSSセレクタのような楽しくて最新の機能がたくさん盛り込まれています。

  use Test::More;
  use Test::Mojo;
  # Include application
  use FindBin;
  require "$FindBin::Bin/../myapp.pl";
  # アプリケーションの取り込み
  use FindBin;
  require "$FindBin::Bin/../myapp.pl";
  # Allow 302 redirect responses
  my $t = Test::Mojo->new;
  $t->ua->max_redirects(1);
  # 302リダイレクトレスポンスの許可
  my $t = Test::Mojo->new;
  $t->ua->max_redirects(1);
  # Test if the HTML login form exists
  $t->get_ok('/')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');
  # HTMLログインフォームが存在するかのテスト
  $t->get_ok('/')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');
  # Test login with valid credentials
  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
    ->status_is(200)
    ->text_like('html body' => qr/Welcome sebastian/);
  # 正しい認証情報でログインしたかのテスト
  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
    ->status_is(200)
    ->text_like('html body' => qr/Welcome sebastian/);
  # Test accessing a protected page
  $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
  # 保護されたベージへのアクセスのテスト
  $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
  # Test if HTML login form shows up again after logout
  $t->get_ok('/logout')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');
  # HTMLフォームがログアウトした後に表示されるかどうかのテスト
  $t->get_ok('/logout')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');

  done_testing();

Your application won't pass these tests, but from now on you can use them to check your progress.

あなたのアプリケーションはこれらのテストに合格しないでしょう。しかしこれからは進捗をチェックするためにこのテストが使えます。

  $ prove -l
  $ prove -l t/login.t
  $ prove -l -v t/login.t

Or perform quick requests right from the command line with Mojolicious::Command::get.

あるいは、Mojolicious::Command::getを使ってコマンドラインから素早くリクエストを実行できます。

  $ ./myapp.pl get /
  Wrong username or password.

  $ ./myapp.pl get -v '/?user=sebastian&pass=secr3t'
  GET /?user=sebastian&pass=secr3t HTTP/1.1
  User-Agent: Mojolicious (Perl)
  Accept-Encoding: gzip
  Content-Length: 0
  Host: localhost:59472

  HTTP/1.1 200 OK
  Date: Sun, 18 Jul 2010 13:09:58 GMT
  Server: Mojolicious (Perl)
  Content-Length: 12
  Content-Type: text/plain

  Welcome sebastian.

ステートの維持

Sessions in Mojolicious pretty much just work out of the box once you start using the method "session" in Mojolicious::Controller, there is no setup required, but we suggest setting a more secure passphrase with "secrets" in Mojolicious.

Mojoliciousのセッションは、"session" in Mojolicious::Controllerメソッドからすぐに使え、しっかり機能しますし、セットアップの必要もありません。しかし、"secrets" in Mojoliciousを使ってより安全なパスフレーズを設定することをお勧めします。

  $app->secrets(['Mojolicious rocks']);

This passphrase is used by the HMAC-SHA1 algorithm to make signed cookies tamper resistant and can be changed at any time to invalidate all existing sessions.

このパスフレーズはHMAC-SHA1アルゴリズムによって利用され、署名つきクッキーに改ざん耐性が付与されます。また既存のすべてのセッションを無効化するためにいつでも変更できます。

  $c->session(user => 'sebastian');
  my $user = $c->session('user');

By default all sessions expire after one hour, for more control you can use the expiration session value to set an expiration date in seconds from now.

デフォルトではすべてのセッションの期限は一時間です。さらに調整したい場合は、セッションのexpirationの値を使って、有効期限の日付を現在から秒で指定できます。

  $c->session(expiration => 3600);

And the whole session can be deleted by using the expires session value to set an absolute expiration date in the past.

すべてのセッションはセッションのexpiresに過去の期限日を設定することで削除できます。

  $c->session(expires => 1);

For data that should only be visible on the next request, like a confirmation message after a 302 redirect performed with "redirect_to" in Mojolicious::Plugin::DefaultHelpers, you can use the flash, accessible through "flash" in Mojolicious::Plugin::DefaultHelpers.

"redirect_to" in Mojolicious::Plugin::DefaultHelpersによって実行される302リダイレクト後の確認メッセージのような、次のリクエストに現れるはずのデータのために、 "flash" in Mojolicious::Plugin::DefaultHelpersを使用できます。

  $c->flash(message => 'Everything is fine.');
  $c->redirect_to('goodbye');

Just remember that all session data gets serialized with Mojo::JSON and stored in HMAC-SHA1 signed cookies, which usually have a 4096 byte (4KiB) limit, depending on browser.

すべてのセッションデータはMojo::JSONによってシリアライズされ、 HMAC-SHA1 による署名つきクッキーに保存されることを思い出してください。ですのでブラウザーに依存して、通常は4096バイト(4KB)の限界があります。

最終的なプロトタイプ

A final myapp.pl prototype passing all of the tests above could look like this.

上記すべての単体テストを通過した最終的なmyapp.plプロトタイプは次のようになります。

  #!/usr/bin/env perl
  use Mojolicious::Lite;

  use lib 'lib';
  use MyApp::Model::Users;
  # Make signed cookies tamper resistant
  app->secrets(['Mojolicious rocks']);
  # 署名付きクッキーを改ざんできないようにする
  app->secrets(['Mojolicious rocks']);

  helper users => sub { state $users = MyApp::Model::Users->new };
  # Main login action
  any '/' => sub {
    my $c = shift;
  # メインのログインアクション
  any '/' => sub {
    my $c = shift;
    # Query or POST parameters
    my $user = $c->param('user') || '';
    my $pass = $c->param('pass') || '';
    # クエリかPOSTパラメーター
    my $user = $c->param('user') || '';
    my $pass = $c->param('pass') || '';
    # Check password and render "index.html.ep" if necessary
    return $c->render unless $c->users->check($user, $pass);
    # パスワードをチェックして、必要ならば"index.html.ep"を描画
    return $c->render unless $c->users->check($user, $pass);
    # Store username in session
    $c->session(user => $user);
    # セッションにユーザー名を保存
    $c->session(user => $user);
    # Store a friendly message for the next page in flash
    $c->flash(message => 'Thanks for logging in.');
    # フラッシュに次のページのための親切なメッセージを保存
    $c->flash(message => 'Thanks for logging in.');
    # Redirect to protected page with a 302 response
    $c->redirect_to('protected');
  } => 'index';
    # 302レスポンスで保護されたページにリダイレクト
    $c->redirect_to('protected');
  } => 'index';
  # Make sure user is logged in for actions in this group
  group {
    under sub {
      my $c = shift;
  # このグループに属するアクションのためにユーザーがログインしていることを確認する
  group {
    under sub {
      my $c = shift;
      # Redirect to main page with a 302 response if user is not logged in
      return 1 if $c->session('user');
      $c->redirect_to('index');
      return undef;
    };
      # ユーザーがログインしていない場合は302レスポンスでメインページにリダイレクト
      return 1 if $c->session('user');
      $c->redirect_to('index');
      return undef;
    };
    # A protected page auto rendering "protected.html.ep"
    get '/protected';
  };
    # "protected.html.ep"を自動的に描画する保護されたページ
    get '/protected';
  };
  # Logout action
  get '/logout' => sub {
    my $c = shift;
  # ログアウトアクション
  get '/logout' => sub {
    my $c = shift;
    # Expire and in turn clear session automatically
    $c->session(expires => 1);
    # 有効期限切れにして自動的にセッションをクリアする
    $c->session(expires => 1);
    # Redirect to main page with a 302 response
    $c->redirect_to('index');
  };
    # 302レスポンスでメインページにリダイレクト
    $c->redirect_to('index');
  };

  app->start;
  __DATA__

  @@ index.html.ep
  % layout 'default';
  %= form_for index => begin
    % if (param 'user') {
      <b>Wrong name or password, please try again.</b><br>
    % }
    Name:<br>
    %= text_field 'user'
    <br>Password:<br>
    %= password_field 'pass'
    <br>
    %= submit_button 'Login'
  % end

  @@ protected.html.ep
  % layout 'default';
  % if (my $msg = flash 'message') {
    <b><%= $msg %></b><br>
  % }
  Welcome <%= session 'user' %>.<br>
  %= link_to Logout => 'logout'

  @@ layouts/default.html.ep
  <!DOCTYPE html>
  <html>
    <head><title>Login Manager</title></head>
    <body><%= content %></body>
  </html>

And the directory structure should be looking like this now.

ディレクトリの構造は、以下のようになっているはずです。

  myapp
  |- myapp.pl
  |- lib
  |  +- MyApp
  |     +- Model
  |        +- Users.pm
  +- t
     +- login.t

Our templates are using quite a few features of the renderer, Mojolicious::Guides::Rendering explains them all in great detail.

私たちのテンプレートはレンダラーのかなりの数の機能を使っています。Mojolicious::Guides::Renderingではそれらすべてを詳細に説明しています。

よく構成されたアプリケーション

Due to the flexibility of Mojolicious there are many variations of the actual growing process, but this should give you a good overview of the possibilities.

Mojoliciousの柔軟性のために、実際の拡張には多くのバリエーションがあります。しかし、以上の説明から可能性の見通しがよく得られたことでしょう。

テンプレートのインフレート

All templates and static files inlined in the DATA section can be automatically turned into separate files in the templates and public directories with the command Mojolicious::Command::Author::inflate.

DATAセクションの中のインライン化されたすべてのテンプレートと静的ファイルは、Mojolicious::Command::Author::inflateを使って、templatespublicディレクトリの中に独立したファイルとして自動的に変換できます。

  $ ./myapp.pl inflate

Those directories have a higher precedence, so inflating can also be a great way to allow your users to customize their applications.

これらのディレクトリはより高い優先度をもちます。ですので、インフレートはユーザーがアプリケーションをカスタマイズできるようにするのにも素晴らしい方法です。

簡素化されたアプリケーションクラス

This is the heart of every full Mojolicious application and always gets instantiated during server startup.

これはすべての完全なMojoliciousアプリケーションの心臓で、いつもサーバのスタートアップの間にインスタンス化されます。

  $ touch lib/MyApp.pm
  $ chmod 644 lib/MyApp.pm

We will start by extracting all actions from myapp.pl and turn them into simplified hybrid routes in the Mojolicious::Routes router, none of the actual action code needs to be changed.

私たちはすべてのアクションをmyapp.plから展開することによってはじめ、それらをMojolicious::Routesのルーターにおける簡素化されたハイブリッドなルートに変換します。実際のアクションのコードは何も変更する必要がありません。

  package MyApp;
  use Mojo::Base 'Mojolicious';

  use MyApp::Model::Users;

  sub startup {
    my $self = shift;

    $self->secrets(['Mojolicious rocks']);
    $self->helper(users => sub { state $users = MyApp::Model::Users->new });

    my $r = $self->routes;

    $r->any('/' => sub {
      my $c = shift;

      my $user = $c->param('user') || '';
      my $pass = $c->param('pass') || '';
      return $c->render unless $c->users->check($user, $pass);

      $c->session(user => $user);
      $c->flash(message => 'Thanks for logging in.');
      $c->redirect_to('protected');
    } => 'index');

    my $logged_in = $r->under(sub {
      my $c = shift;
      return 1 if $c->session('user');
      $c->redirect_to('index');
      return undef;
    });
    $logged_in->get('/protected');

    $r->get('/logout' => sub {
      my $c = shift;
      $c->session(expires => 1);
      $c->redirect_to('index');
    });
  }

  1;

The startup method gets called right after instantiation and is the place where the whole application gets set up. Since full Mojolicious applications can use nested routes they have no need for group blocks.

Mojoliciousstartupメソッドはインスタンス化された直後に呼び出され、 アプリケーション全体がセットアップされる場所です。完全なMojoliciousアプリケーションではネストしたルーティングが使えるので、groupブロックは必要ありません。

簡易化されたアプリケーションスクリプト

myapp.pl itself can now be turned into a simplified application script to allow running tests again.

さて、myapp.plそのものを、再びテストが実行できるように、簡素化したアプリケーションスクリプトに変換できるようになりました。

  #!/usr/bin/env perl

  use strict;
  use warnings;

  use lib 'lib';
  use Mojolicious::Commands;
  # Start command line interface for application
  Mojolicious::Commands->start_app('MyApp');
  # アプリケーションのためにコマンドラインインターフェイスを開始する
  Mojolicious::Commands->start_app('MyApp');

And the directory structure of our hybrid application should be looking like this.

ハイブリッドアプリケーションのディレクトリ構造は、以下のようになっています。

  myapp
  |- myapp.pl
  |- lib
  |  |- MyApp.pm
  |  +- MyApp
  |     +- Model
  |        +- Users.pm
  |- t
  |  +- login.t
  +- templates
     |- layouts
     |  +- default.html.ep
     |- index.html.ep
     +- protected.html.ep

コントローラークラス

Hybrid routes are a nice intermediate step, but to maximize maintainability it makes sense to split our action code from its routing information.

ハイブリッドなルーティングはよい中間的なステップですが、メンテナンス性を最大化するにはルート情報からアクションのコードを分離するのがよいでしょう。

  $ mkdir lib/MyApp/Controller
  $ touch lib/MyApp/Controller/Login.pm
  $ chmod 644 lib/MyApp/Controller/Login.pm

Once again the actual action code does not need to change, we just rename $c to $self since the controller is now the invocant.

実際のアクションのコードにはまったく変更はありません。コントローラーが今度はインボカントになるので、$c$selfに変更するだけです。

  package MyApp::Controller::Login;
  use Mojo::Base 'Mojolicious::Controller';

  sub index {
    my $self = shift;

    my $user = $self->param('user') || '';
    my $pass = $self->param('pass') || '';
    return $self->render unless $self->users->check($user, $pass);

    $self->session(user => $user);
    $self->flash(message => 'Thanks for logging in.');
    $self->redirect_to('protected');
  }

  sub logged_in {
    my $self = shift;
    return 1 if $self->session('user');
    $self->redirect_to('index');
    return undef;
  }

  sub logout {
    my $self = shift;
    $self->session(expires => 1);
    $self->redirect_to('index');
  }

  1;

All Mojolicious::Controller controllers are plain old Perl classes and get instantiated on demand.

すべてのMojolicious::ControllerはふつうのPerlのクラスで、必要に応じてインスタンス化されます。

アプリケーションクラス

The application class lib/MyApp.pm can now be reduced to model and routing information.

アプリケーションクラスlib/MyApp.pmは、モデルとルーティング情報だけに小さくできるようになりました。

  package MyApp;
  use Mojo::Base 'Mojolicious';

  use MyApp::Model::Users;

  sub startup {
    my $self = shift;

    $self->secrets(['Mojolicious rocks']);
    $self->helper(users => sub { state $users = MyApp::Model::Users->new });

    my $r = $self->routes;
    $r->any('/')->to('login#index')->name('index');

    my $logged_in = $r->under('/')->to('login#logged_in');
    $logged_in->get('/protected')->to('login#protected');

    $r->get('/logout')->to('login#logout');
  }

  1;

The router allows many different route variations, Mojolicious::Guides::Routing explains them all in great detail.

さまざまなルーティングが構築できます。詳しくはMojolicious::Guides::Routingで説明しています。

テンプレート

Templates are our views, and usually bound to controllers, so they need to be moved into the appropriate directories.

テンプレートはビューになります。通常、コントローラーに束縛されるため、適切なディレクトリに移動する必要があります。

  $ mkdir templates/login
  $ mv templates/index.html.ep templates/login/index.html.ep
  $ mv templates/protected.html.ep templates/login/protected.html.ep

スクリプト

Finally myapp.pl can be moved into a script directory and renamed to my_app to follow the CPAN standard.

最後に、myapp.plscriptディレクトリに移動できるようになり、 CPANスタンダードにしたがってmy_appにリネームします。

  $ mkdir script
  $ mv myapp.pl script/my_app

Just a few small details change, instead of lib we now use FindBin and @INC, allowing us to start the application from outside its home directory.

ほんの少し変更を加えます。libの代わりにFindBin@INCを使うことにしましょう。こうすることで、アプリケーションをホームディレクトリの外から開始できるようになります。

  #!/usr/bin/env perl

  use strict;
  use warnings;

  use FindBin;
  BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
  use Mojolicious::Commands;
  # Start command line interface for application
  Mojolicious::Commands->start_app('MyApp');
  # アプリケーションのためにコマンドラインインターフェイスを開始する
  Mojolicious::Commands->start_app('MyApp');

簡易化されたテスト

Full Mojolicious applications are a little easier to test, so t/login.t can be simplified.

通常のMojoliciousアプリケーションはテストが少し簡単で、ホームディレクトリの検知は必要ありません。ですので、t/login.tは簡易化されます。

  use Test::More;
  use Test::Mojo;

  # Load application class
  my $t = Test::Mojo->new('MyApp');
  $t->ua->max_redirects(1);

  $t->get_ok('/')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');

  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
    ->status_is(200)
    ->text_like('html body' => qr/Welcome sebastian/);

  $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);

  $t->get_ok('/logout')
    ->status_is(200)
    ->element_exists('form input[name="user"]')
    ->element_exists('form input[name="pass"]')
    ->element_exists('form input[type="submit"]');

  done_testing();

And our final directory structure should be looking like this.

完成したディレクトリ構造は、以下のようになるはずです。

  myapp
  |- script
  |  +- my_app
  |- lib
  |  |- MyApp.pm
  |  +- MyApp
  |     |- Controller
  |     |  +- Login.pm
  |     +- Model
  |        +- Users.pm
  |- t
  |  +- login.t
  +- templates
     |- layouts
     |  +- default.html.ep
     +- login
        |- index.html.ep
        +- protected.html.ep

Test-driven development takes a little getting used to, but can be a very powerful tool.

テスト駆動開発は少し慣れが必要ですが、やる価値は大いにあります。

もっと学ぶには

You can continue with Mojolicious::Guides now or take a look at the Mojolicious wiki, which contains a lot more documentation and examples by many different authors.

さあ、Mojolicious::Guides を続けるか、Mojolicious wikiを見てみましょう。多くの著者がドキュメントやサンプルをたくさん書いています。

サポート

If you have any questions the documentation might not yet answer, don't hesitate to ask on the mailing list or the official IRC channel #mojo on irc.freenode.net (chat now!).

このドキュメントでわからない部分があれば、 mailing listirc.freenode.net (chat now!)の公式IRCチャンネル #mojo まで気軽に質問してください。