<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;CUQAR38zeyp7ImA9WhdVEko.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105</id><updated>2011-09-17T08:29:06.183-07:00</updated><category term="cl-libxml2" /><category term="restas-forum" /><category term="wiki-parser" /><category term="esrap" /><category term="postgres" /><category term="merch" /><category term="javascript" /><category term="clojure" /><category term="gentoo" /><category term="cl-routes" /><category term="clbuild" /><category term="cl-sanitize" /><category term="github" /><category term="cl-popen" /><category term="lisper.ru" /><category term="iolib" /><category term="png" /><category term="mssql" /><category term="chrome" /><category term="routes" /><category term="puri" /><category term="daemon" /><category term="firefox" /><category term="fprog" /><category term="git" /><category term="nginx" /><category term="cl-mssql" /><category term="haskell" /><category term="cliki" /><category term="paredit" /><category term="с++ popen++" /><category term="work" /><category term="ClozureCL" /><category term="gentoo-lisp-overlay" /><category term="closuretemplates" /><category term="apache" /><category term="linux" /><category term="mongrel2" /><category term="scheme" /><category term="xml" /><category term="cl-sphinx" /><category term="emacs" /><category term="java" /><category term="hunchentoot" /><category term="restas" /><category term="REST" /><category term="ajax" /><category term="lispworks" /><category term="wsal" /><category term="garbage-pools" /><category term="lisp" /><category term="xslt" /><category term="slime" /><category term="с++" /><category term="SBCL" /><category term="cgi" /><category term="cl-pdf" /><category term="puri-unicode" /><category term="parenscript" /><category term="dsl" /><category term="restructuredtext" /><category term="json" /><category term="subversion" /><category term="restas-directory-publisher" /><title>archimag</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>191</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/archimag" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="blogspot/archimag" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CEcAQ3k7eCp7ImA9WhZQGE4.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-3346112669558891510</id><published>2011-04-26T08:33:00.000-07:00</published><updated>2011-04-26T08:34:02.700-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-26T08:34:02.700-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><title>Блог переехал</title><content type="html">Блог переехал, теперь я буду жить тут: http://archimag.lisper.ru/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-3346112669558891510?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/3346112669558891510/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/04/blog-post.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3346112669558891510?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3346112669558891510?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/04/blog-post.html" title="Блог переехал" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;CkUAQXs_fSp7ImA9WhZSEEg.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-3052720429150532498</id><published>2011-03-25T03:19:00.000-07:00</published><updated>2011-03-25T03:50:40.545-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-25T03:50:40.545-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl-libxml2" /><category scheme="http://www.blogger.com/atom/ns#" term="cl-sanitize" /><title>cl-sanitize</title><content type="html">Написал &lt;a href="https://github.com/archimag/cl-sanitize"&gt;cl-sanitize&lt;/a&gt; - библиотеку для очистки HTML от нежелательного содержания. Точнее, не написал, а портирова &lt;a href="https://github.com/rgrove/sanitize"&gt;Sanitize&lt;/a&gt; (Ruby). Хотя "портировал" тоже не совсем верное слово, ибо реализация алгоритма очистки тривиальна и его я написал сам (это просто быстрее, чем разбирать код на Ruby), но вот самое важное -  конфигурационные данные и набор тестов взял из &lt;a href="https://github.com/rgrove/sanitize"&gt;Sanitize&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;cl-sanitize основана на cl-libxml2. Я сомневался некоторое время, что взять за основу cxml или cl-libxml2 (сомнения тем более оправданны, что cxml и cl-libxml2 плохо совместимы между собой из-за конфликта имён), но всё таки решил использовать cl-libxml2. Во-первых, оригинальная  версия основанна на libxml2, так что это позволило легко получить идентичные результаты на тестах. Во-вторых, я не имею такого доверия к "Closure HTML", как к libxml2. А это очень важный момент, ибо после просмотра &lt;a href="https://github.com/rgrove/sanitize/blob/master/test/test_sanitize.rb"&gt;тестов&lt;/a&gt; мне стало немного страшно ходить в интерент. &lt;br /&gt;&lt;br /&gt;В процессе пришлось немного допилить cl-libxml2 - как то мне сейчас несколько страшновато заглядывать внутрь, всё таки это был фактически мой первый серьёзный опыт использования CL, отрефракторить бы её основательно, да боюсь, что до этого руки у меня не скоро дойдут.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-3052720429150532498?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/3052720429150532498/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/cl-sanitize.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3052720429150532498?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3052720429150532498?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/cl-sanitize.html" title="cl-sanitize" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;A0cFQXczeip7ImA9WhZTEk4.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-4781660027104276916</id><published>2011-03-15T16:25:00.000-07:00</published><updated>2011-03-15T18:30:10.982-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-15T18:30:10.982-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="esrap" /><category scheme="http://www.blogger.com/atom/ns#" term="closuretemplates" /><title>Новый парсер для cl-closure-template</title><content type="html">Полностью переписал парсер для &lt;a href="https://github.com/archimag/cl-closure-template"&gt;cl-closure-template&lt;/a&gt; на базе &lt;a href="https://github.com/nikodemus/esrap"&gt;esrap&lt;/a&gt;. Я вообще давно хотел переписать его на какой-нибудь более серьёзной базе, но те решения для парсинга на CL, которые мне попадались, меня совершенно не устраивали - запутанно и слишком громоздко. &lt;a href="https://github.com/nikodemus/esrap"&gt;esrap&lt;/a&gt; понравилась мне сразу, во-первых это &lt;a href="http://en.wikipedia.org/wiki/Parsing_expression_grammar"&gt;PEG&lt;/a&gt;, во-вторых описание грамматики можно разбивать на множество отдельных кусков, а не загонять их в один большой макрос, в-третьих - описание грамматики объединяется с кодом по обработке, так что никаких раздельных стадий лексического и синтаксического анализа - всё объединено. &lt;br /&gt;&lt;br /&gt;Вообще, &lt;a href="https://github.com/nikodemus/esrap"&gt;esrap&lt;/a&gt; принципиально очень сильно похожа на моё старое решение (которое я подсмотрел в коде dokuwiki, а туда оно попало из какого-то решения на Perl), но гораздо совершенней и основано на более выразительных инструментах. Благодаря принципиальной схожести переписывание оказалось довольно простым и заняло 2 дня. При этом, не сказать, что размер код сильно уменьшился, но вот соответствие &lt;a href="http://code.google.com/intl/ru/closure/templates/docs/overview.html"&gt;оригинальной спецификации&lt;/a&gt; стало значительно более точным. Например, появилась возможность использовать экспоненциальную форму записи для чисел с плавающей точкой, целые в шестнадцатеричной форме, или напрямую писать коды Unicode в строковых литералах.&lt;br /&gt;&lt;br /&gt;Самое главное изменение - теперь неверно записанный шаблон не будет компилироваться и будет вываливаться сообщение об ошибке, по которому можно примерно установить место ошибки. Точной информации там нет, но локализовать конкретный template можно. &lt;br /&gt;&lt;br /&gt;Мне пришлось исправить несколько своих шаблонов по одной причине - старая версия дозволяла использовать символы { и } достаточно свободным образом (что противоречит спецификации) и пришлось завернуть соответствующие куски в &lt;a href="http://code.google.com/intl/ru/closure/templates/docs/commands.html#literal"&gt;literal&lt;/a&gt; секции. Плюс, было несколько мелких опечаток, которые проглатывались старой версией. Ещё в старой версии можно было записывать имена переменных как $foo-bar-x, что противоречит спецификации и я это запретил, но возможность записывать имена шаблонов в "лисп-стиле" пока всё таки оставил (может тоже запретить?).&lt;br /&gt;&lt;br /&gt;Полный код парсера &lt;a href="https://github.com/archimag/cl-closure-template/blob/master/src/parser.lisp"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;P.S. Если придётся что-нибудь ещё парсить, то однозначно буду использовать &lt;a href="https://github.com/nikodemus/esrap"&gt;esrap&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-4781660027104276916?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/4781660027104276916/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/cl-closure-template.html#comment-form" title="Комментарии: 3" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4781660027104276916?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4781660027104276916?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/cl-closure-template.html" title="Новый парсер для cl-closure-template" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;A0IAQXc4eip7ImA9Wx9aEUQ.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-5455742503375007830</id><published>2011-03-03T15:12:00.000-08:00</published><updated>2011-03-03T16:45:40.932-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-03T16:45:40.932-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><category scheme="http://www.blogger.com/atom/ns#" term="nginx" /><category scheme="http://www.blogger.com/atom/ns#" term="apache" /><title>Ещё пара полезных декораторов</title><content type="html">В веб-приложениях для отдачи статических файлов принято использовать возможности веб-серверов (Apache, Nginx), поскольку традиционные языки веб-разработки (Python, Ruby, PHP, Perl) справляются с этой задачей очень плохо. Однако, это связано с рядом ограничений. Во-первых, нельзя автоматически генерировать ссылки на основе информации о маршрутах. Но если это ещё можно пережить (хотя, честное слово, очень неудобно), то есть куда более серьёзная проблема - часто нужен "управляемый" доступ к статическим файлам. Например, скрипт должен проверить права доступа. Для решения этой дилеммы была придумана техника, когда скрипт выполняет свою логику, а реальную передачу файла делегирует веб-серверу путем настройки специальных заголовков ответа. Для nginx это &lt;a href="http://wiki.nginx.org/XSendfile"&gt;X-Accel-Redirect&lt;/a&gt;, а для Apache - &lt;a href="https://tn123.org/mod_xsendfile/"&gt;X-Sendfile&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;При разработке на Common Lisp проблема отдачи статических файлов стоит значительнее менее остро, ибо язык сам по себе намного быстрее и Hunchentoot справляется с этой работой достаточно быстро. Я для публикации статических файлов пользуюсь restas-directory-publisher - модулем для RESTAS. Однако, следует признать, что возможны ситуации, когда отдавать статику средствами CL окажется не очень разумно.&lt;br /&gt;&lt;br /&gt;Тут вот и возникла идея обеспечить возможность использования механизма XSendfile прозрачным для основной логики образом. В итоге, я добавил в RESTAS соответствующие декораторы для Apache и Nginx.&lt;br /&gt;&lt;br /&gt;Ниже я приведу возможные примеры использования этих декораторов в пакете restas-doc, обеспечивающего работу http://restas.lisper.ru/.&lt;br /&gt;&lt;br /&gt;для публикации файлов я использую такой код: &lt;pre&gt;(restas:mount-submodule -publisher- (#:restas.directory-publisher))&lt;/pre&gt; При этом, сами файлы с документацией лежат в каталоге /usr/share/doc/restas/.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;@apache-xsendfile&lt;/span&gt;&lt;br /&gt;При работе с Apache всё просто: &lt;pre&gt;(restas:mount-submodule -publisher- (#:restas.directory-publisher restas:@apache-xsendfile))&lt;/pre&gt; а в конфиге Apache необходимо установить значение параметра XSendFilePath таким образом, что бы публикуемые файлы оказались внутри указанного каталога. Самый простой путь &lt;pre&gt;XSendFilePath /&lt;/pre&gt; Но это может оказаться не очень разумным с точки зрения администрирования, так что в данном случае можно и так &lt;pre&gt;XSendFilePath /usr/share/doc/restas/&lt;/pre&gt; Более подробная информация о настройке сервера есть в &lt;a href="https://tn123.org/mod_xsendfile/"&gt;официальной документации&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;@nginx-accel-redirect&lt;/span&gt;&lt;br /&gt;Настройка nginx является более сложной. Необходимо создать internal-секцию и дальше есть два варианта: либо установить root, либо задать alias. В зависимости от выбранного варианта необходимо различным образом формировать заголовок X-Accel-Redirect. Для поддержки этих вариантов в RESTAS добавлено три переменных: &lt;pre&gt;(defvar *nginx-internal-location* nil)&lt;br /&gt;(defvar *nginx-internal-alias* nil)&lt;br /&gt;(defvar *nginx-internal-root* nil)&lt;/pre&gt; и их надо настроить точно в соответствии с настройками nginx. При этом, переменные *nginx-internal-alias* и *nginx-internal-root* являются взаимоисключающими и одна из них должна быть установлена в NIL. В принципе, их можно выставить глобально, но RESTAS позволяет сделать лучше. Например, для такого конфига nginx: &lt;pre&gt;&lt;br /&gt;location /restas {&lt;br /&gt;    internal;&lt;br /&gt;    root /usr/share/doc;&lt;br /&gt;}&lt;/pre&gt; Код на CL может быть таким: &lt;pre&gt;(restas:mount-submodule -publisher- (#:restas.directory-publisher restas:@nginx-accel-redirect)&lt;br /&gt;  (restas:*nginx-internal-location* #P"/restas/")&lt;br /&gt;  (restas:*nginx-internal-root* #P"/usr/share/doc/"))&lt;/pre&gt; Настройка с root представляется мне довольно неудобной и если nginx поднят только для CL, то гораздо проще делать так &lt;pre&gt;location /protected/ {&lt;br /&gt;    internal;&lt;br /&gt;    alias /usr/share/doc/restas/;&lt;br /&gt;}&lt;/pre&gt; и &lt;pre&gt;(restas:mount-submodule -publisher- (#:restas.directory-publisher restas:@nginx-accel-redirect)&lt;br /&gt;  (restas:*nginx-internal-location* "/protected/")&lt;br /&gt;  (restas:*nginx-internal-alias* "/usr/share/doc/restas/"))&lt;/pre&gt; Вообще, описанный функционал обеспечивает очень важную возможность работать со статическими файлами средствами CL, например, создавать повторно используемые компоненты, а в случае возникновения необходимости легко подключить механизм XSendFile с помощью подключения нужного декоратора. &lt;br /&gt;&lt;br /&gt;Я пока разбирался с этими вопрос успел посмотреть советы по использованию XSendFile с RoR и Django - там это носит вид хаков, жёстко прибивающих код к конкретной конфигурации веб-сервера. Этот факт подкрепил моё мнение, что модель декораторов RESTAS вкупе с возможностями CL (такими, как CLOS) обеспечивает более гибкие и мощные возможности, чем более традиционная схема с middleware-компонентами.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-5455742503375007830?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/5455742503375007830/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/blog-post.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5455742503375007830?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5455742503375007830?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/blog-post.html" title="Ещё пара полезных декораторов" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>7</thr:total></entry><entry gd:etag="W/&quot;DUADQXs-fip7ImA9Wx9aEUs.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-8528105204659229329</id><published>2011-03-03T07:52:00.000-08:00</published><updated>2011-03-03T07:56:10.556-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-03T07:56:10.556-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><category scheme="http://www.blogger.com/atom/ns#" term="parenscript" /><title>Прозрачное использование Parenscript</title><content type="html">Я использую &lt;a href="http://common-lisp.net/project/parenscript/"&gt;Parenscript&lt;/a&gt; в реализации &lt;a href="https://github.com/archimag/cl-closure-template"&gt;cl-closure-template&lt;/a&gt;. Но обычный код предпочитаю писать на JavaScript. На то есть несколько причин и одна из самых существенных - возникающие инфраструктурные трудности. На самом деле я бы хотел просто размещать файлы с кодом на Parenscript среди других статических файлов и больше ни о чём не думать, а оно пусть там как-нибудь само обрабатывается. После добавления в RESTAS декораторов реализовать такое поведение оказалось очень просто: &lt;pre&gt;(defclass ps-compile-route (routes:proxy-route) ())&lt;br /&gt;&lt;br /&gt;(defmethod restas:process-route ((route ps-compile-route) bindings)&lt;br /&gt;  (let ((result (call-next-method)))&lt;br /&gt;    (cond&lt;br /&gt;      ((and (pathnamep result)&lt;br /&gt;            (string= (pathname-type result) "parenscript"))&lt;br /&gt;       (setf (hunchentoot:content-type*)&lt;br /&gt;             "text/javascript")&lt;br /&gt;       (let ((*package* (find-package '#:ps)))&lt;br /&gt;         (ps:ps-compile-file result)))&lt;br /&gt;      (t result))))&lt;br /&gt;&lt;br /&gt;(defun @ps-compile (origin)&lt;br /&gt;  (make-instance 'ps-compile-route :target origin))&lt;/pre&gt; В случае если основной обработчик маршрута возвращает pathname с расширением ".parenscript", то данный декоратор компилирует его содержимое в код на JavaScript и отправляет клиенту, иначе возвращает результат без какой-либо обработки. Теперь я просто публикую статические файлы &lt;pre&gt;(restas:mount-submodule -static- (#:restas.directory-publisher @ps-compile)&lt;br /&gt;  (restas.directory-publisher:*directory* #P"/path/to/static/")&lt;/pre&gt; И если мой Parenscript-код находится в файле "/path/to/static/js/core.parenscript", то в HTML он подключается как &lt;pre&gt;&amp;lt;script src="/js/test.parenscript"&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-8528105204659229329?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/8528105204659229329/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/parenscript.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8528105204659229329?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8528105204659229329?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/parenscript.html" title="Прозрачное использование Parenscript" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;D0QMRn4zcSp7ImA9Wx9aEEQ.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-2220338578811698779</id><published>2011-03-02T11:33:00.000-08:00</published><updated>2011-03-02T11:49:47.089-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-02T11:49:47.089-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><category scheme="http://www.blogger.com/atom/ns#" term="closuretemplates" /><title>Интернет-магазина на Common Lisp</title><content type="html">Если вы мечтали купить что-нибудь в интернет-магазине, написанном на Common Lisp, то с недавних пор имеете такую счастливую возможность (особенно, если живёте в Питере) - &lt;a href="http://www.320-8080.ru/"&gt;www.320-8080.ru&lt;/a&gt;, разрабатывается &lt;a href="http://axiger.livejournal.com/"&gt;rigidus&lt;/a&gt;-ом на основе &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt; и &lt;a href="https://github.com/archimag/cl-closure-template"&gt;cl-closure-template&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-2220338578811698779?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/2220338578811698779/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/common-lisp.html#comment-form" title="Комментарии: 8" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/2220338578811698779?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/2220338578811698779?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/03/common-lisp.html" title="Интернет-магазина на Common Lisp" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>8</thr:total></entry><entry gd:etag="W/&quot;A0cNSHk_fCp7ImA9Wx9bGUg.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-5682703329992834798</id><published>2011-02-28T15:18:00.000-08:00</published><updated>2011-02-28T21:58:19.744-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-28T21:58:19.744-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>Декораторы</title><content type="html">В &lt;a href="http://archimag-dev.blogspot.com/2011/02/restas-middleware.html"&gt;предыдущем сообщении&lt;/a&gt; я использовал термин "middleware", который показался мне в итоге не очень удачным. Всё же middleware это больше про WSGI или про тот же &lt;a href="http://fukamachi.github.com/clack/"&gt;Clack&lt;/a&gt;. А в контексте &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt; более точным и адекватным видимо является термин "декоратор". Я изменил это в коде, а также добавил несколько новых возможностей. &lt;br /&gt;&lt;br /&gt;Теперь &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-define-module"&gt;restas:define-module&lt;/a&gt; и &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-define-route"&gt;restas:define-rout&lt;/a&gt;e имеют keyword аргумент :decorators, в котором можно указать список декораторов, используемых для преобразования маршрутов. Вместо :decorators в restas:define-module также можно использовать переменную *decorators*, создаваемую при определении модуля. Т.е. теперь указывать декораторы можно при определении модуля, определении маршрута и подключении субмодуля. При этом, декораторы, указанные на различных уровнях, не замещают друг друга, а применяются последовательно. &lt;br /&gt;&lt;br /&gt;В качестве примера декоратора, а также как просто удобную возможность, добавил restas:no-cache-decorator: &lt;pre&gt;(defclass no-cache-route (routes:proxy-route) ())&lt;br /&gt;&lt;br /&gt;(defmethod process-route :before ((route no-cache-route) bindings)&lt;br /&gt;  (setf (hunchentoot:header-out :expires)&lt;br /&gt;        (hunchentoot:rfc-1123-date))  &lt;br /&gt;  (setf (hunchentoot:header-out :cache-control)&lt;br /&gt;        "max-age=0, no-store, no-cache, must-revalidate"))&lt;br /&gt;&lt;br /&gt;(defun no-cache-decorator (route)&lt;br /&gt;  (make-instance 'no-cache-route :target route))&lt;/pre&gt; Кстати, функция no-cache из Hunchentoot, делает какую-то ерунду.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-5682703329992834798?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/5682703329992834798/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/blog-post_28.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5682703329992834798?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5682703329992834798?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/blog-post_28.html" title="Декораторы" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;D0UMRX89eCp7ImA9Wx9bFUs.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6141131987439248967</id><published>2011-02-24T07:41:00.000-08:00</published><updated>2011-02-24T08:34:44.160-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-24T08:34:44.160-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>RESTAS middleware</title><content type="html">Я сегодня рассматривал &lt;a href="http://fukamachi.github.com/clack/"&gt;Clack&lt;/a&gt; и должен сказать, что мне это определённо не нравится. Впрочем WSGI у меня тоже никогда больших восторгов не вызывал. Слишком универсальный протокол и слишком слабый API. Но, сама концепция middleware (в том виде, как она есть, например, в Pylons) не так уж и плоха. Просто, на мой вкус, её надо сделать более конкретной. Кое какие мысли у меня на эту тему уже крутились и я даже &lt;a href="http://archimag-dev.blogspot.com/2011/02/restas.html"&gt;показывал&lt;/a&gt; недавно, как можно тонко влиять на процесс обработки запроса. И вот сегодня идея начала приобретать конкретную форму. В частности, изменился макрос restas:mount-submodule, теперь можно писать примерно так: &lt;pre&gt;(restas:mount-submodule -mysubmodule- (#:mymodule middleware1 middleawre2)&lt;br /&gt;  ..)&lt;/pre&gt; Здесь middleware1 и middleware2 это функции, которые должны принимать маршрут и возвращать другой. &lt;br /&gt;&lt;br /&gt;В момент построения дерева маршрутов (а это происходит каждый раз при вызове функции restas:recconect-all-routes) для конкретного субмодуля строиться список обрабатываемых им маршрутов и пропускается через цепочку middleware-вызовов. middleware-функция может как угодно изменить маршрут или вообще вернуть другой. Специально для поддержки этого я добавил в cl-routes новый класс routes:proxy-route (который реализует известный паттерн proxy).&lt;br /&gt;&lt;br /&gt;Скажем, с помощью модуля restas-directory-publisher можно публиковать директории со статикой:  &lt;pre&gt;(restas:mount-submodule -tmp- (#:restas.directory-publisher)&lt;br /&gt;  (restas.directory-publisher:*baseurl* '("tmp"))&lt;br /&gt;  (restas.directory-publisher:*directory* #P"/tmp/)&lt;br /&gt;  (restas.directory-publisher:*autoindex* t)) &lt;/pre&gt; Такой код позволит любому просматривать содержимое директории /tmp на сервере, но кто знает что там может быть. Теперь с помощью middleware можно защитить содержимое этого каталога, требуя от посетителей пройти HTTP-авторизацию: &lt;pre&gt;(defclass http-auth-route (routes:proxy-route) () )&lt;br /&gt;&lt;br /&gt;(defmethod routes:route-check-conditions ((route http-auth-route) bindings)&lt;br /&gt;  (if (call-next-method)&lt;br /&gt;      (multiple-value-bind (user password) (hunchentoot:authorization)&lt;br /&gt;        (or (and (string= user "hello")&lt;br /&gt;                 (string= password "world"))&lt;br /&gt;            (hunchentoot:require-authorization)))))&lt;br /&gt;&lt;br /&gt;(defun http-auth-middleware (route)&lt;br /&gt;  (make-instance 'http-auth-route :target route))&lt;br /&gt;&lt;br /&gt;(restas:mount-submodule -tmp- (#:restas.directory-publisher http-auth-middleware)&lt;br /&gt;  (restas.directory-publisher:*baseurl* '("tmp"))&lt;br /&gt;  (restas.directory-publisher:*directory* #P"/tmp/)&lt;br /&gt;  (restas.directory-publisher:*autoindex* t))&lt;/pre&gt; Здесь определяется новый класс http-auth-route, наследующий от routes:proxy-route, и для него специализируется метод routes:route-check-conditions, который вызывается для проверки соответствия маршрута условиям запроса. Если маршрут проходит все проверки, то проверяется прошёл ли пользователь HTTP-авторизацию. Функция http-auth-middleware используется для создания таких маршрутов и указывается в списке middlewares макроса restas:mount-submodule.&lt;br /&gt;&lt;br /&gt;Для маршрутов, наследующих от routes:proxy-route, имеет смысл определять специализации методов routes:route-check-conditions и/или restas:process-route. &lt;br /&gt;&lt;br /&gt;Чуть более сложный пример использования описанных возможностей можно найти &lt;a href="https://github.com/archimag/restas/blob/master/example/publish-rst.lisp"&gt;здесь&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6141131987439248967?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6141131987439248967/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-middleware.html#comment-form" title="Комментарии: 10" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6141131987439248967?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6141131987439248967?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-middleware.html" title="RESTAS middleware" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>10</thr:total></entry><entry gd:etag="W/&quot;CE8EQHc6eyp7ImA9Wx9bEkk.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6598365548883291623</id><published>2011-02-20T14:24:00.000-08:00</published><updated>2011-02-20T15:00:01.913-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-20T15:00:01.913-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><category scheme="http://www.blogger.com/atom/ns#" term="mongrel2" /><category scheme="http://www.blogger.com/atom/ns#" term="wsal" /><title>Крякнем, плюнем и надёжно скрепим скотчем (с)</title><content type="html">Итак, мне удалось запустить локальную версию сайта lisper.ru на своей машине под управлением Mongrel2 без Hunchentoot. В наличии имеется некоторое количество костылей, жуткого оверхеда и ещё не решённых вопросов, но это работает, включая обработку POST-запросов (как application/x-www-form-urlencoded, так и multipart/form-data), работу с cookie и отдачу статики. &lt;br /&gt;&lt;br /&gt;Изменение в коде компонентов lisper.ru были самыми минимальными и заключались в переключении с использования Hunchentoot API на библиотеку &lt;a href="https://github.com/archimag/cl-wsal"&gt;cl-wsal&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Common Lisp Web servers abstraction layer&lt;/strong&gt; &lt;br /&gt;&lt;br /&gt;Идея cl-wsal зрела у меня уже давно и нужен был лишь маленький толчок, который и был сделан в &lt;a href="http://archimag-dev.blogspot.com/2011/02/restas-mongrel2.html"&gt;предыдущем обсуждении&lt;/a&gt;. Я вынес из Hunchentoot значительную часть кода, которая может быть полезной в любых веб-серверах для CL в эту библиотеку и с её помощью довольно легко довёл &lt;a href="https://github.com/archimag/cl-mongrel2"&gt;cl-mongrel2&lt;/a&gt; до необходимого состояния. &lt;br /&gt;&lt;br /&gt;cl-wsal определяет &lt;a href="https://github.com/archimag/cl-wsal/blob/master/src/protocol.lisp"&gt;протокол&lt;/a&gt;, с помощью которого строится унифицированный &lt;a href="https://github.com/archimag/cl-wsal/blob/master/src/interface.lisp"&gt;интерфейс&lt;/a&gt; для клиентского кода. Также имеется набор &lt;a href="https://github.com/archimag/cl-wsal/blob/master/src/util.lisp"&gt;утилит&lt;/a&gt;, который могу использовать разработчики веб-сервера.&lt;br /&gt;&lt;br /&gt;Весь этот код хорошо протестирован, поскольку просто взят из Hunchentoot. Правда, кое-что я изменил. Использование flexi-streams я заменил на babel. Пришлось вырвать кусок из chunga. А также скопировать и несколько отредактировать rfc2388.lisp из одноимённого пакета.&lt;br /&gt;&lt;br /&gt;Вся работа над соответствующими изменениями в RESTAS ведётся в бранче Mongrel2. Следующий релиз RESTAS не будет включать этих изменений, посколькуо будет увязан с новым релизом Hunchentoot. А вот после этого я буду полностью переключаться на использование cl-wsal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6598365548883291623?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6598365548883291623/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/blog-post.html#comment-form" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6598365548883291623?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6598365548883291623?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/blog-post.html" title="Крякнем, плюнем и надёжно скрепим скотчем (с)" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>4</thr:total></entry><entry gd:etag="W/&quot;C08MRHo6eip7ImA9Wx9UGUs.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-8318987569348063681</id><published>2011-02-17T08:20:00.000-08:00</published><updated>2011-02-17T08:58:05.412-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-17T08:58:05.412-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><category scheme="http://www.blogger.com/atom/ns#" term="mongrel2" /><title>RESTAS и Mongrel2</title><content type="html">Hunchentoot предоставляет очень удобный интерфейс, но есть некоторые сомнения о возможности использования его под высокой нагрузкой, всё таки схема поток на соединения имеет достаточно понятные пределы для масштабирования. Правда, судя по патчам ITA они таки используют Hunchentoot под достаточно высокой нагрузкой, но хотелось бы всё таки иметь и другое решение. Писать асинхронный веб-сервер на CL, который бы предоставлял уровень сервиса сопоставимый с Hunchentoot, меня сейчас не прельщает. Я достаточно долго хотел просто форкнуть Hunchentoot, переделать его на базе iolib с использованием epoll и т.п., но сейчас уже отказался от этой идеи. Отказался после того, как узнал о существовании &lt;a href="http://mongrel2.org/"&gt;Mongrel2&lt;/a&gt;, который обещает все плюшки асинхронного веб-сервера и при этом не зависит от языка. &lt;br /&gt;&lt;br /&gt;Сейчас моя самая большая цель - научить RESTAS работать с Mongrel2 (сохранив при этом возможность работать и с Hunchentoot). Но есть проблема, что Mongrel2 не предоставляет такого интерфейса, как Hunchentoot, он только умеет обрабатывать HTTP и всё. Соответственно, задача сводится фактически к вырыванию довольно больших кусков кода из Hunchentoot и адаптации их для Mongrel2. Но в данный момент я не имею достаточных временных ресурсов для выполнения этой работы. Отсюда и вопрос. Нет ли желающих помочь в этом (достойном) деле? ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-8318987569348063681?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/8318987569348063681/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-mongrel2.html#comment-form" title="Комментарии: 27" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8318987569348063681?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8318987569348063681?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-mongrel2.html" title="RESTAS и Mongrel2" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>27</thr:total></entry><entry gd:etag="W/&quot;CkMASXw4cSp7ImA9Wx9bEEk.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6027966425803979290</id><published>2011-02-15T05:05:00.000-08:00</published><updated>2011-02-18T06:47:28.239-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-18T06:47:28.239-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>Управление обработкой запросов в RESTAS</title><content type="html">Добавил в &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt; экспериментальную возможность: переменные restas:*before-dispatch-request-hook* и restas:*after-dispatch-request-hook*. В эти переменные можно складывать (pushnew) функции, которые будут вызываться соответственно до и после обработки запроса. restas:*before-dispatch-request-hook* может быть полезен, например, для безусловного требования авторизации от пользователей - не-авторизованных пользователей можно перенаправлять на страницу входа или запрашивать HTTP-авторизацию с помощью hunchentoot:require-authorization. А restas:*after-dispatch-request-hook* можно использовать, например, для сбора статистики обращения к различным URL, или можно делать косметическую пост-обработку результата, скажем, добавлять заголовки ответа.&lt;br /&gt;&lt;br /&gt;Для иллюстрации использования restas:*before-dispatch-request-hook* я написал тривиальный пример модуля для сбора статистики, посмотреть его можно &lt;a href="https://github.com/archimag/restas/blob/master/example/statistics-collection.lisp"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;restas:*before-dispatch-request-hook* и restas:*after-dispatch-request-hook* не позволяют серьёзным образом влиять на обработку запроса (хотя с их помощью и можно вносить небольшие изменения), кроме того - они применяются для всех запросов, обрабатываемых веб-сервером.&lt;br /&gt;&lt;br /&gt;Есть другая, более тонкая возможность влиять на ход обработки запросов. Например, если необходимо ограничить доступ к маршрутам из какого-либо модуля или проводить тонкую настройку обработки маршрутов из конкретного модуля. &lt;pre&gt;(in-package #:mymodule)&lt;br /&gt;&lt;br /&gt;(defclass myroute (restas:route) ())&lt;br /&gt;&lt;br /&gt;(defmethod restas:module-routes ((module (eql #.*package*)) submodule)&lt;br /&gt;  (let ((routes (call-next-method)))&lt;br /&gt;    (iter (for route in routes)&lt;br /&gt;          (change-class route 'myroute))&lt;br /&gt;    routes))&lt;/pre&gt; Здесь объявляется свой класс маршрутов myroute и для конкретного модуля специализируется метод restas:module-routes, в котором основная работа делается с помощью call-next-method, а затем у полученных маршрутов изменяется класс с restas:route на myroute. Теперь можно определить собственные специализации для методов обработки маршрутов и определить произвольную логику обработки любым удобным способом. Для класса myroute имеет смысл специализировать следующие методы: &lt;ul&gt;&lt;li&gt;routes:route-check-conditions (route  bindings) - проверяется соответствует ли запрос требования маршрута&lt;/li&gt;&lt;li&gt;restas:process-route (route  bindings) - здесь происходит вызов обработчика маршрута и, соответственно, генерация контента&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6027966425803979290?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6027966425803979290/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6027966425803979290?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6027966425803979290?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas.html" title="Управление обработкой запросов в RESTAS" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;CEMCRHg-eCp7ImA9Wx9UEks.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-5834364950217066061</id><published>2011-02-09T05:53:00.000-08:00</published><updated>2011-02-09T06:41:05.650-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-09T06:41:05.650-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>swank-js - удивительное рядом</title><content type="html">Поскольку мне приходится писать и отлаживать много JavaScript кода, то я уже давно мечтал о возможность изменять исходный код при работе в Emacs и сразу же отправлять изменения в браузер. Так же очень здорово было бы иметь JavaScript-консоль в Emacs, которая бы реально взаимодействовал с открытой веб-страницей. Или очень часто нужно немного подправить CSS и заставить браузер применить эти изменения без перезагрузки страницы. Звучит несколько фантастически, но сейчас это совершенно реально благодаря проекту &lt;a href="https://github.com/ivan4th/swank-js"&gt;swank-js&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Мне, правда, пришлось внести небольшое изменение в оригинальный код, что бы это заработало для меня.  Суть отличий в том, что в веб-страницу надо дополнительно включать такой JavaScript-код: &lt;/div&gt; &lt;pre&gt; SwankJS.setup("localhost", {port: 8009});&lt;/pre&gt; &lt;div&gt;Мои изменения &lt;a href="https://github.com/archimag/swank-js"&gt;здесь&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-5834364950217066061?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/5834364950217066061/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/swank-js.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5834364950217066061?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5834364950217066061?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/swank-js.html" title="swank-js - удивительное рядом" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;C0EHQnc4fCp7ImA9Wx9UEUs.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-4918312014044608877</id><published>2011-02-08T01:19:00.000-08:00</published><updated>2011-02-08T02:40:33.934-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-08T02:40:33.934-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>RESTAS и JavaScript</title><content type="html">Популярность JavaScript в качестве серверного языка стремительно увеличивается, чему способствует в том числе и свойства самого языка - JavaScript это удивительно гибкий и пластичный язык, он даже мягче, чем лисп. Кроме того, в современных веб-приложениях часто логика перемешивается между клиентом и сервером и очень удобно использовать и там и там один и тот же код. cl-closure-template решает многие проблемы разделения логики, но не все. Поэтому, у меня зародилась мысль дать возможность создавать &lt;a href="http://restas.lisper.ru/ru/manual/modules.html"&gt;модули&lt;/a&gt; для RESTAS на JavaScript. Тем более, что существует &lt;a href="http://marijnhaverbeke.nl/cl-javascript/"&gt;CL-JavaScript&lt;/a&gt; - достаточно качественная реализация JavaScript на Common Lisp.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="https://github.com/archimag/restas-javascript"&gt;restas-javascript&lt;/a&gt; - проект, который должен дать возможность смешивать код на Common Lisp и JavaScript при разработке web-приложений на базе RESTAS. Кое-что уже работает. В частности, я уже смог переписать на JavaScript примеры из статьи &lt;a href="http://restas.lisper.ru/ru/tutorial/hello-world.html"&gt;Hello World&lt;/a&gt; - смотрите  код в файле &lt;a href="https://github.com/archimag/restas-javascript/blob/master/example/demo.js"&gt;demo.js&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Структура этого кода полность аналогична соответствующему кода на Common Lisp, только использует идиомы JavaScript. Доступ к объектам request и reply (которые в CL оформлены в виде специальных переменных) в обработчиках маршрутом осуществляется через this.request и this.reply.  Интерфейсы request и reply повторяет функции из документации к Hunchentoot, но именуются в стиле CamelCase, плюс некоторые методы оформлены в виде "активных свойств".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Загрузить данный файл можно следующим образом: &lt;pre&gt;(restas.javascript:execute #P"/path/to/demo.js")&lt;/pre&gt; после чего уже можно будет идти в браузер смотреть результат. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;С помощью &lt;pre&gt;(restas.javascript:repl)&lt;/pre&gt; можно запустить примитивную JavaScript-консоль и поиграться с маршрутами. Что бы несколько упростить это я добавил простейшую реализацию console.log.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Кстати, дизайн интерфейса для JavaScript мне нравится в некоторых аспектах больше, чем для CL.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-4918312014044608877?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/4918312014044608877/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-javascript.html#comment-form" title="Комментарии: 7" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4918312014044608877?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4918312014044608877?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/restas-javascript.html" title="RESTAS и JavaScript" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>7</thr:total></entry><entry gd:etag="W/&quot;CEEFQ3gzfCp7ImA9Wx9VFkk.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-1140630651872145442</id><published>2011-02-02T02:12:00.000-08:00</published><updated>2011-02-02T02:30:12.684-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-02T02:30:12.684-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>Ещё раз про cl-uglify-js</title><content type="html">Я уже &lt;a href="http://archimag-dev.blogspot.com/2010/11/cl-uglify-js.html"&gt;писал&lt;/a&gt; про  &lt;a href="https://github.com/mishoo/cl-uglify-js"&gt;cl-uglify-js&lt;/a&gt;, но тут случилось примечательное событие, которое на мой взгляд стоит отметить отдельно. &lt;a href="https://github.com/mishoo/cl-uglify-js"&gt;cl-uglify-js&lt;/a&gt; и &lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt; суть библиотеки-близнецы, от одного автора, развивающиеся синхронно и делающие совершенно одно и то же, просто написанные на разных языках - одна на Common Lisp, а другая на JavaScript (для Node.js). Так вот, теперь система сборки &lt;a href="http://jquery.com/"&gt;jquery&lt;/a&gt; использует именно &lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt; (вместо &lt;a href="http://code.google.com/intl/ru-RU/closure/compiler/"&gt;Google Closure Compiler&lt;/a&gt;!), подтверждение чему можно найти &lt;a href="https://github.com/jquery/jquery/pull/173"&gt;здесь&lt;/a&gt; или &lt;a href="http://blog.jquery.com/2011/01/31/jquery-15-released/"&gt;здесь&lt;/a&gt; (раздел про BUILD SYSTEM).&lt;br /&gt;&lt;br /&gt;В общем, сейчас можно смело утверждать, что &lt;a href="https://github.com/mishoo/cl-uglify-js"&gt;cl-uglify-js&lt;/a&gt; (наряду с &lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt;) претендует на роль одного из лучших (или даже лучшего) решений в своей области.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-1140630651872145442?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/1140630651872145442/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/cl-uglify-js.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/1140630651872145442?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/1140630651872145442?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/cl-uglify-js.html" title="Ещё раз про cl-uglify-js" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;C0YAQnwyeip7ImA9Wx9VFkk.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6992895924042875676</id><published>2011-02-02T01:45:00.000-08:00</published><updated>2011-02-02T02:05:43.292-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-02T02:05:43.292-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>IE 9 - я удивлён</title><content type="html">Я, конечно, слышал, что IE 9 это чудо техники и поддерживает все современные стандарты почти полностью и т.п. Слышал, но не верил. Но вот стало любопытно, попросил админа установить на его машине сей продукт и решил попробовать запустить на нём свой &lt;a href="http://archimag-dev.blogspot.com/2010/07/made-with-common-lisp-2-screencast.html"&gt;мерчендайзинг&lt;/a&gt;. Тут должен заметить, что я никогда не планировал использовать данное приложение под IE. Мои пользователи используют Firefox, разработку я веду в основном с помощью Chromium, время от времени тестирую под Opera. Приложение основано на XHTML + SVG, использует достаточно нетривиальную обработку XML (DOMParser и т.п.). Под этими тремя (Firefox, Chrome, Opera) браузерами приложение заработало не сразу, был целый ряд различий в поведении, так что пришлось искать код, который одинаково работает во всех этих браузерах. &lt;br /&gt;&lt;br /&gt;И вот, просто ради любопытства запускаю под IE 9 - и, о чудо, оно просто работает. Вообще без какой-либо адаптации. У меня, честно говоря, культурный шок.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6992895924042875676?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6992895924042875676/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/ie-9.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6992895924042875676?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6992895924042875676?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/02/ie-9.html" title="IE 9 - я удивлён" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;DUEGQHg8fip7ImA9Wx9WFk8.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-3053914421398366731</id><published>2011-01-21T07:12:00.000-08:00</published><updated>2011-01-21T08:33:41.676-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-21T08:33:41.676-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>Автоматическая генерация ссылок в RESTAS</title><content type="html">Одной из типовых проблем веб-разработки является генерация ссылок. В некоторых простых случаях на неё можно просто закрыть глаза и создавать ссылки "вручную". Например, недавно &lt;a href="http://rigidus.livejournal.com/"&gt;rigidus&lt;/a&gt; опубликовал на хабре &lt;a href="http://habrahabr.ru/blogs/webdev/112051/"&gt;статью&lt;/a&gt;, в которой рассказал про создание &lt;a href="http://rigidus.ru/"&gt;простого сайта&lt;/a&gt; на Common Lisp (с использование &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt;). В данном примере для генерации главного меню используется такой код: &lt;pre&gt;(defun menu ()&lt;br /&gt;  (list (list :link "/" :title "Главная")&lt;br /&gt;        (list :link "/about" :title "About")&lt;br /&gt;        (list :link "/articles" :title "Статьи")&lt;br /&gt;        (list :link "/resourses" :title "Ресурсы")&lt;br /&gt;        (list :link "/contacts" :title "Контакты")))&lt;br /&gt;&lt;/pre&gt; Т.е. ссылки жёстко задаются в теле программы. Поскольку здесь их всего 5 и они очень простые, то в данном случае это не создаёт больших проблем.&lt;br /&gt;&lt;br /&gt;Однако, по мере роста приложения, а также в процессе изменения его структуры, проблема сохранения актуальности ссылок приобретает серьёзный характер.&lt;br /&gt;&lt;br /&gt;Самый лучший способ решения данной проблемы использовать автоматическую генерацию ссылок. В &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt; для этого есть специальная поддержка на базе функции &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-genurl"&gt;restas:genurl&lt;/a&gt;. Например, с использованием данной функции вышеприведённый код можно было бы переписать следующим образом: &lt;pre&gt;(defparameter *mainmenu*&lt;br /&gt;  '((main . "Главная")&lt;br /&gt;    (about . "About")&lt;br /&gt;    (articles . "Статьи")&lt;br /&gt;    (resources . "Ресурсы")&lt;br /&gt;    (contacts . "Контакты")))&lt;br /&gt;&lt;br /&gt;(defun menu ()&lt;br /&gt;  (iter (for (route . title) in *mainmenu*)&lt;br /&gt;        (collect (list :link (restas:genurl route)&lt;br /&gt;                       :title title))))&lt;br /&gt;&lt;/pre&gt; Здесь для генерации ссылок используется символ, связанный с конкретным маршрутом, а получающиеся ссылки будут учитывать базовый url, по которому подключается разрабатываемый модуль.&lt;br /&gt;&lt;br /&gt;Это очень простая ситуация, которая решается совершенно тривиальным образом. На сайте &lt;a href="http://lisper.ru/"&gt;lisper.ru&lt;/a&gt; имеет место более сложный случай. Исходный код данного ресурса разбит на несколько совершенно независимых пакетов, которые объединяются в один сайт на основе механизма &lt;a href="http://restas.lisper.ru/ru/manual/modules.html"&gt;модулей&lt;/a&gt;. Простое использование &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-genurl"&gt;restas:genurl&lt;/a&gt; здесь не подходит, поскольку маршруты, на которые ссылается главное меню, находятся в разных модулях. Для определения состава главного меню используется такое объявление: &lt;pre&gt;(defparameter *mainmenu* `(("Главная" nil main)&lt;br /&gt;                           ("Статьи" rulisp-articles restas.wiki:main-wiki-page)&lt;br /&gt;                           ("Планета" rulisp-planet restas.planet:planet-main)&lt;br /&gt;                           ("Форум" rulisp-forum restas.forum:list-forums)&lt;br /&gt;                           ("Сервисы" nil tools-list)&lt;br /&gt;                           ("Practical Common Lisp" rulisp-pcl rulisp.pcl:pcl-main)&lt;br /&gt;                           ("Wiki" rulisp-wiki restas.wiki:main-wiki-page)&lt;br /&gt;                           ("Файлы" rulisp-files restas.directory-publisher:route :path "")&lt;br /&gt;                           ("Поиск" nil google-search)))&lt;br /&gt;&lt;/pre&gt; Здесь каждому элементу меню соответствует список, содержащий следующие элементы: заголовок, субмодуль (символ, который используется при вызове &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-mount-submodule"&gt;restas:mount-submodule&lt;/a&gt;), символ маршрута (указанный в &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-define-route"&gt;restas:define-route&lt;/a&gt;) и возможно несколько ключевых параметров (параметров маршрута). А для непосредственной генерации ссылок используется такой код: &lt;pre&gt;(in-package #:rulisp)&lt;br /&gt;&lt;br /&gt;(restas:with-submodule (restas:find-upper-submodule #.*package*)&lt;br /&gt;  (iter (for item in *mainmenu*)&lt;br /&gt;        (collect (list :href (apply #'restas:genurl-submodule&lt;br /&gt;                                    (second item)&lt;br /&gt;                                    (if (cdddr item)&lt;br /&gt;                                        (cddr item)&lt;br /&gt;                                        (last item)))&lt;br /&gt;                       :name (first item))))) &lt;/pre&gt; Наиболее интересной в данном коде является строка &lt;pre&gt;(restas:with-submodule (restas:find-upper-submodule #.*package*)&lt;/pre&gt; Дело в том, что генерация меню происходит каждый раз при генерации HTML-страницы для всех маршрутов, которые находятся в разных модулях и имеют различный контекст выполнения, а параметр *mainmenu* составлен с точки зрения самого верхнего модуля :rulisp, который используется для запуска приложения с помощью &lt;a href="http://restas.lisper.ru/ru/ref/index.html#restas-start"&gt;start&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Структура субмодулей в &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt; образует иерархию и restas:find-upper-submodule позволяет найти нужный модуль выше по дереву, а макрос restas:with-submodule выполнить код в контексте найденного модуля. Таким образом, генерация ссылок работает всегда одинаково, не зависимо от контекста выполнения этого кода.&lt;br /&gt;&lt;br /&gt;restas:find-upper-submodule и restas:with-submodule я добавил только сегодня, так что они пока есть только в &lt;a href="https://github.com/archimag/restas"&gt;git-версии&lt;/a&gt; &lt;a href="http://restas.lisper.ru/"&gt;RESTAS&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-3053914421398366731?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/3053914421398366731/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2011/01/restas.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3053914421398366731?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/3053914421398366731?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2011/01/restas.html" title="Автоматическая генерация ссылок в RESTAS" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;A0IERn49fyp7ImA9Wx9RGEk.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-5610366185498081017</id><published>2010-12-20T04:50:00.000-08:00</published><updated>2010-12-20T04:51:47.067-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-20T04:51:47.067-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><title>Колличество процессоров</title><content type="html">Если вам вдруг потребуется узнать количество процессоров в коде на Common Lisp, то сделать это с помощью iolib можно так: &lt;pre&gt;(iolib.syscalls:sysconf iolib.syscalls:sc-nprocessors-onln)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-5610366185498081017?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/5610366185498081017/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/12/blog-post.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5610366185498081017?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/5610366185498081017?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/12/blog-post.html" title="Колличество процессоров" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;CUIDRXYzeyp7ImA9Wx9SFU0.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6524886766898684016</id><published>2010-12-04T14:41:00.000-08:00</published><updated>2010-12-04T14:59:34.883-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-12-04T14:59:34.883-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="iolib" /><title>10 тысяч запросов в секунду</title><content type="html">Накидал очень простой (100 строк кода) прототип асинхронного веб-сервера на базе iolib. Он умеет принимать GET-запрос и не обращая на него внимание отдавать одну и ту же страницу. Практической пользы от него никакой, но для исследования вопроса вполне сгодится. Так вот, планка в 10 000 запросов в секунду (тестировал через ab) на моей машине была уверенно взята без каких-либо оптимизаций - временами скорость доходила до 11 000 запросов в секунду. Код не полностью корректен, но от него это и не требуется. Всё обработка ведётся в одном потоке. Собственно, код: &lt;pre&gt;(asdf:operate 'asdf:load-op '#:iolib)&lt;br /&gt;(asdf:operate 'asdf:load-op '#:iterate)&lt;br /&gt;&lt;br /&gt;(defpackage #:http.test&lt;br /&gt;  (:use #:cl #:iter)&lt;br /&gt;  (:export #:start #:stop))&lt;br /&gt;&lt;br /&gt;(in-package #:http.test)&lt;br /&gt;&lt;br /&gt;(defparameter *event-base* nil)&lt;br /&gt;&lt;br /&gt;(defparameter *reply*&lt;br /&gt;  (let ((endl #.(babel:octets-to-string (coerce #(13 10) '(vector (unsigned-byte 8)))))&lt;br /&gt;        (content  "&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Hello world&amp;lt;/title&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;h1&amp;gt;Hello world&amp;lt;/h1&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;"))&lt;br /&gt;    (babel:string-to-octets &lt;br /&gt;     (with-output-to-string (out)&lt;br /&gt;       (write-string "HTTP/1.0 200 OK" out)&lt;br /&gt;       (write-string endl out)&lt;br /&gt;       (format out "Content-Length: ~A" (length content))&lt;br /&gt;       (write-string endl out)&lt;br /&gt;       (write-string "Content-Type: text/html" out)&lt;br /&gt;       (write-string endl out)&lt;br /&gt;       (write-string endl out)&lt;br /&gt;       (write-string content out))&lt;br /&gt;     :encoding :latin1)))&lt;br /&gt;&lt;br /&gt;(defvar *bucket-pool* nil)&lt;br /&gt;&lt;br /&gt;(defun get-bucket ()&lt;br /&gt;  (or (pop *bucket-pool*)&lt;br /&gt;      (make-array 4096 :element-type '(unsigned-byte 8))))&lt;br /&gt;&lt;br /&gt;(defun free-bucket (bucket)&lt;br /&gt;  (push bucket *bucket-pool*))&lt;br /&gt;&lt;br /&gt;(defun read-http-headers (socket callback)&lt;br /&gt;  (let ((headers (get-bucket))&lt;br /&gt;        (size 0))&lt;br /&gt;    (flet ((read-handler (fd event errorp)&lt;br /&gt;             (declare (ignore event errorp fd))&lt;br /&gt;             (multiple-value-bind (buffer count) (iolib.sockets:receive-from socket :buffer headers)&lt;br /&gt;               (declare (ignore buffer))&lt;br /&gt;               (incf size count))&lt;br /&gt;&lt;br /&gt;             (when (and (&gt; size 4)&lt;br /&gt;                        (equal '(13 10 13 10)&lt;br /&gt;                               (coerce (subseq headers (- size 4) size) 'list)))&lt;br /&gt;               (iolib.multiplex:remove-fd-handlers *event-base*&lt;br /&gt;                                                   (iolib.sockets:socket-os-fd socket)&lt;br /&gt;                                                   :read t)&lt;br /&gt;               (free-bucket headers)&lt;br /&gt;               (funcall callback))))&lt;br /&gt;      (iolib.multiplex:set-io-handler *event-base*&lt;br /&gt;                                      (iolib.sockets:socket-os-fd socket)&lt;br /&gt;                                      :read #'read-handler))))&lt;br /&gt;&lt;br /&gt;(defun send-http-reply (socket data callback)&lt;br /&gt;  (let ((curpos 0)&lt;br /&gt;        (total-length (length data)))&lt;br /&gt;    (flet ((write-handler (fd event errorp)&lt;br /&gt;             (declare (ignore fd event errorp))&lt;br /&gt;             (cond&lt;br /&gt;               ((= curpos total-length)&lt;br /&gt;                (iolib.multiplex:remove-fd-handlers *event-base*&lt;br /&gt;                                                    (iolib.sockets:socket-os-fd socket)&lt;br /&gt;                                                    :write t)&lt;br /&gt;                (funcall callback))&lt;br /&gt;               (t (incf curpos&lt;br /&gt;                        (iolib.sockets:send-to socket data :start curpos))))))&lt;br /&gt;      (iolib.multiplex:set-io-handler *event-base*&lt;br /&gt;                                      (iolib.sockets:socket-os-fd socket)&lt;br /&gt;                                      :write #'write-handler))))&lt;br /&gt;&lt;br /&gt;(defun accept-connection (passive-socket)&lt;br /&gt;  (let ((active-socket (iolib.sockets:accept-connection passive-socket)))&lt;br /&gt;    (read-http-headers active-socket&lt;br /&gt;                       (lambda ()&lt;br /&gt;                         (send-http-reply active-socket&lt;br /&gt;                                          *reply*                                            &lt;br /&gt;                                          (lambda ()&lt;br /&gt;                                            (close active-socket)))))))&lt;br /&gt;&lt;br /&gt;(defun start (&amp;optional (port 8080))&lt;br /&gt;  (setf *event-base* (make-instance 'iolib.multiplex:event-base))&lt;br /&gt;  (flet ((impl ()&lt;br /&gt;           (iolib.sockets:with-open-socket (acceptor :connect :passive&lt;br /&gt;                                                     :address-family :internet&lt;br /&gt;                                                     :type :stream&lt;br /&gt;                                                     :external-format '(:utf-8 :eol-style :crlf)&lt;br /&gt;                                                     :ipv6 nil)&lt;br /&gt;             (iolib.sockets:bind-address acceptor&lt;br /&gt;                                         iolib.sockets:+ipv4-unspecified+&lt;br /&gt;                                         :port port&lt;br /&gt;                                         :reuse-addr t)&lt;br /&gt;             (iolib.sockets:listen-on acceptor :backlog 5)&lt;br /&gt;&lt;br /&gt;             (flet ((accept (fd event errorp)&lt;br /&gt;                      (declare (ignore fd event errorp))&lt;br /&gt;                      (accept-connection acceptor)))&lt;br /&gt;               (iolib.multiplex:set-io-handler *event-base*&lt;br /&gt;                                               (iolib.sockets:socket-os-fd acceptor)&lt;br /&gt;                                               :read #'accept))&lt;br /&gt;             &lt;br /&gt;             (iolib.multiplex:event-dispatch *event-base*)&lt;br /&gt;             (close *event-base*))))&lt;br /&gt;    (bordeaux-threads:make-thread #'impl&lt;br /&gt;                                  :name "*http-server*")))&lt;br /&gt;    &lt;br /&gt;(defun stop ()&lt;br /&gt;  (iolib.multiplex:exit-event-loop *event-base*))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6524886766898684016?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6524886766898684016/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/12/10.html#comment-form" title="Комментарии: 3" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6524886766898684016?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6524886766898684016?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/12/10.html" title="10 тысяч запросов в секунду" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;CEQBQ3g6eCp7ImA9Wx9TF00.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6246588817086624072</id><published>2010-11-25T03:36:00.001-08:00</published><updated>2010-11-25T08:25:52.610-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-25T08:25:52.610-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="png" /><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><title>Обработка PNG-изображений на Common Lisp</title><content type="html">Для своего текущего приложения я использую интерфейс, который откровенно содрал &lt;a href="http://mugtug.com/sketchpad/"&gt;отсюда&lt;/a&gt;, при чём, необходимые для таких красивых панелек png-файлы взял как есть (но несколько изменил способ их использования в html-разметке). Всё получается относительно &lt;a href="http://www.youtube.com/watch?v=f6b0sQpDGVM"&gt;неплохо&lt;/a&gt;, но мне захотелось посмотреть как будет выглядеть это приложение в других цветовых схемах.&lt;br /&gt;&lt;br /&gt;А вот с этим проблема, поскольку украденный мной набор png-файлов сделан только в чёрном исполнении. Сам я в дизайне полный ноль, всякими Gimp-ами владею очень слабо и вообще, как создаются &lt;a href="http://mugtug.com/sketchpad/media/gui/win_LB.png"&gt;подобные&lt;/a&gt; изображения понятия не имею: я пробовал создать такое просто кодом с помощью градиентов, закруглений и т.п., но так хорошо никак не получается.&lt;br /&gt;&lt;br /&gt;И я решил просто по-пиксельно заменить все цвета оригинальных изображений на новые, которые будут вычисляться на основе базового цвета. В оригинальных файлах основным цветом является rgb(30, 30, 30), но для создания эффекта тени используется переход данного цвета в чёрный.  Функция translate-color вычисляет новый цвет на основе базового и опирается на rgb(30, 30, 30) как на основу старого изображения: &lt;div class="code"&gt;&lt;span class="paren1"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/m_defun.htm" class="symbol"&gt;&lt;i&gt;&lt;span class="symbol"&gt;defun&lt;/span&gt;&lt;/i&gt;&lt;/a&gt; translate-color &lt;span class="paren2"&gt;(&lt;span class=""&gt;orig base-color&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;span class="paren2"&gt;(&lt;span class=""&gt;iter &lt;span class="paren3"&gt;(&lt;span class=""&gt;for i in orig&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;for j in base-color&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;collect &lt;span class="paren4"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_max_m.htm" class="symbol"&gt;min&lt;/a&gt; &lt;span class="paren5"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_max_m.htm" class="symbol"&gt;max&lt;/a&gt; &lt;span class="paren6"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/a_pl.htm" class="symbol"&gt;+&lt;/a&gt; i j -30&lt;/span&gt;)&lt;/span&gt; 0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;255&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt; &lt;/div&gt; Для создания png-файлов есть известное и хорошее решение - &lt;a href="http://www.xach.com/lisp/zpng/"&gt;ZPNG&lt;/a&gt;, а вот библиотеки для разбора png-файлов я не знал и кажется такая библиотека не освещалось широко где-либо, по крайней мере, я не видел. Однако, быстрый поиск в гугл сразу показал мне библиотеку &lt;a href="http://github.com/Ramarren/png-read"&gt;png-read&lt;/a&gt;. Я опробовал её на нескольких примерах и кажется она "просто работает". Таким образом, я смог записать такой код по изменению цвета нужных мне изображений: &lt;div class="code"&gt;&lt;span class="paren1"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/m_defun.htm" class="symbol"&gt;&lt;i&gt;&lt;span class="symbol"&gt;defun&lt;/span&gt;&lt;/i&gt;&lt;/a&gt; make-other-png &lt;span class="paren2"&gt;(&lt;span class=""&gt;orig dest base-color&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;span class="paren2"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/s_let_l.htm" class="symbol"&gt;&lt;i&gt;&lt;span class="symbol"&gt;let*&lt;/span&gt;&lt;/i&gt;&lt;/a&gt; &lt;span class="paren3"&gt;(&lt;span class=""&gt;&lt;span class="paren4"&gt;(&lt;span class=""&gt;orig-png &lt;span class="paren5"&gt;(&lt;span class=""&gt;png-read:read-png-file orig&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren4"&gt;(&lt;span class=""&gt;orig-image &lt;span class="paren5"&gt;(&lt;span class=""&gt;png-read:image-data orig-png&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren4"&gt;(&lt;span class=""&gt;png &lt;span class="paren5"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_mk_ins.htm" class="symbol"&gt;make-instance&lt;/a&gt; 'zpng:png&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="keyword"&gt;:color-type&lt;/span&gt; &lt;span class="keyword"&gt;:truecolor-alpha&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="keyword"&gt;:width&lt;/span&gt; &lt;span class="paren6"&gt;(&lt;span class=""&gt;png-read:width orig-png&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="keyword"&gt;:height&lt;/span&gt; &lt;span class="paren6"&gt;(&lt;span class=""&gt;png-read:height orig-png&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren4"&gt;(&lt;span class=""&gt;image &lt;span class="paren5"&gt;(&lt;span class=""&gt;zpng:data-array png&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;iter &lt;span class="paren4"&gt;(&lt;span class=""&gt;for w from 0 below &lt;span class="paren5"&gt;(&lt;span class=""&gt;png-read:width orig-png&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren4"&gt;(&lt;span class=""&gt;iter &lt;span class="paren5"&gt;(&lt;span class=""&gt;for h from 0 below &lt;span class="paren6"&gt;(&lt;span class=""&gt;png-read:height orig-png&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren5"&gt;(&lt;span class=""&gt;iter &lt;span class="paren6"&gt;(&lt;span class=""&gt;for c in &lt;span class="paren1"&gt;(&lt;span class=""&gt;translate-color &lt;span class="paren2"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/a_list.htm" class="symbol"&gt;list&lt;/a&gt; &lt;span class="paren3"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; orig-image w h 0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; orig-image w h 1&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; orig-image w h 2&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base-color&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren6"&gt;(&lt;span class=""&gt;for i from 0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren6"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/a_setf.htm" class="symbol"&gt;setf&lt;/a&gt; &lt;span class="paren1"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; image h w i&lt;/span&gt;)&lt;/span&gt; c&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren5"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/a_setf.htm" class="symbol"&gt;setf&lt;/a&gt; &lt;span class="paren6"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; image h w 3&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren6"&gt;(&lt;span class=""&gt;&lt;a href="http://www.lispworks.com/reference/HyperSpec/Body/f_aref.htm" class="symbol"&gt;aref&lt;/a&gt; orig-image w h 3&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="paren3"&gt;(&lt;span class=""&gt;zpng:write-png png dest&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/div&gt;  Функция make-other-png принимает путь к оригинальному файлу, путь для сохранения нового изображения и цвет, который должен являться базовым для нового изображения.&lt;br /&gt;&lt;br /&gt;Опробовал данный код и остался очень доволен результатом. Вот что получается в результате вызова &lt;div class="code"&gt;&lt;span class="paren1"&gt;(&lt;span class=""&gt;make-other-png &lt;span class="string"&gt;"win_LB.png"&lt;/span&gt; &lt;span class="string"&gt;"out.png"&lt;/span&gt; '&lt;span class="paren2"&gt;(&lt;span class=""&gt;0 192 0&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt; &lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_g61JPQ_dZng/TO5Rhix9wJI/AAAAAAAAAFY/3YjC_BEfeOU/s1600/win_LB.png"&gt;&lt;img src="http://4.bp.blogspot.com/_g61JPQ_dZng/TO5Rhix9wJI/AAAAAAAAAFY/3YjC_BEfeOU/s400/win_LB.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5543457828053565586" style="margin-top: 0px; margin-right: 10px; margin-bottom: 10px; margin-left: 0px; cursor: pointer; width: 16px; height: 44px; " /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_g61JPQ_dZng/TO5RvwUoM5I/AAAAAAAAAFg/iUS7nistiWY/s1600/out.png"&gt;&lt;img src="http://4.bp.blogspot.com/_g61JPQ_dZng/TO5RvwUoM5I/AAAAAAAAAFg/iUS7nistiWY/s400/out.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5543458072206783378" style="margin-top: 0px; margin-right: 10px; margin-bottom: 10px; margin-left: 0px; cursor: pointer; width: 16px; height: 44px; " /&gt;&lt;/a&gt;&lt;br /&gt;Слева оригинальное изображение, а с права получившееся в результате преобразования.&lt;br /&gt;&lt;br /&gt;P.S. Ebuild для &lt;a href="http://github.com/Ramarren/png-read"&gt;png-read&lt;/a&gt; я добавил в свой форк gentoo-lisp-overlay.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6246588817086624072?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6246588817086624072/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/png-common-lisp.html#comment-form" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6246588817086624072?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6246588817086624072?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/png-common-lisp.html" title="Обработка PNG-изображений на Common Lisp" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_g61JPQ_dZng/TO5Rhix9wJI/AAAAAAAAAFY/3YjC_BEfeOU/s72-c/win_LB.png" height="72" width="72" /><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;Dk4GRHc5eyp7ImA9Wx9TFkw.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-9064641884244250529</id><published>2010-11-24T07:53:00.000-08:00</published><updated>2010-11-24T08:08:45.923-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-24T08:08:45.923-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl-pdf" /><title>Переделал свой форк cl-pdf</title><content type="html">Форк cl-pdf я сделал довольно давно и тогда я ещё плохо ориентировался как в CL, так и в git, в итоге форк был оформлен очень топорно, без истории изменений. Сейчас дошли руки полностью его переделать используя git svn, так что в него попала полная история изменения. Все свои изменения также внёс одно за другим. Так что стало намного лучше и можно теперь нормально синхронизироваться с основным репозиторием, если там вдруг будут изменения, а они там бывают, хоть и реже чем раз в год.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;От оригинальной версии мой форк отличается следующим:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Почищен разный мусор, типа каких-то левых патчей для поддержки CMUCL, различных вариаций на тему zlib и т.п., которые предлагалось как-то загружать руками&lt;/li&gt;&lt;li&gt;Для сжатия используется salza2 и только она.&lt;/li&gt;&lt;li&gt;Поддерживается загрузка и использования ttf шрифтов с помощью zpb-ttf&lt;/li&gt;&lt;li&gt;У функций draw-centered-text, draw-left-text и draw-right-text имеется дополнительный опциональный параметр max-height (параметр max-width уже был в оригинальной версии)&lt;/li&gt;&lt;li&gt;Добавлена функция append-child-ouline, а также экспортируется функция outline-root&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Вообще надо немного привести в порядок код для генерации PDF, который я использую на работе, а также код для генерации PDF-версии PCL, который используется на lisper.ru и в соответствии с этим также внести ряд небольших изменений. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Плюс, есть желание выкинуть из cl-pdf код для парсинга PNG-файлов и использовать для этого библиотеку &lt;a href="http://github.com/Ramarren/png-read"&gt;png-read&lt;/a&gt; (которую я обнаружил на днях) и сделать возможным использование PNG-изображений с прозрачностью (сейчас мне приходиться насильственно добавлять к таким изображениям фон). &lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Мой форк: &lt;a href="http://github.com/archimag/cl-pdf"&gt;http://github.com/archimag/cl-pdf&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-9064641884244250529?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/9064641884244250529/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-pdf.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/9064641884244250529?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/9064641884244250529?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-pdf.html" title="Переделал свой форк cl-pdf" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;Ak4BRHo5cCp7ImA9Wx9TEUw.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-8413100499947134898</id><published>2010-11-18T13:52:00.000-08:00</published><updated>2010-11-18T14:22:35.428-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-18T14:22:35.428-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="restas-directory-publisher" /><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="restas" /><title>Необычное использование restas-directory-publisher</title><content type="html">Модуль &lt;a href="https://github.com/archimag/restas-directory-publisher/"&gt;restas-directory-publisher&lt;/a&gt; по начальной задумке предназначался для простой публикации директорий, содержащих статические файлы. Но в последнее время я использовал его сразу несколькими способами, которые я никак не ожидал в момент разработки и которые показались мне довольно любопытными. Так что решил немного об этом рассказать.&lt;br /&gt;&lt;br /&gt;Сейчас у меня возникла необходимость показывать на странице пользователю диалог, в котором о мог бы выбрать файл, находящийся на файловой системе сервера. Немного погуглив нашёл несколько решений и примеров для &lt;a href="http://jquery.com/"&gt;jquery&lt;/a&gt;, которые показались мне просто ужасными и я решил, что сделать собственное решение будет значительно проще и быстрее. Как оказалось, делается оно почти тривиально.&lt;br /&gt;&lt;br /&gt;Полученное мною решение состоит из трёх частей: шаблон cl-closure-template для генерации контента на стороне клиента, несколько строк кода на JavaScript для управления и серверная часть, которая возвращает информацию о файловой системе. &lt;br /&gt;&lt;br /&gt;Модуль &lt;a href="https://github.com/archimag/restas-directory-publisher/"&gt;restas-directory-publisher&lt;/a&gt; умеет собирать информацию о файловой системе, но по умолчанию возвращаёт её в формате html, а мне для данной задачи нужно в формате JSON. Исправить этот недостаток можно так: &lt;pre&gt;(defun encode-json (obj)&lt;br /&gt;  (flet ((encode-json-list (list stream)&lt;br /&gt;           (if (keywordp (car list))&lt;br /&gt;               (json:encode-json-plist list stream)&lt;br /&gt;               (json::encode-json-list-guessing-encoder list stream))))&lt;br /&gt;    (let ((json::*json-list-encoder-fn* #'encode-json-list))&lt;br /&gt;      (json:encode-json-to-string obj))))&lt;br /&gt;&lt;br /&gt;(restas:mount-submodule -file-system- (#:restas.directory-publisher)&lt;br /&gt;  (restas.directory-publisher:*baseurl* '("api"))&lt;br /&gt;  (restas.directory-publisher:*directory* #P"/")&lt;br /&gt;  (restas.directory-publisher:*autoindex* t)&lt;br /&gt;  (restas.directory-publisher:*autoindex-template* #'encode-json))&lt;/pre&gt; Здесь производится настройка подключения субмодуля и с переменной restas.directory-publisher:*autoindex-template*, используемой для генерации контента, связывается функция #'encode-json (реализацию данной функции я уже приводил &lt;a href="http://archimag-dev.blogspot.com/2010/06/cl-json-plists.html"&gt;ранее&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Шаблон для генерации разметки: &lt;pre&gt;{template directoryBrowse}&lt;br /&gt;    &amp;lt;table summary="Directory Listing" cellpadding="0" cellspacing="0"&amp;gt;&lt;br /&gt;        &amp;lt;thead&amp;gt;&lt;br /&gt;            &amp;lt;tr&amp;gt;&lt;br /&gt;                &amp;lt;th class="n"&amp;gt;Name&amp;lt;/th&amp;gt;&lt;br /&gt;                &amp;lt;th class="m"&amp;gt;Last Modified&amp;lt;/th&amp;gt;&lt;br /&gt;                &amp;lt;th class="s"&amp;gt;Size&amp;lt;/th&amp;gt;&lt;br /&gt;                &amp;lt;th class="t"&amp;gt;Type&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;        &amp;lt;/thead&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;tbody&amp;gt;&lt;br /&gt;            {if $parent}&lt;br /&gt;                &amp;lt;tr&amp;gt;&lt;br /&gt;                    &amp;lt;td class="n"&amp;gt;&lt;br /&gt;                        &amp;lt;span class="directory" href="{$parent}"&amp;gt;Parent Directory&amp;lt;/span&amp;gt;&lt;br /&gt;                    &amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="m"&amp;gt;&amp;nbsp;&amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="s"&amp;gt;- &amp;nbsp;&amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="t"&amp;gt;Directory&amp;lt;/td&amp;gt;&lt;br /&gt;                &amp;lt;/tr&amp;gt;&lt;br /&gt;            {/if}&lt;br /&gt;&lt;br /&gt;            {foreach $path in $paths}&lt;br /&gt;                &amp;lt;tr&amp;gt;&lt;br /&gt;                    &amp;lt;td class="n"&amp;gt;&lt;br /&gt;                        &amp;lt;span class="{$path.type == 'Directory' ? 'directory' : 'file'}" href="{$path.href}"&amp;gt;&lt;br /&gt;                            {$path.name}&lt;br /&gt;                        &amp;lt;/span&amp;gt;&lt;br /&gt;                        {nil}&lt;br /&gt;                        {if $path.type == 'Directory'}/{/if}&lt;br /&gt;                    &amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="m"&amp;gt;{$path.lastModified}&amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="s"&amp;gt;{$path.size ? $path.size : '- &amp;nbsp;' |noAutoescape}&amp;lt;/td&amp;gt;&lt;br /&gt;                    &amp;lt;td class="t"&amp;gt;{$path.type}&amp;lt;/td&amp;gt;&lt;br /&gt;                &amp;lt;/tr&amp;gt;&lt;br /&gt;            {/foreach}&lt;br /&gt;        &amp;lt;/tbody&amp;gt;&lt;br /&gt;    &amp;lt;/table&amp;gt;&lt;br /&gt;{/template} &lt;/pre&gt; Я лишь немного модифицировал шаблон, используемый в &lt;a href="https://github.com/archimag/restas-directory-publisher/"&gt;restas-directory-publisher&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Управлящий код на JavaScript совсем прост: &lt;pre&gt;$(document).ready( function () { browse("/api/"); } );&lt;br /&gt;&lt;br /&gt;function browse (url) {&lt;br /&gt;    function directoryClick (evt) {&lt;br /&gt;        browse($(evt.currentTarget).attr("href"));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function fileClick (evt) {&lt;br /&gt;        $("h1").html(decodeURI($(evt.currentTarget).attr("href")));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function handler (data) {&lt;br /&gt;        $("#content").html(restas.jsBrowser.view.directoryBrowse(data));&lt;br /&gt;        $("#content .directory").click(directoryClick);&lt;br /&gt;        $("#content .file").click(fileClick);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    $.getJSON(url, handler);&lt;br /&gt;}&lt;/pre&gt; Я организовал этот код в виде отдельного законченного примера jsBrowser, который включил в состав &lt;a href="https://github.com/archimag/restas-directory-publisher/"&gt;restas-directory-publisher&lt;/a&gt;, посмотреть исходный код можно &lt;a href="https://github.com/archimag/restas-directory-publisher/tree/master/example/jsBrowser/"&gt;здесь&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-8413100499947134898?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/8413100499947134898/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/restas-directory-publisher.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8413100499947134898?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8413100499947134898?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/restas-directory-publisher.html" title="Необычное использование restas-directory-publisher" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;CUIBQHc7fyp7ImA9Wx5aGUw.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-119959998920235957</id><published>2010-11-16T05:07:00.001-08:00</published><updated>2010-11-16T05:19:11.907-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-16T05:19:11.907-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl-mssql" /><title>cl-mssql и FreeTDS-0.82</title><content type="html">Обновил у себя &lt;a href="http://www.freetds.org/"&gt;FreeTDS&lt;/a&gt; до версии 0.82 и обнаружил проблемы с кодировками. Я использую &lt;a href="http://code.google.com/p/cl-mssql/"&gt;cl-mssql&lt;/a&gt; для взаимодействия с 1С, данные там лежат в кодировке cp1251, а у меня в системе используется utf-8. Версия FreeTDS-0.62 кажется вообще никак не учитывала кодировки, поэтому в cl-mssql есть параметр соединения :external-format, который использовался для настройки переменной cffi:*default-foreign-encoding* - я устанавливал его в :cp1251 и спокойно работал. Версия FreeTDS-0.82 уже относится к этому не так просто и, вероятно, самостоятельно занимается перекодированием строк (а может как-то по другому взаимодействует с сервером, я не спец в этом вопросе). Теперь приходиться настраивать кодировку в /etc/freetds.conf: &lt;pre&gt;[global]&lt;br /&gt;    client charset = utf8&lt;/pre&gt; Кодировка, указанная в /etc/freetds.conf, должна совпадать с кодировкой, которая указывается в mssql:connect (по-умолчанию - :utf-8).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-119959998920235957?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/119959998920235957/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-mssql-freetds-082.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/119959998920235957?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/119959998920235957?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-mssql-freetds-082.html" title="cl-mssql и FreeTDS-0.82" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;Ck8MQHs4eyp7ImA9Wx5aGE4.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-4064332206962366448</id><published>2010-11-15T06:16:00.000-08:00</published><updated>2010-11-15T06:21:21.533-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-15T06:21:21.533-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl-popen" /><title>cl-popen</title><content type="html">Переименовал свою либу iolib.process в &lt;a href="https://github.com/archimag/cl-popen"&gt;cl-popen&lt;/a&gt; и несколько изменил интерфейс. Использовать для создания и взаимодействия с дочерними потоками через стандартные потоки ввода/вывода можно, например, так: &lt;pre&gt;(popen:with-popen2 ("cat | grep good" conveyer pin pout)&lt;br /&gt;  (write-line "Java is bad" pin)&lt;br /&gt;  (write-line "Python is bad" pin)&lt;br /&gt;  (write-line "Common Lisp is good" pin)&lt;br /&gt;  (write-line "imho" pin)&lt;br /&gt;  (close pin)&lt;br /&gt;  (read-line pout))&lt;/pre&gt; Поскольку стал использовать эту либу по работе, то добавил ebuild в &lt;a href="https://github.com/archimag/archimag-lisp-overlay"&gt;свой оверлей&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-4064332206962366448?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/4064332206962366448/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-popen.html#comment-form" title="Комментарии: 3" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4064332206962366448?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/4064332206962366448?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/cl-popen.html" title="cl-popen" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;DkYAQ3g6fSp7ImA9Wx5aF0s.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-8079708046678303170</id><published>2010-11-14T11:47:00.000-08:00</published><updated>2010-11-14T11:49:02.615-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-14T11:49:02.615-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="lisper.ru" /><title>Предупреждение</title><content type="html">Сегодня ночью ожидается недоступность lisper.ru в связи с техническими работа в датацентре.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-8079708046678303170?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/8079708046678303170/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/blog-post_14.html#comment-form" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8079708046678303170?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/8079708046678303170?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/blog-post_14.html" title="Предупреждение" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkQASH4zcSp7ImA9Wx5aF08.&quot;"><id>tag:blogger.com,1999:blog-5411819754291292105.post-6037713067907081045</id><published>2010-11-14T00:43:00.000-08:00</published><updated>2010-11-14T00:45:49.089-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-11-14T00:45:49.089-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="lisper.ru" /><title>Регистрация на lisper.ru и спам</title><content type="html">При регистрации на &lt;a href="http://lisper.ru/"&gt;lisper.ru &lt;/a&gt; отсылается письмо со ссылкой на продолжение регистрации. Это письмо стабильно в gmail попадает в спам. Прошу помощи у компетентных людей в чём причины и как это победить.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5411819754291292105-6037713067907081045?l=archimag-dev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://archimag-dev.blogspot.com/feeds/6037713067907081045/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/lisperru.html#comment-form" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6037713067907081045?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5411819754291292105/posts/default/6037713067907081045?v=2" /><link rel="alternate" type="text/html" href="http://archimag-dev.blogspot.com/2010/11/lisperru.html" title="Регистрация на lisper.ru и спам" /><author><name>archimag</name><uri>http://www.blogger.com/profile/07997791035847047137</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="30" src="http://4.bp.blogspot.com/_g61JPQ_dZng/SVNE_RXN3yI/AAAAAAAAAAY/ov7IRUooQfM/S220/archimag10%25.png" /></author><thr:total>2</thr:total></entry></feed>

