2. • Какие-то проблемы?
• Методы runtime-
кодогенерации
• Таблица символов:
матчасть
• От теории к практике
• Как не выстрелить себе
в ногу
• RTFM
2
3. Повторяющийся код
• Конструкторы
• Методы-аксессоры
• Идентичная предварительная обработка
данных
• Похожие по функционалу функции с
небольшими отличиями
corp.mail.ru
5. Аксессоры
sub field1 {
my $self = shift;
$self->{field1} = $_[0] if @_;
return $self->{field1};
}
sub field2 {
my $self = shift;
$self->{field2} = $_[0] if @_;
return $self->{field2};
}
sub field3 {
my $self = shift;
$self->{field3} = $_[0] if @_;
return $self->{field3};
}
www.mail.ru 5
6. Предварительная
обработка
sub do_something {
my $self = shift;
$self->check_cookies;
return $self->redirect('/login') unless $self->check_auth;
my $form = $self->load_form('do_something');
$form->fetch;
return $self->render_error() unless $form->validate;
my $some_user_data = $self->load_user_data;
...
}
sub do_another_thing {
my $self = shift;
$self->check_cookies;
...
}
www.mail.ru 6
7. Похожие функции
sub error {
my $message = shift;
my ($package, $line, $sub) = (caller(0))[0, 2, 3];
print $log scalar localtime, "ERROR: ${package}::$sub ($line): $messagen";
print $log Carp::longmess if $Trace_Errors;
}
sub debug {
my $message = shift;
my ($package, $line, $sub) = (caller(0))[0, 2, 3];
print $log scalar localtime, "DEBUG: ${package}::$sub ($line): $messagen";
}
sub info { ... }
sub warning { ... }
www.mail.ru 7
8. Проблемы повторяющегося кода
• Потеря времени на
перепечатывание/копирование
• Ошибки из-за невнимательности
• Трудоемкость сопровождения
corp.mail.ru
9. Сторонние модули
• Ошибки в реализации
• Отсутствие поддержки кириллицы
• Недостаточный функционал
corp.mail.ru
10. Data::Dumper и
кириллица в utf8
print Dumper {test => 'Тестовая строка'};
$VAR1 = {
"test" =>
"x{422}x{435}x{441}x{442}x{43e}x{432}x{430}x
{44f} x{441}x{442}x{440}x{43e}x{43a}x{430}"
};
www.mail.ru 10
11. Решение: CPAN
package Foo;
use Moose;
has field1 => (is => 'rw');
has field2 => (is => 'rw');
has field3 => (is => 'rw');
around [qw(do_something do_another_thing)] => sub {
my ($orig, $self) = @_;
...
$self->$orig(form => $form, user_data => $some_user_data);
};
www.mail.ru 11
12. Проблемы использования
сторонних модулей
• Необходимость доказательства
целесообразности
• Замусоривание системы
• Замусоривание блоков use в коде
• Снижение производительности
• Увеличение времени компиляции
• Расход памяти
• Уменьшение контроля над кодом («чужой
код»)
www.mail.ru 12
13. • Какие-то проблемы?
• Методы runtime-
кодогенерации
• Таблица символов:
матчасть
• От теории к практике
• Как не выстрелить себе
в ногу
• RTFM
13
16. eval
package Wrapper;
sub make_accessors {
my $package = caller(0);
for (@_) {
eval qq{
package $package;
sub $_ {
my $self = shift;
$self->{$_} = $_[0] if @_;
return $self->{$_};
}
};
}
}
www.mail.ru 16
17. eval
package Test;
use Wrapper;
sub new { return bless {}, shift; }
Wrapper::make_accessors( qw(name age) );
package main;
use Test;
my $obj = Test->new;
$obj->name('Ann');
say $obj->name;
www.mail.ru 17
18. • Какие-то проблемы?
• Методы runtime-
кодогенерации
• Таблица символов:
матчасть
• От теории к практике
• Как не выстрелить себе
в ногу
• RTFM
18
19. Таблица символов
• Таблица символов – это хэш
%PackageName::
%main::
• Ключи – глобальные переменные и
подпрограммы
• Значения - тайпглобы
www.mail.ru 19
20. Таблица символов
package Test;
our $data = 'test'; *Test::data
our @data = qw(1 2 3);
our %data = (key1 => 'value1', key2 =>
'value2');
my $fh = *FH;
sub data { return 0; }
package main;
say $Test::{$_} for keys %Test::;
www.mail.ru 20
21. Структура тайпглоба
*glob{PACKAGE} имя пакета
*glob{NAME} имя элемента (переменной или функции)
*glob{SCALAR} ссылка на значение-скаляр
*glob{ARRAY} ссылка на значение-массив
*glob{HASH} ссылка на значение-хэш
*glob{CODE} ссылка на подпрограмму
corp.mail.ru
22. Работа с тайпглобом
Получение данных Запись данных
my $scalar = ${ *Test::data }; *Test::data = 'new value';
my %hash = %{ *Test::data }; *Test::data = [4, 5, 6];
my @array = @{ *Test::data }; *Test::data = {
&{ *Test::data }(); key3 => 'value3',
key4 => 'value4‘
};
*Test::data = sub { return 1; };
www.mail.ru 22
23. • Какие-то проблемы?
• Методы runtime-
кодогенерации
• Таблица символов:
матчасть
• От теории к практике
• Как не выстрелить себе
в ногу
• RTFM
23
24. Генерация аксессоров
package MakeAccessor; package Test;
sub import { use MakeAccessor;
my $package = caller(0);
no strict 'refs';
has 'name';
*{"$package::has"} = &has;
has 'age';
}
sub has ($) { sub new { return bless {},
my $name = shift;
shift; }
my $package = caller(0);
no strict 'refs'; package main;
*{"$package::$name"} = sub {
my $self = shift; my $o = Test->new;
$self->{$name} = $_[0] if @_; $o->name('Ann');
return $self->{$name};
say $o->name;
};
}
www.mail.ru 24
25. Боремся с __ANON__
package MakeAccessor;
sub import {
my $package = caller(0);
no strict 'refs';
*{"$package::has"} = &has;
}
sub has ($) {
my $name = shift;
my $package = caller(0); MakeAccessor::__ANON__
no strict 'refs';
*{"$package::$name"} = sub {
my $self = shift;
say ((caller(0))[3]);
$self->{$name} = $_[0] if @_;
return $self->{$name};
};
}
www.mail.ru 25
26. Боремся с __ANON__
package MakeAccessor;
sub import {
my $package = caller(0);
no strict 'refs';
*{"$package::has"} = &has;
}
sub has ($) {
my $name = shift;
Test::name
my $package = caller(0);
my $method = sub {
local *__ANON__ = "$package::$name";
my $self = shift;
$self->{$name} = $_[0] if @_;
return $self->{$name};
};
no strict 'refs';
*{"$package::$name"} = $method;
}
www.mail.ru 26
27. От теории к практике
• Генераторы классов: десериализация, ORM
• Реализация паттерна «прокси»
• Тестирование: mock, stub, fake object
• Хуки для подпрограмм: before, after, around
• Патчи во время выполнения
• Расширение функционала сторонних модулей
• Синонимы для устаревших функций при
рефакторинге
www.mail.ru 27
28. Прокси
package Response; package main;
use CGI; my $obj = Response->new;
sub new {
print $obj->header('text/html');
return bless { cgi => CGI->new }, shift;
}
our $AUTOLOAD;
sub AUTOLOAD {
my ($method) = $AUTOLOAD =~ /([^:]+)$/;
Content-Type: text/html;
return if $method eq 'DESTROY';
return unless CGI->can($method); charset=ISO-8859-1
my $sub = sub {
local *__ANON__ = $AUTOLOAD;
my $self = shift;
return $self->{cgi}->$method(@_);
};
{ no strict 'refs';
*{$AUTOLOAD} = $sub;
};
return $sub->(@_);
}
www.mail.ru 28
29. Синонимы
package Wrapper; package Test1;
sub make_deprecated { package Test2;
my $deprecated = shift;
{ sub new_func {
no strict 'refs'; Wrapper::make_deprecated(
return if *{$deprecated}{CODE}; 'Test1::old_func');
}; return 'test';
my $package = scalar caller(0); }
my $method = (caller(1))[3];
package main;
my $func = sub { say Test2::new_func();
local *__ANON__ = $deprecated; say Test1::old_func();
warn "$deprecated called at " . sprintf("%s (%s)",
(caller)[1, 2]) . " is deprecated. Use $package::$methodn"; test
eval "use $package;" unless $package->can('can');
Test1::old_func called at test12.pl
my $sub = $package->can($method);
(40) is deprecated. Use
$sub->(@_);
Test2::Test2::new_func
};
test
no strict 'refs';
*{$deprecated} = $func;
}
www.mail.ru 29
30. Stub
use Test::More;
my $user_data = { ... };
{
no strict 'refs';
*{'Cache::Memcached::set'} = sub {
return 1;
};
*{'Cache::Memcached::get'} = sub {
return to_json($user_data);
};
};
is_deeply($user->get_info($session_id), $user_data, 'Some test...');
www.mail.ru 30
31. Хуки
package Wrapper; package Test;
sub before(@&) { sub test { say 'test'; }
my ($methods, $wrapper) = @_;
my $package = caller(0); Wrapper::before [ 'test' ], sub { say 'wrapper';
};
for my $method (@$methods) {
my $orig = $package->can($method);
package main;
my $sub = sub {
Test::test();
local *__ANON__ =
"$package::$method";
$wrapper->(@_);
$orig->(@_);
};
no strict 'refs';
*{"$package::$method"} = $sub; wrapper
} test
}
www.mail.ru 31
32. • Какие-то проблемы?
• Методы runtime-
кодогенерации
• Таблица символов:
матчасть
• От теории к практике
• Как не выстрелить себе
в ногу
• RTFM
32
33. Проблемы
кодогенерации
• Прагмы strict и warnings
• __ANON__ в трассировке стэка
• Ссылки на переопределяемые функции
• Снижение читабельности кода и повышение
требований к профессиональному уровню
программистов
• Рост стэка при использовании хуков
• Пространство имен модуля
www.mail.ru 33
35. Пространство имен
package Wrapper;
our $name = 'Ann';
*{Test::test} = sub {
say $name;
say $Test::name;
};
package Test;
our $name = 'Bob';
package main;
Test::test();
www.mail.ru 35
36. eval Таблица символов
• Компиляция во время • Компиляция при загрузке
выполнения • Проверка синтаксиса
• Ускоренная загрузка, компилятором при запуске
возможность компиляции по • Высокая эффективность
запросу повторного использования
• Создание символов в генераторов
пространстве имен нужного • Невозможность создания
модуля символов в пространстве
• Неэффективная генерация имен нужного модуля, если
большого количества имя последнего не известно
похожих функций во время компиляции
www.mail.ru 36
37. Что почитать
• perlmod: Symbol tables
• perlref
• perldata: Typeglobs and
Filehandlers
• Sriram Srinivasan. Advanced
Perl Programming
• Modern Perl
(http://modernperlbooks.com)
www.mail.ru 37