SlideShare une entreprise Scribd logo
1  sur  56
Télécharger pour lire hors ligne
Пластилиновый код
Как перестать кодить и начать жить
Елена Шишкина,
ведущий программист
Деньги.Мэйл.Ру
Москва, 2015
1
ПОДУМАЕМ, КАК ЕГО НЕ ПИСАТЬ!
Надоело писать код?
2
С чего все начиналось
• Веб-сервис (JSON API)
– nginx
– Mojolicious
– PostgreSQL
– Вся логика в процедурах СУБД
• Архитектура веб-приложения
– Вертикальная нарезка на сервисы: auth, profile, contactlist, chat, …
– Горизонтальная нарезка
• www-layer
• Service layer
• Data layer
– Сервисы могут обращаться к друг другу через service layer
3
Типичная функция веб-слоя
• Проверка CSRF-токена
• Аутентификация
• Авторизация
• Чтение и валидация входных данных
• Обращение к сервисному слою
• Перехват и маппинг ошибок
• Генерация вывода
4
Функция веб-слоя
sub message {
my $self = shift;
my $result = eval {
my $form = $self->helper->read_form('chat/message');
die $form->export_errors if $form->has_errors;
die 'ERROR_CSRF_TOKEN'
unless $self->helper->token_ok($form);
die 'ERROR_NOT_AUTHORIZED'
unless $self->helper->check_auth($form);
$self->service->message($form->export);
};
unless ($result) {
my $err = $@;
$self->helper->logerr($err);
$result = $self->helper->map_error($err);
}
$self->render(json => $result);
}
5
Типичная функция сервисного слоя
• Обработка входящих данных
• Обращение к слою данных
• Сохранение в ленту активности пользователя
• Рассылка уведомлений
• Подготовка возвращаемых данных
–result: OK или код ошибки
–собственно данные:
• нет данных
• одно значение
• хэш
• массив хэшей
6
Функция сервисного слоя
sub message {
my ($self, $opts) = @_;
my $result = $self->data->message($opts);
$self->send_notify(chat_message => {
sender => $opts->{profile_id},
addressee => $result,
message => $opts->{message},
});
return $self->ok;
}
7
Типичная функция слоя данных
• Запрос данных из кэша (для статических запросов)
• Вызов процедуры СУБД
• Сохранение в кэш
• Инвалидация кэша
• Нормализация выходных данных
8
Функция слоя данных
sub contactlist {
my ($self, $opts) = @_;
my $cache_key = $self->to_cache_key(
'proifle.contactlist',
$opts
);
my $result = $self->cache->get($cache_key);
unless ($result) {
$result = $self->db->table(
'profiles.contactlist',
[ $opts->{profile_id} ]
);
$self->cache->set($cache_key, $result);
}
return $result;
}
9
Новая фича
• 1 процедура СУБД
• 3 копипасты с небольшими изменениями
В половине случаев меняются только названия,
ключи конфигов и имена процедур!
10
Можно как-нибудь так:
sub god_method {
my $self = shift;
my $cfg = $self->resolve;
my $result = eval {
my $form = $self->helper->read_form($cfg->{form});
die $form->export_errors if $form->has_errors;
if ($cfg->{check_token}) {
die 'ERROR_CSRF_TOKEN'
unless $self->helper->token_ok($form);
}
if ($cfg->{check_auth}) {
die 'ERROR_NOT_AUTHORIZED'
unless $self->helper->check_auth($form);
}
$self->service->god_method($cfg, $form->export);
};
unless ($result) {
my $err = $@;
$self->helper->logerr($err);
$result = $self->helper->map_error($err);
}
$self->render(json => $result);
}
Но это скучно!11
Будем генерировать методы на лету
• Не делаем лишних телодвижений: генерируем из
AUTOLOAD
• Чтобы не попросили странного, нам нужен список
разрешенных методов
• Генератор в базовом классе, списки методов – в
наследниках
• В наследниках можно описать вариации поведения
12
Проверка по списку разрешенных методов
sub _has_method {
my ($module, $method) = @_;
my $methods = ${ "$module::valid_methods" };
if (ref $methods && ref $methods eq 'HASH') {
return $methods->{$method};
} else {
return;
}
}
13
Метод-генератор
use Sub::Name;
sub _generate_sub {
my ($module, $method) = @_;
my $sub = subname "$module::$method", sub {
...
};
no strict 'refs';
*{"$module::$method"} = $sub;
return $sub;
}
14
AUTOLOAD
our $AUTOLOAD;
sub AUTOLOAD {
my $self = $_[0];
my ($method) = $AUTOLOAD =~ /^.+::(.*)$/;
my $package = blessed $self ? ref $self : $self;
return if !$method || $method eq 'DESTROY' || !
_has_method($package, $method);
my $sub = _generate_sub($package, $method);
goto ⊂
}
15
Oops! Mojolicious зовет can…
sub can {
my ($self, $method) = @_;
my $module = blessed $self ? ref $self : $self;
if (_has_method($module, $method)) {
return _generate_sub($module, $method);
} else {
return __PACKAGE__->SUPER::can($method);
}
}
16
Модули с фичами
use strict;
use warnings;
package MyProject::Controller::Chat;
#package MyProject::ServiceLayer::Chat;
#package MyProject::DataLayer::Chat;
use Mojo::Base 'MyProject::Controller::Base';
#use parent 'MyProject::ServiceLayer::Base';
#use parent 'MyProject::DataLayer::Base';
our $valid_methods = {
message => 1
};
1;
17
Схема модулей
MyProject::Base
_has_method
AUTOLOAD
MyProject::Controller::Base
_generate_sub
MyProject::ServiceLayer::Base
_generate_sub
MyProject::DataLayer::Base
_generate_sub
MyProject::Controller::Chat
$valid_methods
MyProject::ServiceLayer::Chat
$valid_methods
MyProject::DataLayer::Chat
$valid_methods
can
18
БОРЕМСЯ С ОДНОТИПНЫМ КОДОМ
19
Функция веб-слоя
Всегда:
• читает и валидирует входные
параметры
• зовет сервисный слой
• перехватывает и маппит ошибки
• генерирует вывод
Может:
• читать определение веб-формы
из разных конфигов
• проверять CSRF-токен
• проверять аутентификацию
• проверять авторизацию
20
Определение метода для веб-слоя
use strict;
use warnings;
package
MyProject::Controller::Chat;
use Mojo::Base
'MyProject::Controller::Base';
our $valid_methods = {
message => {
check_token => 1,
check_auth => 1,
form => 'chat/message'
}
};
1;
Немного упростим
• Токен будем проверять по
умолчанию
• Аутентификацию тоже будем
проверять по умолчанию
• Имя формы = имя модуля + имя
метода
21
Определение метода для веб-слоя
use strict;
use warnings;
package MyProject::Controller::Chat;
use Mojo::Base 'MyProject::Controller::Base';
our $valid_methods = {
message => { }
};
1;
22
Генератор для метода в веб-слое
23
sub _generate_sub {
my ($module, $method) = @_;
my $def = dclone(_get_definition($module, $method) || {});
my $form_name = _form_name($module, $method, $def);
$def->{check_token} = 1 unless exists $def->{check_token};
$def->{check_auth} = 1 unless exists $def->{check_auth};
my $service_method = $def->{service_method} || $method;
my $sub = subname "$module::$method", sub {
my $self = shift;
my $result = eval {
my $form = $self->helper->read_form($form_name);
die $form->export_errors if $form->has_errors;
if ($def->{check_token} && !$self->helper->token_ok($form)) {
die 'ERROR_CSRF_TOKEN';
}
if ($def->{check_auth} && !$self->helper->check_auth($form)) {
die 'ERROR_NOT_AUTHORIZED';
}
$self->service->$service_method($form->export);
};
unless ($result) {
my $err = $@;
$self->helper->logerr($err); $result = $self->helper->map_error($err);
}
$self->render(json => $result);
};
no strict 'refs'; *{"$module::$method"} = $sub;
return $sub;
} 24
Функция сервисного слоя
Всегда:
• обращается к слою данных
• подготавливает возвращаемые
данные
Может:
• обрабатывать входящие данные
• сохранять данные в ленту
активности пользователя
• рассылать уведомления
• возвращать данные в разных
структурах:
– нет данных (только код
результата)
– одно значение
– хэш
– массив хэшей
25
Определение метода для сервисного слоя
use strict;
use warnings;
package MyProject::ServiceLayer::Chat;
use parent 'MyProject::ServiceLayer::Base';
our $valid_methods = {
message => {
returns => 'none',
notify => 1,
save_history => 0
}
};
1;
26
Генератор метода для сервисного слоя
27
use Sub::Name;
use Storable qw(dclone);
sub _generate_sub {
my ($module, $method) = @_;
my $def = dclone(_get_definition($module, $method) || {});
my ($service) = $module =~ /^.+::(.*)$/;
my $data_method = $def->{data_method} || $method;
my $sub = subname "$module::$method", sub {
my ($self, $opts) = @_;
my $result = $self->service->$method($opts);
if ($def->{notify}) {
$self->send_notify((lc $service) . "_$method", $opts,
$result);
}
if ($def->{save_history}) {
$self->save_history((lc $service) . "_$method", $opts,
$result);
}
return $self->parse_answer($result, $def->{returns} || 'none');
};
no strict 'refs';
*{"$module::$method"} = $sub;
return $sub;
}
28
Функция слоя данных
Всегда:
• вызывает процедуру СУБД
Может:
• запрашивать данные из кэша
• передавать в процедуру разные
наборы параметров
• читать результат работы процедуры
в разном формате:
– нет возвращаемого значения
– одно значение
– строка
– таблица
• сохранять данные в кэш
• инвалидировать кэш
• нормализовывать выходные
данные
29
Определение метода для слоя данных
use strict;
use warnings;
package
MyProject::DataLayer::Chat;
use parent
'MyProject::DataLayer::Base';
our $valid_methods = {
message => {
args => [qw(profile_id
room_id reftime message)],
returns => 'table',
func => 'chat.message',
}
};
1;
Немного сократим
• Кэш по умолчанию не зовем и
не валидируем
• Имя процедуры базы строим по
шаблону:
– tablespace = имя модуля
– имя процедуры = имя метода
• Возвращаем по умолчанию
таблицу
30
Генератор метода для слоя данных
31
use Sub::Name;
use Storable qw(dclone);
sub _generate_sub {
my ($module, $method) = @_;
my $def = dclone(_get_definition($module, $method) || {});
my ($service) = $module =~ /^.+::(.*)$/;
$service = lc $service;
my $db_func = $def->{func} || $service . '.' . $method;
my $layer_func = $def->{returns} || 'table';
$layer_func = 'exec' if $layer_func eq 'none';
my $sub = subname "$module::$method", sub {
my ($self, $opts) = @_;
my $cache_key = ($def->{use_cache} || $def->{invalidate_cache})
? $self->to_cache_key($service . '.' . $method, $opts)
: undef;
my $result = $def->{use_cache}
? $self->cache->get($cache_key)
: undef;
unless ($result) {
$result = $self->db->$layer_func($db_func, @$opts{@{ $def->{args} }});
$self->cache->set($cache_key, $result) if $def->{use_cache};
}
$self->cache->invalidate($cache_key, $opts) if $def->{invalidate_cache};
return $result;
};
no strict 'refs';
*{"$module::$method"} = $sub;
return $sub;
}
32
Чего мы добились
• Поигрались с кодогенерацией
• Убрали дублирование кода
• Формализовали декларацию данных для генерации
методов
Получилась отличная модель, но…
33
БОРЕМСЯ С НЕОДНОТИПНЫМ КОДОМ
34
Гладко было на бумаге…
• После логина надо поставить куки
• После регистрации надо отправить email
• При добавлении фотографии нужно сохранить файл и
собрать метаинформацию
• При добавлении поста в ленту нужно распарсить и
обработать ссылки
• …
Нужен механизм для вызова произвольного кода!
35
Добавляем в определение метода коллбэки
prepare
• Вызывается до обращения к
нижележащему слою
• В качестве аргумента получает
входящие параметры метода
(для веб-слоя – объект формы)
• Может модифицировать
параметры (форму)
finish
• Вызывается после обращения к
нижележащему слою
• В качестве аргумента получает
данные, которые вернул
нижележащий слой
• Может модифицировать эти
данные
36
Для веб-слоя
our $valid_methods = {
method_name => {
prepare => sub {
my ($self, $form) = @_;
...
return $form;
},
finish => sub {
my ($self, $data) = @_;
...
return $data;
}
}
};
37
Чего мы добились
• Поигрались с кодогенерацией
• Убрали дублирование кода
• Формализовали декларацию данных для генерации
методов
• Научились добавлять вариативное поведение
Но нам все еще нужно добавлять по три файла,
в которых почти нет кода!
38
КОД, КОТОРОГО НЕ СУЩЕСТВУЕТ
39
Избавляемся от файлов-модулей
• Собираем воедино разрозненные определения методов
• Выделяем инструмент – генератор модулей
• Выносим отдельно заполнение определения методов
значениями по умолчанию
• Добавляем в определение HTTP-метод запроса (GET, POST,
PUT, DELETE)
• Строим роутинг веб-фреймворка
40
Новое определение метода в сервисе
• URL запроса (по умолчанию – имя_сервиса/имя_метода
• Метод запроса (по умолчанию – GET)
• Параметры веб-слоя
• Параметры сервисного слоя
• Параметры слоя данных
41
Параметры веб-слоя
• Проверка токена (по умолчанию включена)
• Проверка аутентификации (по умолчанию включена)
• Имя формы (по умолчанию имя_сервиса/имя_метода)
• Коллбэки prepare и finish (по умолчанию отсутствуют)
• Имя метода сервисного слоя (по умолчанию то же самое)
42
Параметры сервисного слоя
• Имя метода слоя данных (по умолчанию то же самое)
• Отправка уведомлений (по умолчанию выключена)
• Сохранение в историю (по умолчанию выключена)
• Формат возвращаемых данных (по умолчанию
определяется слоем данных)
• Коллбэки prepare и finish (по умолчанию отсутствуют)
43
Параметры слоя данных
• Взятие данных из кэша (по умолчанию выключено)
• Инвалидация кэша (по умолчанию выключена)
• Имя процедуры СУБД (по умолчанию
имя_сервиса.имя_метода)
• Набор входящих аргументов процедуры СУБД
• Формат возвращаемых данных (по умолчанию – таблица)
44
Простейшее определение метода
my $services => {
chat => {
message => {
data_layer => {
args => [qw(profile_id room_id reftime
message)],
},
},
},
};
45
Генератор сервиса
• package MyProject::Core::ServiceGenerator;
sub init_service {
my ($self, $service_name, $definition) = @_;
$service_name = ucfirst $service_name;
no strict 'refs';
$definition = $self->normalize_definition($definition);
for my $layer (qw(Controller ServiceLayer DataLayer)) {
unshift @{*{ "MyProject::$layer::$service_name::ISA" }},
'MyProject::$layer::Base';
*{ "MyProject::$layer::$service_name::_get_definition" } =
sub {
return $definition;
};
}
my $method = lc($definition->{method});
$self->routes->$method($definition->{url})->to(
controller => $service_name,
action => $name
);
}
46
КОД, КОТОРЫЙ СУЩЕСТВУЕТ
47
Код, который существует
• Множество уже написанных модулей
• Нестардартные методы
48
Добавляем в генератор сервиса проверку ISA
my $module = "MyProject::$layer::$service_name";
unless ($module->isa('MyProject::$layer::Base')) {
unshift @{*{ "$module::ISA" }}, 'MyProject::$layer::Base';
}
*{ "MyProject::$layer::$service_name::_get_definition" } = sub {
return $definition;
};
49
С AUTOLOAD все ОК, но can надо поправить
sub can {
my ($self, $method) = @_;
my $module = blessed $self ? ref $self : $self;
no strict 'refs';
if (my $sub = *{"$module::$method"}{CODE}) {
return $sub;
} elsif (_has_method($module, $method)) {
return _generate_sub($module, $method);
} else {
return __PACKAGE__->SUPER::can($method);
}
}
50
Чего мы добились
• Поигрались с кодогенерацией
• Убрали дублирование кода
• Формализовали декларацию данных для генерации методов
• Научились добавлять вариативное поведение
• Собрали определение сервисов и методов воедино
• Сделали определение типичных методов максимально
лаконичным
• Для новых и отлично ложащихся в шаблон сервисов мы даже
избавились от модулей
• Но при этом сохранили обратную совместимость
• А также возможность добавлять нестандартные методы
• Ленивая инициализация – при запуске сервера не генерируется
ничего лишнего
Но определение сервиса и метода –
это все еще код! 51
СЛЕДИТЕ ЗА РУКАМИ:
ПРОГРАММИРУЕМ НА КОНФИГАХ!
52
Декларациям место в текстовом формате
• Окончательно разделяем код и декларации
• Коллбэки prepare и finish выносим в модули, а в
декларациях оставляем имя модуля и функции
• Более компактный формат
• Легкое отключение сервиса на отдельных нодах: просто
удаляем конфиг!
53
Пример конфига
service: Chat
create_room:
web_layer:
form: chat/create_room
check_token: 1
check_auth: 1
method: put
url: common_chat/room/create
service_layer:
returns: room_id
finish: MyProject::Callback::Chat::gather_userinfo
notify: 1
data_layer:
func: chat.create_room
args: [profile_id, room_name, participant_id]
returns: single
54
ЧТО ДАЛЬШЕ?
55
ЕСТЬ ВОПРОСЫ?
56

Contenu connexe

Tendances

TypeScript: особенности разработки / Александр Майоров (Tutu.ru)
TypeScript: особенности разработки / Александр Майоров (Tutu.ru)TypeScript: особенности разработки / Александр Майоров (Tutu.ru)
TypeScript: особенности разработки / Александр Майоров (Tutu.ru)Ontico
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Vasya Petrov
 
UWDC 2013, Как мы используем Yii
UWDC 2013, Как мы используем YiiUWDC 2013, Как мы используем Yii
UWDC 2013, Как мы используем YiiAlexander Makarov
 
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИСTARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС2ГИС Технологии
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорьdrupalconf
 
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...Ontico
 
Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Anatoly Sharifulin
 
DevConf 2012 - Yii, его разработка и Yii2
DevConf 2012 - Yii, его разработка и Yii2DevConf 2012 - Yii, его разработка и Yii2
DevConf 2012 - Yii, его разработка и Yii2Alexander Makarov
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWPositive Hack Days
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование LinuxAnthony Shoumikhin
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)ygoltsev
 
Блоки, лямбды, замыкания
Блоки, лямбды, замыканияБлоки, лямбды, замыкания
Блоки, лямбды, замыканияDmitriy Kiriyenko
 
Контроль качества верстки или как начать делать Makeup
Контроль качества верстки или как начать делать MakeupКонтроль качества верстки или как начать делать Makeup
Контроль качества верстки или как начать делать MakeupTimophy Chaptykov
 

Tendances (20)

TypeScript: особенности разработки / Александр Майоров (Tutu.ru)
TypeScript: особенности разработки / Александр Майоров (Tutu.ru)TypeScript: особенности разработки / Александр Майоров (Tutu.ru)
TypeScript: особенности разработки / Александр Майоров (Tutu.ru)
 
Суперсилы Chrome developer tools
Суперсилы Chrome developer toolsСуперсилы Chrome developer tools
Суперсилы Chrome developer tools
 
UWDC 2013, Yii2
UWDC 2013, Yii2UWDC 2013, Yii2
UWDC 2013, Yii2
 
Decorators' recipes
Decorators' recipesDecorators' recipes
Decorators' recipes
 
Erlang tasty & useful stuff
Erlang tasty & useful stuffErlang tasty & useful stuff
Erlang tasty & useful stuff
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
UWDC 2013, Как мы используем Yii
UWDC 2013, Как мы используем YiiUWDC 2013, Как мы используем Yii
UWDC 2013, Как мы используем Yii
 
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИСTARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
TARS: Сделай уровень frontend-рутины 0% — Артём Малко, 2ГИС
 
Easy authcache 2 кеширование для pro родионов игорь
Easy authcache 2   кеширование для pro родионов игорьEasy authcache 2   кеширование для pro родионов игорь
Easy authcache 2 кеширование для pro родионов игорь
 
Component Inspector
Component InspectorComponent Inspector
Component Inspector
 
Perl: Symbol table
Perl: Symbol tablePerl: Symbol table
Perl: Symbol table
 
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...
Как перестать отлаживать асинхронный код и начать жить / Андрей Саломатин (Pr...
 
Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16Что нового в Perl? 5.10 — 5.16
Что нового в Perl? 5.10 — 5.16
 
DevConf 2012 - Yii, его разработка и Yii2
DevConf 2012 - Yii, его разработка и Yii2DevConf 2012 - Yii, его разработка и Yii2
DevConf 2012 - Yii, его разработка и Yii2
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWW
 
Программирование Linux
Программирование LinuxПрограммирование Linux
Программирование Linux
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)
 
Блоки, лямбды, замыкания
Блоки, лямбды, замыканияБлоки, лямбды, замыкания
Блоки, лямбды, замыкания
 
Контроль качества верстки или как начать делать Makeup
Контроль качества верстки или как начать делать MakeupКонтроль качества верстки или как начать делать Makeup
Контроль качества верстки или как начать делать Makeup
 
Javascript
JavascriptJavascript
Javascript
 

Similaire à Пластилиновый код: как перестать кодить и начать жить

Psgi app
Psgi appPsgi app
Psgi appund3f
 
PHP Tricks
PHP TricksPHP Tricks
PHP TricksBlackFan
 
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCАрхитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCDevDay
 
Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов ИгорьPVasili
 
Web весна 2013 лекция 4
Web весна 2013 лекция 4Web весна 2013 лекция 4
Web весна 2013 лекция 4Technopark
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4Technopark
 
М. Боднарчук Современное функциональное тестирование с Codeception
М. Боднарчук Современное функциональное тестирование с CodeceptionМ. Боднарчук Современное функциональное тестирование с Codeception
М. Боднарчук Современное функциональное тестирование с CodeceptionAlbina Tiupa
 
Михаил Боднарчук Современное функциональное тестирование с Codeception
Михаил Боднарчук Современное функциональное тестирование с CodeceptionМихаил Боднарчук Современное функциональное тестирование с Codeception
Михаил Боднарчук Современное функциональное тестирование с CodeceptionAlbina Tiupa
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Minktyomo4ka
 
Yii2
Yii2Yii2
Yii2Noveo
 
Опыт разработки и тестирования RESTful JSON сервиса
Опыт разработки и тестирования RESTful JSON сервисаОпыт разработки и тестирования RESTful JSON сервиса
Опыт разработки и тестирования RESTful JSON сервисаIlya Chesnokov
 
HSE{Consult}: DevOps – новая методология разработки
HSE{Consult}: DevOps – новая методология разработкиHSE{Consult}: DevOps – новая методология разработки
HSE{Consult}: DevOps – новая методология разработкиBusiness incubator HSE
 
DevOps или исскуство ухода за Интернет-проектом
DevOps или исскуство ухода за Интернет-проектомDevOps или исскуство ухода за Интернет-проектом
DevOps или исскуство ухода за Интернет-проектомAlexander Titov
 

Similaire à Пластилиновый код: как перестать кодить и начать жить (20)

Mojolicious
MojoliciousMojolicious
Mojolicious
 
Psgi app
Psgi appPsgi app
Psgi app
 
PHP Tricks
PHP TricksPHP Tricks
PHP Tricks
 
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCАрхитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
 
Easy authcache 2 кэширование для pro. Родионов Игорь
Easy authcache 2   кэширование для pro. Родионов ИгорьEasy authcache 2   кэширование для pro. Родионов Игорь
Easy authcache 2 кэширование для pro. Родионов Игорь
 
Web весна 2013 лекция 4
Web весна 2013 лекция 4Web весна 2013 лекция 4
Web весна 2013 лекция 4
 
Web осень 2012 лекция 4
Web осень 2012 лекция 4Web осень 2012 лекция 4
Web осень 2012 лекция 4
 
М. Боднарчук Современное функциональное тестирование с Codeception
М. Боднарчук Современное функциональное тестирование с CodeceptionМ. Боднарчук Современное функциональное тестирование с Codeception
М. Боднарчук Современное функциональное тестирование с Codeception
 
Михаил Боднарчук Современное функциональное тестирование с Codeception
Михаил Боднарчук Современное функциональное тестирование с CodeceptionМихаил Боднарчук Современное функциональное тестирование с Codeception
Михаил Боднарчук Современное функциональное тестирование с Codeception
 
бегун
бегунбегун
бегун
 
Ci
CiCi
Ci
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Mink
 
Perl – жив?!
Perl – жив?!Perl – жив?!
Perl – жив?!
 
Yii2
Yii2Yii2
Yii2
 
Perl in practice
Perl in practicePerl in practice
Perl in practice
 
Опыт разработки и тестирования RESTful JSON сервиса
Опыт разработки и тестирования RESTful JSON сервисаОпыт разработки и тестирования RESTful JSON сервиса
Опыт разработки и тестирования RESTful JSON сервиса
 
HSE{Consult}: DevOps – новая методология разработки
HSE{Consult}: DevOps – новая методология разработкиHSE{Consult}: DevOps – новая методология разработки
HSE{Consult}: DevOps – новая методология разработки
 
Yserver
YserverYserver
Yserver
 
DevOps или исскуство ухода за Интернет-проектом
DevOps или исскуство ухода за Интернет-проектомDevOps или исскуство ухода за Интернет-проектом
DevOps или исскуство ухода за Интернет-проектом
 
бегун
бегунбегун
бегун
 

Plus de Moscow.pm

О работе с документами .xls, .xlsx, .rtf
О работе с документами .xls, .xlsx, .rtfО работе с документами .xls, .xlsx, .rtf
О работе с документами .xls, .xlsx, .rtfMoscow.pm
 
Fast queue – как мы сделали свою очередь на perl и redis
Fast queue – как мы сделали свою очередь на perl и redisFast queue – как мы сделали свою очередь на perl и redis
Fast queue – как мы сделали свою очередь на perl и redisMoscow.pm
 
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013Moscow.pm
 
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013Moscow.pm
 
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....Moscow.pm
 
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...Moscow.pm
 
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...Moscow.pm
 
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013Moscow.pm
 
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...Moscow.pm
 

Plus de Moscow.pm (9)

О работе с документами .xls, .xlsx, .rtf
О работе с документами .xls, .xlsx, .rtfО работе с документами .xls, .xlsx, .rtf
О работе с документами .xls, .xlsx, .rtf
 
Fast queue – как мы сделали свою очередь на perl и redis
Fast queue – как мы сделали свою очередь на perl и redisFast queue – как мы сделали свою очередь на perl и redis
Fast queue – как мы сделали свою очередь на perl и redis
 
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013
Perl для не программистов. Николай Мишин. Moscow.pm 4 июля 2013
 
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013
Язык Go для Perl-программистов v1.1. Александр Орловский. Moscow.pm 4 июля 2013
 
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....
Разработка документации для RESTful API: как убить трёх зайцев одним. Moscow....
 
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...
Особенности создания XS-модулей на языке C++. Владимир Тимофеев. Moscow.pm 4 ...
 
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...
Ленивые итераторы для разбора разнородных данных. Михаил Озеров. Moscow.pm 6 ...
 
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013
Преобразование Perl-структур в XML. Трефилова Екатерина. Moscow.pm 6 июля 2013
 
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...
Play Perl — распределенная социальная игра для Perl-разработчиков. Вячеслав М...
 

Dernier (9)

СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 
MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 

Пластилиновый код: как перестать кодить и начать жить

  • 1. Пластилиновый код Как перестать кодить и начать жить Елена Шишкина, ведущий программист Деньги.Мэйл.Ру Москва, 2015 1
  • 2. ПОДУМАЕМ, КАК ЕГО НЕ ПИСАТЬ! Надоело писать код? 2
  • 3. С чего все начиналось • Веб-сервис (JSON API) – nginx – Mojolicious – PostgreSQL – Вся логика в процедурах СУБД • Архитектура веб-приложения – Вертикальная нарезка на сервисы: auth, profile, contactlist, chat, … – Горизонтальная нарезка • www-layer • Service layer • Data layer – Сервисы могут обращаться к друг другу через service layer 3
  • 4. Типичная функция веб-слоя • Проверка CSRF-токена • Аутентификация • Авторизация • Чтение и валидация входных данных • Обращение к сервисному слою • Перехват и маппинг ошибок • Генерация вывода 4
  • 5. Функция веб-слоя sub message { my $self = shift; my $result = eval { my $form = $self->helper->read_form('chat/message'); die $form->export_errors if $form->has_errors; die 'ERROR_CSRF_TOKEN' unless $self->helper->token_ok($form); die 'ERROR_NOT_AUTHORIZED' unless $self->helper->check_auth($form); $self->service->message($form->export); }; unless ($result) { my $err = $@; $self->helper->logerr($err); $result = $self->helper->map_error($err); } $self->render(json => $result); } 5
  • 6. Типичная функция сервисного слоя • Обработка входящих данных • Обращение к слою данных • Сохранение в ленту активности пользователя • Рассылка уведомлений • Подготовка возвращаемых данных –result: OK или код ошибки –собственно данные: • нет данных • одно значение • хэш • массив хэшей 6
  • 7. Функция сервисного слоя sub message { my ($self, $opts) = @_; my $result = $self->data->message($opts); $self->send_notify(chat_message => { sender => $opts->{profile_id}, addressee => $result, message => $opts->{message}, }); return $self->ok; } 7
  • 8. Типичная функция слоя данных • Запрос данных из кэша (для статических запросов) • Вызов процедуры СУБД • Сохранение в кэш • Инвалидация кэша • Нормализация выходных данных 8
  • 9. Функция слоя данных sub contactlist { my ($self, $opts) = @_; my $cache_key = $self->to_cache_key( 'proifle.contactlist', $opts ); my $result = $self->cache->get($cache_key); unless ($result) { $result = $self->db->table( 'profiles.contactlist', [ $opts->{profile_id} ] ); $self->cache->set($cache_key, $result); } return $result; } 9
  • 10. Новая фича • 1 процедура СУБД • 3 копипасты с небольшими изменениями В половине случаев меняются только названия, ключи конфигов и имена процедур! 10
  • 11. Можно как-нибудь так: sub god_method { my $self = shift; my $cfg = $self->resolve; my $result = eval { my $form = $self->helper->read_form($cfg->{form}); die $form->export_errors if $form->has_errors; if ($cfg->{check_token}) { die 'ERROR_CSRF_TOKEN' unless $self->helper->token_ok($form); } if ($cfg->{check_auth}) { die 'ERROR_NOT_AUTHORIZED' unless $self->helper->check_auth($form); } $self->service->god_method($cfg, $form->export); }; unless ($result) { my $err = $@; $self->helper->logerr($err); $result = $self->helper->map_error($err); } $self->render(json => $result); } Но это скучно!11
  • 12. Будем генерировать методы на лету • Не делаем лишних телодвижений: генерируем из AUTOLOAD • Чтобы не попросили странного, нам нужен список разрешенных методов • Генератор в базовом классе, списки методов – в наследниках • В наследниках можно описать вариации поведения 12
  • 13. Проверка по списку разрешенных методов sub _has_method { my ($module, $method) = @_; my $methods = ${ "$module::valid_methods" }; if (ref $methods && ref $methods eq 'HASH') { return $methods->{$method}; } else { return; } } 13
  • 14. Метод-генератор use Sub::Name; sub _generate_sub { my ($module, $method) = @_; my $sub = subname "$module::$method", sub { ... }; no strict 'refs'; *{"$module::$method"} = $sub; return $sub; } 14
  • 15. AUTOLOAD our $AUTOLOAD; sub AUTOLOAD { my $self = $_[0]; my ($method) = $AUTOLOAD =~ /^.+::(.*)$/; my $package = blessed $self ? ref $self : $self; return if !$method || $method eq 'DESTROY' || ! _has_method($package, $method); my $sub = _generate_sub($package, $method); goto ⊂ } 15
  • 16. Oops! Mojolicious зовет can… sub can { my ($self, $method) = @_; my $module = blessed $self ? ref $self : $self; if (_has_method($module, $method)) { return _generate_sub($module, $method); } else { return __PACKAGE__->SUPER::can($method); } } 16
  • 17. Модули с фичами use strict; use warnings; package MyProject::Controller::Chat; #package MyProject::ServiceLayer::Chat; #package MyProject::DataLayer::Chat; use Mojo::Base 'MyProject::Controller::Base'; #use parent 'MyProject::ServiceLayer::Base'; #use parent 'MyProject::DataLayer::Base'; our $valid_methods = { message => 1 }; 1; 17
  • 20. Функция веб-слоя Всегда: • читает и валидирует входные параметры • зовет сервисный слой • перехватывает и маппит ошибки • генерирует вывод Может: • читать определение веб-формы из разных конфигов • проверять CSRF-токен • проверять аутентификацию • проверять авторизацию 20
  • 21. Определение метода для веб-слоя use strict; use warnings; package MyProject::Controller::Chat; use Mojo::Base 'MyProject::Controller::Base'; our $valid_methods = { message => { check_token => 1, check_auth => 1, form => 'chat/message' } }; 1; Немного упростим • Токен будем проверять по умолчанию • Аутентификацию тоже будем проверять по умолчанию • Имя формы = имя модуля + имя метода 21
  • 22. Определение метода для веб-слоя use strict; use warnings; package MyProject::Controller::Chat; use Mojo::Base 'MyProject::Controller::Base'; our $valid_methods = { message => { } }; 1; 22
  • 23. Генератор для метода в веб-слое 23
  • 24. sub _generate_sub { my ($module, $method) = @_; my $def = dclone(_get_definition($module, $method) || {}); my $form_name = _form_name($module, $method, $def); $def->{check_token} = 1 unless exists $def->{check_token}; $def->{check_auth} = 1 unless exists $def->{check_auth}; my $service_method = $def->{service_method} || $method; my $sub = subname "$module::$method", sub { my $self = shift; my $result = eval { my $form = $self->helper->read_form($form_name); die $form->export_errors if $form->has_errors; if ($def->{check_token} && !$self->helper->token_ok($form)) { die 'ERROR_CSRF_TOKEN'; } if ($def->{check_auth} && !$self->helper->check_auth($form)) { die 'ERROR_NOT_AUTHORIZED'; } $self->service->$service_method($form->export); }; unless ($result) { my $err = $@; $self->helper->logerr($err); $result = $self->helper->map_error($err); } $self->render(json => $result); }; no strict 'refs'; *{"$module::$method"} = $sub; return $sub; } 24
  • 25. Функция сервисного слоя Всегда: • обращается к слою данных • подготавливает возвращаемые данные Может: • обрабатывать входящие данные • сохранять данные в ленту активности пользователя • рассылать уведомления • возвращать данные в разных структурах: – нет данных (только код результата) – одно значение – хэш – массив хэшей 25
  • 26. Определение метода для сервисного слоя use strict; use warnings; package MyProject::ServiceLayer::Chat; use parent 'MyProject::ServiceLayer::Base'; our $valid_methods = { message => { returns => 'none', notify => 1, save_history => 0 } }; 1; 26
  • 27. Генератор метода для сервисного слоя 27
  • 28. use Sub::Name; use Storable qw(dclone); sub _generate_sub { my ($module, $method) = @_; my $def = dclone(_get_definition($module, $method) || {}); my ($service) = $module =~ /^.+::(.*)$/; my $data_method = $def->{data_method} || $method; my $sub = subname "$module::$method", sub { my ($self, $opts) = @_; my $result = $self->service->$method($opts); if ($def->{notify}) { $self->send_notify((lc $service) . "_$method", $opts, $result); } if ($def->{save_history}) { $self->save_history((lc $service) . "_$method", $opts, $result); } return $self->parse_answer($result, $def->{returns} || 'none'); }; no strict 'refs'; *{"$module::$method"} = $sub; return $sub; } 28
  • 29. Функция слоя данных Всегда: • вызывает процедуру СУБД Может: • запрашивать данные из кэша • передавать в процедуру разные наборы параметров • читать результат работы процедуры в разном формате: – нет возвращаемого значения – одно значение – строка – таблица • сохранять данные в кэш • инвалидировать кэш • нормализовывать выходные данные 29
  • 30. Определение метода для слоя данных use strict; use warnings; package MyProject::DataLayer::Chat; use parent 'MyProject::DataLayer::Base'; our $valid_methods = { message => { args => [qw(profile_id room_id reftime message)], returns => 'table', func => 'chat.message', } }; 1; Немного сократим • Кэш по умолчанию не зовем и не валидируем • Имя процедуры базы строим по шаблону: – tablespace = имя модуля – имя процедуры = имя метода • Возвращаем по умолчанию таблицу 30
  • 31. Генератор метода для слоя данных 31
  • 32. use Sub::Name; use Storable qw(dclone); sub _generate_sub { my ($module, $method) = @_; my $def = dclone(_get_definition($module, $method) || {}); my ($service) = $module =~ /^.+::(.*)$/; $service = lc $service; my $db_func = $def->{func} || $service . '.' . $method; my $layer_func = $def->{returns} || 'table'; $layer_func = 'exec' if $layer_func eq 'none'; my $sub = subname "$module::$method", sub { my ($self, $opts) = @_; my $cache_key = ($def->{use_cache} || $def->{invalidate_cache}) ? $self->to_cache_key($service . '.' . $method, $opts) : undef; my $result = $def->{use_cache} ? $self->cache->get($cache_key) : undef; unless ($result) { $result = $self->db->$layer_func($db_func, @$opts{@{ $def->{args} }}); $self->cache->set($cache_key, $result) if $def->{use_cache}; } $self->cache->invalidate($cache_key, $opts) if $def->{invalidate_cache}; return $result; }; no strict 'refs'; *{"$module::$method"} = $sub; return $sub; } 32
  • 33. Чего мы добились • Поигрались с кодогенерацией • Убрали дублирование кода • Формализовали декларацию данных для генерации методов Получилась отличная модель, но… 33
  • 35. Гладко было на бумаге… • После логина надо поставить куки • После регистрации надо отправить email • При добавлении фотографии нужно сохранить файл и собрать метаинформацию • При добавлении поста в ленту нужно распарсить и обработать ссылки • … Нужен механизм для вызова произвольного кода! 35
  • 36. Добавляем в определение метода коллбэки prepare • Вызывается до обращения к нижележащему слою • В качестве аргумента получает входящие параметры метода (для веб-слоя – объект формы) • Может модифицировать параметры (форму) finish • Вызывается после обращения к нижележащему слою • В качестве аргумента получает данные, которые вернул нижележащий слой • Может модифицировать эти данные 36
  • 37. Для веб-слоя our $valid_methods = { method_name => { prepare => sub { my ($self, $form) = @_; ... return $form; }, finish => sub { my ($self, $data) = @_; ... return $data; } } }; 37
  • 38. Чего мы добились • Поигрались с кодогенерацией • Убрали дублирование кода • Формализовали декларацию данных для генерации методов • Научились добавлять вариативное поведение Но нам все еще нужно добавлять по три файла, в которых почти нет кода! 38
  • 39. КОД, КОТОРОГО НЕ СУЩЕСТВУЕТ 39
  • 40. Избавляемся от файлов-модулей • Собираем воедино разрозненные определения методов • Выделяем инструмент – генератор модулей • Выносим отдельно заполнение определения методов значениями по умолчанию • Добавляем в определение HTTP-метод запроса (GET, POST, PUT, DELETE) • Строим роутинг веб-фреймворка 40
  • 41. Новое определение метода в сервисе • URL запроса (по умолчанию – имя_сервиса/имя_метода • Метод запроса (по умолчанию – GET) • Параметры веб-слоя • Параметры сервисного слоя • Параметры слоя данных 41
  • 42. Параметры веб-слоя • Проверка токена (по умолчанию включена) • Проверка аутентификации (по умолчанию включена) • Имя формы (по умолчанию имя_сервиса/имя_метода) • Коллбэки prepare и finish (по умолчанию отсутствуют) • Имя метода сервисного слоя (по умолчанию то же самое) 42
  • 43. Параметры сервисного слоя • Имя метода слоя данных (по умолчанию то же самое) • Отправка уведомлений (по умолчанию выключена) • Сохранение в историю (по умолчанию выключена) • Формат возвращаемых данных (по умолчанию определяется слоем данных) • Коллбэки prepare и finish (по умолчанию отсутствуют) 43
  • 44. Параметры слоя данных • Взятие данных из кэша (по умолчанию выключено) • Инвалидация кэша (по умолчанию выключена) • Имя процедуры СУБД (по умолчанию имя_сервиса.имя_метода) • Набор входящих аргументов процедуры СУБД • Формат возвращаемых данных (по умолчанию – таблица) 44
  • 45. Простейшее определение метода my $services => { chat => { message => { data_layer => { args => [qw(profile_id room_id reftime message)], }, }, }, }; 45
  • 46. Генератор сервиса • package MyProject::Core::ServiceGenerator; sub init_service { my ($self, $service_name, $definition) = @_; $service_name = ucfirst $service_name; no strict 'refs'; $definition = $self->normalize_definition($definition); for my $layer (qw(Controller ServiceLayer DataLayer)) { unshift @{*{ "MyProject::$layer::$service_name::ISA" }}, 'MyProject::$layer::Base'; *{ "MyProject::$layer::$service_name::_get_definition" } = sub { return $definition; }; } my $method = lc($definition->{method}); $self->routes->$method($definition->{url})->to( controller => $service_name, action => $name ); } 46
  • 48. Код, который существует • Множество уже написанных модулей • Нестардартные методы 48
  • 49. Добавляем в генератор сервиса проверку ISA my $module = "MyProject::$layer::$service_name"; unless ($module->isa('MyProject::$layer::Base')) { unshift @{*{ "$module::ISA" }}, 'MyProject::$layer::Base'; } *{ "MyProject::$layer::$service_name::_get_definition" } = sub { return $definition; }; 49
  • 50. С AUTOLOAD все ОК, но can надо поправить sub can { my ($self, $method) = @_; my $module = blessed $self ? ref $self : $self; no strict 'refs'; if (my $sub = *{"$module::$method"}{CODE}) { return $sub; } elsif (_has_method($module, $method)) { return _generate_sub($module, $method); } else { return __PACKAGE__->SUPER::can($method); } } 50
  • 51. Чего мы добились • Поигрались с кодогенерацией • Убрали дублирование кода • Формализовали декларацию данных для генерации методов • Научились добавлять вариативное поведение • Собрали определение сервисов и методов воедино • Сделали определение типичных методов максимально лаконичным • Для новых и отлично ложащихся в шаблон сервисов мы даже избавились от модулей • Но при этом сохранили обратную совместимость • А также возможность добавлять нестандартные методы • Ленивая инициализация – при запуске сервера не генерируется ничего лишнего Но определение сервиса и метода – это все еще код! 51
  • 53. Декларациям место в текстовом формате • Окончательно разделяем код и декларации • Коллбэки prepare и finish выносим в модули, а в декларациях оставляем имя модуля и функции • Более компактный формат • Легкое отключение сервиса на отдельных нодах: просто удаляем конфиг! 53
  • 54. Пример конфига service: Chat create_room: web_layer: form: chat/create_room check_token: 1 check_auth: 1 method: put url: common_chat/room/create service_layer: returns: room_id finish: MyProject::Callback::Chat::gather_userinfo notify: 1 data_layer: func: chat.create_room args: [profile_id, room_name, participant_id] returns: single 54