Mojolicious::Guides::Cookbook
НАЗВАНИЕ
Mojolicious::Guides::Cookbook — Сборник полезных программных решений
ОБЗОР
Готовим с помощью Mojolicious. Рецепты на любой вкус.
РАЗВЕРТЫВАНИЕ
Запуск Mojolicious и Mojolicious::Lite приложений на различных платформах.
Встроенный сервер
Mojolicious имеет ультрапортативный HTTP 1.1 совместимый web-сервер. Обычно он используются при разработке, но он также достаточно стабильный и быстрый для небольших и средних приложений
$ ./script/myapp daemon Server available at http://127.0.0.1:3000.
Имеет множество опций, и, как известно, работает на любой платформе где есть Perl.
$ ./script/myapp help daemon ...Перечень доступных опций...
Другим большим преимуществом является то, что он поддерживает из коробки TLS и ВебСокеты.
$ ./script/myapp daemon --listen https://*:3000 Server available at https://127.0.0.1:3000.
Сертификат разработчика для тестирования встроен, потому это просто работает.
Hypnotoad
Для больших приложений Mojolicious содержит UNIX preforking веб-сервер Mojo::Server::Hypnotoad, что позволит вам воспользоваться преимуществом многоядерного процессора и механизмом копирования при записи.
Mojo::Server::Hypnotoad |- Mojo::Server::Daemon [1] |- Mojo::Server::Daemon [2] |- Mojo::Server::Daemon [3] `- Mojo::Server::Daemon [4]
Он основан на обычном встроенном веб-сервере, но оптимизирован специально для рабочих сред из коробки.
$ hypnotoad script/myapp Server available at http://127.0.0.1:8080.
Конфигурационные файлы являются простыми Perl скриптами для максимальной настраиваемости.
# hypnotoad.conf {listen => ['http://*:80'], workers => 10};
Но одним из его главных преимуществ является поддержка легкого обновления программного обеспечения с нулевым временем простоя. Это означает, что вы можете обновлять Mojolicious, Perl или даже системные библиотеки во время исполнения, никогда не останавливая сервер и не теряя ни одного входящего соединения, просто повторным запуском вышеуказанной команды.
$ hypnotoad script/myapp Starting hot deployment for Hypnotoad server 31841.
Nginx
Одним из наиболее популярных способов установки в настоящее время является встроенный web сервер за обратным прокси-сервером Nginx.
upstream myapp { server 127.0.0.1:8080; } server { listen 80; server_name localhost; location / { proxy_read_timeout 300; proxy_pass http://myapp; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
Вы также можете включить поддержку обратного прокси-сервера в hypnotoad
.
Это позволяет Mojolicious автоматически подбирать заголовки X-Forwarded-For
,
X-Forwarded-Host
и X-Forwarded-HTTPS
.
# hypnotoad.conf {proxy => 1};
Apache/CGI
CGI
поддерживается «из коробки», и ваше приложение на Mojolicious
автоматически определит, что оно запущено как CGI
-скрипт.
ScriptAlias / /home/sri/myapp/script/myapp/
Apache/FastCGI
FastCGI
тоже поддерживается «из коробки», и ваше приложение
на Mojolicious автоматически определит, что оно запущено
как FastCGI
-скрипт.
FastCgiIpcDir /home/sri/myapp FastCgiServer /home/sri/myapp/script/myapp -processes 1 Alias / /home/sri/myapp/script/myapp/
PSGI/Plack
PSGI - это интерфейс между Perl веб-фреймворками и веб-серверами. Plack - это Perl модуль и набор разработчика, который содержит связующее ПО PSGI, вспомогательные инструменты и адаптеры для веб-серверов. На создание PSGI и Plack вдохновили Python WSGI и Ruby Rack. Приложения Mojolicious смехотворно просто разверачивать с помощью Plack.
$ plackup ./script/myapp HTTP::Server::PSGI: Accepting connections at http://0:5000/
Plack предоставляет много серверных и протокольных адаптеров,
вы можете выбирать из FCGI
, SCGI
и mod_perl
.
Удостоверьтесь что запускаете plackup
из вашего рабочего
каталога приложений, иначе библиотеки могут быть не найдены.
$ plackup ./script/myapp -s FCGI -l /tmp/myapp.sock
Из-за того что plackup
использует причудливый способ загрузки вашего
скрипта, Mojolicious не всегда в состоянии определить рабочую директорию.
В таком случае используйте переменную окружения MOJO_HOME
. По той же
самой причине строчка app->start
должна быть последним
Perl-оператором в приложении.
$ MOJO_HOME=/home/sri/myapp plackup ./script/myapp HTTP::Server::PSGI: Accepting connections at http://0:5000/
Некоторые серверные адаптеры могут запросить файл .psgi
, если это
происходит, вы просто указываете на ваш скрипт приложения. Он будет себя
вести так как надо, если обнаружит пременную окружения PLACK_ENV
.
Apache/mod_perl (PSGI/Plack)
mod_perl
это хороший пример PSGI адаптера без использования
plackup
, помните что установка переменной окружения PLACK_ENV
необходима Mojolicious для автоматического обнаружения PSGI.
<VirtualHost *:80> ServerName localhost DocumentRoot /home/sri/myapp/public <Perl> $ENV{PLACK_ENV} = 'production'; $ENV{MOJO_HOME} = '/home/sri/myapp'; </Perl> <Location /myapp> SetHandler perl-script PerlHandler Plack::Handler::Apache2 PerlSetVar psgi_app /home/sri/myapp/script/myapp </Location> </VirtualHost>
Перезапись
Иногда вам возможно приходится разворачивать ваше приложение в незнакомой
среде окружения, в которой вы можете только менять конфигурацию сервера или
находитесь за обратным прокси, который передает дополнительную информацию в
заголовках X-*
. В таких случаях, вы можете использовать хук before_dispatch
для перезаписи входящих запросов.
app->hook(before_dispatch => sub { my $self = shift; $self->req->url->base->scheme('https') if $self->req->headers->header('X-Forwarded-Protocol') eq 'https'; });
Встраивание
Время от времени, вы можете повторно использовать части Mojolicious приложения, такие как конфигурационные файлы, подключение к базе данных или хэлперы для других скриптов, с этим небольшим фиктивным сервером вы можете просто встраивать их.
use Mojo::Server; # Загрузка приложения с фиктивным сервером my $server = Mojo::Server->new; my $app = $server->load_app('./myapp.pl'); # Доступ к полностью инициализированному приложению print $app->static->root;
Вы также можете использовать встроенный[внутренний] веб-сервер для встраивания Mojolicious приложений в чужих средах, таких как незнакомые event loops.
use Mojolicious::Lite; use Mojo::Server::Daemon; # Обычные действия get '/' => sub { my $self = shift; $self->render(text => 'Hello World!'); }; # Подключение приложения с пользовательским демоном my $daemon = Mojo::Server::Daemon->new(app => app, listen => ['http://*:8080']); $daemon->prepare_ioloop; # Вызов "one_tick" неоднократно из чужой среды $daemon->ioloop->one_tick while 1;
АГЕНТ ПОЛЬЗОВАТЕЛЯ
Когда мы говорим Mojolicious является веб-фреймворком, мы на самом деле подразумеваем это.
Парсинг веб-страниц
Сбор информации с веб-сайтов никогда не приносило столько удовольствия раньше. Встроенный HTML5/XML парсер Mojo::DOM поддерживает все CSS3 селекторы, что имеет смысл для автономного анализатора.
# Скачать веб-страничку my $ua = Mojo::UserAgent->new; my $tx = $ua->get('mojolicio.us/perldoc'); # Извлечь заголовок print 'Заголовок: ', $tx->res->dom->at('head > title')->text, "\n"; # Извлечь заголовки $tx->res->dom('h1, h2, h3')->each(sub { print 'Заголовок: ', shift->all_text, "\n"; });
Специально для модульного тестирования вашего Mojolicious приложения это может быть очень мощным инструментом.
Веб-службы JSON
Большинство веб-служб в наши дни базируются на формате обмена данными JSON. Вот почему Mojolicious поставляется со встроенной, возможно самой быстрой реализацией Mojo::JSON на чистом Perl.
# Fresh user agent my $ua = Mojo::UserAgent->new; # загрузка последних новостей Mojolicious из Твиттера my $search = 'http://search.twitter.com/search.json?q=Mojolicious'; for $tweet (@{$ua->get($search)->res->json->{results}}) { # Текст твита my $text = $tweet->{text}; # Пользователь Твиттера my $user = $tweet->{from_user}; # Результирующий вывод my $result = "$text --$user\n\n"; utf8::encode $result; print $result; }
Базовая аутентификация
Вы можете просто добавить имя пользователя и пароль в URL.
my $ua = Mojo::UserAgent->new; print $ua->get('https://sri:secret@mojolicio.us/hideout')->res->body;
Оформление дополнительных запросов
Mojo::UserAgent может автоматически следовать перенаправлениям, функция
обратного вызова on_start
позволяет вам прямой доступ к каждой
транзакции сразу после их подготовки и перед получением коннекта,
ассоциированного(связанного) с ним.
# User agent, допускающий максимум 10 редиректов my $ua = Mojo::UserAgent->new(max_redirects => 10); # Добавить остроумный заголовок для каждого запроса $ua->on_start(sub { my $tx = pop; $tx->req->headers->header('X-Bender' => 'Bite my shiny metal ass!'); print 'Request: ', $tx->req->url->clone->to_abs, "\n"; }); # Запрос, в котором скорее всего будет перенаправление print 'Title: ', $ua->get('google.com')->res->dom->at('head > title')->text, "\n";
Это работает даже для запросов через прокси методом CONNECT
.
Потоковый ответ
Прием потокового ответа может быть очень сложным в большинстве HTTP клиентов, Mojo::UserAgent делает его действительно легким.
my $ua = Mojo::UserAgent->new; my $tx = $ua->build_tx(GET => 'http://mojolicio.us'); $tx->res->body(sub { print $_[1] }); $ua->start($tx);
Функция обратного вызова body
будет вызвана для каждого блока данных,
которые были получены, даже фрагментированное
кодирование будет
осуществляться прозрачно, если необходимо.
Потоковый запрос
Отправка потокового запроса почти так же легко.
my $ua = Mojo::UserAgent->new; my $tx = $ua->build_tx(GET => 'http://mojolicio.us'); my $content = 'Hello world!'; $tx->req->headers->content_length(length $content); my $drain; $drain = sub { my $req = shift; my $chunk = substr $content, 0, 1, ''; $drain = undef unless length $content; $req->write($chunk, $drain); }; $drain->($tx->req); $ua->start($tx);
Функция обратного вызова drain передается методу write
будет вызываться
всякий раз, когда весь предыдущий кусок был написан для ядра буфера передачи.
Загрузка больших файлов
При загрузке больших файлов с Mojo::UserAgent вам совсем не придется
беспокоиться об использовании памяти, потому что она будет автоматический
поток все, что выше 250KB
во временной файл
# Загрузка последнего архива Mojolicious my $ua = Mojo::UserAgent->new(max_redirects => 5); my $tx = $ua->get('latest.mojolicio.us'); $tx->res->content->asset->move_to('mojo.tar.gz');
Чтобы защитить вас от слишком больших файлов, есть также глобальный лимит
5MB
по умолчанию, который мы можете настроить с переменной
среды MOJO_MAX_MESSAGE_SIZE
.
# Увеличить лимит до 1GB $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824;
Передача больших файлов
Передача больших файлов еще проще.
# передача файла методом POST и "multipart/form-data" my $ua = Mojo::UserAgent->new; $ua->post_form('mojolicio.us/upload', {image => {file => '/home/sri/hello.png'}});
И еще раз, вам не придется беспокоиться об использовании памяти, все данные будут передаваться непосредственно из файла.
# Передача файла методом PUT my $ua = Mojo::UserAgent->new; my $asset = Mojo::Asset::File->new(path => '/home/sri/hello.png'); my $tx = $ua->build_tx(PUT => 'mojolicio.us/upload'); $tx->req->content->asset($asset); $ua->start($tx);
Non-Blocking
Mojo::UserAgent был разработан с нуля, чтобы быть неблокирующим(non-blocking), весь блокирующий API является простой удобной оболочкой. Особенно для задач с высокой задержкой, как сканирование интернета это может быть чрезвычайно полезным, потому что вы можете хранить много параллельных соединений активными одновременно.
# FIFO queue my @urls = qw/google.com/; # Агент пользователя, поддерживающий до 5ти редиректов my $ua = Mojo::UserAgent->new(max_redirects => 5); # Сканер my $crawl; $crawl = sub { my $id = shift; # Выйти из очереди или ожидать for more URLs return Mojo::IOLoop->timer(2 => sub { $crawl->($id) }) unless my $url = shift @urls; # Выборка non-blocking просто добавлением функции обратного вызова $ua->get($url => sub { my $tx = pop; # Извлечение URL print "[$id] $url\n"; $tx->res->dom('a[href]')->each(sub { my $e = shift; # Сформировать абсолютный URL my $url = Mojo::URL->new($e->{href})->to_abs($tx->req->url); print " -> $url\n"; # Добавить в очередь push @urls, $url; }); # Next $crawl->($id); }); }; # Запустить несколько параллельных сканеров совместно # использующих один и тот же агент пользователя $crawl->($_) for 1 .. 3; # Запустить цикл с ожиданием событий(event loop) Mojo::IOLoop->start;
Вы можете иметь полный контроль за Mojo::IOLoop event loop.
Параллельные блокирующие запросы
Вы можете имитировать поведение блокирования с помощью триггера Mojo::IOLoop для синхронизации нескольких неблокирующих запросов.
# Синхронизация неблокирующих запросов и захват результата my $ua = Mojo::UserAgent->new; my $t = Mojo::IOLoop->trigger; $ua->get('http://mojolicio.us' => $t->begin); $ua->get('http://mojolicio.us/perldoc' => $t->begin); my ($tx, $tx2) = $t->start;
Только знайте, что результаты транзакций будут получены в случайном порядке.
Командная строка
Разве вы не ненавидите проверку больших HTML файлов из командной строки?
Благодаря команде mojo get
это вот-вот изменится[это должно измениться].
Вы можете просто выбрать части, которые действительно имеют значение с помощью
CSS3 селекторов от Mojo::DOM.
$ mojo get http://mojolicio.us 'head > title'
Как вывести списком все идентификаторы тегов?
$ mojo get http://mojolicio.us '*' attr id
Или текстовое содержимое всех заголовков ?
$ mojo get http://mojolicio.us 'h1, h2, h3' text
Может быть, просто текст третьего заголовка ?
$ mojo get http://mojolicio.us 'h1, h2, h3' 3 text
Можно также извлечь весь текст из вложенных дочерних элементов.
$ mojo get http://mojolicio.us '#mojobar' all
Кроме того, запрос может быть настроен.
$ mojo get --method post --content 'Hello!' http://mojolicio.us $ mojo get --header 'X-Bender: Bite my shiny metal ass!' http://google.com
Вы можете следить за перенаправлениями и просматривать заголовки всех сообщений.
$ mojo get --redirect --verbose http://reddit.com 'head > title'
Это может быть бесценным инструментом для тестирования приложений.
$ ./myapp.pl get /welcome 'head > title'
Хаки
Полезные хаки, которые вы может быть не будете использовать часто, но которые однажды могут пригодиться.
Быстрые тесты
Разве вы не ненавидите ждать, пока make test
наконец завершится?
В новых версиях Perl можно установить переменную окружения
HARNESS_OPTIONS
чтобы получить преимущество нескольких ядер
процессора и выполнять тесты параллельно.
$ HARNESS_OPTIONS=j5 make test ...
Значение j5
позволяет выполняться одновременно 5ти тестам, что приводит
к тому, что, например набор тестов Mojolicious исполняется в 3 раза
быстрее на двухъядерном ноутбуке!
Запуск кода вместе с вашим приложением
Когда-нибудь задумывались о запуске быстрого однострочника с вашим Mojolicious
приложением для тестирования чего либо?
Благодаря команде eval
вы можете сделать как раз то, что может сделать
сам экземпляр приложения, доступный через app
.
$ mojo generate lite_app $ ./myapp.pl eval 'print app->static->root, "\n"'
Опция verbose
будет автоматически печатать возвращаемое значение в
STDOUT
.
$ ./myapp.pl eval -v 'app->static->root'
Создание установщика приложения
Когда-нибудь задумывались о выпуске вашего Mojolicious приложения для CPAN? Это на самом деле гораздо проще, чем вы думаете.
$ mojo generate app $ cd my_mojolicious_app $ mv public lib/MyMojoliciousApp/ $ mv templates lib/MyMojoliciousApp/
Весь фокус в том, чтобы переместить каталоги public
и templates
так, чтобы
они могли быть автоматически установлены с модулями.
package MyMojoliciousApp; use Mojo::Base 'Mojolicious'; use File::Basename 'dirname'; use File::Spec; # Every CPAN module needs a version our $VERSION = '1.0'; sub startup { my $self = shift; # Переключить в режим устанавливаемого каталога home $self->home->parse( File::Spec->catdir(dirname(__FILE__), 'MyMojoliciousApp')); # Переключить в режим устанавливаемого каталога public $self->static->root($self->home->rel_dir('public')); # Переключиться в режим устанавливаемого каталога "templates" $self->renderer->root($self->home->rel_dir('templates')); $self->plugin('pod_renderer'); my $r = $self->routes; $r->route('/welcome')->to('example#welcome'); } 1;
Это действительно все, теперь вы можете упаковать приложение, как и любой CPAN модуль.
$ ./script/my_mojolicious_app generate makefile $ perl Makefile.PL $ make test $ make manifest $ make dist
Hello World
Если экономить на каждом байте, то самое маленькое Hello World
приложение, написанное на Mojolicious::Lite, имеет следующий вид:
use Mojolicious::Lite; any {text => 'Hello World!'}; app->start;
Это работает, потому что все маршруты без шаблона по умолчанию /
и автоматический
рендеринг умирает, даже если не фактический код запускается на выполнение
маршрутизатором. Рендерер просто забирает значение text
из stash и формирует
ответ.
Hello World однострочник
Пример Hello World
, приведенный выше можно получить еще немного короче
в однострочнике ojo.
perl -Mojo -e'a({text => "Hello World!"})->start' daemon
И вы можете использовать все команды из Mojolicious::Commands.
perl -Mojo -e'a({text => "Hello World!"})->start' get -v /
Как всегда иметь в наличии последнюю версию Mojolicious
Этот изящный однострочник будет держать ваш Mojolicious свежим, насколько возможно.
$ sudo sh -c "curl -L cpanmin.us | perl - http://latest.mojolicio.us"
jQuery (Сеть доставки интернета)
В наши дни Mojolicious поставляется с упакованной версией JQuery, которую вы можете легко использовать в качестве запасного варианта для приложений, которые могут быть использованы в автономном[рабочем] режиме время от времени.
<%= javascript 'http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js' %> <%= javascript begin %> if (typeof jQuery == 'undefined') { var e = document.createElement('script'); e.src = '/js/jquery.js'; e.type = 'text/javascript'; document.getElementsByTagName("head")[0].appendChild(e); } <% end %>
Дополнительно
Вы можете продолжить перейдя на страницу Mojolicious::Guides или на wiki-страничку Mojolicious http://github.com/kraih/mojo/wiki, в котором содержится много документации и примеров от различных авторов.