Mojolicious-приложение на виртуальном хостинге

в 17:52, , рубрики: cgi, perl, shared hosting, web-framework, веб-приложение, виртуальный хостинг, метки: , , , , ,

О Mojolicious уже написано какое-то количество статей из которых можно получить общие впечатления о этом веб-фреймворке. Читая публикации можно понять, как легко разворачивается приложение, в том числе и PSGI, маштабируется, взаимодействует с веб-серверами и выдерживает высокие нагрузки. Всё это хорошо, конечно, но почему-то нет статьи о том, как запустить приложение на обыкновенном вирутальном хостинге. Хотя, может быть её нет по тому, что это сделать до неприличия просто?

Требования.

Как бы там ни было, но виртуальный хостинг должен удовлетворять некоторые требования.

  • установлен Perl версии 5.10 или выше;
  • разрешено использование cgi-скриптов;
  • поддерживаться mod_rewrite;

В общем-то, это и всё. Конечно, хорошо, если есть возможность использовать пространство за пределами веб-директории, что бы хранить там все необходимые модули. Будем считать, что сам Mojolicious у себя на компьютере вы уже установили самостоятельно, на пример, используя любую из статей с обзорами этого веб-фреймворка.

Mojolicious-приложение

Если у вас такового ещё нет, то предлагаю немного развлечься и проверить Хабрахабр на толерантность. Для этого нам понадобится сам Mojolicious, ваше любимое IDE и немного терпения.

Каркас приложения

Первый шаг, это генерации каркаса приложения. Так как мы хотим выбраться за пределы однострочного приложения и положить конец противостоянию Dancer vs Mojolicious, откажемся от Lite-версии:

habra@cynovg-notebook:~$ mojo generate app Habra
  [mkdir] /home/habra/habra/script
  [write] /home/habra/habra/script/habra
  [chmod] habra/script/habra 744
  [mkdir] /home/habra/habra/lib
  [write] /home/habra/habra/lib/Habra.pm
  [mkdir] /home/habra/habra/lib/Habra
  [write] /home/habra/habra/lib/Habra/Example.pm
  [mkdir] /home/habra/habra/t
  [write] /home/habra/habra/t/basic.t
  [mkdir] /home/habra/habra/log
  [mkdir] /home/habra/habra/public
  [write] /home/habra/habra/public/index.html
  [mkdir] /home/habra/habra/templates/layouts
  [write] /home/habra/habra/templates/layouts/default.html.ep
  [mkdir] /home/habra/habra/templates/example
  [write] /home/habra/habra/templates/example/welcome.html.ep

Удаляем директории с примерами, если мешают и приступаем к развлечению.

Проектирование приложения

Что бы приложение было хоть чуть-чуть серьёзнее, чем «Hello, World!», предлагаю использовать поиск Хабрахабры для отработки навыков. А именно, запрашивать у пользователя поисковую строку, отдавать её Хабру, получать ответ и выводить его пользователю.

Начнем с малого, научимся генерировать форму ввода поискового запроса и выводить шаблон-заглушку, как реакцию на получение данных от пользователя. Для этого понадобится маршрут к форме ввода, сам шаблон формы и ответ-заглушка.

Все маршруты у нас хранятся в модуле Habra.pm, расположенном в директории lib, содержимое которого после генерации будет выглядеть следующим образом.

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

# This method will run once at server start
sub startup {
  my $self = shift;

  # Documentation browser under "/perldoc"
  $self->plugin('PODRenderer');

  # Router
  my $r = $self->routes;

  # Normal route to controller
  $r->get('/')->to('example#welcome');
}

1;

Так как мы занимаемся поиском, то логично назвать наш контроллер Search, а маршруты к нему, соотвественно, search#default и search#result. По умолчанию будем выводить форму ввода:

  $r->route( '/' )->via('get')->to( 'search#default' );

и создадим шаблон для формы ввода, расположив его в /templates/search/deafult.html.ep:

% layout 'default';
% title 'Форма поискового запроса';
<form action='<% url_for %>' method='post'>
    <input type='text' name='q' placeholder='Что будем искать?'>
    <input type='submit' value='Поехали!'>
</form>

Теперь, после того, как удалили все лишнее из модуля Habra.pm и изменили маршрут по умолчанию, наш модуль должен выглядеть следующим образом:

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

sub startup {
  my $self = shift;
  my $r = $self->routes;
  $r->route( '/' )->via('get')->to( 'search#default' );
}

1;

Проверим, как он работает, запустив из командной строки скрипт из одноименной директории:

morbo script/habra 
Server available at http://127.0.0.1:3000.

Обратившись к нему из браузера по адресу 127.0.0.1:3000 должна быть отображена наша форма ввода, которая, конечно же, приводит к краху приложения как только мы куда-нить собираемся поехать. А происходит это по тому, что у нас нет маршрута к контролу для метода post, который используется для передачи данных из формы ввода. Добавим маршрут в основном модуле и временный шаблон-заглушку в шаблонах, Для этого в основном модуле добавим строчку:

  $r->route( '/' )->via('post')->to( 'search#result' )

и создадим шаблон вывода данных, templates/search/result.html.ep

% layout 'default';
% title 'Результат поиска';

Если мы сейчас проверим, как работает наше приложение, то увидим, что в качестве реакции на наше желание куда-нить поехать, будет пустая страница с заголовком «Результат поиска».

Что бы достигнуть нашей цели, понадобится реализация метода result в ко́нтроле, который бы принимал запрос, отправлял его на хабр и преобразовывал ответ из Хабра в удобную нам форму. Для этого создаем модуль lib/Habra/Search.pm с следующим содержимым:

package Habra::Search;
use Mojo::Base 'Mojolicious::Controller';

sub result
{
    my $self = shift;
    my $str  = $self->param( 'q' );
    my $ua  = Mojo::UserAgent->new;
    my $dom = $ua->get( 'http://habrahabr.ru/search/?q='.$str )->res->dom;
    my $pull = ();
    for my $raw ( $dom->find( 'div[id^="post"]' )->each )
        {
            my $header= $raw->find( 'a[class="post_title"]' )->first;
            push @$pull, {
                title => $header->text,
                url => $header->{href},
                content => $raw->find( 'div[class^="content"]' )->first->text
            };
        }
    $self->render( result => $pull );
}

1;

и дополняем шаблон-заглушку для вывода результата запроса:

% layout 'default';
% title 'Результат поиска';
<ul>
    % for my $row ( @$result ) {
        <li>
            <h2><%= $row->{title} %></h2>
            <p><%= $row->{content} %></p>
            <p>Ссылка на статью: <a href="<%= $row->{url} %>"><%= $row->{url} %></a></p>
        </li>
    %}
</ul>

Если есть какой-то более простой или, на ваш взгляд, более «правильный» способ извлечения данных, то я бы с удовольствием узнал о нём.

Будем считать, это для первого знакомства этого будет достаточно, но основной цели мы на данном этапе мы всё ещё не достигли, так как наше приложение всё ещё находится на локальной машине. Самое время запустить его на виртуальном хостинге.

Публикация приложения

На данном этапе мы имеем хостинг, соотвествующий требованиям и рабочее приложение. Осталось дело за малым, опубликовать его. Ах да, давайте будем считать, что доменное имя у нас mojoex.am.pl, а путь к сайту /home/mojoex.am.pl/www

Для запуска приложения необходимо скачать исходные коды Mojolicious, на пример, с GitHub

git clone https://github.com/kraih/mojo.git

после чего удалить всё лишнее из дистрибутива, оставив только директорию lib, посколько именно там хранятся все модули. Что бы не путаться между модулями приложения и модулями самого Моджолишес, можно переименовать директорию lib на пример, в mojo.

Следующим шагом немного модифицируем скрипт, запускающий приложение, добавив в его заголовок указатель на директории с модулями Моджолишис и самого приложения и переименуем его в habra.cgi

use lib ( '/home/mojoex.am.pl/mojo' );
use lib ( '/home/mojoex.am.pl/lib' );

Очередным шагом надо выложить модули Моджолишес за пределамы сайта, по пути /home/mojoex.am.pl/mojo. По этому же пути выложить модули приложения и шаблоны, /home/mojoex.am.pl/lib и /home/mojoex.am.pl/templates, соотвествтенно. Так же создадим директорию для логов, /home/mojoex.am.pl/log. А вот скрипт, который стартует приложение надо разместить в директории /home/mojoex.am.pl/cgi-bin, с правом на исполнение.

Последним шагом будет немного магии с mod_rewrite. Необходимо создать правила, для в файле .htaccess, который должен раполагаться в директории /home/mojoex.am.pl/www, с следующим содержимым:

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /cgi-bin/habra.cgi/$1 [L]
RewriteRule ^$ /cgi-bin/habra.cgi [L]

Всё, на этом наши мытарства должны закончится, в чём можно убедиться, перейдя на главную страницу сайта. Если всё сделать правильно, то должно появится приглашение к вводу данных для поиска. Если же этого не случилось, то следует посмотреть причину в логах ошибок сервера, скорее всего проблема в доступе к скрипту запуска приложения.

Резюме

В итоге структура хостинга должна выглядеть схожим образом:

/home/mojoex.am.pl/                             # корень сайта
/home/mojoex.am.pl/mojo/                        # директория с модулями Mojolicious
/home/mojoex.am.pl/lib/                         # директория с модулями приложения
/home/mojoex.am.pl/log/                         # директория с логами работы приложения
/home/mojoex.am.pl/www/.htaccess                # файл с правилами для mode_rewrite
/home/mojoex.am.pl/www/cgi-bin/habra.cgi        # скрипт, запускающий приложение

Статичные файлы, такие как стили, изображения и прочее хранятся обычным образом, благодаря правилам в .htaccess они игнорируются при переадресации. При обращении к несуществующим ресурсам происходит переадресация к приложению, которое самостоятельно принимает решение, как поступить. Если просиходит обращение по заданым путям, то управление передается в ко́нтрол, в противном случае генерируется сообщение о ошибке, формировать которе можно самостоятельно.

Если кому-то фрагмент кода может показаться зубодробительным, либо есть какие-то непонятные моменты, замечания или предложения, оставляйте замечания комментариях.

Автор: cynovg

* - обязательные к заполнению поля