名前¶
Mojolicious::Guides::Cookbook - Cooking with Mojolicious
Mojolicious::Guides::Cookbook - Mojoliciousのクックブック
説明¶
This document contains many fun recipes for cooking with Mojolicious.
Mojoliciousで料理できる楽しいレシピが満載。
概念¶
Essentials every Mojolicious developer should know.
すべてのMojolicious開発者が知るべき本質。
ブロッキング / ノンブロッキング処理¶
A blocking operation is a subroutine that blocks the execution of the calling subroutine until the subroutine is finished.
ブロッキング処理は、サブルーチンが終了するまで呼び出し元サブルーチンの実行を ブロックするサブルーチンです。
sub foo {
my $result = blocking_subroutine();
...
}
A non-blocking operation on the other hand lets the calling subroutine continue execution even though the subroutine is not yet finished. Instead of waiting, the calling subroutine passes along a callback to be executed once the subroutine is finished, this is called continuation-passing style.
一方、ノンブロッキング処理は、サブルーチンがまだ終了していない場合でも 呼び出しサブルーチンの実行が継続できるようにします。 待機する代わりに、呼び出し元のサブルーチンは、 サブルーチンが終了すると実行されるコールバックを渡します。 これは、継続渡しスタイルと呼ばれます。
sub foo {
non_blocking_subroutine(sub {
my $result = shift;
...
});
...
}
While Mojolicious has been designed from the ground up for non-blocking I/O and event loops, it is not possible to magically make Perl code non-blocking. You have to use specialized non-blocking code available through modules like Mojo::IOLoop and Mojo::UserAgent, or third-party event loops. You can wrap your blocking code in subprocesses though to prevent it from interfering with your non-blocking code.
MojoliciousはノンブロッキングI/Oおよびイベントループのために ゼロから設計されていますが、 魔法のごとくPerlコードをノンブロッキングにはできません。 そのためには、Mojo::IOLoopやMojo::UserAgent、または サードパーティのイベントループのようなモジュールを介して利用可能な 特殊なノンブロッキングコードを使用する必要があります。 ただし、subprocessesでブロッキングコードを ラップして、ノンブロッキングコードを妨害することを防げます。
イベントループ¶
An event loop is basically a loop that continually tests for external events and executes the appropriate callbacks to handle them, it is often the main loop in a program. Non-blocking tests for readability/writability of file descriptors and timers are commonly used events for highly scalable network servers, because they allow a single process to handle thousands of client connections concurrently.
イベントループは基本的に、外部イベントを継続的にテストし、 適切なコールバックを実行してそれらを処理するループです。 多くの場合、これがプログラムのメインループとなります。 ファイル記述子の読み取り/書き込みのノンブロッキングテストとタイマーは、 単一のプロセスで同時に数千のクライアント接続を処理できるため、 非常にスケーラブルなネットワークサーバーでよく使用されるイベントです。
while (1) {
my @readable = test_fds_for_readability();
handle_readable_fds(@readable);
my @writable = test_fds_for_writability();
handle_writable_fds(@writable);
my @expired = test_timers();
handle_timers(@expired);
}
In Mojolicious this event loop is Mojo::IOLoop.
Mojoliciousでは、このイベントループはMojo::IOLoopです。
リバースプロキシ¶
A reverse proxy architecture is a deployment technique used in many production environments, where a reverse proxy server is put in front of your application to act as the endpoint accessible by external clients. It can provide a lot of benefits, like terminating SSL connections from the outside, limiting the number of concurrent open sockets towards the Mojolicious application (or even using Unix sockets), balancing load across multiple instances, or supporting several applications through the same IP/port.
リバースプロキシアーキテクチャは、多くの運用環境で使用されるデプロイ手法です; リバースプロキシサーバーはアプリケーションの前に配置され、 外部クライアントからアクセス可能なエンドポイントとして機能します。 これには次のような多くの利点があります; 外部からのSSL接続の終端、Mojoliciousアプリケーションへの 同時オープンソケット数の制限(またはUnixソケットの使用についても)、 複数インスタンス間での負荷分散、または複数アプリケーション間での 同一IP/ポートの共有など。
..........................................
: :
+--------+ : +-----------+ +---------------+ :
| |-------->| | | | :
| client | : | reverse |----->| Mojolicious | :
| |<--------| proxy | | application | :
+--------+ : | |<-----| | :
: +-----------+ +---------------+ :
: :
.. system boundary (e.g. same host) ......
..........................................
: :
+--------+ : +-----------+ +---------------+ :
| |-------->| | | | :
| client | : | reverse |----->| Mojolicious | :
| |<--------| proxy | | application | :
+--------+ : | |<-----| | :
: +-----------+ +---------------+ :
: :
.. システム境界(例. 同一ホスト) ......
This setup introduces some problems, though: the application will receive requests from the reverse proxy instead of the original client; the address/hostname where your application lives internally will be different from the one visible from the outside; and if terminating SSL, the reverse proxy exposes services via HTTPS while using HTTP towards the Mojolicious application.
ただし、このセットアップではいくつかの問題が発生します: アプリケーションは、オリジナルのクライアントではなくリバースプロキシから リクエストを受け取ります; アプリケーション内部のアドレス/ホスト名は、外部から見えるものとは異なります; また、SSLを終端すると、リバースプロキシはHTTPS経由でサービスを公開し、 Mojoliciousアプリケーションに対してはHTTPを使用します。
As an example, compare a sample request from the client and what the Mojolicious application receives:
例として、クライアントからのサンプルリクエストとMojoliciousアプリケーションが 受け取るリクエストを比べてみましょう。
client reverse proxy Mojolicious app
__|__ _______________|______________ ____|____
/ \ / \ / \
1.2.3.4 --HTTPS--> api.example.com 10.20.30.39 --HTTP--> 10.20.30.40
クライアント リバースプロキシ Mojoliciousアプリ
__|__ _______________|______________ ____|____
/ \ / \ / \
1.2.3.4 --HTTPS--> api.example.com 10.20.30.39 --HTTP--> 10.20.30.40
GET /foo/1 HTTP/1.1 | GET /foo/1 HTTP/1.1
Host: api.example.com | Host: 10.20.30.40
User-Agent: Firefox | User-Agent: ShinyProxy/1.2
... | ...
However, now the client address is no longer available (which might be useful for analytics, or Geo-IP) and URLs generated via "url_for" in Mojolicious::Controller will look like this:
ただし、クライアントアドレスは使用できなくなり (分析やGeo-IPに役立つ可能性があります)、 "url_for" in Mojolicious::Controllerによって生成された URLは次のようになります:
http://10.20.30.40/bar/2
instead of something meaningful for the client, like this:
以下のようであればクライアントにとってわかりやすいのですが:
https://api.example.com/bar/2
To solve these problems, you can configure your reverse proxy to send the missing data (see "Nginx" and "Apache/mod_proxy") and tell your application about it by setting the environment variable MOJO_REVERSE_PROXY
. For finer control, "Rewriting" includes examples of how the changes could be implemented manually.
これらの問題を解決するには、不足しているデータを送信するように リバースプロキシを構成します(/Nginx
および/"Apache/mod_proxy"
)。 そして、環境変数MOJO_REVERSE_PROXY
を設定して、 アプリケーションに通知します。 きめ細かく制御するために、"Rewriting"には変更を手動で実装する方法の サンプルが含まれています。
デプロイメント¶
Getting Mojolicious and Mojolicious::Lite applications running on different platforms. Note that many real-time web features are based on the Mojo::IOLoop event loop, and therefore require one of the built-in web servers to be able to use them to their full potential.
Mojolicious と Mojolicious::Lite アプリケーションをさまざまな プラットフォームで実行させます。 多くのリアルタイムWeb機能がMojo::IOLoopのイベントループに基づいているので、 イベントループの機能を完全に引き出すためには、組み込みウェブサーバーの ひとつ以上を使用できる必要があります。
組み込みサーバ¶
Mojolicious contains a very portable non-blocking I/O HTTP and WebSocket server with Mojo::Server::Daemon. It is usually used during development and in the construction of more advanced web servers, but is solid and fast enough for small to mid sized applications.
Mojolicious には、とてもポータブルなノンブロッキング I/O の HTTP と WebSocket サーバ Mojo::Server::Daemon が含まれます。 通常これらは開発用やより高度なウェブサーバの構築中に利用されますが、 小中規模のアプリケーションであれば、 十分に堅牢かつ高速に動きます。
$ ./script/my_app daemon
Server available at http://127.0.0.1:3000
It is available to every application through the command Mojolicious::Command::daemon, which has many configuration options and is known to work on every platform Perl works on with its single-process architecture.
コマンドMojolicious::Command::daemonを介して すべてのアプリケーションで使用できます; 多くの設定オプションがあり、Perlが動作するすべてのプラットフォームにおいて 単一プロセスアーキテクチャで動きます。
$ ./script/my_app daemon -h
...List of available options...
$ ./script/my_app daemon -h
...利用可能オプションのリスト...
Another huge advantage is that it supports TLS and WebSockets out of the box, a development certificate for testing purposes is built right in, so it just works, but you can specify all listen locations supported by "listen" in Mojo::Server::Daemon.
もうひとつの大きな利点は、そのままで TLS と WebSoket を サポートしていることです; テスト目的のための開発証明書が適切に組み込まれているので、うまく動きます; ただし、すべての対応するリッスン先を "listen" in Mojo::Server::Daemonによって 指定できます。
$ ./script/my_app daemon -l https://[::]:3000
Server available at https://[::]:3000
To manage the web server with systemd, you can use a unit configuration file like this.
systemdを使用してWebサーバーを管理するには、次のようなユニット構成ファイルを 使用できます。
[Unit]
Description=My Mojolicious application
After=network.target
[Service]
Type=simple
ExecStart=/home/sri/myapp/script/my_app daemon -m production -l http://*:8080
[Install]
WantedBy=multi-user.target
プリフォーク¶
On UNIX platforms you can also add pre-forking to the built-in web server and switch to a multi-process architecture with Mojolicious::Command::prefork, to take advantage of multiple CPU cores and copy-on-write memory management.
UNIXプラットフォームでは、Mojolicious::Command::preforkによって 組み込みWebサーバにプリフォークが追加でき、複数プロセスアークテクチャに 切り替えることができます; 複数CPUコアとコピーオンライトメモリ管理を利用できるという利点があります。
$ ./script/my_app prefork
Server available at http://127.0.0.1:3000
Since all built-in web servers are based on the Mojo::IOLoop event loop, they scale best with non-blocking operations. But if your application for some reason needs to perform many blocking operations, you can improve performance by increasing the number of worker processes and decreasing the number of concurrent connections each worker is allowed to handle (often as low as 1
).
全ての組み込みのWebサーバーはMojo::IOLoopのイベントループに基づいているため、 ノンブロッキング処理を利用するときに一番スケールします。 しかし、何らかの理由によって多数のブロッキング処理をアプリケーションで 実行する必要がある場合は、ワーカープロセスの数を増やしつつ、ワーカーあたりの 同時接続数を減らすことでパフォーマンスを向上できます(多くの場合1
とする)。
$ ./script/my_app prefork -m production -w 10 -c 1
Server available at http://127.0.0.1:3000
During startup your application is preloaded in the manager process, which does not run an event loop, so you can use "next_tick" in Mojo::IOLoop to run code whenever a new worker process has been forked and its event loop gets started.
スタートアップの間にマネージャプロセスにおいてアプリケーションが 事前ロードされます; このときイベントループは開始しないので、新しいワーカープロセスがフォークされ、 イベントループが開始されるときはいつでも、"next_tick" in Mojo::IOLoopを使って コードを実行できます。
use Mojolicious::Lite;
Mojo::IOLoop->next_tick(sub {
app->log->info("Worker $$ star...ALL GLORY TO THE HYPNOTOAD!");
});
get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
app->start;
And to manage the pre-forking web server with systemd, you can use a unit configuration file like this.
また、systemdを使用してプリフォーク前のWebサーバーを管理するには、 次のようなユニット構成ファイルを使用できます。
[Unit]
Description=My Mojolicious application
After=network.target
[Service]
Type=simple
ExecStart=/home/sri/myapp/script/my_app prefork -m production -l http://*:8080
[Install]
WantedBy=multi-user.target
Morbo¶
After reading the Mojolicious::Guides::Tutorial, you should already be familiar with Mojo::Server::Morbo.
Mojolicious::Guides::Tutorialを読んだ後なら、Mojo::Server::Morboを すでに知っていることでしょう。
Mojo::Server::Morbo
+- Mojo::Server::Daemon
It is basically a restarter that forks a new Mojo::Server::Daemon web server whenever a file in your project changes, and should therefore only be used during development. To start applications with it you can use the morbo script.
基本的には、プロジェクト内の変更されたファイルを検知して、新しいMojo::Server::DaemonWebサーバーをフォークするリスターターです。 よって、これは開発用途でのみ使用してください。 Morboでアプリケーションを起動するには、morboスクリプトを使用します。
$ morbo ./script/my_app
Server available at http://127.0.0.1:3000
Hypnotoad¶
For bigger applications Mojolicious contains the UNIX optimized pre-forking web server Hypnotoad, which can take advantage of multiple CPU cores and copy-on-write memory management to scale up to thousands of concurrent client connections.
もっと大きいアプリケーションのために、Mojolicious には UNIX に最適化された プレフォーキングウェブサーバ Hypnotoad が 含まれています; 複数のCPUコアと書き込み時コピー (copy-on-write)が活用でき、 スケールアップして数千の並列クライアントに対応できます。
Mojo::Server::Hypnotoad
|- Mojo::Server::Daemon [1]
|- Mojo::Server::Daemon [2]
|- Mojo::Server::Daemon [3]
+- Mojo::Server::Daemon [4]
It is based on the Mojo::Server::Prefork web server, which adds pre-forking to Mojo::Server::Daemon, but optimized specifically for production environments out of the box. To start applications with it you can use the hypnotoad script, which listens on port 8080
, automatically daemonizes the server process and defaults to production
mode for Mojolicious and Mojolicious::Lite applications.
サーバーはMojo::Server::Prefork Webサーバーをベースにしています; これはMojo::Server::Daemonにプリフォーク機能を追加するものですが、 運用環境ですぐに使えるよう最適化されています。 アプリケーションを開始するには、hypnotoadスクリプトを使用します; ポート8080
でリッスンし、サーバープロセスを自動的にデーモン化し、 MojoliciousとMojolicious::Liteアプリケーションのモードを既定で production
にします。
$ hypnotoad ./script/my_app
Many configuration settings can be tweaked right from within your application with "config" in Mojolicious, for a full list see "SETTINGS" in Mojo::Server::Hypnotoad.
多くの構成設定はアプリケーションから"config" in Mojoliciousを使って 調整できます; すべての設定のリストは"SETTINGS" in Mojo::Server::Hypnotoadの項目を 見てください.
use Mojolicious::Lite;
app->config(hypnotoad => {listen => ['http://*:80']});
get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'};
app->start;
Or just add a hypnotoad
section to your Mojolicious::Plugin::Config or Mojolicious::Plugin::JSONConfig configuration file.
Mojolicious::Plugin::ConfigかMojolicious::Plugin::JSONConfigの 設定ファイルにhypnotoad
セクションを追加することもできます。
# myapp.conf
{
hypnotoad => {
listen => ['https://*:443?cert=/etc/server.crt&key=/etc/server.key'],
workers => 10
}
};
But one of its biggest advantages is the support for effortless zero downtime software upgrades (hot deployment). That means you can upgrade Mojolicious, Perl or even system libraries at runtime without ever stopping the server or losing a single incoming connection, just by running the command above again.
しかし、最大の利点の一つは、ダウンタイムなしのソフトウェア更新 (ホットデプロイメント)をサポートしていることです。 つまり、サーバーを止めたり、受信接続をひとつも失うことなく、 上記のコマンドを実行するだけで Mojolicious や Perl、そして 実行中のシステムライブラリでさえ更新できます。
$ hypnotoad ./script/my_app
Starting hot deployment for Hypnotoad server 31841.
$ hypnotoad ./script/my_app
Starting hot deployment for Hypnotoad server 31841.(Hypnotoadサーバー31841のホットデプロイメントを開始しています。)
You might also want to enable proxy support if you're using Hypnotoad behind a reverse proxy. This allows Mojolicious to automatically pick up the X-Forwarded-For
and X-Forwarded-Proto
headers.
リバースプロキシの後ろでHypnotoadを 使用している場合は、プロキシサポートを有効にすることもできます。 MojoliciousがX-Forwarded-For
やX-Forwarded-Proto
ヘッダーを 自動的に検知できるようになります。
# myapp.conf
{hypnotoad => {proxy => 1}};
To manage Hypnotoad with systemd, you can use a unit configuration file like this.
Hypnotoadをsystemdで管理するには、次のような ユニット構成ファイルを使用できます。
[Unit]
Description=My Mojolicious application
After=network.target
[Service]
Type=forking
PIDFile=/home/sri/myapp/script/hypnotoad.pid
ExecStart=/path/to/hypnotoad /home/sri/myapp/script/my_app
ExecReload=/path/to/hypnotoad /home/sri/myapp/script/my_app
KillMode=process
[Install]
WantedBy=multi-user.target
ゼロダウンタイム・ソフトウェア更新¶
Hypnotoad makes zero downtime software upgrades (hot deployment) very simple, as you can see above, but on modern operating systems that support the SO_REUSEPORT
socket option, there is also another method available that works with all built-in web servers.
Hypnotoadは、上記のように、ダウンタイムのない ソフトウェアアップグレード(ホットデプロイメント)を非常に簡単にします; しかし、SO_REUSEPORT
をサポートするモダンなオペレーティングシステムでは、 すべての組み込みWebサーバーで使える別の方法もあります。
$ ./script/my_app prefork -P /tmp/first.pid -l http://*:8080?reuse=1
Server available at http://127.0.0.1:8080
All you have to do, is to start a second web server listening to the same port, and stop the first web server gracefully afterwards.
すべきことは、同じポートで二つ目のWebサーバーを起動し、 その後に一つ目のWebサーバーをGraceful Shutdownさせることです。
$ ./script/my_app prefork -P /tmp/second.pid -l http://*:8080?reuse=1
Server available at http://127.0.0.1:8080
$ kill -s TERM `cat /tmp/first.pid`
Just remember that both web servers need to be started with the reuse
parameter.
両方のWebサーバーはreuse
パラメーターを付けて起動する必要があります。
Nginx¶
One of the most popular setups these days is Hypnotoad behind an Nginx reverse proxy, which even supports WebSockets in newer versions.
この頃、最も人気のある構成のひとつは、Hypnotoadを Nginxのリバースプロキシの後ろに置くものです; Nginxの新しいバージョンはWebSocketもサポートしています。
upstream myapp {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://myapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Apache/mod_proxy¶
Another good reverse proxy is Apache with mod_proxy
, the configuration looks quite similar to the Nginx one above. And if you need WebSocket support, newer versions come with mod_proxy_wstunnel
.
その他ですぐれたリバースプロキシといえばApacheの mod_proxy
でしょう; 設定は先ほどのNginxにとてもよく似ています。 また、WebSocketサポートが必要な場合、新しいバージョンには mod_proxy_wstunnel
が付属します。
<VirtualHost *:80>
ServerName localhost
<Proxy *>
Require all granted
</Proxy>
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /echo ws://localhost:8080/echo
ProxyPass / http://localhost:8080/ keepalive=On
ProxyPassReverse / http://localhost:8080/
RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>
Apache/CGI¶
CGI
is supported out of the box and your Mojolicious application will automatically detect that it is executed as a CGI
script. Its use in production environments is discouraged though, because as a result of how CGI
works, it is very slow and many web servers are making it exceptionally hard to configure properly. Additionally, many real-time web features, such as WebSockets, are not available.
CGI
はそのままですぐにサポートされ、Mojoliciousアプリケーションは、 CGI
スクリプトとして実行されていることを自動的に検出します。 ただし、本番環境での使用は推奨されません; CGI
の動作の仕組みがゆえに、非常に遅く、Webサーバーの種類が多いことから 適切な設定が非常に難しくなっています。 また、WebSocketなどの多くのリアルタイムWeb機能が使用できません。
ScriptAlias / /home/sri/my_app/script/my_app/
PSGI/Plack¶
PSGI is an interface between Perl web frameworks and web servers, and Plack is a Perl module and toolkit that contains PSGI middleware, helpers and adapters to web servers. PSGI and Plack are inspired by Python's WSGI and Ruby's Rack. Mojolicious applications are ridiculously simple to deploy with Plack, but be aware that many real-time web features, such as WebSockets, are not available.
PSGIは、Perl WebフレームワークとWebサーバー間のインターフェースです; Plackは、Perlモジュールおよびツールキットであり、PSGIのミドルウェア、 ヘルパー、およびWebサーバーへのアダプターを含みます。 PSGI と Plack は Python の WSGI と Ruby の Rack に触発されています。 Plackを使用したMojoliciousアプリケーションのデプロイはあっけないほど 簡単です; ただし、WebSocketなどの多くのリアルタイムWeb機能は使用できないことに 注意してください。
$ plackup ./script/my_app
Plack provides many server and protocol adapters for you to choose from, such as FCGI
, uWSGI
and mod_perl
.
Plack は、FCGI
、uWSGI
、mod_perl
など、多くのサーバーや プロトコルのためのアダプタを提供します。
$ plackup ./script/my_app -s FCGI -l /tmp/myapp.sock
The MOJO_REVERSE_PROXY
environment variable can be used to enable proxy support, this allows Mojolicious to automatically pick up the X-Forwarded-For
and X-Forwarded-Proto
headers.
MOJO_REVERSE_PROXY
環境変数は、プロキシのサポートのために利用できます; Mojoliciousが自動的にX-Forwarded-For
とX-Forwarded-Proto
ヘッダーを 取得できるようになります。
$ MOJO_REVERSE_PROXY=1 plackup ./script/my_app
If an older server adapter is unable to correctly detect the application home directory, you can simply use the MOJO_HOME
environment variable.
古いサーバーのアダプタがアプリケーションのホームディレクトリを 正しく検知できなかった場合は、単純にMOJO_HOME
環境変数を使用できます。
$ MOJO_HOME=/home/sri/my_app plackup ./script/my_app
There is no need for a .psgi
file, just point the server adapter at your application script, it will automatically act like one if it detects the presence of a PLACK_ENV
environment variable.
.psgi
ファイルは必要ありません; サーバーアダプターを アプリケーションスクリプトにて指定しておけば、PLACK_ENV
環境変数を 検知したときに、自動的に.psgi
ファイルであるかのように振舞います。
Plackミドルウェア¶
Wrapper scripts like myapp.fcgi
are a great way to separate deployment and application logic.
myapp.fcgi
などのラッパースクリプトを使うのは、デプロイメントと アプリケーションロジックを分離するとてもよい方法です。
#!/usr/bin/env plackup -s FCGI
use Plack::Builder;
builder {
enable 'Deflater';
require './script/my_app';
};
Mojo::Server::PSGI can be used directly to load and customize applications in the wrapper script.
Mojo::Server::PSGIを直接使うと、ラッパースクリプトのなかで アプリケーションを読み込んだりカスタマイズしたりできます。
#!/usr/bin/env plackup -s FCGI
use Mojo::Server::PSGI;
use Plack::Builder;
builder {
enable 'Deflater';
my $server = Mojo::Server::PSGI->new;
$server->load_app('./script/my_app');
$server->app->config(foo => 'bar');
$server->to_psgi_app;
};
But you could even use middleware right in your application.
アプリケーションの中でミドルウェアを使うことさえできます。
use Mojolicious::Lite;
use Plack::Builder;
get '/welcome' => sub {
my $c = shift;
$c->render(text => 'Hello Mojo!');
};
builder {
enable 'Deflater';
app->start;
};
書き換え¶
Sometimes you might have to deploy your application in a blackbox environment where you can't just change the server configuration or behind a reverse proxy that passes along additional information with X-Forwarded-*
headers. In such cases you can use the hook "before_dispatch" in Mojolicious to rewrite incoming requests.
ときとして、アプリケーションを、自分でサーバの設定を変えることができない ブラックボックス環境、または、X-Forwarded-*
ヘッダーで補助情報を伝える リバースプロキシの背後でデプロイする必要があるかもしれません。 そのような場合、"before_dispatch" in Mojolicious フックを使用して 受信リクエストを書き換えられます。
# Change scheme if "X-Forwarded-HTTPS" header is set
$app->hook(before_dispatch => sub {
my $c = shift;
$c->req->url->base->scheme('https')
if $c->req->headers->header('X-Forwarded-HTTPS');
});
# "X-Forwarded-HTTPS"ヘッダーが"https"に設定されていた場合にスキーマを変更
$app->hook(before_dispatch => sub {
my $c = shift;
$c->req->url->base->scheme('https')
if $c->req->headers->header('X-Forwarded-HTTPS');
});
Since reverse proxies generally don't pass along information about path prefixes your application might be deployed under, rewriting the base path of incoming requests is also quite common. This allows "url_for" in Mojolicious::Controller for example, to generate portable URLs based on the current environment.
一般的にリバースプロキシはアプリケーションがデプロイされたパスの前部分を 渡さないので、受信するリクエストのベースパスの書き換えがよく行われます。 これにより、たとえば"url_for" in Mojolicious::Controllerを使って 現在の環境に合わせたURLを生成できます。
# Move first part and slash from path to base path in production mode
$app->hook(before_dispatch => sub {
my $c = shift;
push @{$c->req->url->base->path->trailing_slash(1)},
shift @{$c->req->url->path->leading_slash(0)};
}) if $app->mode eq 'production';
# productionモードではパスの最初の部分とスラッシュをベースパスに移動
$app->hook(before_dispatch => sub {
my $c = shift;
push @{$c->req->url->base->path->trailing_slash(1)},
shift @{$c->req->url->path->leading_slash(0)};
}) if $app->mode eq 'production';
Mojo::URL objects are very easy to manipulate, just make sure that the URL (foo/bar?baz=yada
), which represents the routing destination, is always relative to the base URL (http://example.com/myapp/
), which represents the deployment location of your application.
Mojo::URLオブジェクトの操作はとても簡単です; 常に、ルーティングの行き先を 表すURL (foo/bar?baz=yada
)を、アプリケーションのデプロイメント場所を示す ベースURL (http://example.com/myapp/
)からの相対パスとなるようにします。
アプリケーションの埋め込み¶
From time to time you might want to reuse parts of Mojolicious applications like configuration files, database connection or helpers for other scripts, with this little Mojo::Server based mock server you can just embed them.
時によって、設定ファイル、データベース接続、その他スクリプトのための ヘルパーなど、Mojoliciousアプリケーションのパーツを再利用したい場合が あるかもしれません; 次のようなMojo::Serverをベースにしたモックサーバーでそれらを 埋め込むことができます。
use Mojo::Server;
# Load application with mock server
my $server = Mojo::Server->new;
my $app = $server->load_app('./myapp.pl');
# モックサーバーでアプリケーションをロード
my $server = Mojo::Server->new;
my $app = $server->load_app('./myapp.pl');
# Access fully initialized application
say for @{$app->static->paths};
say $app->config->{secret_identity};
say $app->dumper({just => 'a helper test'});
say $app->build_controller->render_to_string(template => 'foo');
# 完全に初期化されたアプリケーションにアクセス
say for @{$app->static->paths};
say $app->config->{secret_identity};
say $app->dumper({just => 'a helper test'});
say $app->build_controller->render_to_string(template => 'foo');
The plugin Mojolicious::Plugin::Mount uses this functionality to allow you to combine multiple applications into one and deploy them together.
Mojolicious::Plugin::Mountは、この機能を使って複数のアプリケーションを ひとつに結合し、まとめてデプロイできるようにします。
use Mojolicious::Lite;
app->config(hypnotoad => {listen => ['http://*:80']});
plugin Mount => {'test1.example.com' => '/home/sri/myapp1.pl'};
plugin Mount => {'test2.example.com' => '/home/sri/myapp2.pl'};
app->start;
Webサーバーの埋め込み¶
You can also use "one_tick" in Mojo::IOLoop to embed the built-in web server Mojo::Server::Daemon into alien environments like foreign event loops that for some reason can't just be integrated with a new reactor backend.
"one_tick" in Mojo::IOLoopを使用して、組み込みWebサーバーMojo::Server::Daemonを、なんらかの理由で新しいリアクターのバックエンドに統合できない外部イベントループのような異なった環境に埋め込むことができます。
use Mojolicious::Lite;
use Mojo::IOLoop;
use Mojo::Server::Daemon;
# Normal action
get '/' => {text => 'Hello World!'};
# 通常のアクション
get '/' => {text => 'Hello World!'};
# Connect application with web server and start accepting connections
my $daemon
= Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
$daemon->start;
# アプリケーションをWebサーバーと接続して、接続の受付を開始する
my $daemon
= Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
$daemon->start;
# Call "one_tick" repeatedly from the alien environment
Mojo::IOLoop->one_tick while 1;
# 外部の環境から"one_tick"を繰り返し呼び出す
Mojo::IOLoop->one_tick while 1;
リアルタイムWeb¶
The real-time web is a collection of technologies that include Comet (long polling), EventSource and WebSockets, which allow content to be pushed to consumers with long-lived connections as soon as it is generated, instead of relying on the more traditional pull model. All built-in web servers use non-blocking I/O and are based on the Mojo::IOLoop event loop, which provides many very powerful features that allow real-time web applications to scale up to thousands of concurrent client connections.
リアルタイムWebとは、Comet(ロングポーリング)、EventSource、WebSockeといったテクノロジーの集まりのことです。 伝統的なプルモデルに頼る代わりに長時間接続を用いることで、コンテンツが生成されるとすぐにクライアントにプッシュすることができます。 すべての組み込みサーバーはノンブロッキングI/Oを使っていて、Mojo::IOLoopのリアクターをベースにしています; 多くの強力な機能によって、リアルタイムWebアプリケーションをスケールアップすることができ、数千の並列のクライアントを処理できます。
Webサービスのバックエンド¶
Since Mojo::UserAgent is also based on the Mojo::IOLoop event loop, it won't block the built-in web servers when used non-blocking, even for high latency backend web services.
Mojo::UserAgentはMojo::IOLoopのリアクターをベースに作られているため、ノンブロッキングで利用されるとき、高レイテンシのバックエンドWebサービスであっても、組み込みWebサーバーをブロックしません。
use Mojolicious::Lite;
# Search MetaCPAN for "mojolicious"
get '/' => sub {
my $c = shift;
$c->ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
my ($ua, $tx) = @_;
$c->render('metacpan', hits => $tx->result->json->{hits}{hits});
});
};
# MetaCPANで"mojolicious"を検索
get '/' => sub {
my $c = shift;
$c->ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
my ($ua, $tx) = @_;
$c->render('metacpan', hits => $tx->result->json->{hits}{hits});
});
};
app->start;
__DATA__
@@ metacpan.html.ep
<!DOCTYPE html>
<html>
<head><title>MetaCPAN results for "mojolicious"</title></head>
<body>
% for my $hit (@$hits) {
<p><%= $hit->{_source}{release} %></p>
% }
</body>
</html>
The callback passed to "get" in Mojo::UserAgent will be executed once the request to the backend web service has been finished, this is called continuation-passing style.
"get" in Mojo::UserAgentに渡されるコールバックは、バックエンドWebサービスへのリクエストが完了すると実行されます; これは継続渡しスタイルと呼ばれます。
ノンブロッキング処理の同期¶
Multiple non-blocking operations, such as concurrent requests, can be easily synchronized with promises and "all" in Mojo::Promise. You create Mojo::Promise objects manually or use methods like "get_p" in Mojo::UserAgent that create them for you.
同時リクエストなどの複数のノンブロッキング処理は、promiseおよび"all" in Mojo::Promiseを使って簡単に同期できます。 Mojo::Promiseオブジェクトを手動で作成するか、"get_p" in Mojo::UserAgentなどのメソッドを使用してそれらが作成されるようにします。
use Mojolicious::Lite;
use Mojo::Promise;
use Mojo::URL;
# Search MetaCPAN for "mojo" and "minion"
get '/' => sub {
my $c = shift;
# MetaCPANで"mojo"と"minion"を検索
get '/' => sub {
my $c = shift;
# Create two promises
my $url = Mojo::URL->new('fastapi.metacpan.org/v1/module/_search');
my $mojo = $c->ua->get_p($url->clone->query({q => 'mojo'}));
my $minion = $c->ua->get_p($url->clone->query({q => 'minion'}));
# promiseをふたつ作成
my $url = Mojo::URL->new('fastapi.metacpan.org/v1/module/_search');
my $mojo = $c->ua->get_p($url->clone->query({q => 'mojo'}));
my $minion = $c->ua->get_p($url->clone->query({q => 'minion'}));
# Render a response once both promises have been resolved
Mojo::Promise->all($mojo, $minion)->then(sub {
my ($mojo, $minion) = @_;
$c->render(json => {
mojo => $mojo->[0]->result->json('/hits/hits/0/_source/release'),
minion => $minion->[0]->result->json('/hits/hits/0/_source/release')
});
})->catch(sub {
my $err = shift;
$c->reply->exception($err);
})->wait;
};
# promiseがふたつとも完了したらレスポンスを描画
Mojo::Promise->all($mojo, $minion)->then(sub {
my ($mojo, $minion) = @_;
$c->render(json => {
mojo => $mojo->[0]->result->json('/hits/hits/0/_source/release'),
minion => $minion->[0]->result->json('/hits/hits/0/_source/release')
});
})->catch(sub {
my $err = shift;
$c->reply->exception($err);
})->wait;
};
app->start;
To create promises manually you just wrap your continuation-passing style APIs in functions that return promises. Here's an example for how "get_p" in Mojo::UserAgent works internally.
promiseを手動で作成するには、継続渡しスタイルのAPIをpromiseを返す関数でラップするだけです。 ここで、"get_p" in Mojo::UserAgentが内部でどのように動作するかを例にして説明します。
use Mojo::UserAgent;
use Mojo::Promise;
# Wrap a user agent method with a promise
my $ua = Mojo::UserAgent->new;
sub get_p {
my $promise = Mojo::Promise->new;
$ua->get(@_ => sub {
my ($ua, $tx) = @_;
my $err = $tx->error;
$promise->resolve($tx) if !$err || $err->{code};
$promise->reject($err->{message});
});
return $promise;
}
# ユーザーエージェントメソッドをpromiseでラップする
my $ua = Mojo::UserAgent->new;
sub get_p {
my $promise = Mojo::Promise->new;
$ua->get(@_ => sub {
my ($ua, $tx) = @_;
my $err = $tx->error;
$promise->resolve($tx) if !$err || $err->{code};
$promise->reject($err->{message});
});
return $promise;
}
# Use our new promise generating function
get_p('https://mojolicious.org')->then(sub {
my $tx = shift;
say $tx->result->dom->at('title')->text;
})->wait;
# 作成したpromiseを生成する関数を使う
get_p('https://mojolicious.org')->then(sub {
my $tx = shift;
say $tx->result->dom->at('title')->text;
})->wait;
Promises have three states, they start out as pending
and you call "resolve" in Mojo::Promise to transition them to fulfilled
, or "reject" in Mojo::Promise to transition them to rejected
.
promiseは3つの状態を持ちます; はじめはpending
となり、"resolve" in Mojo::Promiseをコールするとfulfilled
に遷移し、あるいは、"reject" in Mojo::Promiseをコールするとrejected
に遷移します。
タイマー¶
Timers, another primary feature of the event loop, are created with "timer" in Mojo::IOLoop and can, for example, be used to delay rendering of a response, and unlike sleep
, won't block any other requests that might be processed concurrently.
イベントループのもう1つの主要な機能であるタイマーは、"timer" in Mojo::IOLoopで作成します; タイマーは、たとえばレスポンスの描画を遅らせるために使用できます; sleep
とは異なり、並列に処理される他のリクエストをブロックしません。
use Mojolicious::Lite;
use Mojo::IOLoop;
# Wait 3 seconds before rendering a response
get '/' => sub {
my $c = shift;
Mojo::IOLoop->timer(3 => sub {
$c->render(text => 'Delayed by 3 seconds!');
});
};
# 3秒待ってレスポンスを描画する
get '/' => sub {
my $c = shift;
Mojo::IOLoop->timer(3 => sub {
$c->render(text => '3秒遅れています!');
});
};
app->start;
Recurring timers created with "recurring" in Mojo::IOLoop are slightly more powerful, but need to be stopped manually, or they would just keep getting emitted.
"recurring" in Mojo::IOLoopで作成した繰り返しタイマーは、もう少し強力ですが、手動で停止する必要があります; そうしないと、ひたすら動作し続けます。
use Mojolicious::Lite;
use Mojo::IOLoop;
# Count to 5 in 1 second steps
get '/' => sub {
my $c = shift;
# 1秒刻みで5まで数える
get '/' => sub {
my $c = shift;
# Start recurring timer
my $i = 1;
my $id = Mojo::IOLoop->recurring(1 => sub {
$c->write_chunk($i);
$c->finish if $i++ == 5;
});
# 繰り返しタイマーを開始
my $i = 1;
my $id = Mojo::IOLoop->recurring(1 => sub {
$c->write_chunk($i);
$c->finish if $i++ == 5;
});
# Stop recurring timer
$c->on(finish => sub { Mojo::IOLoop->remove($id) });
};
# 繰り返しタイマーを停止
$c->on(finish => sub { Mojo::IOLoop->remove($id) });
};
app->start;
Timers are not tied to a specific request or connection, and can even be created at startup time.
タイマーは特定のリクエストや接続に関連付けられてはいないため、スタートアップ時に生成することもできます。
use Mojolicious::Lite;
use Mojo::IOLoop;
# Check title in the background every 10 seconds
my $title = 'Got no title yet.';
Mojo::IOLoop->recurring(10 => sub {
app->ua->get('https://mojolicious.org' => sub {
my ($ua, $tx) = @_;
$title = $tx->result->dom->at('title')->text;
});
});
# 10秒ごとにバックグラウンドでタイトルをチェック
my $title = 'Got no title yet.';
Mojo::IOLoop->recurring(10 => sub {
app->ua->get('https://mojolicious.org' => sub {
my ($ua, $tx) = @_;
$title = $tx->result->dom->at('title')->text;
});
});
# Show current title
get '/' => sub {
my $c = shift;
$c->render(json => {title => $title});
};
# 現在のタイトルを表示
get '/' => sub {
my $c = shift;
$c->render(json => {title => $title});
};
app->start;
Just remember that all these non-blocking operations are processed cooperatively, so your callbacks shouldn't block for too long.
これらのノンブロッキング処理はすべて協調して処理されるため、コールバック関数が長時間ブロックされることはありません。
サブプロセス¶
You can also use subprocesses, created with "subprocess" in Mojo::IOLoop, to perform computationally expensive operations without blocking the event loop.
サブプロセスを"subprocess" in Mojo::IOLoopで作成して使用することで、イベントループをブロックせずに計算負荷の高い操作を実行できます。
use Mojolicious::Lite;
use Mojo::IOLoop;
# Operation that would block the event loop for 5 seconds
get '/' => sub {
my $c = shift;
Mojo::IOLoop->subprocess(
sub {
my $subprocess = shift;
sleep 5;
return '♥', 'Mojolicious';
},
sub {
my ($subprocess, $err, @results) = @_;
$c->reply->exception($err) and return if $err;
$c->render(text => "I $results[0] $results[1]!");
}
);
};
# イベントループを5秒間ブロックする処理
get '/' => sub {
my $c = shift;
Mojo::IOLoop->subprocess(
sub {
my $subprocess = shift;
sleep 5;
return '♥', 'Mojolicious';
},
sub {
my ($subprocess, $err, @results) = @_;
$c->reply->exception($err) and return if $err;
$c->render(text => "I $results[0] $results[1]!");
}
);
};
app->start;
The first callback will be executed in a child process, without blocking the event loop of the parent process. The results of the first callback will then be shared between both processes, and the second callback executed in the parent process.
最初のコールバックは、親プロセスのイベントループをブロックせずに、子プロセスとして実行されます。 最初のコールバックの結果は両方のプロセス間で共有され、2番目のコールバックが親プロセスで実行されます。
ノンブロッキング処理における例外¶
Since timers and other non-blocking operations are running solely in the event loop, outside of the application, exceptions that get thrown in callbacks can't get caught and handled automatically. But you can handle them manually by subscribing to the event "error" in Mojo::Reactor or catching them inside the callback.
タイマーと他のノンブロッキング処理は、アプリケーション外部において単一のイベントループの中のみで実行されているため、コールバックの中で発生した例外は自動的にはキャッチできません。 しかし、"error" in Mojo::Reactorイベントを購読することによって手動で処理することができます。また、コールバックの中でキャッチすることもできます。
use Mojolicious::Lite;
use Mojo::IOLoop;
# Forward error messages to the application log
Mojo::IOLoop->singleton->reactor->on(error => sub {
my ($reactor, $err) = @_;
app->log->error($err);
});
# エラーメッセージをアプリケーションのログに転送する
Mojo::IOLoop->singleton->reactor->on(error => sub {
my ($reactor, $err) = @_;
app->log->error($err);
});
# Exception only gets logged (and connection times out)
get '/connection_times_out' => sub {
my $c = shift;
Mojo::IOLoop->timer(2 => sub {
die 'This request will not be getting a response';
});
};
# 例外(と接続タイムアウト)だけをロギング
get '/connection_times_out' => sub {
my $c = shift;
Mojo::IOLoop->timer(2 => sub {
die 'このリクエストにはレスポンスが返ってこない。';
});
};
# Exception gets caught and handled
get '/catch_exception' => sub {
my $c = shift;
Mojo::IOLoop->timer(2 => sub {
eval { die 'This request will be getting a response' };
$c->reply->exception($@) if $@;
});
};
# 例外をキャッチして処理する
get '/catch_exception' => sub {
my $c = shift;
Mojo::IOLoop->timer(2 => sub {
eval { die 'このリクエストにはレスポンスが返る' };
$c->reply->exception($@) if $@;
});
};
app->start;
A default subscriber that turns all errors into warnings will usually be added by Mojo::IOLoop as a fallback.
通常、デフォルトですべてのエラーを警告に変換するサブスクライバーが、Mojo::IOLoopによってフォールバックとして追加されます。
Mojo::IOLoop->singleton->reactor->unsubscribe('error');
During development or for applications where crashing is simply preferable, you can also make every exception that gets thrown in a callback fatal by removing all of its subscribers.
開発時またはクラッシュするのが望ましいアプリケーションのためには、サブスクライバーをすべて取り除くことによって、コールバックで発生するすべての例外が致命的なものになります。
WebSocketによるWebサービス¶
The WebSocket protocol offers full bi-directional low-latency communication channels between clients and servers. Receive messages just by subscribing to events such as "message" in Mojo::Transaction::WebSocket with "on" in Mojolicious::Controller and return them with "send" in Mojolicious::Controller.
WebSocketプロトコルは、サーバーとクライアントの間を双方向で、低レイテンシでつなぐ通信チャンネルです。 メッセージは"message" in Mojo::Transaction::WebSocketなどのイベントを"on" in Mojolicious::Controllerでサブスクライブするだけで受信できます; そして、"send" in Mojolicious::Controllerでメッセージを返します 。
use Mojolicious::Lite;
# Template with browser-side code
get '/' => 'index';
# ブラウザ側コードを含むテンプレート
get '/' => 'index';
# WebSocket echo service
websocket '/echo' => sub {
my $c = shift;
# WebSocketのエコーサービス
websocket '/echo' => sub {
my $c = shift;
# Opened
$c->app->log->debug('WebSocket opened');
# 接続を開く
$c->app->log->debug('WebSocket opened');
# Increase inactivity timeout for connection a bit
$c->inactivity_timeout(300);
# 接続のタイムアウト時間を少し増やす
$c->inactivity_timeout(300);
# Incoming message
$c->on(message => sub {
my ($c, $msg) = @_;
$c->send("echo: $msg");
});
# 受信メッセージ
$c->on(message => sub {
my ($c, $msg) = @_;
$c->send("echo: $msg");
});
# Closed
$c->on(finish => sub {
my ($c, $code, $reason) = @_;
$c->app->log->debug("WebSocket closed with status $code");
});
};
# 接続を閉じる
$c->on(finish => sub {
my ($c, $code, $reason) = @_;
$c->app->log->debug("WebSocket closed with status $code");
});
};
app->start;
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head><title>Echo</title></head>
<body>
<script>
var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
// Incoming messages
ws.onmessage = function (event) {
document.body.innerHTML += event.data + '<br/>';
};
// 受信メッセージ
ws.onmessage = function (event) {
document.body.innerHTML += event.data + '<br/>';
};
// Outgoing messages
ws.onopen = function (event) {
window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
};
</script>
</body>
</html>
// 送信メッセージ
ws.onopen = function (event) {
window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
};
</script>
</body>
</html>
The event "finish" in Mojo::Transaction::WebSocket will be emitted right after the WebSocket connection has been closed.
"finish" in Mojo::Transaction::WebSocketイベントは、WebSocket接続が閉じるとすぐに発行されます。
$c->tx->with_compression;
You can activate permessage-deflate
compression with "with_compression" in Mojo::Transaction::WebSocket, this can result in much better performance, but also increases memory usage by up to 300KiB per connection.
"with_compression" in Mojo::Transaction::WebSocketを使って、permessage-deflate
圧縮を有効にすることができます; これはパフォーマンスを大きく改善しますが、接続あたりのメモリ使用量が最大で300KB増えます。
my $proto = $c->tx->with_protocols('v2.proto', 'v1.proto');
You can also use "with_protocols" in Mojo::Transaction::WebSocket to negotiate a subprotocol.
"with_protocols" in Mojo::Transaction::WebSocketを使ってサブプロトコルをネゴシエートすることもできます。
EventSourceによるWebサービス¶
EventSource is a special form of long polling where you can use "write" in Mojolicious::Controller to directly send DOM events from servers to clients. It is uni-directional, that means you will have to use Ajax requests for sending data from clients to servers, the advantage however is low infrastructure requirements, since it reuses the HTTP protocol for transport.
EventSourceは、特別な形式のロングポーリング("write" in Mojolicious::Controllerを使うなど)です; サーバーからクライアントへDOMイベントを直接送信できます。 送信は一方行なので、クライアントからサーバーへのデータの送信にはAjaxリクエストを使う必要があります; しかしながら、データ送信にHTTPプロトコルを再利用しており、インフラ要件が少ないという利点があります。
use Mojolicious::Lite;
# Template with browser-side code
get '/' => 'index';
# ブラウザ側コードを含むテンプレート
get '/' => 'index';
# EventSource for log messages
get '/events' => sub {
my $c = shift;
# ログメッセージのためのEventSource
get '/events' => sub {
my $c = shift;
# Increase inactivity timeout for connection a bit
$c->inactivity_timeout(300);
# 接続のタイムアウト時間を少し増やす
$c->inactivity_timeout(300);
# Change content type and finalize response headers
$c->res->headers->content_type('text/event-stream');
$c->write;
# コンテンツタイプを変更して、レスポンスヘッダをファイナライズ
$c->res->headers->content_type('text/event-stream');
$c->write;
# Subscribe to "message" event and forward "log" events to browser
my $cb = $c->app->log->on(message => sub {
my ($log, $level, @lines) = @_;
$c->write("event:log\ndata: [$level] @lines\n\n");
});
# "message"イベントをサブスクライブし、"log"イベントをブラウザに送る
my $cb = $c->app->log->on(message => sub {
my ($log, $level, @lines) = @_;
$c->write("event:log\ndata: [$level] @lines\n\n");
});
# Unsubscribe from "message" event again once we are done
$c->on(finish => sub {
my $c = shift;
$c->app->log->unsubscribe(message => $cb);
});
};
# 終わったら、"message"イベントのサブスクライブを解除する
$c->on(finish => sub {
my $c = shift;
$c->app->log->unsubscribe(message => $cb);
});
};
app->start;
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head><title>LiveLog</title></head>
<body>
<script>
var events = new EventSource('<%= url_for 'events' %>');
// Subscribe to "log" event
events.addEventListener('log', function (event) {
document.body.innerHTML += event.data + '<br/>';
}, false);
</script>
</body>
</html>
// "log"イベントをサブスクライブする
events.addEventListener('log', function (event) {
document.body.innerHTML += event.data + '<br/>';
}, false);
</script>
</body>
</html>
The event "message" in Mojo::Log will be emitted for every new log message and the event "finish" in Mojo::Transaction right after the transaction has been finished.
"message" in Mojo::Logイベントは、ログメッセージが発生するごと放出され、"finish" in Mojo::Transactionイベントはトランザクションが完了した直後に放出されます。
マルチパートアップロードのストリーミング¶
Mojolicious contains a very sophisticated event system based on Mojo::EventEmitter, with ready-to-use events on almost all layers, and which can be combined to solve some of the hardest problems in web development.
Mojoliciousは、Mojo::EventEmitterを基盤とするとても洗練されたイベントシステムをもち、ほとんどすべての層でイベントがすぐに使えます; そして、このイベントシステムを組み合わせることで、WEB開発のなかで難易度が高い問題を解決することができるでしょう。
use Mojolicious::Lite;
use Scalar::Util 'weaken';
# Intercept multipart uploads and log each chunk received
hook after_build_tx => sub {
my $tx = shift;
# マルチパートアップロードの間に割り入って、チャンクを受け取るごとにログを出力する
hook after_build_tx => sub {
my $tx = shift;
# Subscribe to "upgrade" event to identify multipart uploads
weaken $tx;
$tx->req->content->on(upgrade => sub {
my ($single, $multi) = @_;
return unless $tx->req->url->path->contains('/upload');
# "upgrade"イベントをサブスクライブして、マルチパートアップロードを識別する
weaken $tx;
$tx->req->content->on(upgrade => sub {
my ($single, $multi) = @_;
return unless $tx->req->url->path->contains('/upload');
# Subscribe to "part" event to find the right one
$multi->on(part => sub {
my ($multi, $single) = @_;
# "part"イベントをサブスクライブして、目当てのモノを探す
$multi->on(part => sub {
my ($multi, $single) = @_;
# Subscribe to "body" event of part to make sure we have all headers
$single->on(body => sub {
my $single = shift;
# "body"イベントをサブスクライブして、ヘッダがすべてあることを確認する
$single->on(body => sub {
my $single = shift;
# Make sure we have the right part and replace "read" event
return unless $single->headers->content_disposition =~ /example/;
$single->unsubscribe('read')->on(read => sub {
my ($single, $bytes) = @_;
# 正しいパーツを持っていることを確認し、"read"イベントを置き換える
return unless $single->headers->content_disposition =~ /example/;
$single->unsubscribe('read')->on(read => sub {
my ($single, $bytes) = @_;
# Log size of every chunk we receive
app->log->debug(length($bytes) . ' bytes uploaded');
});
});
});
});
};
# 受け取ったチャンクごとにサイズをログに出力する
app->log->debug(length($bytes) . ' bytes uploaded');
});
});
});
});
};
# Upload form in DATA section
get '/' => 'index';
# DATAセクションにあるアップロードフォーム
get '/' => 'index';
# Streaming multipart upload
post '/upload' => {text => 'Upload was successful.'};
# マルチパートアップロードをストリーミング
post '/upload' => {text => 'アップロードが成功しました。'};
app->start;
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head><title>Streaming multipart upload</title></head>
<body>
%= form_for upload => (enctype => 'multipart/form-data') => begin
%= file_field 'example'
%= submit_button 'Upload'
% end
</body>
</html>
その他のイベントループ¶
Internally, the Mojo::IOLoop event loop can use multiple reactor backends, EV for example, will be automatically used if possible. Which in turn allows other event loops like AnyEvent to just work.
内部的にMojo::IOLoopリアクターは複数のイベントループのバックエンドを利用できます; たとえばEVは、インストールされていれば自動的に使用されます。 したがって、AnyEventのようなイベントループも正しく動かすことができます。
use Mojolicious::Lite;
use EV;
use AnyEvent;
# Wait 3 seconds before rendering a response
get '/' => sub {
my $c = shift;
my $w;
$w = AE::timer 3, 0, sub {
$c->render(text => 'Delayed by 3 seconds!');
undef $w;
};
};
# 3秒待ってレスポンスを描画する
get '/' => sub {
my $c = shift;
my $w;
$w = AE::timer 3, 0, sub {
$c->render(text => '3秒遅れています!');
undef $w;
};
};
app->start;
Who actually controls the event loop backend is not important.
バックエンドで実際に誰がイベントループを制御するかは重要ではありません。
use Mojo::UserAgent;
use EV;
use AnyEvent;
# Search MetaCPAN for "mojolicious"
my $cv = AE::cv;
my $ua = Mojo::UserAgent->new;
$ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
my ($ua, $tx) = @_;
$cv->send($tx->result->json('/hits/hits/0/_source/release'));
});
say $cv->recv;
# MetaCPANで"mojolicious"を検索
my $cv = AE::cv;
my $ua = Mojo::UserAgent->new;
$ua->get('fastapi.metacpan.org/v1/module/_search?q=mojolicious' => sub {
my ($ua, $tx) = @_;
$cv->send($tx->result->json('/hits/hits/0/_source/release'));
});
say $cv->recv;
You could, for example, just embed the built-in web server into an AnyEvent application.
たとえば、組み込みのWebサーバーをAnyEventアプリケーションに組み込むこともできます。
use Mojolicious::Lite;
use Mojo::Server::Daemon;
use EV;
use AnyEvent;
# Normal action
get '/' => {text => 'Hello World!'};
# 通常のアクション
get '/' => {text => 'Hello World!'};
# Connect application with web server and start accepting connections
my $daemon
= Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
$daemon->start;
# アプリケーションをWebサーバーと接続して、接続の受付を開始する
my $daemon
= Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']);
$daemon->start;
# Let AnyEvent take control
AE::cv->recv;
# AnyEventにコントロールさせる
AE::cv->recv;
ユーザーエージェント¶
When we say Mojolicious is a web framework we actually mean it, with Mojo::UserAgent there's a full featured HTTP and WebSocket user agent built right in.
わたしたちが Mojolicious はウェブフレームワークだと言うとき、それは本気です; Mojo::UserAgentには、フル機能を備えたHTTPとWebSocketのユーザーエージェントが組み込まれています。
RESTウェブサービス¶
Requests can be performed very comfortably with methods like "get" in Mojo::UserAgent, and always result in a Mojo::Transaction::HTTP object, which has many useful attributes and methods. You can check for connection errors with "result" in Mojo::Transaction, or access HTTP request and response information directly through "req" in Mojo::Transaction and "res" in Mojo::Transaction.
リクエストは、"get" in Mojo::UserAgentなどのメソッドによって快適に実行できます; 返り値は常にMojo::Transaction::HTTPオブジェクトになります。 このオブジェクトには多くの便利な属性とメソッドがあります; "result" in Mojo::Transactionで接続エラーが確認できます; または、<Mojo::Transaction/"req">や<Mojo::Transaction/"res">からHTTPリクエストおよびレスポンス情報に直接アクセスできます。
use Mojo::UserAgent;
# Request a resource and make sure there were no connection errors
my $ua = Mojo::UserAgent->new;
my $tx = $ua->get('mojolicious.org/perldoc/Mojo' => {Accept => 'text/plain'});
my $res = $tx->result;
#リソースをリクエストし、接続エラーがないことを確認する
my $ua = Mojo::UserAgent->new;
my $tx = $ua->get('mojolicious.org/perldoc/Mojo' => {Accept => 'text/plain'});
my $res = $tx->result;
# Decide what to do with its representation
if ($res->is_success) { say $res->body }
elsif ($res->is_error) { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else { say 'Whatever...' }
# 応答に合わせて何をするかを決める
if ($res->is_success) { say $res->body }
elsif ($res->is_error) { say $res->message }
elsif ($res->code == 301) { say $res->headers->location }
else { say 'Whatever...' }
While methods like "is_success" in Mojo::Message::Response and "is_error" in Mojo::Message::Response serve as building blocks for more sophisticated REST clients.
"is_success" in Mojo::Message::Responseと"is_error" in Mojo::Message::Responseのようなメソッドが、RESTクライアントをより洗練されたものにしています。
ウェブスクレイピング¶
Scraping information from websites has never been this much fun before. The built-in HTML/XML parser Mojo::DOM is accessible through "dom" in Mojo::Message and supports all CSS selectors that make sense for a standalone parser, it can be a very powerful tool especially for testing web application.
過去、ウェブサイトから情報をスクレイピングするのがこれほど面白かったことはありません。 組み込みの HTML/XML パーサ Mojo::DOM は、"dom" in Mojo::Messageを通して利用でき、スタンドアロンパーサが理解できる全ての CSS セレクタをサポートします; とくにWebアプリケーションをテストするためのツールとしてとても強力です。
use Mojo::UserAgent;
# Fetch website
my $ua = Mojo::UserAgent->new;
my $res = $ua->get('mojolicious.org/perldoc')->result;
# ウェブサイトを取得
my $ua = Mojo::UserAgent->new;
my $res = $ua->get('mojolicious.org/perldoc')->result;
# Extract title
say 'Title: ', $res->dom->at('head > title')->text;
# タイトルを抽出
say 'Title: ', $res->dom->at('head > title')->text;
# Extract headings
$res->dom('h1, h2, h3')->each(sub { say 'Heading: ', shift->all_text });
# 見出しを抽出
$res->dom('h1, h2, h3')->each(sub { say 'Heading: ', shift->all_text });
# Visit all nodes recursively to extract more than just text
for my $n ($res->dom->descendant_nodes->each) {
# すべてのノードを再帰的にたどって、テキストとその他のものを抽出する
for my $n ($res->dom->descendant_nodes->each) {
# Text or CDATA node
print $n->content if $n->type eq 'text' || $n->type eq 'cdata';
# テキストまたはCDATAノード
print $n->content if $n->type eq 'text' || $n->type eq 'cdata';
# Also include alternate text for images
print $n->{alt} if $n->type eq 'tag' && $n->tag eq 'img';
}
# 画像のAltテキストを含める
print $n->{alt} if $n->type eq 'tag' && $n->tag eq 'img';
}
For a full list of available CSS selectors see "SELECTORS" in Mojo::DOM::CSS.
利用可能なCSSセレクタの完全なリストについては、"SELECTORS" in Mojo::DOM::CSS を見てください。
JSON ウェブサービス¶
Most web services these days are based on the JSON data-interchange format. That's why Mojolicious comes with the possibly fastest pure-Perl implementation Mojo::JSON built right in, which is accessible through "json" in Mojo::Message.
最近ではウェブサービスのほとんどが、データ交換フォーマットとしてJSONを使っています。 なのでMojoliciousには、ピュアPerl実装としてはおそらく最速であるMojo::JSONが組み込まれています; ここには"json" in Mojo::Messageからアクセスできます。
use Mojo::UserAgent;
use Mojo::URL;
# Fresh user agent
my $ua = Mojo::UserAgent->new;
# 新しいユーザーエージェント
my $ua = Mojo::UserAgent->new;
# Search MetaCPAN for "mojolicious" and list latest releases
my $url = Mojo::URL->new('http://fastapi.metacpan.org/v1/release/_search');
$url->query({q => 'mojolicious', sort => 'date:desc'});
for my $hit (@{$ua->get($url)->result->json->{hits}{hits}}) {
say "$hit->{_source}{name} ($hit->{_source}{author})";
}
# MetaCPANで"mojolicious"を検索して、最新のリリースを表示する
my $url = Mojo::URL->new('http://fastapi.metacpan.org/v1/release/_search');
$url->query({q => 'mojolicious', sort => 'date:desc'});
for my $hit (@{$ua->get($url)->result->json->{hits}{hits}}) {
say "$hit->{_source}{name} ($hit->{_source}{author})";
}
ベーシック認証¶
You can just add username and password to the URL, an Authorization
header will be automatically generated.
ユーザ名とパスワードを URL に追加するだけで、Authorization
ヘッダーが自動的に生成されます。
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new;
say $ua->get('https://sri:[email protected]/hideout')->result->body;
追加リクエストの装飾¶
Mojo::UserAgent can automatically follow redirects, the event "start" in Mojo::UserAgent allows you direct access to each transaction right after they have been initialized and before a connection gets associated with them.
Mojo::UserAgentは自動的にリダイレクトを辿り、"start" in Mojo::UserAgentイベントが、トランザクションが初期化された後かつそれが接続と関連付けられる前に、それぞれのトランザクションへの直接アクセスを可能にします。
use Mojo::UserAgent;
# User agent following up to 10 redirects
my $ua = Mojo::UserAgent->new(max_redirects => 10);
# 最大10回までリダイレクトを追跡するユーザーエージェント
my $ua = Mojo::UserAgent->new(max_redirects => 10);
# Add a witty header to every request
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
say 'Request: ', $tx->req->url->clone->to_abs;
});
# 各リクエストに気の利いたヘッダを追加
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!');
say 'Request: ', $tx->req->url->clone->to_abs;
});
# Request that will most likely get redirected
say 'Title: ', $ua->get('google.com')->result->dom->at('head > title')->text;
# リダイレクトされるであろうリクエスト
say 'Title: ', $ua->get('google.com')->result->dom->at('head > title')->text;
This even works for proxy CONNECT
requests.
これはプロキシへの CONNECT
リクエストに対しても使えます。
コンテンツジェネレーター¶
Content generators can be registered with "add_generator" in Mojo::UserAgent::Transactor to generate the same type of content repeatedly for multiple requests.
コンテンツジェネレーターは、"add_generator" in Mojo::UserAgent::Transactorで登録できます; こうすると、複数のリクエストに対して同じタイプのコンテンツを繰り返し生成することができます。
use Mojo::UserAgent;
use Mojo::Asset::File;
# Add "stream" generator
my $ua = Mojo::UserAgent->new;
$ua->transactor->add_generator(stream => sub {
my ($transactor, $tx, $path) = @_;
$tx->req->content->asset(Mojo::Asset::File->new(path => $path));
});
# "stream"ジェネレーターを追加
my $ua = Mojo::UserAgent->new;
$ua->transactor->add_generator(stream => sub {
my ($transactor, $tx, $path) = @_;
$tx->req->content->asset(Mojo::Asset::File->new(path => $path));
});
# Send multiple files streaming via PUT and POST
$ua->put('http://example.com/upload' => stream => '/home/sri/mojo.png');
$ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
# PUTとPOSTを通して複数のファイルのストリーミングを送信
$ua->put('http://example.com/upload' => stream => '/home/sri/mojo.png');
$ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
The json
, form
and multipart
content generators are always available.
json
、form
およびmultipart
コンテンツジェネレーターが常に利用可能です。
use Mojo::UserAgent;
# Send "application/json" content via PATCH
my $ua = Mojo::UserAgent->new;
my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'});
# PATCHを使って"application/json"コンテンツを送信
my $ua = Mojo::UserAgent->new;
my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'});
# Send query parameters via GET
my $tx2 = $ua->get('search.example.com' => form => {q => 'test'});
# GETを使ってクエリパラメーターを送信
my $tx2 = $ua->get('search.example.com' => form => {q => 'test'});
# Send "application/x-www-form-urlencoded" content via POST
my $tx3 = $ua->post('http://search.example.com' => form => {q => 'test'});
# POSTを使って"application/x-www-form-urlencoded"コンテンツを送信
my $tx3 = $ua->post('http://search.example.com' => form => {q => 'test'});
# Send "multipart/form-data" content via PUT
my $tx4 = $ua->put(
'upload.example.com' => form => {test => {content => 'Hello World!'}});
# PUTを使って"multipart/form-data"コンテンツを送信
my $tx4 = $ua->put(
'upload.example.com' => form => {test => {content => 'Hello World!'}});
# Send custom multipart content via PUT
my $tx5 = $ua->put('api.example.com' => multipart => ['Hello', 'World!']);
# PUTを使ってカスタムマルチパートを送信
my $tx5 = $ua->put('api.example.com' => multipart => ['Hello', 'World!']);
For more information about available content generators see also "tx" in Mojo::UserAgent::Transactor.
コンテンツジェネレーターについて、より詳しい情報は"tx" in Mojo::UserAgent::Transactorを見てください。
大きなファイルのダウンロード¶
When downloading large files with Mojo::UserAgent you don't have to worry about memory usage at all, because it will automatically stream everything above 250KiB into a temporary file, which can then be moved into a permanent file with "save_to" in Mojo::Message.
Mojo::UserAgentで大きなファイルをダウンロードする場合、メモリ使用量を心配する必要はまったくありません; 250KB以上のものはすべてが自動的に一時ファイルにストリーミングされ、"save_to" in Mojo::Messageで永続ファイルに移動できるからです。
use Mojo::UserAgent;
# Fetch the latest Mojolicious tarball
my $ua = Mojo::UserAgent->new(max_redirects => 5);
my $tx = $ua->get('https://www.github.com/mojolicious/mojo/tarball/master');
$tx->result->save_to('mojo.tar.gz');
# 最新のMojolicious tarballを取得する
my $ua = Mojo::UserAgent->new(max_redirects => 5);
my $tx = $ua->get('https://www.github.com/mojolicious/mojo/tarball/master');
$tx->result->save_to('mojo.tar.gz');
To protect you from excessively large files there is also a limit of 2GiB by default, which you can tweak with the attribute "max_response_size" in Mojo::UserAgent.
極端に大きいファイルから保護するために、デフォルトで2GBの制限もあります。上限サイズは"max_response_size" in Mojo::UserAgent属性で調整できます。
# Increase limit to 10GiB
$ua->max_response_size(10737418240);
# 上限を10GBに増やす
$ua->max_response_size(10737418240);
大きなファイルのアップロード¶
Uploading a large file is even easier.
大きなファイルのアップロードはさらに簡単です。
use Mojo::UserAgent;
# Upload file via POST and "multipart/form-data"
my $ua = Mojo::UserAgent->new;
$ua->post('example.com/upload' =>
form => {image => {file => '/home/sri/hello.png'}});
# POST と "multipart/form-data" 経由でファイルをアップロード
my $ua = Mojo::UserAgent->new;
$ua->post('example.com/upload' =>
form => {image => {file => '/home/sri/hello.png'}});
And once again you don't have to worry about memory usage, all data will be streamed directly from the file.
ここでもメモリ使用量を心配する必要はなく、すべてのデータがファイルから直接ストリーミングされます。
ストリーミングレスポンス¶
Receiving a streaming response can be really tricky in most HTTP clients, but Mojo::UserAgent makes it actually easy.
ストリーミングレスポンスの受信は、多くのHTTPクライアントで非常に難しくなりがちですが、Mojo::UserAgentでは実に簡単です。
use Mojo::UserAgent;
# Accept responses of indefinite size
my $ua = Mojo::UserAgent->new(max_response_size => 0);
# 不定サイズのレスポンスを受け入れる
my $ua = Mojo::UserAgent->new(max_response_size => 0);
# Build a normal transaction
my $tx = $ua->build_tx(GET => 'http://example.com');
# 通常のトランザクションを立てる
my $tx = $ua->build_tx(GET => 'http://example.com');
# Replace "read" events to disable default content parser
$tx->res->content->unsubscribe('read')->on(read => sub {
my ($content, $bytes) = @_;
say "Streaming: $bytes";
});
# "read"イベントを置き換えて、デフォルトのコンテントパーサーを無効にする
$tx->res->content->unsubscribe('read')->on(read => sub {
my ($content, $chunk) = @_;
say "Streaming: $chunk";
});
# Process transaction
$tx = $ua->start($tx);
# トランザクションの処理
$tx = $ua->start($tx);
The event "read" in Mojo::Content will be emitted for every chunk of data that is received, even chunked transfer encoding and gzip content encoding will be handled transparently if necessary.
"read" in Mojo::Contentイベントが受信されるすべてのデータチャンクに対して発行されます; チャンク転送エンコードやgzipコンテンツエンコードも、必要に応じて透過的に処理されます。
ストリーミングリクエスト¶
Sending a streaming request is almost just as easy.
ストリーミングリクエストを送るのもほとんど同じくらい簡単です。
use Mojo::UserAgent;
# Build a normal transaction
my $ua = Mojo::UserAgent->new;
my $tx = $ua->build_tx(GET => 'http://example.com');
# 通常のトランザクションを立てる
my $ua = Mojo::UserAgent->new;
my $tx = $ua->build_tx(GET => 'http://example.com');
# Prepare body
my $body = 'Hello World!';
$tx->req->headers->content_length(length $body);
# ボディを準備する
my $body = 'Hello World!';
$tx->req->headers->content_length(length $body);
# Start writing directly with a drain callback
my $drain;
$drain = sub {
my $content = shift;
my $chunk = substr $body, 0, 1, '';
$drain = undef unless length $body;
$content->write($chunk, $drain);
};
$tx->req->content->$drain;
# 排出コールバックに直接書き込みを開始する
my $drain;
$drain = sub {
my $content = shift;
my $chunk = substr $body, 0, 1, '';
$drain = undef unless length $body;
$content->write($chunk, $drain);
};
$tx->req->content->$drain;
# Process transaction
$tx = $ua->start($tx);
# トランザクションの処理
$tx = $ua->start($tx);
The drain callback passed to "write" in Mojo::Content will be executed whenever the entire previous chunk of data has actually been written.
"write" in Mojo::Contentに渡されるドレインコールバックは、前のデータチャンク全体が実際に書き込まれるたびに実行されます。
ノンブロッキング¶
Mojo::UserAgent has been designed from the ground up to be non-blocking, the whole blocking API is just a simple convenience wrapper. Especially for high latency tasks like web crawling this can be extremely useful, because you can keep many concurrent connections active at the same time.
Mojo::UserAgentはノンブロッキングとしてゼロから設計されており、ブロッキングAPI全体は単に便宜的なラッパーです。 特に、ウェブクローリングのような待ち時間が大きな処理では、同時に多くの並列接続をアクティブに保つことができて非常に便利です。
use Mojo::UserAgent;
use Mojo::IOLoop;
# Concurrent non-blocking requests
my $ua = Mojo::UserAgent->new;
$ua->get('https://metacpan.org/search?q=mojo' => sub {
my ($ua, $mojo) = @_;
say $mojo->result->dom->at('title')->text;
});
$ua->get('https://metacpan.org/search?q=minion' => sub {
my ($ua, $minion) = @_;
say $minion->result->dom->at('title')->text;
});
# 並列のノンブロッキングリクエスト
my $ua = Mojo::UserAgent->new;
$ua->get('https://metacpan.org/search?q=mojo' => sub {
my ($ua, $mojo) = @_;
say $mojo->result->dom->at('title')->text;
});
$ua->get('https://metacpan.org/search?q=minion' => sub {
my ($ua, $minion) = @_;
say $minion->result->dom->at('title')->text;
});
# Start event loop if necessary
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
# 必要であればイベントループを開始
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
But don't try to open too many connections to one server at the same time, it might get overwhelmed. Better use a queue to process requests in smaller batches.
ただし、1つのサーバーに対して同時に多くの接続を開かないでください; 接続過多になる可能性があります。 キューを使用して、リクエストを小さなバッチで処理する方が適切です。
use Mojo::UserAgent;
use Mojo::IOLoop;
my @urls = (
'mojolicious.org/perldoc/Mojo/DOM', 'mojolicious.org/perldoc/Mojo',
'mojolicious.org/perldoc/Mojo/File', 'mojolicious.org/perldoc/Mojo/URL'
);
# User agent with a custom name, following up to 5 redirects
my $ua = Mojo::UserAgent->new(max_redirects => 5);
$ua->transactor->name('MyParallelCrawler 1.0');
# 最大5つのリダイレクトに追跡するカスタム名を持つユーザーエージェント
my $ua = Mojo::UserAgent->new(max_redirects => 5);
$ua->transactor->name('MyParallelCrawler 1.0');
# Use a delay to keep the event loop running until we are done
my $delay = Mojo::IOLoop->delay;
my $fetch;
$fetch = sub {
# 遅延を使用して、完了するまでイベントループを回し続ける
my $delay = Mojo::IOLoop->delay;
my $fetch;
$fetch = sub {
# Stop if there are no more URLs
return unless my $url = shift @urls;
# URLがなくなったら停止
return unless my $url = shift @urls;
# Fetch the next title
my $end = $delay->begin;
$ua->get($url => sub {
my ($ua, $tx) = @_;
say "$url: ", $tx->result->dom->at('title')->text;
# 次のタイトルを取得
my $end = $delay->begin;
$ua->get($url => sub {
my ($ua, $tx) = @_;
say "$url: ", $tx->result->dom->at('title')->text;
# Next request
$fetch->();
$end->();
});
};
# 次のリクエスト
$fetch->();
$end->();
});
};
# Process two requests at a time
$fetch->() for 1 .. 2;
$delay->wait;
# 一度に2つのリクエストを処理する
$fetch->() for 1 .. 2;
$delay->wait;
It is also strongly recommended to respect every sites robots.txt
file as well as terms of service, and to wait a little before reopening connections to the same host, or the operators might be forced to block your access.
また、どんなときもサイトのrobots.txt
ファイルと利用規約を尊重し、同じホストへの接続を再度開く前には少し待つようにしましょう; そうでないと、管理者はアクセスをブロックせざるを得なくなるかもしれません。
並列のブロッキングリクエスト¶
You might have seen "wait" in Mojo::Promise already in some examples above. It is used to make non-blocking operations portable, allowing them to work inside an already running event loop or start one on demand.
これまでのサンプルですでに"wait" in Mojo::Promiseを見たことがあるかもしれません。 これはノンブロッキング処理を移植可能にするために使用され、これらの処理を既に実行中のイベントループ内で動かしたり、必要に応じて開始したりできます。
use Mojo::UserAgent;
use Mojo::Promise;
# Synchronize non-blocking requests with promises
my $ua = Mojo::UserAgent->new;
my $mojo = $ua->get_p('https://metacpan.org/search?q=mojo');
my $minion = $ua->get_p('https://metacpan.org/search?q=minion');
Mojo::Promise->all($mojo, $minion)->then(sub {
my ($mojo, $minion) = @_;
say $mojo->[0]->result->dom->at('title')->text;
say $minion->[0]->result->dom->at('title')->text;
})->wait;
# ノンブロッキングリクエストをプロミスと同期する
my $ua = Mojo::UserAgent->new;
my $mojo = $ua->get_p('https://metacpan.org/search?q=mojo');
my $minion = $ua->get_p('https://metacpan.org/search?q=minion');
Mojo::Promise->all($mojo, $minion)->then(sub {
my ($mojo, $minion) = @_;
say $mojo->[0]->result->dom->at('title')->text;
say $minion->[0]->result->dom->at('title')->text;
})->wait;
WebSocket¶
WebSockets are not just for the server-side, you can use "websocket_p" in Mojo::UserAgent to open new connections, which are always non-blocking. The WebSocket handshake uses HTTP, and is a normal GET
request with a few additional headers. It can even contain cookies, and is followed by a 101
response from the server, notifying our user agent that the connection has been established and it can start using the bi-directional WebSocket protocol.
WebSocketはサーバー側だけのものではなく、"websocket_p" in Mojo::UserAgentを使って、常にノンブロッキングで動く新しい接続を開くことができます。 WebSocketハンドシェイクはHTTPを使用しています; また、通常のGET
メソッドにいくつかヘッダーが追加されたリクエストです。 ここにクッキーを含むこともできます; ハンドシェイクに続いて、サーバーからの101
レスポンスによって、接続が確立したことがユーザーエージェントに通知されます; するとWebSocketプロトコルを使った双方向通信が開始します。
use Mojo::UserAgent;
use Mojo::Promise;
# Open WebSocket to echo service
my $ua = Mojo::UserAgent->new;
$ua->websocket_p('ws://echo.websocket.org')->then(sub {
my $tx = shift;
# エコーサービスのためにWebSocketを開く
my $ua = Mojo::UserAgent->new;
$ua->websocket_p('ws://echo.websocket.org')->then(sub {
my $tx = shift;
# Prepare a followup promise so we can wait for messages
my $promise = Mojo::Promise->new;
# メッセージを待つことができるようにフォローアップ用のプロミスを準備する
my $promise = Mojo::Promise->new;
# Wait for WebSocket to be closed
$tx->on(finish => sub {
my ($tx, $code, $reason) = @_;
say "WebSocket closed with status $code.";
$promise->resolve;
});
# WebSocketが閉じるのを待つ
$tx->on(finish => sub {
my ($tx, $code, $reason) = @_;
say "WebSocket closed with status $code.";
$promise->resolve;
});
# Close WebSocket after receiving one message
$tx->on(message => sub {
my ($tx, $msg) = @_;
say "WebSocket message: $msg";
$tx->finish;
});
# ひとつのメッセージを受け取った後にWebSocketを閉じる
$tx->on(message => sub {
my ($tx, $msg) = @_;
say "WebSocket message: $msg";
$tx->finish;
});
# Send a message to the server
$tx->send('Hi!');
# サーバーにメッセージを送信
$tx->send('Hi!');
# Insert a new promise into the promise chain
return $promise;
})->catch(sub {
my $err = shift;
# 新しいプロミスをプロミスチェーンに挿入する
return $promise;
})->catch(sub {
my $err = shift;
# Handle failed WebSocket handshakes and other exceptions
warn "WebSocket error: $err";
})->wait;
# 失敗したWebSocketハンドシェイクおよびその他の例外を処理する
warn "WebSocket error: $err";
})->wait;
UNIXドメインソケット¶
Not just TCP/IP sockets are supported, but also UNIX domain sockets, which can have significant security and performance benefits when used for inter-process communication. Instead of http://
and ws://
you can use the http+unix://
and ws+unix://
schemes, and pass along a percent encoded path (/
becomes %2F
) instead of a hostname.
TCP/IPソケットだけでなく、UNIXドメインソケットもサポートされています; これはプロセス間通信に使用すると、セキュリティとパフォーマンスの面で大きなメリットがあります。 http://
およびws://
の代わりにhttp+unix://
とws+unix://
スキーマを使用できます; パーセントエンコードパス(/
は%2F
になる)をホストネームの代わりに渡します。
use Mojo::UserAgent;
use Mojo::Promise;
# GET request via UNIX domain socket "/tmp/foo.sock"
my $ua = Mojo::UserAgent->new;
say $ua->get('http+unix://%2Ftmp%2Ffoo.sock/index.html')->result->body;
# UNIXドメインソケット"/tmp/foo.sock"を介したGETリクエスト
my $ua = Mojo::UserAgent->new;
say $ua->get('http+unix://%2Ftmp%2Ffoo.sock/index.html')->result->body;
# GET request with HOST header via UNIX domain socket "/tmp/bar.sock"
my $tx = $ua->get('http+unix://%2Ftmp%2Fbar.sock' => {Host => 'example.com'});
say $tx->result->body;
# UNIXドメインソケット "/tmp/bar.sock"を介したHOSTヘッダー付きのGETリクエスト
my $tx = $ua->get('http+unix://%2Ftmp%2Fbar.sock' => {Host => 'example.com'});
say $tx->result->body;
# WebSocket connection via UNIX domain socket "/tmp/baz.sock"
$ua->websocket_p('ws+unix://%2Ftmp%2Fbaz.sock/echo')->then(sub {
my $tx = shift;
# UNIXドメインソケット "/tmp/baz.sock" を介したWebSocket接続
$ua->websocket_p('ws+unix://%2Ftmp%2Fbaz.sock/echo')->then(sub {
my $tx = shift;
my $promise = Mojo::Promise->new;
$tx->on(finish => sub { $promise->resolve });
$tx->on(message => sub {
my ($tx, $msg) = @_;
say "WebSocket message: $msg";
$tx->finish;
});
$tx->send('Hi!');
return $promise;
})->catch(sub {
my $err = shift;
warn "WebSocket error: $err";
})->wait;
You can set the Host
header manually to pass along a hostname.
Host
ヘッダーを手動で設定してホスト名を渡すことができます。
コマンドライン¶
Don't you hate checking huge HTML files from the command line? Thanks to the command Mojolicious::Command::get that is about to change. You can just pick the parts that actually matter with the CSS selectors from Mojo::DOM and JSON Pointers from Mojo::JSON::Pointer.
コマンドラインから巨大な HTML ファイルをチェックするのは嫌ですよね? コマンドMojolicious::Command::getのおかげでそれは変わろうとしています。 実際に重要な部分だけをMojo::DOMのCSSセレクターやMojo::JSON::PointerのJSONポインターで取り出せます。
$ mojo get https://mojolicious.org 'head > title'
How about a list of all id attributes?
すべての id 属性のリストを見るには?
$ mojo get https://mojolicious.org '*' attr id
Or the text content of all heading tags?
または、すべての見出しタグのテキスト内容は?
$ mojo get https://mojolicious.org 'h1, h2, h3' text
Maybe just the text of the third heading?
三番目の見出しのテキストは?
$ mojo get https://mojolicious.org 'h1, h2, h3' 3 text
You can also extract all text from nested child elements.
ネストした子要素からもテキストをすべて抽出できます。
$ mojo get https://mojolicious.org '#mojobar' all
The request can be customized as well.
リクエストはカスタマイズすることもできます。
$ mojo get -M POST -H 'X-Bender: Bite my shiny metal ass!' http://google.com
Store response data by redirecting STDOUT
.
レスポンスデータをSTDOUT
にリダイレクトして保存します。
$ mojo get mojolicious.org > example.html
Pass request data by redirecting STDIN
.
リクエストデータをSTDIN
にリダイレクトして渡します。
$ mojo get -M PUT mojolicious.org < example.html
Or use the output of another program.
または、別のプログラムの出力を使用します。
$ echo 'Hello World' | mojo get -M PUT https://mojolicious.org
Submit forms as application/x-www-form-urlencoded
content.
application/x-www-form-urlencoded
コンテンツとしてフォームを送信します。
$ mojo get -M POST -f 'q=Mojo' -f 'size=5' https://metacpan.org/search
And upload files as multipart/form-data
content.
さらに、multipart/form-data
コンテンツとしてファイルをアップロードします。
$ mojo get -M POST -f '[email protected]' mojolicious.org
You can follow redirects and view the headers for all messages.
リダイレクトを辿ってすべての (HTTP) ヘッダメッセージを見ることができます。
$ mojo get -r -v http://google.com 'head > title'
Extract just the information you really need from JSON data structures.
JSONデータ構造から本当に必要な情報を抽出できます。
$ mojo get https://fastapi.metacpan.org/v1/author/SRI /name
This can be an invaluable tool for testing your applications.
アプリケーションのテストに真価を発揮するツールとなることでしょう。
$ ./myapp.pl get /welcome 'head > title'
ワンライナー¶
For quick hacks and especially testing, ojo one-liners are also a great choice.
手早くハックしたいとき、とくにテストのためには、ojoワンライナーはすぐれた選択です。
$ perl -Mojo -E 'say g("mojolicious.org")->dom->at("title")->text'
アプリケーション¶
Fun Mojolicious application hacks for all occasions.
楽しい Mojolicious アプリケーションはどんな場面でもハックが満載です。
ベーシック認証¶
Basic authentication data will be automatically extracted from the Authorization
header.
ベーシック認証のデータは、Authorization
ヘッダから自動的に抽出されます。
use Mojolicious::Lite;
use Mojo::Util 'secure_compare';
get '/' => sub {
my $c = shift;
# Check for username "Bender" and password "rocks"
return $c->render(text => 'Hello Bender!')
if secure_compare $c->req->url->to_abs->userinfo, 'Bender:rocks';
# ユーザー名"Bender"とパスワード"rocks"をチェック
return $c->render(text => 'Hello Bender!')
if secure_compare $c->req->url->to_abs->userinfo, 'Bender:rocks';
# Require authentication
$c->res->headers->www_authenticate('Basic');
$c->render(text => 'Authentication required!', status => 401);
};
# 認証を要求
$c->res->headers->www_authenticate('Basic');
$c->render(text => 'Authentication required!', status => 401);
};
app->start;
This can be combined with TLS for a secure authentication mechanism.
TLSを一緒に使って安全に認証することもできます。
$ ./myapp.pl daemon -l 'https://*:3000?cert=./server.crt&key=./server.key'
設定ファイルの追加¶
Adding a configuration file to your application is as easy as adding a file to its home directory and loading the plugin Mojolicious::Plugin::Config. The default name is based on the value of "moniker" in Mojolicious (myapp
), appended with a .conf
extension (myapp.conf
).
構成ファイルをアプリケーションに追加するのは、ファイルをホームディレクトリに追加してMojolicious::Plugin::Configプラグインをロードするのと同じくらい簡単です。 デフォルト名は"moniker" in Mojolicious(C <myapp> )に.conf
拡張子を付け足したものになります(myapp.conf
)。
$ mkdir myapp
$ cd myapp
$ touch myapp.pl
$ chmod 744 myapp.pl
$ echo '{name => "my Mojolicious application"};' > myapp.conf
Configuration files themselves are just Perl scripts that return a hash reference with configuration settings of your choice. All those settings are then available through the method "config" in Mojolicious and the helper "config" in Mojolicious::Plugin::DefaultHelpers.
構成ファイル自体は、選択した構成設定を含むハッシュ参照を返す単なるPerlスクリプトです。 これらの設定はすべて、メソッド"config" in Mojoliciousと"config" in Mojolicious::Plugin::DefaultHelpers で利用できます。
use Mojolicious::Lite;
plugin 'Config';
my $name = app->config('name');
app->log->debug("Welcome to $name");
get '/' => 'with_config';
app->start;
__DATA__
@@ with_config.html.ep
<!DOCTYPE html>
<html>
<head><title><%= config 'name' %></title></head>
<body>Welcome to <%= config 'name' %></body>
</html>
Alternatively you can also use configuration files in the JSON format with Mojolicious::Plugin::JSONConfig.
または、Mojolicious::Plugin::JSONConfigでJSON形式の設定ファイルを使用することもできます。
アプリケーションにプラグインを追加¶
To organize your code better and to prevent helpers from cluttering your application, you can use application specific plugins.
コードをよく整理し、アプリケーションがヘルパーでごちゃごちゃになるのを防ぐために、アプリケーションを指定してプラグインを作成できます。
$ mkdir -p lib/MyApp/Plugin
$ touch lib/MyApp/Plugin/MyHelpers.pm
They work just like normal plugins and are also subclasses of Mojolicious::Plugin. Nested helpers with a prefix based on the plugin name are an easy way to avoid conflicts.
通常のプラグインと同じように動き、Mojolicious::Pluginのサブクラスにもなっています。 プラグインの名前にプレフィックスを付け、ヘルパーをネストさせると、簡単に衝突を避けることができます。
package MyApp::Plugin::MyHelpers;
use Mojo::Base 'Mojolicious::Plugin';
sub register {
my ($self, $app) = @_;
$app->helper('my_helpers.render_with_header' => sub {
my ($c, @args) = @_;
$c->res->headers->header('X-Mojo' => 'I <3 Mojolicious!');
$c->render(@args);
});
}
1;
You can have as many application specific plugins as you like, the only difference to normal plugins is that you load them using their full class name.
アプリケーション固有のプラグインはいくつでも使用できますが、通常のプラグインとの唯一の違いは、ロードするために完全なクラス名を使用するところです。
use Mojolicious::Lite;
use lib 'lib';
plugin 'MyApp::Plugin::MyHelpers';
get '/' => sub {
my $c = shift;
$c->my_helpers->render_with_header(text => 'I ♥ Mojolicious!');
};
app->start;
Of course these plugins can contain more than just helpers, take a look at "PLUGINS" in Mojolicious::Plugins for a few ideas.
もちろん、これらのプラグインにはヘルパー以外のものを含めることができます; "PLUGINS" in Mojolicious::Pluginsにいくつかサンプルがあります。
Mojoliciousにコマンドを追加¶
By now you've probably used many of the built-in commands described in Mojolicious::Commands, but did you know that you can just add new ones and that they will be picked up automatically by the command line interface if they are placed in a directory from @INC
?
おそらくこれまでに、Mojolicious::Commandsにある組み込みコマンドを多く使ったことでしょう; でも、新しいコマンドを追加して、コマンドを@INC
が参照するディレクトリに追加すると、自動的にコマンドラインインターフェイスから使えるようになることは知っていましたか?
package Mojolicious::Command::spy;
use Mojo::Base 'Mojolicious::Command';
has description => 'Spy on application';
has usage => "Usage: APPLICATION spy [TARGET]\n";
sub run {
my ($self, @args) = @_;
# Leak secret passphrases
if ($args[0] eq 'secrets') { say for @{$self->app->secrets} }
# シークレットパスフレーズを漏らす
if ($args[0] eq 'secrets') { say for @{$self->app->secrets} }
# Leak mode
elsif ($args[0] eq 'mode') { say $self->app->mode }
}
# モードを漏らす
elsif ($args[0] eq 'mode') { say $self->app->mode }
}
1;
Command line arguments are passed right through and there are many useful attributes and methods in Mojolicious::Command that you can use or overload.
コマンドライン引数はそのまま渡され、Mojolicious::Commandには、使用したりオーバーロードでる多くの便利な属性とメソッドがあります。
$ mojo spy secrets
HelloWorld
$ ./script/myapp spy secrets
secr3t
And to make your commands application specific, just add a custom namespace to "namespaces" in Mojolicious::Commands and use a class name like MyApp::Command::spy
instead of Mojolicious::Command::spy
.
また、指定のアプリケーションでコマンドを使うようには、"namespaces" in Mojolicious::Commandsにカスタム名前空間を追加するだけです; このとき、MyApp::Command::spy
の代わりにMojolicious::Command::spy
のように名前を付けてください。
# Application
package MyApp;
use Mojo::Base 'Mojolicious';
# アプリケーション
package MyApp;
use Mojo::Base 'Mojolicious';
sub startup {
my $self = shift;
# Add another namespace to load commands from
push @{$self->commands->namespaces}, 'MyApp::Command';
}
# コマンドを読み込む名前空間を追加
push @{$self->commands->namespaces}, 'MyApp::Command';
}
1;
The options -h
/--help
, --home
and -m
/--mode
are handled automatically by Mojolicious::Commands and are shared by all commands.
オプション-h
/ --help
、--home
および-m
/ --mode
は、Mojolicious::Commandsによって自動的に処理され、すべてのコマンドで共有されます。
$ ./script/myapp spy -m production mode
production
For a full list of shared options see "SYNOPSIS" in Mojolicious::Commands.
共有オプションの全一覧は、"SYNOPSIS" in Mojolicious::Commandsを見てください。
アプリケーションに対してコードを実行する¶
Ever thought about running a quick one-liner against your Mojolicious application to test something? Thanks to the command Mojolicious::Command::eval you can do just that, the application object itself can be accessed via app
.
あなたのMojoliciousアプリケーションをテストするために、ささっとワンライナーを走らせたいと思ったことはありますか? コマンドMojolicious::Command::evalを使うと、まさにこれが実現できます; アプリケーションオブジェクト自体にはapp
を通じてアクセスできます。
$ mojo generate lite_app myapp.pl
$ ./myapp.pl eval 'say for @{app->static->paths}'
$ ./myapp.pl eval 'say for sort keys %{app->renderer->helpers}'
The verbose
options will automatically print the return value or returned data structure to STDOUT
.
verbose
オプションで、返り値またはPerlのデータ構造をSTDOUT
に自動的に出力できます。
$ ./myapp.pl eval -v 'app->static->paths->[0]'
$ ./myapp.pl eval -V 'app->static->paths'
アプリケーションをインストール可能できるようにする¶
Ever thought about releasing your Mojolicious application to CPAN? It's actually much easier than you might think.
MojoliciousアプリケーションをCPANにリリースしたいと思ったことはありますか? これは思ったよりも簡単にできます。
$ mojo generate app MyApp
$ cd my_app
$ mv public lib/MyApp/
$ mv templates lib/MyApp/
The trick is to move the public
and templates
directories so they can get automatically installed with the modules. Additionally author commands from the Mojolicious::Command::Author
namespace are not usually wanted by an installed application so they can be excluded.
コツはpublic
とtemplates
ディレクトリを移動させ、モジュールとともに自動的にインストールされるようにすることです。 Mojolicious::Command::Author
名前空間から追加される作成者コマンドは、通常、インストールされたアプリケーションには不要なので、除外できます。
# Application
package MyApp;
use Mojo::Base 'Mojolicious';
# アプリケーション
package MyApp;
use Mojo::Base 'Mojolicious';
use Mojo::File 'path';
use Mojo::Home;
# Every CPAN module needs a version
our $VERSION = '1.0';
# CPANモジュールには常にバージョンが必要です
our $VERSION = '1.0';
sub startup {
my $self = shift;
# Switch to installable home directory
$self->home(Mojo::Home->new(path(__FILE__)->sibling('MyApp')));
# インストール可能なホームディレクトリへ切替
$self->home(Mojo::Home->new(path(__FILE__)->sibling('MyApp')));
# Switch to installable "public" directory
$self->static->paths->[0] = $self->home->child('public');
# インストール可能な "public" ディレクトリへ切替
$self->static->paths->[0] = $self->home->child('public');
# Switch to installable "templates" directory
$self->renderer->paths->[0] = $self->home->child('templates');
# インストール可能な "templates" ディレクトリへ切替
$self->renderer->paths->[0] = $self->home->child('templates');
# Exclude author commands
$self->commands->namespaces(['Mojolicious::Commands']);
# 作成者コマンドを除外
$self->commands->namespaces(['Mojolicious::Commands']);
my $r = $self->routes;
$r->get('/welcome')->to('example#welcome');
}
1;
Finally there is just one small change to be made to the application script. The shebang line becomes the recommended #!perl
, which the toolchain can rewrite to the proper shebang during installation.
最後に、アプリケーションスクリプトに小さな変更を1つ加えるだけです。 シェバン行は、推奨される#!perl
になり、これはインストール中にツールチェーンによって適切なshebangに書き換えることができます。
#!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');
That's really everything, now you can package your application like any other CPAN module.
これが本当に全部です。これで他のCPAN モジュールのようにアプリケーションをパッケージすることができます。
$ ./script/my_app generate makefile
$ perl Makefile.PL
$ make test
$ make manifest
$ make dist
And if you have a PAUSE account (which can be requested at http://pause.perl.org) even upload it.
また、PAUSEアカウントを持っている場合(http://pause.perl.orgでリクエストできます)アップロードすることもできます。
$ mojo cpanify -u USER -p PASS MyApp-0.01.tar.gz
Hello World¶
If every byte matters this is the smallest Hello World
application you can write with Mojolicious::Lite.
1バイトも無駄にしたくないのであれば、これがMojolicious::Liteで作ることのできる最小のHello World
アプリケーションです。
use Mojolicious::Lite;
any {text => 'Hello World!'};
app->start;
It works because all routes without a pattern default to /
and automatic rendering kicks in even if no actual code gets executed by the router. The renderer just picks up the text
value from the stash and generates a response.
パターンがない場合、ルートはすべてデフォルトで /
となり、ルータによって コードが実際には実行されなくとも自動レンダリングが開始します。 レンダラは、スタッシュから text
の値を拾い、レスポンスを生成します。
Hello World ワンライナー¶
The Hello World
example above can get even a little bit shorter in an ojo one-liner.
上記のHello World
の例は、ojoワンライナーを使うともう少し短くできます。
$ perl -Mojo -E 'a({text => "Hello World!"})->start' daemon
And you can use all the commands from Mojolicious::Commands.
そしてすべてのコマンドが Mojolicious::Commandsから使用できます。
$ perl -Mojo -E 'a({text => "Hello World!"})->start' get -v /
もっと学ぶには¶
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 list かirc.freenode.net
(chat now!)の公式IRCチャンネル #mojo
まで気軽に質問してください。