Написание DSL в Perl

1,169 views

Published on

YAPC Russia 2009 "May Perl 2"

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,169
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
10
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Написание DSL в Perl

  1. 1. DSLs в Perl Как? Руслан Закиров <ruz@bestpractical.com> <ruz@bestpractical.com> Best Practical Solutions, 2008
  2. 2. Что?
  3. 3. under 'user/*' => run { my $u = U->load($1); ... }
  4. 4. under 'user/*' => run { … abort(404) unless $u; on 'profile' => run {}; on 'stats' => run {}; on 'blog' => run {}; }
  5. 5. Зачем?
  6. 6. Выразительно
  7. 7. Управляемо
  8. 8. Как?
  9. 9. Прототипы
  10. 10. Прототипы ($) (&) (@)
  11. 11. Прототипы (завтрак) sub set($$) {...} set key => $val;
  12. 12. Прототипы (запястья) ()
  13. 13. Прототипы (&) блок функция
  14. 14. Прототипы grep {} @_ sub grep(&@) {}
  15. 15. Методы
  16. 16. Методы $o = new X k => $v;
  17. 17. Методы perl -e 'title is „foo”;'
  18. 18. Методы Невозможно найти метод quot;titlequot; в пакете quot;isquot;
  19. 19. Методы может вмы забыли загрузить quot;isquot;?
  20. 20. Методы этап исполнения
  21. 21. Методы perl -e 'sub foo { title is „foo” }' нет ошибки
  22. 22. Методы my $a = {}; local *is::AUTOLOAD = sub { shift; # is $a->{$AUTLOAD} = join ' ', @_ }; $call->(); # что-то
  23. 23. Объединим
  24. 24. Объеденим (img_simple.pl) sub img(&) { my $code = shift; my %attr; local *is::AUTOLOAD = sub {...}; $code->(); my $attrs = join ' ', map $_.'=”'.$attr{$_}, keys %attr; print „<img $attrs />”; }
  25. 25. Слишком просто?
  26. 26. Усложним (img_strict.pl) my %attr_checks = ( img => { _mandatory => [qw(src alt)], src => { canonicalizer => sub {...}, provides => sub {...}, }, # ... }, );
  27. 27. Усложним (img_strict.pl) canonicalizer => sub { return quot;/static/images/$_[0]quot; unless $_[0] =~ /^//; return $_[0]; }
  28. 28. Усложним (img_strict.pl) provides => sub { return unless my ($w, $h) = ($_[0] =~ /-(d+)x(d+)./); return (width => $w, height => $h); }
  29. 29. Результат (img_strict.pl) img { alt is 'feed', src is 'f-14x14.png' };
  30. 30. Результат (img_strict.pl) <img width=quot;14quot; height=quot;14quot; alt=quot;feedquot; src=quot;/s/i/f-14x14.pngquot; />
  31. 31. Отладка
  32. 32. Отладка (carp.pl) нет атрибута 'boo' у тега 'img' at carp.pl line 10
  33. 33. Отладка (carp.pl) local $Carp::CarpLevel = 1; Carp::croak(...);
  34. 34. Отладка (carp.pl) нет атрибута 'boo' у тега 'img' at carp.pl line 16 место вызова
  35. 35. Отладка (carp.pl) main::__ANON__() main::__ANON__() called at carp.pl line 13
  36. 36. Отладка (carp.pl) sub img(&) { local *__ANON__ = quot;img_implquot;; ... }
  37. 37. Отладка (carp.pl) bla-bla at carp.pl line 17 main::img_impl() called at... main::img_impl() main::img('CODE(...)') called at...
  38. 38. Грабли №1
  39. 39. Грабли №1 page {...}; sub page(&) {...};
  40. 40. Грабли №1 Невозможно вызвать метод quot;pagequot; без пакета или объекта
  41. 41. Грабли №2
  42. 42. Грабли №2 sub page(&) { local *title::page = sub {} shift->(); }; page { page title „qwe” };
  43. 43. Грабли №2 Методы проигрывают протипам
  44. 44. Избавляемся от методов
  45. 45. Без методов (wo_methods.pl) our %ATTR; sub attrs(&) { %ATTR = shift->() } sub img(&) { local %ATTR; $code->(); ... }
  46. 46. Без методов (wo_methods.pl) img { attrs { alt => 'boo', src => 'href' } };
  47. 47. Вложенные структуры
  48. 48. Вложения (div_simple.pl) <div><div> что-то </div></div>
  49. 49. Вложения (div_simple.pl) sub div(&) { my $code = shift; my $inside = $code->(); return quot;<div>$inside</div>quot;; } print div { div {'some'} };
  50. 50. Если что-то сложнее?
  51. 51. Усложняем div { div {1};div{2} };
  52. 52. Усложняем <div> <div>2</div> </div>
  53. 53. Усложняем Фигня вышла
  54. 54. Простое решение
  55. 55. Простое решение (div_sol1.pl) sub div(&) { my $code = shift; my @inside = $code->(); return quot;<div>@inside</div>quot;; }
  56. 56. Простое решение (div_sol1.pl) print div { div {1}, div {2} }, div { div {3}, div {4} };
  57. 57. Код не вставишь :(
  58. 58. явный print отстой
  59. 59. запятые в топку
  60. 60. Да прибудут с вами контексты
  61. 61. Контексты (context1.pl) unless ( defined wantarray ) { # void } elsif ( wantarray ) { # array } else { # scalar }
  62. 62. Контексты sub div(&) { my $res = join '', shift->(); unless ( defined wantarray ) { print „<div>$res</div>”; } else { return „<div>$res</div>”; } }
  63. 63. Контексты (context1.pl) div { div {1}; div {2} }; <div>1</div> <div><div>2</div></div>
  64. 64. Буферизация
  65. 65. Бу-эфиры (buffers1.pl) sub buffered { my $buf = ''; local *STDOUT; open STDOUT, '>', $buf; return $buf . join('', shift->()); }
  66. 66. Бу-эфиры (buffers1.pl) sub div(&) { my $res = buffered(shift); return „<div>$res</div>” if defined wantarray; print „<div>$res</div>”; }
  67. 67. Бу-эфиры (buffers1.pl) div { div{'menu'}; my $some = 'some'; div{$some}, div{'tail'} };
  68. 68. ЯХУ! :)
  69. 69. Грабли №3
  70. 70. Грабли №3 div { 'some'; my $some = 'some'; div{$some} };
  71. 71. Грабли №3 print 'some'; outs('some'); x {'some'} { my $x=...; 'some', div{$x} }
  72. 72. Установка функций
  73. 73. Установка функций my @tags = qw(a b); foreach my $t ( @tags ) { no strict 'refs'; *{'main::'.$t} = sub (&) { ... }; }
  74. 74. Установка функций sub import; Exporter; Symbol; И прочие
  75. 75. Прото-цепочки
  76. 76. Это не химия И не биология
  77. 77. Это (&;$)
  78. 78. Прото-цепочки sub a(&;$) { print 'a ', context wantarray, quot;nquot; } sub b(&;$) { print 'b ', context wantarray, quot;nquot; }
  79. 79. Прото-цепочки a {} b {}; b scalar a void
  80. 80. Промежуточное прдставление
  81. 81. Пром-представления *{'main::'.$t} = sub (&;$) { my ($code, $next) = @_; unless ( defined wantarray ) { return _tag($t, $code, $next); } else { return bless sub { return _tag($t, $code, $next) }, 'MyTag'; } };
  82. 82. Пром-представления package MyTag; use overload 'quot;quot;' => sub { return buffered($_[0]) }; 1;
  83. 83. Пром-представления a {'head'} b {'middle'} c {'tail'}; im_represent.pl
  84. 84. Экранирование
  85. 85. Экранирование sub _escape { return unless defined $_[0]; my $v = shift; $v = „$v”; „$v”; $v =~ s/.../.../g; return $v; }
  86. 86. Экранирование sub escape(@_) { return map blessed($_) && $_->isa('MyTag') ? $_ : _escape($_), @_; }
  87. 87. Экранирование sub buffered { ... return join '', $buf, escape(@tail); } sub _tag { my ($tag, $code, $next) = @_; ... print join '', $res, escape $next; }
  88. 88. Все работает escaping.pl
  89. 89. На закуску к экранам sub outs(@) { print _escape(join '', @_) } sub raw(@) { print join '', @_ } # грабли №3 div { outs('some'); div {...} };
  90. 90. MyTD готов. Осталось только...
  91. 91. Недостающие части объединить все примеры запаковать все sub template($$); # template 'index' => run {}; sub show($@); # show 'index', arg => $arg, ...;
  92. 92. Посмотреть Template::Declare Jifty::Dispatcher Jifty::Param::Schema Jifty::DBI::Schema Object::Declare B::*
  93. 93. ВСЕ
  94. 94. Вопросы?

×