SlideShare a Scribd company logo
Почему Mojolicious?
     Анатолий Шарифулин
      YAPC::Russia 2011
mojolicio.us
Кто знает, что такое
    Mojolicious?
Кто использует
 Mojolicious?
На кого повлиял я? :)
Коротко
Современный и
   молодой
веб-фреймворк
Веб в коробке!
Mojo::Base
Mojo::DOM, Mojo::JSON
   Mojo::UserAgent
     Mojo::IOLoop
    Mojo::Template
Mojo
 Mojolicious
Mojolicious::Lite
Test::Mojo
   ojo
     ...
385 вотчеров
 88 форков
 По данным github.com
и один Шарифулин :-)
      По моему мнению
Почему всё-таки
   Mojolicious?
Когда есть Dancer, Plack, Python и Node.js
Помогает решить
почти любую задачу
     Почему Mojolicious?
Начиная от простого
 сайта в 5 страниц
     Почему Mojolicious?
tochkak.ru
use Mojolicious::Lite;

get '/'      => 'about/what' ;
get '/who' => 'about/who' ;
get '/what' => 'about/what' ;
get '/where' => 'about/where';
get '/code' => 'about/code' ;

app->log->level('error');

app->start;
Заканчивая стартапом
c 5000+ пользователями
       Почему Mojolicious?
frodio.com
или
узкоспециализированной
  системой управления
    для кинотеатров
       Почему Mojolicious?
db.dcp24.ru
От простых скриптов
     Почему Mojolicious?
#!/usr/bin/env perl
use ojo;

g( 'http://bobina.pdj.ru/rss.xml' )
	

 ->dom
   	

 ->find( 'enclosure[url]' )
	

 ->each(sub {
	

 	

 say shift->attrs->{url}
	

 })
;
или TCP-клиента для
сотни радио-потоков
     Почему Mojolicious?
my $url = Mojo::URL->new(
	

 'http://frod.io:8000/station20'
);
$loop->connect(
	

 address       => $url->host,
	

 port           => $url->port,
	

 on_connect => sub { ... },
    on_read        => sub { ... },
);
$loop->start;
on_connect => sub {
	

 	

 my ($self, $id) = @_;
	

 	

	

 	

 my $r = Mojo::Message::Request->new;
	

 	

 $r->headers->header(
           'Icy-MetaData' => 1
        );
	

 	

 $r->url( $url );
	

 	

	

 	

 $self->write($id, $r->to_string);
},
on_read => sub {
	

 my ($self, $id, $chunk) = @_;

	

 return unless my %tag =
	

 	

 $chunk =~ /Stream(w+)='(.*?)';/g;
	

	

 say $tag{Title};
},
До софта по
   тиражированию
     фильмов для
цифровых кинотеатров
      Почему Mojolicious?
CopyDisk
Mojo::Base
  Mojo::Log
 MojoX::Run
Curses::Widgets
А также тесты для
  веб-сервисов
    Почему Mojolicious?
use Test::More tests => 252;
use Test::Mojo;

my $t = Test::Mojo->new(app => 'App'); # App.pm
my $url = '/api';
# my $url = 'http://api.dev.frodio.com';

$t->get_ok( "$url/" )
  ->status_is( 200 )
  ->json_content_is({ hello => 'Hello, Frodio!' })
;
my $data = $t->post_form_ok("$url/like",
{station_id => 2}, {'X-Frodio-Auth' => $auth})
   ->status_is(200)
   ->tx->res->json
;
{
	

 is ref    $data, 'HASH';
	

 is exists $data->{ok}, 1;
	

 is exists $data->{count}, 1;
	

 is defined $data->{sign}, 1, 'Like station';
}
$t->post_form_ok("$url/logout/",
	

 {'X-Frodio-Auth' => $auth}
)
   ->status_is(200)
;
Большинство своих
   задач я решаю,
используя Mojolicious
«Всегда хотел
научиться делать
     сайты»
etnogenez.ru
Небольшой сайт с
полноценной панелью
     управления
26 модулей (140k), 91 шаблон (408k), 16 таблиц
Структура проекта
Все пути и запуски — от корня проекта
bin/
conf/
data/
lib/
log/
script/
t/
tmpl/
tmp/
bin/check.sh
bin/logs.sh
bin/mysql
bin/mysqldump
bin/restart.sh
bin/start.sh
bin/stop.sh
bin/start.sh
(
	

 # script/etnogenez daemon --reload
	

 starman --listen :3000 script/etnogenez
) >> log/error.log &
conf/app.conf
conf/mysql.conf
conf/nginx.conf
conf/app.conf
{
	

   secret => '*****',
	

   server => {
	

   	

 www => $ENV{DEV} ? 'http://...' : 'http:/...',
	

   	

 ...
	

   },
	

   session => { ... },
	

   log       => {
	

   	

 level => $ENV{DEV} ? 'debug' : 'warn',
	

   	

 path => 'log/app.log',
	

   }, ...
}
conf/mysql.conf
{
	

   drivername => 'mysql',
	

   user           => $ENV{DEV} ? 'dev' : 'не-dev',
	

   password       => '******',
	

   datasource => {
	

   	

 database => $ENV{DEV} ? 'dev' : 'не-dev',
	

   	

 host     => 'localhost',
	

   },
};
data/
 log/
tmp/
  t/
script/
Стартовый скрипт
Пути к библиотекам, настройка переменных
               окружения
use common::sense;
use lib qw(lib /tk/lib);

BEGIN {
	

 $ENV{DEV}++ if qx(pwd) =~ /dev/;
	

 $ENV{MOJO_MODE} ||= $ENV{DEV} ? 'dev' : 'production';
	

 $ENV{MOJO_TMPDIR} = 'tmp/upload';
	

 $ENV{MOJO_MAX_MESSAGE_SIZE} = 2 * 1024 ** 3;
};
$ENV{MOJO_APP} ||= 'App';

use Mojolicious::Commands;
Mojolicious::Commands->start;
lib/
App.pm
package App;
use Mojo::Base 'Mojolicious';

has conf => sub { do 'conf/app.conf' };
has db => sub { use Util; Util->db(
	

 do 'conf/mysql.conf'
) };

sub startup { ... }
use DBI 1.58; use DBD::mysql 4.004; use DBI::Util;

return DBI->connect(DBI::Util::_parse_cfg(
	

 $conf,
	

 {
	

 	

 RootClass => 'DBI::Util',
	

 	

 mysql_enable_utf8 => 1,
	

 	

 mysql_auto_reconnect => 1,
	

 }
));
select
query
   in
 limit
values
use dw;
Lazy-обертка, связи parent/child и прочее
       Контекстно проекту и БД
sub book { my $self = shift;
	

 SLICELY { $self->dw::g::part ('book_id') } 'id' => 'part' =>
	

 ...
}

sub part { my $self = shift;
	

 SLICELY { CHV {$_->[0]} $self->dw::g::book('id') } 'book_id'
=> 'book' =>
	

 ...
}
sub _list {
	

 my $self = shift;
	

 ...	

	

 return $self->dw->book(
	

 	

 $self->db->select(
	

 	

 	

 "select * from book where hidden=0
	

 	

 	

 order by $order $limit"
	

 	

 )
	

 );
}
bin/mysql
С базой данной работаю через консоль
Настройка путей,
логов, сессий, типов
        startup
Подключение
плаггинов и хелперов
        startup
$app->helper(db => sub { shift->app->db });

sub action {
	

 my $self = shift;
	

 # my $DB = $self->app->db;

	

 $self->db->select('...');
}
$app->helper(u => sub {
	

 	

 my $self = shift;
	

 	

 my $func = shift || return;
	

 	

	

 	

 return &{"Util::$func"};
	

 });

$self->u(iso2human => '...');

%=u iso2human => '...'
# в каждом контроллере
use Util;

# в шаблоне или коде
Util::iso2human(...);

# это boilercode и некрасиво
# поэтому хелпер
Общие и
контексно проекта
     хелперы
Mojolicious::Plugin::UtilHelpers и App::Helpers
Все роутеры проекта
        startup
route, bridge, waypoint,
    name, shortcut
     Mojolicious::Guides::Routing
my $ad = $r->route('/admin')->to->name('admin');
$ad->route('/login')->post->to('admin-enter#login');
	

my $a = $ad->bridge->to('admin-enter#check');

# shortcut / /sort /add /:id /:id/edit /:id/remove /:filter
$a->crud($_ => "admin-$_") for qw(book part ...);

$a->route('/(*any)')->to('admin#not_found');
App::Helpers
Различные форматирования, работы со строками,
          повторяющиеся действия
$app->helper(format_mmss => sub {
	

   	

 my $self = shift;
	

   	

 my $int = shift || return '00:00';
	

   	

	

   	

 return sprintf "%02d:%02d",
	

   	

 	

 $int / 60, $int % 60;
	

   });
$app->helper(user_img => sub {
	

   	

 my $self = shift;
	

   	

 my $user = shift || $self->stash('USER');
	

   	

	

   	

 return $user->{avatar} || '/.../default.png';
	

   });
App::Index
        Контроллер
$r->route->to('index#main');
package App::Index;
use App::Base -controller, with =>
['App::News', 'App::Book', 'App::Audio'];

sub main { ... }
package App::Index;
use Mojo::Base 'Mojolicious::Controller';
use common::sense;

use App::News;
use App::Book;
use App::Audio;

has news => sub { App::News->new(%{ +shift }) };
has book => sub { App::Book->new(%{ +shift }) };
has audio => sub { App::Audio->new(%{ +shift }) };
package App::Index;
use Mojo::Base 'Mojolicious::Controller';
use common::sense;

use App::News;
use App::Book;
use App::Audio;

__PACKAGE__->attr(news => sub { App::News->new
(%{ +shift }) });
__PACKAGE__->attr(book => sub { App::Book->new
(%{ +shift }) });
__PACKAGE__->attr(audio => sub { App::Audio->new
(%{ +shift }) };
Mojo::Base vs. App::Base
    common::sense, -controller, with
package App::Index;
use App::Base -controller, with =>
['App::News', 'App::Book', 'App::Audio'];

sub main { ... }
my $self = shift;
my $limit = $self->conf('limit')->{index};

$self->render('index',
	

 news => $self->news->_last(limit =>
$limit->{news}),
	

 book => $self->book->_list,
	

 part => $self->audio->_last(limit =>
$limit->{part}),
);
App::Book
  Контроллер
package App::Book;
use App::Base -controller;

sub check { ... } # для bridge

sub list { ... }

sub item { ... }

sub _list { ... } # возращает данные
sub check {
	

 my $self = shift;
	

	

 return 0 unless my $book = $self->dw->book(
	

 	

 $self->db->select(
	

 	

 	

 'select * from book where name=? limit 1',
	

 	

 	

 $self->stash('book_name')
	

 	

 )
	

 )->[0];
	

	

 $self->stash(book => $book);
	

 return 1;
}
sub item {
	

 my $self = shift;
	

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

 ...
}

# роутеры

my $bn = $r->bridge('/book/:book_name')
->to('book#check');

$bn->route->to('book#item')->name('book');
tmpl/
index.html.ep
          Шаблон
$r->route->to('index#main');
% layout 'default', title => '...';

<div id="column1">
% for (@$news) {
	

 %== include 'news/item.inc', item => $_
%}
</div>

<span class="date">
	

 %=u iso2humanM => $_->{published}
</span>

<span class="download_count">
	

 <%= format_digital($_->{listened}) %> раз
</span>
$news
     vs.
stash 'news'
layouts/default.html.ep
layouts/default.mail.ep
  layouts/default.rss.ep
 layouts/admin.html.ep
etc/page.html.ep
etc/submenu.html.ep
admin/etc/sort.txt.ep
Ни в коем случае
сложной логики, тем
   более SQL :-)
exception.html.ep
  exception.mail.ep
exception.dev.html.ep
% layout 'default', title => 'Страница временно
недоступна', simple => 1;

<div class="error_page">Ошибка 500. Страница
временно недоступна. Попробуйте позднее.</
div>

% mail(to => conf('mail')->{devel}, template =>
'exception', format => 'mail');
stash и defaults
Справочники и работа с ними
include 'etc/vars'
Раньше был шаблон, который подключался везде
# app.conf
defaults => {
	

 book_status => [
	

 	

 [soon => 'Готовится к изданию'],
	

 	

 ...,
	

 	

 [new => 'Новинки'],
	

 ],
	

 ...
}
# App.pm
if (my $d = $conf->{defaults}) {
	

 	

 $self->defaults( $d );
	

 	

	

 	

 for (keys %$d) {
	

 	

 	

 next unless ref $d->{$_} eq 'ARRAY';
	

 	

 	

 $self->defaults($_ . '_hash' => {
	

 	

 	

 	

 map { $_->[0] => $_->[1] }
	

 	

 	

 	

 @{ $d->{$_} }
	

 	

 	

 });
	

 	

 }
}
# в шаблоне

@$book_status

# или

$book_status_hash->{new}
Работы с формами
Я не использую
никаких генераторов
       форм
Формы для
 пользователей и
модель данных —
  разные вещи
# App::Admin::Book
sub add {
	

 my $self = shift;
	

 return $self->form unless $self->validate->book;
	

 # работа с полученными данными
}

sub edit {
	

 my $self = shift;
	

 my $item = $self->stash('item'); # через bridge
	

	

 return $self->form unless $self->validate->book;
	

 ...
}
admin/book/
form.html.ep
Шаблон может быть один
В итоге получаются
  очень простые
  контроллеры и
     шаблоны
И весь проект в целом
Вспомогательные
      скрипты
Рассылка по пользователям, графики для munin,
                cron-скрипты
# script/munin/user.pl

use MojoX::Loader;
my $user = MojoX::Loader->load(
	

 controller => 'App::User'
);

say $user->_total;

# $user->db->select(...);
# $user->conf('server')->{www}
# $user->render_partial('...', stash1 => '..', stash2 => '..')
# $user->mail(to => '..', template => '..')
MojoX::Loader
https://github.com/sharifulin/mojox-loader
Mojolicious очень
удобный и простой
   инструмент
С большим количеством
  современных фитч
Хороший open source
     проект
Активное сообщество
И в принципе
адекватый автор :-)
«Удивлен насколько
легко читается код,
 даже для человека,
который Perl видит
второй раз в жизни»
Попробуйте
Mojolicious прямо
     сейчас!
Не будьте
«I am just slow to get
      things...»
       http://frd.io/gf6
use Mojolicious or die;
use Perl or die;
JFDI
Спасибо за внимание!
     Анатолий Шарифулин
      YAPC::Russia 2011
Mojolicous by @vti

More Related Content

PDF
Пластилиновый код: как перестать кодить и начать жить
PDF
Erlang tasty & useful stuff
PDF
Почему Mojolicious?
PPTX
Nginx.pm
PDF
Что нового в Perl? 5.10 — 5.16
PDF
Perl 5.10 и 5.12
PPT
PHP Tricks
PDF
Perl – жив?!
Пластилиновый код: как перестать кодить и начать жить
Erlang tasty & useful stuff
Почему Mojolicious?
Nginx.pm
Что нового в Perl? 5.10 — 5.16
Perl 5.10 и 5.12
PHP Tricks
Perl – жив?!

What's hot (20)

PDF
Чуть сложнее чем Singleton: аннотации, IOC, АОП
PPTX
PHP basic
PDF
Алексей Плеханов: 25 причин попробовать Laravel
PDF
Psgi app
PDF
Symfony2. На чем можно сэкономить время при разработке?
PDF
Страх и ненависть в исходном коде
PDF
Миша Рудрастых: Введение в HTTP API WordPress
PPTX
О безопасном использовании PHP wrappers
PPTX
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
PPT
plwww (24.03) MEPHI (PHDays)
PPTX
Не верь никому или разработка эффективных приложений (Как писать по настоящем...
PPT
Юрий Гольцев - Сервис PLWWW
PPTX
Особенности совместной работы Ruby и Oracle
PPT
Что нового в PHP-5.3
ODP
Saint Perl 2009: CGI::Ajax demo
PDF
Примеры решения типичных задач за рамками ядра Yii2
PPT
Общая архитектура Yii2
PDF
YiiConf: Миграции и инсталляции
KEY
Chef коротко об инфраструктуре
PDF
Perl: Symbol table
Чуть сложнее чем Singleton: аннотации, IOC, АОП
PHP basic
Алексей Плеханов: 25 причин попробовать Laravel
Psgi app
Symfony2. На чем можно сэкономить время при разработке?
Страх и ненависть в исходном коде
Миша Рудрастых: Введение в HTTP API WordPress
О безопасном использовании PHP wrappers
Динамический код: модифицируем таблицу символов во время выполнения. Елена Ши...
plwww (24.03) MEPHI (PHDays)
Не верь никому или разработка эффективных приложений (Как писать по настоящем...
Юрий Гольцев - Сервис PLWWW
Особенности совместной работы Ruby и Oracle
Что нового в PHP-5.3
Saint Perl 2009: CGI::Ajax demo
Примеры решения типичных задач за рамками ядра Yii2
Общая архитектура Yii2
YiiConf: Миграции и инсталляции
Chef коротко об инфраструктуре
Perl: Symbol table
Ad

Viewers also liked (9)

PDF
Curscatalyst
ZIP
Web Apps in Perl - HTTP 101
PDF
Developing apps using Perl
PDF
TELEMEDICINE OUR VISION TO FUTURE
PDF
Inside Bokete: Web Application with Mojolicious and others
PPTX
Webrtc mojo
KEY
Mojo as a_client
PPT
Expresiones Regulares
PDF
The Outcome Economy
Curscatalyst
Web Apps in Perl - HTTP 101
Developing apps using Perl
TELEMEDICINE OUR VISION TO FUTURE
Inside Bokete: Web Application with Mojolicious and others
Webrtc mojo
Mojo as a_client
Expresiones Regulares
The Outcome Economy
Ad

Similar to Mojolicious (20)

PPT
Мульти-доменность в Django проекте
PDF
Behat в PHP с использованием Behat и Mink
PPT
Эффективное программирование на NodeJS
PDF
Crazy owl yii1=> yii2
PDF
Интуит. Разработка приложений для iOS. Лекция 7. Работа с сетью
PPT
Ubercart -nemnogo_primerov_iz_zhizni
PPT
Easy authcache 2 кеширование для pro родионов игорь
PDF
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
PPT
Easy authcache 2 кэширование для pro. Родионов Игорь
PPTX
Микрофреймворки PHP
PDF
UWDC 2013, Yii2
PDF
Knockoutjs на примере 2ГИС-Онлайн
PDF
BDD для PHP проектов
PPTX
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
PDF
DSLs в Perl
PDF
Разработка на Perl под Raspberry PI
PDF
MODX 3: Что нового?
PPT
Take more from Jquery
PDF
Инструментируй это
Мульти-доменность в Django проекте
Behat в PHP с использованием Behat и Mink
Эффективное программирование на NodeJS
Crazy owl yii1=> yii2
Интуит. Разработка приложений для iOS. Лекция 7. Работа с сетью
Ubercart -nemnogo_primerov_iz_zhizni
Easy authcache 2 кеширование для pro родионов игорь
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
Easy authcache 2 кэширование для pro. Родионов Игорь
Микрофреймворки PHP
UWDC 2013, Yii2
Knockoutjs на примере 2ГИС-Онлайн
BDD для PHP проектов
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
DSLs в Perl
Разработка на Perl под Raspberry PI
MODX 3: Что нового?
Take more from Jquery
Инструментируй это

Mojolicious