• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Почему Mojolicious?
 

Почему Mojolicious?

on

  • 5,927 views

YAPC::Russia "May Perl" 2011

YAPC::Russia "May Perl" 2011
http://event.perlrussia.org/mayperl4/talk/130

Statistics

Views

Total Views
5,927
Views on SlideShare
3,085
Embed Views
2,842

Actions

Likes
3
Downloads
20
Comments
0

15 Embeds 2,842

http://allframeworks.ru 1646
http://lj-toys.com 932
http://l.lj-toys.com 161
http://www.allframeworks.ru 64
http://planetperl.ru 10
http://feeds.feedburner.com 9
http://xss.yandex.net 4
http://www.linkedin.com 4
http://www.informatica.md 3
http://up.allframeworks.ru 2
https://twitter.com 2
http://sayperl.org 2
http://content.mail.ru 1
http://yupe.local 1
https://www.linkedin.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Почему Mojolicious? Почему Mojolicious? Presentation Transcript

    • Почему Mojolicious? Анатолий Шарифулин YAPC::Russia 2011
    • mojolicio.us
    • Кто знает, что такое Mojolicious?
    • Кто использует Mojolicious?
    • На кого повлиял я? :)
    • Коротко
    • Современный и молодойвеб-фреймворк
    • Веб в коробке!
    • Mojo::BaseMojo::DOM, Mojo::JSON Mojo::UserAgent Mojo::IOLoop Mojo::Template
    • Mojo MojoliciousMojolicious::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 perluse 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::RunCurses::Widgets
    • А также тесты для веб-сервисов Почему Mojolicious?
    • use Test::More tests => 252;use Test::Mojo;my $t = Test::Mojo->new(app => App); # App.pmmy $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.shbin/logs.shbin/mysqlbin/mysqldumpbin/restart.shbin/start.shbin/stop.sh
    • bin/start.sh
    • ( # script/etnogenez daemon --reload starman --listen :3000 script/etnogenez) >> log/error.log &
    • conf/app.confconf/mysql.confconf/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, }));
    • selectquery in limitvalues
    • 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 { ... } # для bridgesub 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.eplayouts/default.mail.ep layouts/default.rss.ep layouts/admin.html.ep
    • etc/page.html.epetc/submenu.html.epadmin/etc/sort.txt.ep
    • Ни в коем случаесложной логики, тем более SQL :-)
    • exception.html.ep exception.mail.epexception.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.confdefaults => { book_status => [ [soon => Готовится к изданию], ..., [new => Новинки], ], ...}
    • # App.pmif (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::Booksub 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.pluse 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::Loaderhttps://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