SlideShare una empresa de Scribd logo
1 de 123
Descargar para leer sin conexión
Почему 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

Más contenido relacionado

La actualidad más candente

Symfony2. На чем можно сэкономить время при разработке?
Symfony2. На чем можно сэкономить время при разработке?Symfony2. На чем можно сэкономить время при разработке?
Symfony2. На чем можно сэкономить время при разработке?
Stepan Tanasiychuk
 
О безопасном использовании PHP wrappers
О безопасном использовании PHP wrappersО безопасном использовании PHP wrappers
О безопасном использовании PHP wrappers
Positive Hack Days
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)
ygoltsev
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWW
Positive Hack Days
 
Что нового в PHP-5.3
Что нового в PHP-5.3 Что нового в PHP-5.3
Что нового в PHP-5.3
phpclub
 
Chef коротко об инфраструктуре
Chef коротко об инфраструктуреChef коротко об инфраструктуре
Chef коротко об инфраструктуре
Andrey Subbota
 

La actualidad más candente (20)

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

Destacado

Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
Kar Juan
 

Destacado (9)

Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
 
Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101Web Apps in Perl - HTTP 101
Web Apps in Perl - HTTP 101
 
Developing apps using Perl
Developing apps using PerlDeveloping apps using Perl
Developing apps using Perl
 
TELEMEDICINE OUR VISION TO FUTURE
TELEMEDICINE OUR VISION TO FUTURE TELEMEDICINE OUR VISION TO FUTURE
TELEMEDICINE OUR VISION TO FUTURE
 
Inside Bokete: Web Application with Mojolicious and others
Inside Bokete:  Web Application with Mojolicious and othersInside Bokete:  Web Application with Mojolicious and others
Inside Bokete: Web Application with Mojolicious and others
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
 
Mojo as a_client
Mojo as a_clientMojo as a_client
Mojo as a_client
 
Expresiones Regulares
Expresiones RegularesExpresiones Regulares
Expresiones Regulares
 
The Outcome Economy
The Outcome EconomyThe Outcome Economy
The Outcome Economy
 

Similar a Mojolicious

Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Mink
tyomo4ka
 
Интуит. Разработка приложений для iOS. Лекция 7. Работа с сетью
Интуит. Разработка приложений для iOS. Лекция 7. Работа с сетьюИнтуит. Разработка приложений для iOS. Лекция 7. Работа с сетью
Интуит. Разработка приложений для iOS. Лекция 7. Работа с сетью
Глеб Тарасов
 
Ubercart -nemnogo_primerov_iz_zhizni
Ubercart  -nemnogo_primerov_iz_zhizniUbercart  -nemnogo_primerov_iz_zhizni
Ubercart -nemnogo_primerov_iz_zhizni
drupalconf
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорь
drupalconf
 
Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов Игорь
PVasili
 
Knockoutjs на примере 2ГИС-Онлайн
Knockoutjs на примере 2ГИС-ОнлайнKnockoutjs на примере 2ГИС-Онлайн
Knockoutjs на примере 2ГИС-Онлайн
2ГИС Технологии
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf Conference
 
Take more from Jquery
Take more from JqueryTake more from Jquery
Take more from Jquery
Magento Dev
 

Similar a Mojolicious (20)

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

Mojolicious