Блог

Mojolicious::Lite стартуем!

Mojolicious::Lite Стартуем!


Mojolicious Perl framework

Mojolicious::Lite — микро веб-фреймворк, написанный на Perl, основанный на Mojolicious и входящий в состав Mojo.

Рассмотрим основные возможности данного фреймворка.

Статья основана на официальной документации:
perldoc Mojolicious::Lite

Поехали!
Использование в вашем скрипте Mojolicious::Lite автоматически подключает прагмы 'use strict' и 'use warnings', так что в отдельном их подключении необходимости нет.

Т.е. скрипт приложения может выглядеть примерно вот так:
#!/usr/bin/env perl

use Mojolicious::Lite

…................................
app->start;

Для начала работы с Mojolicious::Lite необходимо создать начальное приложение или проект (в терминах иных фреймворков).

Для этого предназначена команда:
mojolicious generate lite_app <application_name>.pl

Если не передать последний параметр (имя приложения), созданное приложение получит название myapp.pl.

Открываем файл нашего приложения (О да! Полноценное MVC-приложение всего в одном файле!).

И вот что видим:

#!/usr/bin/env perl

use Mojolicious::Lite;

get '/' => 'index';

get '/:groovy' => sub {

    my $self = shift;

    $self->render(text => $self->param('groovy'), layout => 'funky');

};

app->start;

__DATA__

@@ index.html.ep

% layout 'funky';

Yea baby!


@@ layouts/funky.html.ep

<!doctype html><html>

    <head><title>Funky!</title></head>

    <body><%== content %></body>

</html>

Как видим, в одном файле находится и логика приложения и шаблоны, тем не менее они разделены (кроме того, здесь же можно расположить и файлы изображений).

Для запуска только что созданного приложения, выполним следующую команду:
./myapp.pl daemon


В результате должны увидеть приблизительно следующий вывод:
Mon Oct 4 14:11:06 2010 info Mojo::Server::Daemon:363 [12764]: Server listening (http://*:3000)

Тем самым мы запустили встроенный веб-сервер.

Откроем браузер и перейдем по адресу: localhost:3000/

В результате увидим сообщение:
Yea baby!

Наш первый проект на Mojolicious::Lite заработал! При этом мы не написали еще ни строчки кода.

У файла приложения (myapp.pl) существует довольно много опций.
Для получения справки — введите:
./myapp.pl help

Для получения справки по конкретной команде — введите:
./myapp.pl help <command_name>

Если сейчас поменять содержимое html-шаблона в нашем приложении (вы ведь нашли шаблон, не так ли ?) и обновить страницу в браузере — изменений мы не увидим.

Для того чтобы изменения «вступили в силу» необходимо перезапустить приложение или запустить его с ключом --reload.

Возьмем за правило: все приложения, при разработке, запускать с ключом --reload, для простоты и удобства.

./myapp.pl daemon —reload
— правильная команда для запуска приложения в среде разработки.

Ну все! С лирикой закончили! Приступим к программированию!

Для начала выведем всем знакомый 'Hello World!'.
Только в нашем случае это будет строка 'Hello Mojolicious::Lite!'.

Для этого добавим следующий код в наше приложение:
get '/hello' => sub {

    my $self = shift;

    $self->render(text => 'Hello Mojolicious::Lite!');

};


Данный код определяет, так называемый, роут (маршрут или путь), который может обработать наше приложение.

Кусок из официальной документации:
'Routes are basically just fancy paths that can contain different kinds of placeholders.'

В данном случае строка '/hello' — это путь, который должен быть набран в строке браузера, чтобы вызвался наш новый обработчик.

Если сейчас мы перейдем по адресу localhost:3000/hello/, то как раз и увидим наш текст. Строка 'get' стоящая перед строкой '/hello' — означает метод http-запроса, который мы хотим обработать. Можно использовать следующие методы: get, post, put, delete. Таким образом Mojolicious прекрасно подойдет для написания REST-приложений.

Каждый роут может иметь имя. Наличие имени у роута позволяет автоматически определить какой шаблон следует отобразить по умолчанию. Имя всегда указывается последним параметром при создании роута. Если в качестве имени указать символ '*', это будет означать что имя роута совпадает с самим роутом.

Пример:
get '/all'   => '*'; #именем роута (и шаблона) в этом случае будет 'all'


Давайте перепишем наш предыдущий пример и сделаем его более MVC-ориентированным, т.е. отделим логику от представления.

Для начала в файл нашего приложения, в любое место после строки '__DATA__' добавим следующий код:
@@ hello.html.ep
% layout 'funky';
Hello Mojolicious::Lite! MVC is cool!

Именно так выглядит шаблон странички, которую мы скоро увидим в браузере.

Разберем построчно, что мы тут написали:

— строка 1 '@@ hello.html.ep' — название нашего шаблона (или представления)

— '@@' — обязательное начало для оформления всех шаблонов

— 'hello' — имя шаблона

— 'html.ep' — расширение шаблона. По расширению Mojo определяет какой обработчик использовать для отрисовки шаблона (об обработчиках расскажу позже)

— строка 2 '% layout 'funky';' — здесь мы имеем дело с так называемым, общим шаблоном, говоря коротко, мы указываем, что наш шаблон 'hello' оборачивается шаблоном 'funky' (об этом тоже немного позже)

— строка 3 — содержание самого шаблона, т.е. именно здесь размещается тело html-документа, который и представляет собой наш шаблон

И так шаблон добавлен.
Кроме шаблона нам необходимо изменить и наш обработчик, который примет следующий вид:
get '/hello' => sub {

    my $self = shift;

 } => 'hello';    # задали имя для роута 

Что изменилось:

1 Убрали строку $self->render(text => 'Hello Mojolicious::Lite!');

2 Добавили имя для роута ( => 'hello' после закрывающей скобки функции)

Как видно из кода выше, наш обработчик не выполняет никаких действий кроме вывода шаблона по умолчанию. Если он ничего не делает, для чего он нам?

Уберем его:
# один вариант - мало чем отличается от предыдущего,

# просто тело функции пусто

get '/hello' => sub {} => 'hello';    


# вот так уже гораздо лучше

get '/hello' => 'hello';  

Последняя строка наиболее проста для понимания. Мы «говорим» Mojo:

— при получении строки «hello» — ничего не делать, просто отобразить шаблон «hello».

Перейдем по адресу localhost:3000/hello/ и мы должны увидеть содержание нашего шаблона.

Можно добавить в шаблон html-разметку и посмотреть на результат.

Очень часто (практически всегда =)) у сайта есть общие части шаблона, такие как «шапка», «подвал», «меню» и т.д. И так называемая, 'контентная часть', которая различна для каждой странички. Общие части, как правило, выносят в отдельный шаблон, называемый 'layout' или 'общий шаблон'. При использовании Mojolicious::Lite делается это очень просто.

Для начала создадим общий шаблон. Он, как и все остальные шаблоны, создается в секции __DATA__ файла нашего приложения.
@@ layouts/funky.html.ep

<!doctype html><html>

    <head><title>Hello Mojolicious::Lite! MVC is cool!</title></head>

    <body><%== content %></body>

</html>

Здесь мы видим синтаксис, практически идентичный синтаксису объявления простых шаблонов, но есть некоторые особенности.

— название шаблона задается как 'layouts/funky.html.ep' т.е. к имени шаблона добавляется префикс 'layouts/', таким образом мы создали общий шаблон с названием 'funky'

— в теле шаблона есть выражение <%== content %> вместо которого, при отображении страницы, будет подставляться контент конкретного шаблона.

И так, общий шаблон готов. Создадим новый шаблон для нашей страницы 'hello':
@@ hello.html.ep

% layout 'funky';

<b>Hello content with layout!</b>  

Ничего нового в этом коде нет — задаем имя шаблона, указываем что общим шаблоном является шаблон 'funky' и указываем собственно тело шаблона. Обновляем страницу в браузере и видим страничку с новым содержанием.

Отмечу, что используемый layout можно указать как непосредственно в шаблоне, так и в обработчике роута, при вызове метода 'render'

Пример. Изменим шаблон для страницы 'hello' следующим образом:
@@ hello.html.ep

<b>Hello content with layout!</b>  


Т.е. мы просто убрали строку, указывающую на то, что у шаблона 'hello' есть еще и общий шаблон. Обновим страничку. Видим, что вывода общего шаблона не произошло.

Наш обработчик для роута 'hello' изменим следующим образом:
get '/hello' => sub{

    my $self = shift;

    $self->render(layout => 'funky');

} => 'hello';    

Здесь мы видим, что наш обработчик, снова принял вид Perl-функции, в которой вызывается метод 'render' с параметром 'layout' = 'funky'. Обновим страничку. Результат должен быть тем же самым. Более того, этот обработчик можно переписать следующим образом:
get '/hello' => sub{

    my $self = shift;

    $self->render('hello',layout => 'funky');

};

Здесь мы вместо использования шаблона по умолчанию (вы ведь помните, что если роуту задать имя, это имя будет и именем его шаблона по умолчанию) явно указываем какой шаблон необходимо отобразить (первый параметр метода 'render'). При обновлении страницы, результат измениться не должен.

Кроме простых или статичных роутов, Mojolicious позволяет задавать роуты с параметрами. Рассмотрим пример. Предположим, что нам необходимо сделать страницу для вывода информации о пользователе. Каждый пользователь приложения имеет свой уникальный идентификатор (id).
get '/user/:id' => sub {

    my $self = shift;

    my $user_id = $self->stash('id');

    # получаем информацию о пользователе с указанным идентификатором

    #.......

    $self->render(text => "User id = $user_id !");

};

Строка роута '/user/:id' имеет часть ':id' — ее можно назвать 'плейсхолдером' или 'меткой' она и является параметром, который может меняться от запроса к запросу.

Создав такой обработчик, попробуем обратиться по следующим адресам:

localhost:3000/user/1

localhost:3000/user/678

localhost:3000/user/987

В результате увидим, что каждый раз на странице отображается новое значение.

Параметры в роуте могут быть расположены где угодно.

Пример:
get '/archive/:year/month/:month' => sub {

    my $self  = shift;

    my $year  = $self->stash('year');

    my $month = $self->stash('month');

    $self->render(text => "Archive for $year and $month!");

};

Очень полезно иметь возможность указать формат параметра, который ожидается получить. И Mojolicious, конечно же, позволяет это сделать.
get '/archive/:year/month/:month' => [year => qr/\d{4}/, month => qr/\d{2}/] => sub {

    my $self  = shift;

    my $year  = $self->stash('year');

    my $month = $self->stash('month');

    $self->render(text => "Archive for $year and $month!");

};

В этом примере мы добавили следующую конструкцию:

[year => qr/\d{4}/, month => qr/\d{2}/]

Здесь мы видим самые простые регулярные выражения Perl.
qr/\d{4}/ — год должен состоять строго из 4-х цифр,
qr/\d{2}/ — месяц — строго из 2-х.

Отмечу, что мы не использовали символы привязки регулярного выражения к позиции в строке (^ и $), Mojolicious делает это автоматически. Таким образом, если теперь обратится по адресу:

localhost:3000/archive/2010/month/april

Мы получим сообщение, что страница не найдена (Page Not Found), так как значение 'april' не совпадает с регулярным выражением qr/\d{2}/.

Mojolicious позволяет задавать значения по умолчанию для меток/параметров роута. Предположим, что в нашем архиве из предыдущего примера, если не передан месяц — необходимо отображать все записи за декабрь (12-ый месяц). Сделать это очень просто:
get '/archive/:year/month/:month' => { month => 12 } => [year => qr/\d{4}/, month => qr/\d{2}/] => sub {

    my $self  = shift;

    my $year  = $self->stash('year');

    my $month = $self->stash('month');

    $self->render(text => "Archive for $year and $month!");

};

Всего навсего мы добавили конструкцию { month => 12 }, в которой и указали значение по умолчанию.

Если необходимо выполнить какой-то код перед каждым роутом — такой код необходимо оформить следующим образом:
under sub {
    my $self = shift;
    app->log->warn('Execute before all routes!');
    return 1;
}; 

Для того чтобы выполнение приложения продолжилось — необходимо вернуть 1.
Именно здесь можно разместить код проверки авторизации или что-то подобное.

Роуты могут содержать дополнительные условия. Допустим, что мы хотим отображать какую-либо страницу только тем посетителям, которые используют браузер FireFox, для этого создадим новый роут:
get '/ff' => (agent => qr/Firefox/) => sub {
    shift->render(text => 'FireFox is cool!!!');
};

Выражение (agent => qr/Firefox/) говорит о том, что данный роут сработает только при обращении через FireFox. Если попытаться обратится через иной браузер — увидим сообщение о том, что страница отсутствует.

Вот такое большое получилось введение в Mojolicious::Lite!
Тем не менее в статье описаны далеко не все возможности и особенности данного фреймворка и в последующих статьях будет продолжение. За кадром осталось: встроенные сессии, тестирование, websockets, плагины, DOM-парсер и еше много всего!

Пишите на Perl, пишите о Perl!
use Perl or die;