Документация Perl 5

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, в котором содержится много документации и примеров от различных авторов.