tag:blogger.com,1999:blog-40073427361330409432024-03-20T12:11:05.936+03:00ClojureВсе о clojure и функциональном программированииDmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.comBlogger69125tag:blogger.com,1999:blog-4007342736133040943.post-36870672864903158402013-06-20T11:34:00.003+03:002013-06-20T11:35:21.702+03:00Clojure-вакансия<div dir="ltr" style="text-align: left;" trbidi="on">
<h5 class="uiStreamMessage userContentWrapper" data-ft="{"type":1,"tn":"K"}">
<span style="font-size: small; font-weight: normal;"><span class="messageBody" data-ft="{"type":3}"><span class="userContent">
Друзья, нам на проект нужен Сlojure-программист. Мы делаем американскую копию сайта youdo.ru. Часть исходника я уже написал, но мне подкинули
еще один проект параллельно, и теперь мне нужен помошник.<br /> <br />
Используемые технологии: Clojure в бэкенде, ClojureScript на фронтенде.
Архитектура в стиле Single-Page Application. Интеграция с Google+,
Facebook, Google Maps и платежными системами.<br /><span class="text_exposed_show"> <br /> Варианты сотрудничества обсуждаемые, но очень желательно, чтобы хотя бы полдня вы были в офисе.</span></span></span></span></h5>
<h5 class="uiStreamMessage userContentWrapper" data-ft="{"type":1,"tn":"K"}">
<span style="font-size: small; font-weight: normal;"><span class="messageBody" data-ft="{"type":3}"><span class="userContent"><span class="text_exposed_show">Если кого заинтересовал -- пишите d.bushenko@gmail.com </span></span></span></span></h5>
</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com3tag:blogger.com,1999:blog-4007342736133040943.post-64978217929732934842013-05-02T11:58:00.000+03:002013-05-02T11:58:21.558+03:00Курсы по Clojure закончились<div dir="ltr" style="text-align: left;" trbidi="on">
В марте я начал курсы по языку Clojure. Все участники были поделены на три группы: А -- очники, B -- дистанционщики с преподавателем, С -- дистанционщики без преподавателя. Из примерно сотни стартовавших до конца обучения дошли всего 18 человек.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://fbcdn-sphotos-f-a.akamaihd.net/hphotos-ak-ash3/524781_464026087006855_150418915_n.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="585" src="https://fbcdn-sphotos-f-a.akamaihd.net/hphotos-ak-ash3/524781_464026087006855_150418915_n.jpg" width="640" /></a></div>
<div style="text-align: center;">
На фото -- мы с Никитой Белоглазовым толкаем речь.</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Поскольку слушателей курсов было много, я попросил помочь мне вести группы Никиту Белоглазова, Алекса Отта, Никиту Прокопова и Виталия Монастырева. Благодаря их помощи, курсы получились довольно масштабными, если бы я был один, то смог бы взять максимум человек 30.</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-ash3/601758_464026177006846_523319292_n.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<div style="text-align: left;">
Параллельно я провел три занятия по Clojure и функциональному программированию в БГУ у третьекурсников ФПМ. Двум студентам, выполнившим ДЗ, я тоже вручил сертификаты.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
</div>
<div style="text-align: center;">
<a href="https://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-ash3/601758_464026177006846_523319292_n.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="406" src="https://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-ash3/601758_464026177006846_523319292_n.jpg" width="640" /></a><br />На фото я вручаю сертификат, а Никита награждает значком с символом Сlojure</div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
На курсах мы разобрали следующие основные темы:</div>
<div style="text-align: left;">
1. Основы ФП</div>
<div style="text-align: left;">
2. Параллельное программирование</div>
<div style="text-align: left;">
3. Макросы и предметно-ориентированные языки</div>
<div style="text-align: left;">
4. Веб-разработка</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
По итогам курсов те, кто дошли до конца, разработали свое небольшое веб-приложение в стиле Single-Page Application. Самое забавное, на мой взгляд, сделал Виталий Монастырев (http://www.komoku.org:3000/index.html).<br /><br />После вручения сертификатов как-то сама собой случилась посиделка с чаем и тортиком. На заседании было высказано несколько очень здравых идей и предложений. Вот краткие итоги:<br /><br />1. Разработаем сайт минского коммьюнити clojure.by</div>
<div style="text-align: left;">
2. Задачи комьюнити: <br />а) пропаганда clojure, культурная экспансия в умы ООП-инфицированных;<br />б) обзоры интересных clojure-технологий в виде докладов на митапах;</div>
<div style="text-align: left;">
в) совместная разработка best practices по использованию clojure в различных областях. На данный момент следующие темы требуют выработки общих best practices: веб-разработка, разработка параллельных алгоритмов средствами clojure, разработка предметно-ориентированных языков.</div>
<div style="text-align: left;">
г) организация clojure-хакатонов.<br /><br />Цели комьюнити всего две: повысить собстенный уровень образования в использовании Clojure, а также создать условия, при которых на коммерческих фирмах будут разрешать выполнять проекты на Clojure.<br /><br />3. Создать чисто технарьскую конференцию thestrangeloop.by по примеру thestrangeloop.com. С этим пунктом пока много неясного, предлагаю его обсудить. Но в целом, на этой конфе должны быть исключительно программерские доклады от классных докладчиков (возможно даже дистанционно). Языки и технологии в основном маргинальные, вроде Clojure, Haskell, CL, Scheme, Emaсs Lisp, Scala, ML, Erlang и т.д. Задачи конфы -- дать возможность обсудить проблемы использования маргинальных технологий "вживую", познакомиться с грамотными специалистами. Причем я думаю, тезисы докладов стоит публиковать заранее, чтобы слушатели ознакомились и могли заготовить вопросы для обсуждения. Возможно даже такую конфу стоило бы проводить в онлайне -- дешево и сердито. <br /><br />По поводу сlojure.by и thestrangeloop.by прошу высказаться всех заинтересованных.</div>
</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com0tag:blogger.com,1999:blog-4007342736133040943.post-12710332741680645372013-04-14T18:02:00.002+03:002013-04-14T18:02:38.056+03:00Настройка окружения Clojure и IntelliJ IDEA на MS Windows<div dir="ltr" style="text-align: left;" trbidi="on">
Во-первых, необходимо установить leiningen2. Заходим на leiningen.org, читаем инструкции.<br />
<br />
Качаем <a href="https://raw.github.com/technomancy/leiningen/stable/bin/lein.bat">lein.bat</a> куда-нибудь, например, в q:\programs\lein. Затем необходимо прописать этот путь в переменную %PATH% таким образом, чтобы lein.bat был доступен отовсюду.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF4ThI5V0iLhJMpn8Rt2CIMEZ9fv4nU383TwxLgXW6KQfkA7cgX3YoWSvXzvWvI_i_V5j9lbFy6kTRD32KFasfnluPyanQqS8ku5SBX3p8isvmbUVJeNbdTB3T-CFvW-w1IVf0oGukuhc/s1600/path.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="386" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF4ThI5V0iLhJMpn8Rt2CIMEZ9fv4nU383TwxLgXW6KQfkA7cgX3YoWSvXzvWvI_i_V5j9lbFy6kTRD32KFasfnluPyanQqS8ku5SBX3p8isvmbUVJeNbdTB3T-CFvW-w1IVf0oGukuhc/s640/path.png" width="640" /></a></div>
<br />
<br />
Откройте cmd.exe и выполните команду lein self-install. Leiningen скачает и самостоятельно установит свою последнюю версию.<br />
<br />
Домашний каталог утилиты lein -- C:\users\<i><user></i>\.lein, где <i><user> -- </i>имя вашего пользователя. В домашнем каталоге lein находятся файлы настройки. Создайте там файл profiles.clj с таким содержимым:<br />
<br />
{:user {:plugins [[lein-pprint "1.1.1"]
<br /> [lein2-eclipse "2.0.0"]
<br /> [lein-idea "1.0.1"]]
<br /> :dependencies [[slamhound "1.3.1"]]}}<br />
<br />
Здесь перечислино несколько плагинов для интеграции с IDE Eclipse и IntelliJ IDEA.<br />
<br />
Мы в любом случае будем использовать lein для того, чтобы создавать проекты и управлять зависимостями. На данный момент lein делает это гораздо лучше IDE.<br />
<br />
Чтобы создать новый проект, выполните команду lein new myclojure1, где myclojure1 -- имя нового проекта.<br />
<br />
Зайдите в каталог myclojure1 и выполните там команду lein deps. Leiningen подятянет все зависимости (в том числе и саму Clojure).<br />
<br />
Сконвертируйте полученный проект в IDEA-project командой lein idea.<br />
<br />
Далее, необходимо импортировать этот проект в IntelliJ IDEA. Чтобы IDEA могла работать с Clojure -- установите плагин LaClojure. Затем импортните проект myclojure1 (не забудьте настроить JDK для этого проеката, если не настроили для всей IDEA).<br />
<br />
Запустить REPL можно выбрав пункт меню Tool -> Start Clojure Console.<br />
<br />
Скомпилировать исходник на Clojure можно выбрав его в дереве проекта (src\myclojure1\core.clj), и выбрав пункт меню Tools -> Clojure REPL -> Load file in REPL.<br />
<br />
Для того, чтобы перейти в неймспейс этого файла, обратите внимание на первую строчку core.clj:<br />
<br />
(ns myclojure1.core)<br />
<br />
Введите в REPL следующий текст:<br />
<br />
(in-ns 'myclojure1.core)<br />
<br />
После выполнения этой команды, все определения, доступные в core.clj станут доступны и в REPL.<br />
<br />
Успехов! </div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com10tag:blogger.com,1999:blog-4007342736133040943.post-42503088808402502852013-04-04T14:42:00.001+03:002013-04-04T14:42:12.930+03:00Как деплоить Clojure на OpenShift<div dir="ltr" style="text-align: left;" trbidi="on">
<style type="text/css">P { margin-bottom: 0.21cm; }A:link { }</style>
<br />
<div style="margin-bottom: 0cm;">
Прежде всего вам
необходимо зарегистрироваться на
OpenShift и залогиниться. Второй этап —
настроить ssh-доступ. Да, y OpenShift есть
специальные консольные утилиты для
работы без ssh, но это не наш случай.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
1. Генерим открытый
ssh-ключ:</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ ssh-keygen -t rsa
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
В каталоге .ssh вы получите
два файла: id_rsa и id_rsa.pub.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
2. Зайдите в настройки
своего аккаунта.</div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyQsL0poG936NP0NW7c7gumSdnQ9eoP9XLKWOXYtfV9Pxxozogn6eztyv_HCvBjrV5kta9RdtRdJPvmLX-8HSzrjZ6rq4c8eYJkTW7FsOeXyUWhi8TNkLQGc51HAgw-ZCG9fqUsa0NV1A/s1600/01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyQsL0poG936NP0NW7c7gumSdnQ9eoP9XLKWOXYtfV9Pxxozogn6eztyv_HCvBjrV5kta9RdtRdJPvmLX-8HSzrjZ6rq4c8eYJkTW7FsOeXyUWhi8TNkLQGc51HAgw-ZCG9fqUsa0NV1A/s640/01.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div style="margin-bottom: 0cm;">
3. Выберите «Change settings»,
попадете на экран Personal Information.</div>
<div style="margin-bottom: 0cm;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkndrLCYFDCRqqK3rQfGEPyZWUxm8TgqUejJq7Se1RrizxW-usKJQZWiqk4LNIkIWQYdtJf01sSpF-yGQLeHO6dd-ATUNCcp5oJc1SR1HciTHX8xFvJY5-b-vQJIYhaXoNgFZVoyS37KY/s1600/02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="460" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkndrLCYFDCRqqK3rQfGEPyZWUxm8TgqUejJq7Se1RrizxW-usKJQZWiqk4LNIkIWQYdtJf01sSpF-yGQLeHO6dd-ATUNCcp5oJc1SR1HciTHX8xFvJY5-b-vQJIYhaXoNgFZVoyS37KY/s640/02.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div style="margin-bottom: 0cm;">
4. Нажмите кнопку «Add a
new key...», попадете на экран Add a public key. Скопируйте туда содержимое файла
id_rsa.pub.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKzeEqhCouS8RX_e9V49BMEKYImcSEUKNCZc8M7vrfIblFUrqRfd2IfMeYrC-iEEVUsCl-KQIjcrCxDpKu0z2h3UVipcZWTbhy0-wEkT3wIiigSbKtkagqJ-F0oleD6ygK7pGj5jFL-EE/s1600/03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="388" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKzeEqhCouS8RX_e9V49BMEKYImcSEUKNCZc8M7vrfIblFUrqRfd2IfMeYrC-iEEVUsCl-KQIjcrCxDpKu0z2h3UVipcZWTbhy0-wEkT3wIiigSbKtkagqJ-F0oleD6ygK7pGj5jFL-EE/s640/03.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
Далее необходимо создать
приложение.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
5. Выберите вкладку «My
Applications» и нажмите кнопку «Add application».
OpenShift покажет вам экран выбора типа
приложения. Выберите приложение
Tomcat7.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKkcg9Luu451T7kJ5lnCB7qekaycegH2SHGv9SVkJe3GinJi69Hy3sMlrHbyUDVPv-AR3S8e7TaomU8gZ7wgK9GFbqZRY3alrJsxvEfKJ0R9tmTYAcO8ypl-4EOOfbdC3jSkpEz_lq7eM/s1600/04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="444" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKkcg9Luu451T7kJ5lnCB7qekaycegH2SHGv9SVkJe3GinJi69Hy3sMlrHbyUDVPv-AR3S8e7TaomU8gZ7wgK9GFbqZRY3alrJsxvEfKJ0R9tmTYAcO8ypl-4EOOfbdC3jSkpEz_lq7eM/s640/04.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
6. На экране конфигурации
нового приложения введите его имя и нажмите кнопку «Create application.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMeioZHm3kLLXt8w_ELBDuCvez9ZwWyGX_FlI2hHbQgXlIr5AI6_yEcIjus3oKXFOqQ2SQ0z_5dww_a9tkl8ddVD2SGcq-j4JzUCfwjmrNszDHzMOnVYf2KqnPrnrG524lwgreblyXlCU/s1600/05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMeioZHm3kLLXt8w_ELBDuCvez9ZwWyGX_FlI2hHbQgXlIr5AI6_yEcIjus3oKXFOqQ2SQ0z_5dww_a9tkl8ddVD2SGcq-j4JzUCfwjmrNszDHzMOnVYf2KqnPrnrG524lwgreblyXlCU/s640/05.png" width="632" /></a></div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
7. Откроется экран с
подробностями о созданном приложении. Нажмите на ссылку «Add cartridge».</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinFhAJTj6oFZlBoP9zGeZjL7CbAcduXEeoIIWna7M4lxJKxMzs9c7erEbdMMwmv4-c5Pjykd2nXR1ojMwYfkT3MaANql8uzRnQ-p7NWkGM1ftkYIY_mILlUL-G3ZpdeEl5ivQROAwb6P8/s1600/06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinFhAJTj6oFZlBoP9zGeZjL7CbAcduXEeoIIWna7M4lxJKxMzs9c7erEbdMMwmv4-c5Pjykd2nXR1ojMwYfkT3MaANql8uzRnQ-p7NWkGM1ftkYIY_mILlUL-G3ZpdeEl5ivQROAwb6P8/s640/06.png" width="602" /></a></div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
8. В открывшемся экране выберите нужную вам базу данных.
В этом примере мы рассматриваем MySQL 5.1.
Добавьте этот картридж.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqCJJvCKwYurLlBRN2ZLge21CnKkgDjr0W6KyFPEYhdBXTF1ysqukCogC6Qw1udxbKzvpTX4ndo2RJMMvrA2z4FbWWOYDvrbDXa2TNhkyY5nql9HYiGEsAAPBkGctBkL-G1cJ2jKDnmDY/s1600/07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="538" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqCJJvCKwYurLlBRN2ZLge21CnKkgDjr0W6KyFPEYhdBXTF1ysqukCogC6Qw1udxbKzvpTX4ndo2RJMMvrA2z4FbWWOYDvrbDXa2TNhkyY5nql9HYiGEsAAPBkGctBkL-G1cJ2jKDnmDY/s640/07.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
9. OpenShift покажет вам
экран с информацией о вашей БД. Важный
момент: хост и порт для коннекта из
приложения хранятся в переменных
окружения среды OPENSHIFT_MYSQL_DB_HOST и
OPENSHIFT_MYSQL_DB_PORT.</div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXCTzE1fRB2A6vw15qOIELfybEdrOaleyYjVz5Nd44fH4kj9N21nes51sRK_GLuS3bWiaw0CGUnvJT_Fh-aiorLe5beR10c-Np3vwUzkUw0KZppIochFun6u-f0_2DFq9W4zsO_p9yqm8/s1600/08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="408" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXCTzE1fRB2A6vw15qOIELfybEdrOaleyYjVz5Nd44fH4kj9N21nes51sRK_GLuS3bWiaw0CGUnvJT_Fh-aiorLe5beR10c-Np3vwUzkUw0KZppIochFun6u-f0_2DFq9W4zsO_p9yqm8/s640/08.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div style="margin-bottom: 0cm;">
10. Зайдите на вкладку
My applications, выберите ваше приложение и
нажмите на ссылку «Want to login to your
application?». OpenShift покажет вам ssh-команду
для логина на сервер.</div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjllax-2ZX-r6uTUeLdDjFVDbOJIC_T2p2ilqlyEjC99cyDMS9g_E7fbwejuPM0GHeTBEL1ggLWzetxxb17u9DKQ9iNqYb6vaF5OoSdZYsEJEPGZIqM06i3sJOpUzxU5HfyMoE22wqHBPM/s1600/09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjllax-2ZX-r6uTUeLdDjFVDbOJIC_T2p2ilqlyEjC99cyDMS9g_E7fbwejuPM0GHeTBEL1ggLWzetxxb17u9DKQ9iNqYb6vaF5OoSdZYsEJEPGZIqM06i3sJOpUzxU5HfyMoE22wqHBPM/s640/09.png" width="640" /></a></div>
<div style="margin-bottom: 0cm;">
<br /></div>
<div style="margin-bottom: 0cm;">
11. Введите показанную
команду</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ ssh
515c...00401@helloworld-writing.rhcloud.com
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
Если до этого вы правильно
зарегистрировали ваш id_rsa.pub, то ssh тихо
залогинит вас на сервер и не спросит
пароля.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
Выполните команду
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ ctl_all restart</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
Эта команда необходима
для того, чтобы OpenShift рестартанул
приложение и подтянул картридж с MySQL.
После перезагрузки в окружении среды
перезапущенного Tomcat появятся переменные
OPENSHIFT_MYSQL_DB_HOST и OPENSHIFT_MYSQL_DB_PORT.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
12. Выйдите из ssh-сессии
командой exit. Сконфигурируйте строку
соединения к MySQL следующим образом.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
(def env (into {} (System/getenv)))
</div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
(def dbhost (get env
"OPENSHIFT_MYSQL_DB_HOST"))
</div>
<div style="margin-bottom: 0cm;">
(def dbport (get env
"OPENSHIFT_MYSQL_DB_PORT"))
</div>
<div style="margin-bottom: 0cm;">
</div>
<div style="margin-bottom: 0cm;">
(def default-conn {:classname
"com.mysql.jdbc.Driver"
</div>
<div style="margin-bottom: 0cm;">
:subprotocol
"mysql"
</div>
<div style="margin-bottom: 0cm;">
:user "myuser"
</div>
<div style="margin-bottom: 0cm;">
:password
"mypassword"
</div>
<div style="margin-bottom: 0cm;">
:subname (str "//"
dbhost ":" dbport
"/helloworld?useUnicode=true&characterEncoding=utf8")
</div>
<div style="margin-bottom: 0cm;">
:delimiters "`"})</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
В поля :user и :password
необходимо подставить значения, которые
OpenShift выдал вам на шаге 9. В поле :subname
слово helloworld — это имя БД (также получено
на шаге 9).</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
13. Соберите ваше
веб-приложение командой lein ring uberwar.
Переименуйте её в ROOT.war.</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
14. Скопируйте war-ку на
сервер:</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ scp ROOT.war
515....01@helloworld-writing.rhcloud.com:app-root/data
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
15. Залогиньтесь на
сервер</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ ssh
515...401@helloworld-writing.rhcloud.com
</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
16. Переместите
скопированную war-ку в каталог с
веб-приложениями:</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
$ mv app-root/data/ROOT.war
app-root/runtime/repo/webapps/</div>
<div style="margin-bottom: 0cm;">
<br />
</div>
<div style="margin-bottom: 0cm;">
17. Через некоторое время
ваше новое приложение будет доступно.
В моем случае — по адресу
<a href="http://helloworld-writing.rhcloud.com/">http://helloworld-writing.rhcloud.com</a>. </div>
<div style="margin-bottom: 0cm;">
</div>
</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com0tag:blogger.com,1999:blog-4007342736133040943.post-50392305147778437102013-03-27T17:57:00.002+03:002013-03-27T17:57:41.559+03:00Clojure в БГУ<div dir="ltr" style="text-align: left;" trbidi="on">
Сегодня договорились с деканом ФПМ БГУ о том, что я проведу три занятия по Clojure на ФПМ (занятия парные, лекция + ПЗ) в рамках спецкурса по функциональному программированию.<br />
<br />
Главная печаль в том, что до крайности не хочется заниматься настройкой окружения в лаборатории, т.к. у них там компы c Windows XP и дохлым интернетом. Вот как студентам установить lein?<br />
<br />
Одно из решений, которое я придумал: предложу студентам залогиниться на мой ноутбук по SSH и работать там в консольном Emacs-е. Заодно и ктулхуугодный редактор пропиарю :-)</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com4tag:blogger.com,1999:blog-4007342736133040943.post-42871064478469140092013-03-10T01:19:00.000+03:002013-03-10T01:19:47.747+03:00Почему Emacs?<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifRPaV7e8zfk-zM3fv1F4eeb9tozm4awCuiK2O3gLuc52A77ox6GKPXok9fbs2WGxr_iGz5sUKtnB_tySGTN8Ba38N__GisZmWRD2lJ0samxJdfFb4-LOOZX-RFsLl7MIIZuK7QBtUilw/s1600/emacs_vi_power_drill_cartoon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifRPaV7e8zfk-zM3fv1F4eeb9tozm4awCuiK2O3gLuc52A77ox6GKPXok9fbs2WGxr_iGz5sUKtnB_tySGTN8Ba38N__GisZmWRD2lJ0samxJdfFb4-LOOZX-RFsLl7MIIZuK7QBtUilw/s640/emacs_vi_power_drill_cartoon.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ3IBvUP44MGbJJx5wu4PahR1a7B-HR_Ims4t_WCo9r2UaQmW4G8_xKeOHm5N-ESKc6503SqhL-YRElmkqCBVpNHf0Pcgwkaz7d2YZ5LOBIqeRgBU4O34JvlzKEd-JYok7ZvLGkPuoGkE/s1600/editor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
На сегодняшний день основным инструментом программистов являются супер-технологичные IDE, помогающие выполнять процентов 80 ежедневной работы. Они умеют очень многое: автоматически дополнять код, отлаживать приложение, проводить сложный рефакторинг. Поэтому люди, пользующиеся такими старыми, с виду, инструментами, как Emacs, кажутся совсем уж чудаками. Но чудачество ли это в самом деле? И почему этот текстовый редактор до сих пор жив и все еще пользуется уважением?<br />
<br /> На моей предыдущей работе я программировал в основном J2EE-приложения. Мне очень нравились Eclipse и Jetbrains Idea, с их помощью мне удавалось за очень короткое время разрабатывать формочки, веб-сервисы и даже архитектуру приложения. Да я, в общем, не особо и задумывался о том, на чем писать. Все пишут в Eclipse -- ну и я тоже.<br />
<br /> А потом все резко изменилось. Мне предложили написать для платформы Android виртуальное казино. В приложении была всего одна формочка, зато масса логики, которую нужно было алгоритмизировать. Самым неприятным занятием было объявить в xml-файле 130 полей разметки на столе для ставок, затем их же проинициализировать в коде на java. Ни Eclipse, ни Idea уже ничем не могли мне в этом помочь, а выполнять эту работу вручную совсем не хотелось. Мои коллеги относились к этому проще. Они полагали, что раз платят, то нужно выполнять задание, даже если оно такое бездумное и скучное. Также полагал и мой менеджер. А как считаете вы?<br />
<br /> Еще во время учебы в школе я решил, что стану программистом. Я обажал каверзные задачки, участвовал в олимпиадах, мне очень нравилось составлять алгоритмы. Профессия программиста меня привлекала своей загадочностью. В то время мне казалось, что программисты занимаются невероятно сложной и очень интеллектуальной работой. Сейчас уже прошло примерно пятнадцать лет с тех пор, и я уже думаю несколько иначе.<br />
<br /> Чем же сегодня занимаются программисты на крупных фирмах, особенно на тех, которые практикуют "промышленное программирование"? Разве программированием? Большая часть времени уходит на всякую конфигурацию Spring или других фреймворков, генерацию заготовок кода при помощи IDE, добавление всяческих аннотаций для связи различных частей кода, написание и прогон тестов и т.д. Изредка код писать все-таки приходится. Но это, как правило, очень простой код: взять что-то из базы, привести объект к другому типу, в цикле пройтись по коллекции. Давайте спросим себя: неужели именно такое программирование нам нравится?<br />
<br /> Когда я взялся писать виртуальное казино для Android, было ощущение, что меня разбудили после долгого сна. Я почуствовал, что передо мной задача очень отличающаяся от тех, что мне приходилось делать раньше. Мне не нужно было писать никаких конфигураций, да и IDE ничем не могло мне помочь. Мне нужно было писать алгоритмы и думать, много думать и много писать.<br />
<br /> Первое, что я обнаружил, так это то, что текстовый редактор в Eclipse никуда не годится. Я набираю код всеми десятью пальцами сразу, и мне не надо смотреть на клавиатуру, когда мои пальцы находятся над клавишами алфавита. А для перемещения по тексту необходимо сдвигать правую руку на клавиши со стрелочками или даже еще дальше -- к PgDown/PgUp. Но даже это не идет ни в какое сравнение с тем, как отвлекает необходимость хвататься за мышку! Это здорово выбивает из состояния потока!<br />
<br /> Во-вторых, конечно же, я не стал писать эти 130 полей вручную. Нужно было разработать какой-то генератор кода, и здесь Eclipse опять ничем не смог мне помочь. Писать плагины для Eclipse невероятно затратное дело. Следовало выбрать один из скриптовых языков вроде Perl, Python или Ruby. Но я все равно не понимал, почему не сделать возможность в Eclipse для написания простых плагинов как раз для таких случаев. В редакторе Eclipse есть курсор, и им можно было бы выделять фрагменты текста для обработки. А когда пишешь скрипт для обработки текста, например, на Perl, курсора нет, и необходимо как-то парсить структуру текста регулярными выражениями или помечать места для упрощения анализа текста.<br />
<br /> В-третьих, когда огромный xml-файл был уже готов, время от времени приходилось подправлять сгенерированные координаты ячеек. Например, поднять весь ряд немного вверх, или сдвинуть часть ячеек чуть-чуть правее. Теоретически, можно было переделать генератор, сгенерировать новый код, вставить его на замену старому и решить задачу. Но в моем случае первоначально сгенерированный код уже был обработан вручную, и повторная генерация кода потребовала бы повторной его ручной обработки. Мне дико не хватало возможности написать макрос, чтобы текстовый редактор выполнял всю обработку за меня.<br />
<br /> Мне не понравилось программировать казино на Android в Eclipse. Оказалось, что Eclipse прекрасно подходит для задач code monkeys, ежедневно клепающих формочки для очередной CRM на Java. Но этот же Eclipse совсем плохо показывает себя в задачах, для которых у него нет заготовок вроде генераторов кода.<br />
<br /> Давайте спросим себя, что же это за задачи, для которых IDE подходят не очень хорошо?<br />
<br /> Это самые нестандартные задачи, это проекты, содержащие элемент изобретательности, технического творчества. Это задачи, для которых в индустрии до сих пор не выработаны типовые решения. Это задачи, где вам приходится много алгоритмизировать и много думать.<br />
<br /> Вернемся к самому первому тезису статьи. В самом ли деле, те, кто используют Emacs, безнадежно устаревшие чудаки? Или все-таки действительно есть причины для применения этого древнего текстового редактора?<br />
<br /> А вы знаете, что, например, в Emacs можно настроить сочетания клавиш таким образом, чтобы всю работу с текстом, в том числе и перемещение, можно было выполнять, не убирая пальцы с алфавитно-цифровой клавиатуры? И тем более не хватаясь за мышку? Когда концентрация на коде максимальна, очень важно не отвлекаться на посторонние вещи!<br />
<br /> Редактору Emacs легко придать абсолютно аскетичный вид. Казалось бы, зачем убирать панели инструментов, строку меню, боковые навигаторы кода и прочие элементы управления, если у нас итак достаточно вместительные экраны мониторов? Ответ очень простой: пространства на экране никогда не бывает достаточно! Когда-нибудь в будущем, когда картинка будет проецироваться пользователю напрямую в мозг и не будет ограничена размерами монитора, все равно будут те, кому и такое изображение покажется недостаточно вместительным. Эти будущие гики, также, как и сегодняшние, будут стараться убрать с изображения все отвлекающие детали и максимально освободить себе рабочее пространство, чтобы заполнить его действительно полезной информацией.<br />
<br /> Таким образом, Emacs идеально подходит для тех, кому требуется полная концентрация внимания на редактируемом тексте.<br />
<br /> Текстовый редактор Emacs -- это, по сути, небольшое ядро, включающее в себя интерпретатор языка Emacs Lisp, и громадное количество скриптов, написанных на этом Lisp-е. Весь интерфейс, все функции, вообще всё-всё-всё в Emacs -- это программы на Emacs Lisp. Все эти скрипты хорошо документируемы и доступны в исходных кодах. Поэтому, если вас что-то не устраивает в Emacs, то вы вольны изменить это так, как вам нравится. Писать плагины для Emacs невероятно просто.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwdbv_7-4NOKRGlIkdyiAo24pwB3he7vM6bv85nQOKrWGRXOQiVC9YgdwSoXeCZ0nUYYXoLV4Ipni2YgLsb66D8nLTgd7ObDTgik8T-SiVLw95Ig2RqkUDMDoL9x4vtGLs7qcLiYt2AkA/s1600/geek_vs_non_geek_repetitive_tasks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="419" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwdbv_7-4NOKRGlIkdyiAo24pwB3he7vM6bv85nQOKrWGRXOQiVC9YgdwSoXeCZ0nUYYXoLV4Ipni2YgLsb66D8nLTgd7ObDTgik8T-SiVLw95Ig2RqkUDMDoL9x4vtGLs7qcLiYt2AkA/s640/geek_vs_non_geek_repetitive_tasks.png" width="640" /></a></div>
<br /> Я специально хочу остановиться на этой мысли: писать плагины для Emacs так просто, что я, не задумываясь, пишу их чуть ли не для каждой сложной задачи. Посмотрите, как выглядит "Hello World" в стиле Emacs Lisp:<br /> <br /> (defun my-test-message ()<br /> (interactive)<br /> (message "Hello, World!"))<br /> <br /> Обратите внимание, сколько xml-конфигураций мы здесь не правим, сколько интерфейсов не реализовываем, сколько классов не наследуем! Всего одна простая функция, и если её скомпилировать (C-x C-e за последней скобкой функции), она тут же становится частью редактора Emacs!<br />
<br /> Именно эта простота написания плагинов так сильно подкупает в Emacs. Когда я программирую на Java, я пишу расширения Emacs для генерации всего того boilerplate-кода, который не умеет генерировать Eclipse. Emacs автоматизирует мне много по-настоящему скучной рутинной работы!<br />
<br /> Есть и еще одна проблема, с которой плохо справляются IDE -- это нестандартные и предметно-ориентированные языки программирования. Для них нет и никогда не будет поддержки в Eclipse, Idea, Netbeans и т.д. Но зато вы с лёгкостью можете добавить поддержку любого нужного вам языка в Emacs!<br /> <br /> У Emacs есть еще один существенный плюс: превосходная интеграция с Clojure. Исторически сложилось так, что программисты на Lisp-подобных языках предпочитают разрабатывать программы в Emacs, потому что в этом редакторе есть все, что нужно лисперу. Это и правильная подсветка кода и скобочек, и автодополнение кода, и REPL для интерактивной разработки, и просмотр документации, и многое другое. Если вы планируете посвятить много времени Lisp-подобным языкам, то вам однозначно стоит попробовать Emacs. Тем более, что освоив Clojure вам будет уже довольно просто разобраться с Emacs Lisp и разрабатывать плагины для этого текстового редактора. Если у вас действительно возникнет желание изучить также и Emacs Lisp, могу порекомендовать вам <a href="https://docs.google.com/file/d/0BzmL7xzGeOtOOWE1ZTc3NzAtZjkyNy00ZDU5LTg2Y2UtZDc4MmNkNTI1ZmJl/edit?usp=sharing">пособие</a>.<br /> <br /> Откровенно говоря, нужно признать, что у Emacs есть одна проблема: довольно высокий порог вхождения. Далеко не с первого раза можно понять, как там что устроено и почему именно так, а еще как переделать, чтобы было удобнее. В Emacs множество комбинаций клавиш начинаются с Ctrl, от чего сильно устает левый мизинец; комбинации для перемещения курсора придумали как будто специально, чтобы ими было неудобно пользоваться: Ctrl-f, Ctrl-b, Ctrl-n, Ctrl-p. Это связано с тем, что Emacs создали очень давно, когда у клавиатур не только не было клавиши "Win", но даже и само расположение клавиш было другим. Например, Клавиша Ctrl находилась на том месте, где сейчас традиционно расположена Alt, и Ctrl в то время нажимали большим пальцем левой руки, а вовсе не мизинцем.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidl7kmSA8ezEx3f4PDmVPzffgLd92MvRXMZhYz6tGVoXUOAi1x-CtN87Kg4JKk6YXdZ-t7HKu_cisITCnWxNY7d8XPcrZ24TyygCjyRJPHN-etIiVygTmXuxB4WunVzbZRTvCN-S8shEE/s1600/kb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidl7kmSA8ezEx3f4PDmVPzffgLd92MvRXMZhYz6tGVoXUOAi1x-CtN87Kg4JKk6YXdZ-t7HKu_cisITCnWxNY7d8XPcrZ24TyygCjyRJPHN-etIiVygTmXuxB4WunVzbZRTvCN-S8shEE/s1600/kb.png" /></a></div>
<br /> Но, как уже было сказано, ничто не мешает вам изменить управляющие клавиши. Например, известный Emacs-ер Xah Lee создал проект <a href="http://ergoemacs.org/emacs/ergonomic_emacs_keybinding.html">ergoemacs</a>, в котором он предлагает совершенно иную раскладку, намного более эргономичную.<br /> <br /> Для слушателей Clojure Course, которые хотели бы попробовать Emacs, я создал немного <a href="https://github.com/dbushenko/emacs-config">модифицированную версию</a> Ergo Emacs. Этот конфиг, конечно, не заменит начального ознакомления с документацией, но зато поможет сделать работу с Emacs несколько удобнее.<br /> <br /> Ниже несколько основных клавиатурных сочетаний, которые я использую в своем конфиге. Здесь используются следующие традиционные для Emacs обозначения клавиш: C -- Ctrl, M -- Alt, S -- Shift.<br /> <br /> Перемещение курсора:<br /> <br /> M-i -- up<br /> M-k -- down<br /> M-j -- left<br /> M-l -- right<br /> M-u -- word left<br /> M-o -- word right<br /> M-S-i -- page up<br /> M-S-k -- page down<br /> M-S-j -- beginning of document<br /> M-S-l -- end of document<br /> M-h -- end of line<br /> M-S-h -- beginning of line<br /> C-l -- go to line<br /> <br /> Редактирование текста:<br /> <br /> M-m -- new line<br /> M-d -- backspace<br /> M-f -- delete<br /> M-Space -- start/end selection<br /> M-c -- copy<br /> M-x -- cut<br /> M-v -- paste<br /> M-s -- save document<br /> C-o -- open file<br /><br /> Работа с окнами:<br /><br /> C-x 2 -- split window horizontally<br /> C-x 3 -- split window vertically<br /> C-x 0 -- close current window<br /> C-x 1 -- leave only current window<br /> M-q -- next window<br /> M-S-9 -- shrink horizontal window<br /> M-S-0 -- enlarge horizontal window<br /> M-9 -- shrink vertical window<br /> M-0 -- enlarge vertical window<br /> <br /> Закладки:<br /> <br /> C-b -- set bookmark<br /> M-b -- jump to bookmark<br /> <br /> Системные операции:<br /> <br /> F5 -- run Emacs command<br /> C-x C-c -- close Emacs<br /> C-x b -- select buffer<br /> C-x k -- kill buffer<br /> C-c l -- truncate long lines on/off<br /> C-g -- сancel<br />
F11 -- show help for my keybindings<br /><br /> Для того, чтобы воспользоваться моим конфигом, просто скопируйте его себе в домашнюю дирректорию.<br /><br /> А вообще, конечно, очень рекомендую прочесть <a href="http://alexott.net/ru/emacs/TUTORIAL.ru">учебник</a> по Emacs в переводе Алекса Отта.<br /></div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com27tag:blogger.com,1999:blog-4007342736133040943.post-24691633147647207262013-03-01T13:26:00.002+03:002013-03-01T13:27:21.534+03:0014 марта стартуют курсы Clojure<div dir="ltr" style="text-align: left;" trbidi="on">
<style type="text/css">P { margin-bottom: 0.21cm; direction: ltr; color: rgb(0, 0, 0); }P.western { font-family: "Liberation Serif",serif; font-size: 12pt; }P.cjk { font-family: "WenQuanYi Zen Hei Sharp"; font-size: 12pt; }P.ctl { font-family: "DejaVu Sans"; font-size: 12pt; }A:link { color: rgb(0, 0, 255); }</style>
<br />
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="font-family: Calibri, serif;">Вам надоела многословность
Java или C#? Вы хотите, чтобы писать программы
было проще и быстрее? Вас достало писать
вручную одни и те же паттерны проектирования?</span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="font-family: Calibri, serif;">Решение есть! Приходите
на курсы Clojure, мы научим вас самым
современным методам программирования!
Вы будете использовать преимущества
</span><span style="font-family: Calibri, serif;"><b>функционального
подхода</b></span><span style="font-family: Calibri, serif;">, </span><span style="font-family: Calibri, serif;"><b>интерактивной
разработки и метапрограммирования</b></span><span style="font-family: Calibri, serif;">.
Мы покажем вам, как автоматизировать
программерскую рутину!</span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<br /></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;">С
</span><span style="font-family: Calibri, serif;"><b>14 марта по 18 апреля
2013г. </b></span><span style="font-family: Calibri, serif;">в офисе компании
</span><span style="font-family: Calibri, serif;"><b>ITA</b></span><span style="font-family: Calibri, serif;">
(г.Минск, проспект Независимости, 58) будут
проходить курсы программирования на
Clojure. Программа курсов включает в себя
изучение основ </span><span style="font-family: Calibri, serif;"><i>функционального</i></span><span style="font-family: Calibri, serif;">
и </span><span style="font-family: Calibri, serif;"><i>мета-программирования</i></span><span style="font-family: Calibri, serif;">
средствами языка Clojure, а также применение
Clojure для разработки современных
веб-приложений.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;">Примерный
план занятий:</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>14.03.2013
</i></span><span style="font-family: Calibri, serif;">— Установка и
настройка необходимых инструментов,
введение в Clojure и функциональное
программирование.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>21.03.2013</i></span><span style="font-family: Calibri, serif;">
— Функции высшего порядка, ленивые
вычисления, полиморфизм, STM, взаимодействие
с Java.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>28.03.2013
</i></span><span style="font-family: Calibri, serif;">— Макросы,
предметно-ориентированные языки,
генерация кода.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>04.04.2013</i></span><span style="font-family: Calibri, serif;">
— Введение в веб-стек Clojure. Ring, Compojure,
lib-noir, развертывание приложений.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>11.04.2013
</i></span><span style="font-family: Calibri, serif;">— Генерация html
(Clostache, Hiccup, Enlive), Взаимодействие с БД
(Korma).</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;"><i>18.04.2013
</i></span><span style="font-family: Calibri, serif;">— ClojureScript вместо
JavaScript.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;">Для
успешного прохождения курсов необходимо
иметь представление о Java, SQL, HTML, CSS и
JavaScript.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<a href="http://www.blogger.com/blogger.g?blogID=4007342736133040943" name="__DdeLink__1_504709992"></a>
<span style="font-family: Calibri, serif;"><span style="background: transparent;">Количество
мест на очную форму обучения ограничено.
Поэтому те, кто хочет посетить курсы
лично, должны будут выполнить тестовое
задание на алгоритмизацию. Задание
будет объявлено в группе «<a href="http://www.facebook.com/groups/clojure.course/">Clojure Course</a>»</span></span><span style="color: red;"><span style="font-family: Calibri, serif;"><span style="background: transparent;">
</span></span></span><span style="font-family: Calibri, serif;"><span style="background: transparent;">в
facebook 7 марта 2013г. Если у Вас есть желание
получить задание предварительно,
присылайте Ваши заявки на </span></span><span style="font-family: Calibri, serif;"><span lang="en-US"><span style="background: transparent;">email</span></span></span><span style="font-family: Calibri, serif;"><span style="background: transparent;">: </span></span><a href="mailto:dmitry.bushenko@ita.by"><span style="font-family: Calibri, serif;"><span lang="en-US"><span style="background: transparent;">d.bushenko@gmail.com</span></span></span><span style="font-family: Calibri, serif;"><span lang="en-US"><span style="background: transparent;"></span></span></span></a><span style="font-family: Calibri, serif;"><span style="background: transparent;">.
После успешного прохождения очной формы
курсов, слушатели получат сертификаты.</span></span></div>
<div align="JUSTIFY" class="western" style="margin-bottom: 0.35cm; text-indent: 1.25cm;">
<span style="background: transparent;"><span style="font-family: Calibri, serif;">Для
тех, кто не сможет попасть на очную форму
обучения, мы предложим вам дистанционную.
Информацию о том, как попасть на
дистанционную форму обучения мы объявим
в группе «<a href="http://www.facebook.com/groups/clojure.course/">Clojure Course</a>» в facebook 7 марта 2013г.</span></span></div>
</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com4tag:blogger.com,1999:blog-4007342736133040943.post-63713602473380984412013-02-24T21:50:00.000+03:002013-02-24T21:51:45.126+03:00Обзор Clojure на тусовке Software Craftmanship<div dir="ltr" style="text-align: left;" trbidi="on">
На днях делал презентацию языка Clojure на тусовке Software Craftmanship.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmpTVlgUO9k6K1ilFML0xYD0-370xP6lPmDXyFZlmnG2in9qApaj67kallQka8PtFQjtF2q7B_cmGKL3lJ50ZUwYlJkOUSxgX5YqLkAaZaC0LFTDQa6H5OFzlHBURH4j1UmrOHYUNzos/s1600/me.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmpTVlgUO9k6K1ilFML0xYD0-370xP6lPmDXyFZlmnG2in9qApaj67kallQka8PtFQjtF2q7B_cmGKL3lJ50ZUwYlJkOUSxgX5YqLkAaZaC0LFTDQa6H5OFzlHBURH4j1UmrOHYUNzos/s320/me.png" width="320" /></a></div>
<br />
Саму презентацию можно взять <a href="https://docs.google.com/file/d/0BzmL7xzGeOtOVFJlMk5zdmhRX2c/edit?usp=sharing">здесь</a>. Видео доклада -- <a href="http://video.yandex.by/users/d-bushenko/view/4/">здесь</a>.<br />
<br />
Были разобраны следующие темы:<br />
1. Какие причины заставляют программистов искать замену языкам промышленного программирования (Java, C#, C++, etc.).<br />
2. Что такое Clojure.<br />
3. Синтаксис Clojure.<br />
4. Макросы -- самое мощное средство метапрограммирования.<br />
5. Зачем нужны eDSL и как их пишут на Clojure.<br />
6. Когда стоит, а когда не стоит применять Clojure.<br />
7. Как продавать Clojure менеджеру и заказчику.<br />
8. Как пользоваться Clojure, если от вас все-таки требуют код на языке промышленного уровня (например, Java).</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-45663859715446520242013-01-28T14:44:00.000+03:002013-01-28T14:44:21.513+03:00Курсы Clojure -- продолжение<div dir="ltr" style="text-align: left;" trbidi="on">
Спасибо всем, кто высказался за проведение курсов. Ваше мнение имело важное значение для того, чтобы мы решились их провести.<br />
<br />
Скорее всего курсы начнутся через месяц, в последних числах февраля (это пока не точно). Еще мы планируем выдавать сертификаты о прохождении курсов тем, кто выполнит все домашние задания.<br />
<br />
С помощью Алекса Отта я составил предварительную <a href="https://docs.google.com/document/d/1qzMgbTOXEAZYYPz1zLqAFDK885pfwxeQOvw5CL1Wvx0/edit">программу курсов</a>. Опять же, это пока не официально, возможны изменения.<br />
<br />
Вообще я предлагаю всем заинтересованным обсудить программу, что-то подкорректировать, добавить-убрать. Буду очень благодарен за feedback.<br />
<br />
Побеседовав с Алексом, решил вынести из основной части программы почти всю "эзотерику" и включить её в виде бонуса (бонусы буду записывать по мере сил и возможностей). Вначале предполагалось, что метацикилческий интерпретатор мы будем тоже писать на курсах. Но раз уж я вынес его в бонус, то мою реализацию можно посмотреть <a href="https://github.com/dbushenko/clj-lisp">здесь</a>.<br />
<br />
Всем, кто хочет и может помочь мне с материалами курсов, предлагаю выбрать тему/подтему или даже какой-то совсем уж ограниченный фрагмент занятия. Пожалуйста, связывайтесь по этому поводу со мной через email: d.bushenko-at-gmail.com Имена всех, кто будет помогать мне с материалами, тоже каким-нибудь образом увековечим.</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com15tag:blogger.com,1999:blog-4007342736133040943.post-67047730943420190882013-01-25T19:33:00.000+03:002013-01-25T19:33:02.873+03:00Курсы Clojure онлайн<div dir="ltr" style="text-align: left;" trbidi="on">
Мне удалось в какой-то мере заинтересовать руководство нашей фирмы языком Clojure. Если все будет хорошо, то в ближайшее время начну курс по Clojure для сотрудников. Лекции буду записывать и выкладывать по мере возможности.<br />
<br />
Скорее всего, для всех желающих мы организуем этот же самый курс онлайн. Т.е. те же лекции (только в записи), те же задания (их я планирую проверять вручную).<br />
<br />
Если есть желающие поучаствовать (либо помочь с подбором материала, либо пройти курс) -- пишите.</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com30tag:blogger.com,1999:blog-4007342736133040943.post-1681732815955012202013-01-06T17:14:00.002+03:002013-01-06T17:14:17.310+03:00Web-разработка на Clojure<div dir="ltr" style="text-align: left;" trbidi="on">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
A:link { so-language: zxx }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Как я уже писал в своей
прошлой статье, мне удалось заинтересовать
начальство разработкой на Clojure. Поскольку
еще на предыдущей работе мне удалось
выбить разрешение на использование
Clojure в одном из проектов, можно сделать
определенные выводы.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Легче всего убедить
начальство в случае, если предстоит
небольшой fixed-price проект, на который,
предположительно, ресурсов выделено
или впритык, или меньше, чем нужно. Ваш
главный аргумент здесь – время (оно же
– деньги). Вы берете предложенную вам
оценку сроков и поясняете начальству,
что вот на этом, этом и этом этапах
(итерациях, модулях, системах и т.д.)
можно сократить сроки разработки на
30-40%, если отказаться от Java в пользу
Clojure. Любой умный начальник (ну а зачем
вам работать у глупого?) как минимум
выслушает ваше предложение о том, как
сократить сроки.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Будьте готовы ответить
на вопрос, за счет чего уменьшатся сроки.
Это самый шаткий момент, здесь нужна
максимальная предметность. Не стоит
рассказывать о вредности мутабельного
состояния или славном прошлом лиспа.
Идеальный вариант – на примерах показать
насколько меньше в Clojure различных
церемоний, а также насколько проще в
Clojure автоматизировать всю ручную работу,
привычную Java-программистам.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
В этот раз мне повезло
сыграть на контрасте. Во-первых, я
показал, насколько проще в Clojure работать
с REST-сервисами, по сравнению с “правильным”
способом на Java – библиотекой JAX-RS.
Во-вторых, я немного рассказал о применении
макросов для автоматизации, а также о
том, насколько просто получить доступ
к данным из БД (на примере библиотеки
Korma).</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Следующий вопрос,
который необходимо будет решить – кто
будет поддерживать ваш исходник, ведь
Clojure кроме вас никто и не знает. Я предложил
провести небольшой мастер-класс по
Clojure, чтобы научить людей хотя бы читать
и немного понимать Clojure-код. Главное
здесь – избавить коллектив от страха
перед скобочками. Кроме того, я раздумываю
над тем, чтобы написать небольшую
методичку, максимально приближенную к
практике, о применении Clojure в web-разработке.
Алекс Отт написал введение в сам язык
(<a href="http://alexott.net/ru/clojure/clojure-intro/">http://alexott.net/ru/clojure/clojure-intro/</a>),
но я думаю, есть смысл добавить описание
популярных библиотек, а также рассказать
о распространенных приемах разработки
на Clojure.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Само собой, не рекомендую
ввязываться во всю эту авантюру, если
не опробовали Clojure раньше в хобби-проектах.
Крах будет фатальным как для вас, так и
для репутации Clojure на вашей фирме.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Помимо текущего, у меня
уже есть один коммерческий проект на
Clojure в продакшене и два хобби-проекта,
поработавшие в продакшене какое-то
время. Но последний из них я писал в
сентябре 2012, с тех пор кое-что поменялось.
Ниже я расскажу, что, с моей точки зрения,
изменилось, а также о том, какие грабли
бывают с Clojure.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Самое заметное изменение
– смерть микро-фреймворка Noir. Как
известно, Крис Гранжер собрал на
Kistsarter-е нужную сумму для своего стартапа
и сейчас во всю пилит экспериментальную
супер-модерновую IDE LightTable. Поэтому на
веб-фреймворк Noir и ORM Korma времени у него
не осталось. Общими усилиями выпустили
версию Noir 1.3, и на этом развитие фреймворка
прекратилось.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
К счастью, теперешний
мейнтейнер Noir – Raynes (Энтони Гримс) –
исхитрился разделить этот фреймворк
на две части: сам Noir и библиотеку lib-noir.
Так вот, библиотека lib-noir, которая содержит
полюбившиеся всем “батарейки”, жива
и развивается. Теперь её используют в
связке с Compojure.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://secure.gravatar.com/avatar/54222b6321f0504e0a312c24e97adfc1?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://secure.gravatar.com/avatar/54222b6321f0504e0a312c24e97adfc1?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" width="200" /></a></div>
<div style="margin-bottom: 0in; text-align: center;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
A:link { so-language: zxx }
--</style>Энтони Гримс</div>
<div style="margin-bottom: 0in; text-align: center;">
<br /></div>
<div style="margin-bottom: 0in;">
Библиотеку Korma постигла
более счастливая судьба. Пару недель
назад в чате #clojure на irc.freenode.net Алекс
Барановский предложил взять развитие
Korma в свои руки. Ему дали права мейнтейнера,
и теперь он по-стахановски постит в
гитхаб коммит за коммитом. Пару дней
назад Алекс опубликовал новую версию
Korma – 13-ю бету, которая вышла вслед за
11-й спустя несколько месяцев.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://secure.gravatar.com/avatar/7d2cc799db6f5916a1ca4df1259d7d32?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://secure.gravatar.com/avatar/7d2cc799db6f5916a1ca4df1259d7d32?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" width="200" /></a></div>
<div style="margin-bottom: 0in; text-align: center;">
Алекс Барановский</div>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
Compojure и Enlive ничем
особенным не отметились за последние
месяцы. Зато вышло несколько бет
библиотеки Enfocus (ClojureScript-версии Enlive).
Автор Enfocus Крейг Киркендалл полностью
переработал архитектуру библиотеки. И
теперь, в 1.0 версиях, в отличие от 0.9, для
доступа к элементам DOM используется
библиотека Domina. В целом, 1.0-версия Enfocus
лучше и стабильнее 0.9 несмотря на
значительные архитектурные изменения.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://secure.gravatar.com/avatar/c29cd3a5f182e6de85cbd172fb9b5ab8?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://secure.gravatar.com/avatar/c29cd3a5f182e6de85cbd172fb9b5ab8?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" width="200" /></a></div>
<div style="text-align: center;">
Крейг Киркендалл</div>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
A:link { so-language: zxx }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Ну и пара слов о личных
впечатлениях. Когда разрабатываешь
Single-Page Application на Clojure, наиважнейшее
значение приобретают unit-тесты. В
jvm-версии Clojure все не так печально.
Худо-бедно, стектрейс вам подскажет где
и что отвалилось, хотя и здесь функциональное
тестирование лишним не будет (статической
типизации-то нету). Но в ClojureScript типизация
настолько слабая, что на определенном
этапе это может даже быть неудобным.
Сизифов труд вам покажется лёгким и
осмысленным по сравнению с рефакторингом
работающего ClojureScript-приложения. Компилятор
почти ни в чем вам не поможет, и основной
способ докопаться до сути – как-то
запустить засбоивший фрагмент, найти
поломанную функцию и долго медитировать
над её исходником.
</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Чтобы рефакторинг
ClojureScript-кода был менее болезненным,
необходимо писать юнит-тесты. Причем,
юнит-тестами нужно проверять не столько
логику, сколько количество полученных
параметров и даже наличие вызываемой
функции впринципе (возможно вы все
переименовали, но до сих пор где-то
сохранился код, вызывающий старые,
несуществующие функции).</div>
<div style="margin-bottom: 0in;">
Юнит-тесты на ClojureScript
– это, пока что, пичалька. ClojureScript –
инструмент молодой, развивающийся. На
сегодняшнем этапе нет устоявшейся
практики правильного написания
юнит-тестов. Местами можно наткнуться
на использование библиотеки PhantomJS. Но,
признаюсь честно, мне не удалось найти
толкового руководство по тому, как её
завести. Да и у самого ничего не получилось.
Поэтому, в моих проектах юнит-тесты
ClojureScript достаточно кустарные.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Завершить обзор хотелось
бы на приятной ноте. В 2012-м году вышли
еще две очень классные книжки по Clojure.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Очень полезная и
практическая книжка Clojure Programming. Помимо
самого языка, в ней есть обзор основных
популярных библиотек и практик разработки
на Clojure.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://akamaicovers.oreilly.com/images/0636920013754/cat.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://akamaicovers.oreilly.com/images/0636920013754/cat.gif" /></a></div>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
A:link { so-language: zxx }
-->
</style>
<div style="margin-bottom: 0in;">
И вторая, но единственная
в своем роде, – ClojureScript: up and running. В ней
всего 116 страницы, тянет на крупный
туториал. Зато все по делу, без воды.
Подробно рассказано, как завести
ClojureScript и как разрабатывать на нем
приложения.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://akamaicovers.oreilly.com/images/0636920025139/cat.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://akamaicovers.oreilly.com/images/0636920025139/cat.gif" /></a></div>
<br />
</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-90824482384052693602012-12-27T23:00:00.001+03:002012-12-27T23:00:06.775+03:00Clojure пропаганда<div dir="ltr" style="text-align: left;" trbidi="on">
Недавно предложил директору фирмы, где с недавних пор работаю, использовать Clojure для новых проектов. Не целиком, конечно, писать весь проект на Clojure (у нас на такой радикальный шаг еще не готовы). Но хотя бы часть функционала, например -- бизнес-логику.<br />
А для того, чтобы убедить директора, что Clojure кое-в-чем лучше Java, я написал небольшой <a href="https://docs.google.com/document/d/1WWiUEhIfumv_RQfBFZBvIDriUY1hd1NREh698oq-7hY/edit">документ</a> для сравнения плюсов и минусов от применения кложуры. Возможно, пригодится и вам.<br />
Если есть идеи, что можно добавить или исправить, -- прошу в комменты.</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com11tag:blogger.com,1999:blog-4007342736133040943.post-48131971447741867372012-09-17T11:33:00.000+03:002012-09-17T11:33:14.376+03:00Emacs Lisp Tutorial<div dir="ltr" style="text-align: left;" trbidi="on">
Недавно один товарищ опубликовал <a href="http://cjohansen.no/an-introduction-to-elisp">учебник</a> по Emacs Lisp-у. Неплохой, надо сказать, туториал, жаль что на английском. Что-то похожее мы написали с Алексом Оттом почти год назад. Наш учебник по Emacs Lisp-у можно найти <a href="https://docs.google.com/open?id=0BzmL7xzGeOtOOWE1ZTc3NzAtZjkyNy00ZDU5LTg2Y2UtZDc4MmNkNTI1ZmJl">здесь</a>.</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com0tag:blogger.com,1999:blog-4007342736133040943.post-15737175082767003102012-08-24T13:14:00.000+03:002012-08-24T13:14:10.545+03:00Swank Clojure заброшен, да здравствует nrepl.el!<div dir="ltr" style="text-align: left;" trbidi="on">
На днях главный мейнтейнер swank-clojure Фил Хагельберг <a href="https://groups.google.com/forum/?fromgroups=&hl=ru#%21topic/clojure/xzvI_kCaeNI">объявил</a> о прекращении этого проекта. Вместо поддержки этого устаревшего проекта он объединяется с разработчиками <a href="https://github.com/kingtim/nrepl.el">nrepl.el</a>.<br />
<br />
Обычно такие новости не приносят ничего хорошего, т.к. нужно обновлять зависимости, пытаться запустить новую библиотеку, пройтись по полю с граблями. Но не в этот раз. Просто установив nrepl.el через пакетный менеджер емакса и запустив команду nrepl-jack-in (вместо ставшей уже привычной clojure-jack-in), оказалось, что я получил практически такой же REPL с теми же самыми keybinding-ами.<br />
<br />
Вообще, на мой взгляд, в последний год в развитии Clojure наметился очень позитивный тренд: заявленные возможности, библиотеки и инструменты работают с полпинка, безо всякой магии. Интересно, встанет ли Clojure в один ряд с таким мейнстримными языками, как ruby или python? Все-таки, инфраструктура Clojure уже довольно хороша и даже относительно user-friendly.</div>
Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-71381562864954138292012-08-10T15:17:00.000+03:002012-08-10T15:18:44.467+03:00Как я провел лето<div dir="ltr" style="text-align: left;" trbidi="on">
Ну, не все лето, конечно, а отпуск. В отпуске мне удалось посвятить много времени любимым занятиям: изучению новых технологий и программированию. Наибольшее количество времени я посвятил ClojureScript, а результатом его изучения стало небольшое приложение учета посещений в танцевальном клубе (или фитнесс-клубе).<br />
<br />
Вот как выглядит интерфейс этого приложения:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_NUbAy46no15xZAnMOWVcL2D6i3oAp-89J5uMlkoVPveXsLpnHq7oDecjEyVYhjSJyq_wNgDDaC8yFIriRCSWdI-v1I4kgpkyMl4A3ttSRFlbzn49i97D0c1Q70piD5R_dZSDiLYYQIo/s1600/01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_NUbAy46no15xZAnMOWVcL2D6i3oAp-89J5uMlkoVPveXsLpnHq7oDecjEyVYhjSJyq_wNgDDaC8yFIriRCSWdI-v1I4kgpkyMl4A3ttSRFlbzn49i97D0c1Q70piD5R_dZSDiLYYQIo/s640/01.png" width="640" /></a></div>
<div style="text-align: center;">
Рис.1 Список абонементов</div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQl5ToBZ0Ab1Mecawp3zo4P05uIZBRXHdHeqHzNlJkBh5m1A4lm1em4f8MMoQGw4dV51w26bmnCzJpBxigXpJ5tRSj5BdAi_OUhdycVbyWssldZ-HI4DBgsIOO86Tp70c0lt6zMtz5aL4/s1600/02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQl5ToBZ0Ab1Mecawp3zo4P05uIZBRXHdHeqHzNlJkBh5m1A4lm1em4f8MMoQGw4dV51w26bmnCzJpBxigXpJ5tRSj5BdAi_OUhdycVbyWssldZ-HI4DBgsIOO86Tp70c0lt6zMtz5aL4/s640/02.png" width="640" /></a></div>
<div style="text-align: center;">
Рис. 2 Детали абонемента</div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyrtdH6QmYf3jqe6d8ZX3QBziEgP-ol-8mt1mXn8b4C0tmo13sd5bQvRIpxBpgsVj2Bj6YE8nckszdyyHJ7h9fh_Ztqskiu_tv7K3bHINuSFZE-xLKQEitjJJZAw9gIeg8lT_XdrRtqEE/s1600/03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyrtdH6QmYf3jqe6d8ZX3QBziEgP-ol-8mt1mXn8b4C0tmo13sd5bQvRIpxBpgsVj2Bj6YE8nckszdyyHJ7h9fh_Ztqskiu_tv7K3bHINuSFZE-xLKQEitjJJZAw9gIeg8lT_XdrRtqEE/s640/03.png" width="640" /></a></div>
<div style="text-align: center;">
Рис. 3 Выбран абонемент Чебурашки</div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq7w9k19iPEdNNULFe459nju58ukZp0VEAy9kDKBmgcq7fRI7JL_Jt-u0yAM9mdOaDSF3vaG_v6YxTqUixlXSyuk5EjE7LtS7scPItaYI6uLmP94TYiaYCWBQIYgJpLSjmPU6i06Xz7bY/s1600/04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq7w9k19iPEdNNULFe459nju58ukZp0VEAy9kDKBmgcq7fRI7JL_Jt-u0yAM9mdOaDSF3vaG_v6YxTqUixlXSyuk5EjE7LtS7scPItaYI6uLmP94TYiaYCWBQIYgJpLSjmPU6i06Xz7bY/s640/04.png" width="640" /></a></div>
<div style="text-align: center;">
Рис. 4 Отчет по оплатам за месяц</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: justify;">
На данный момент приложение задеплоено по адресу: http://clubmanager1.com:8080/login . Логин и пароль такие: demo, demo123. (Оперу и IE ниже 9 не подериживаю)</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
Как видите, приложение -- достаточно простое, чтобы его можно было не сильно напрягаясь написать за отпуск. Но и достаточно большое, чтобы опробовать ClojureScript на деле.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Я использовал следующие библиотеки:</div>
<div style="text-align: left;">
[org.clojure/clojure "1.4.0"]<br />
[noir "1.3.0-beta10"]<br />
[com.mysql/connectorj "5.1.12"]<br />
[korma "0.3.0-beta11"]<br />
[cheshire "4.0.1"]<br />
[enlive "1.0.1"]<br />
[enfocus "0.9.1-SNAPSHOT"]</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Изначально, для передачи данных из броузера на сервер я использовал JSON, вот почему здесь есть cheshire. Но через некоторое время (и после мощнейшего фейс-палма) обнаружил работающий ридер в ClojureScript, так что оставшиеся данные я передавал уже безо всякого json-а, прямо в виде списков, векторов и мапов Clojure.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Для отрисовки шаблонов изначально использовал Enlive. На клиентской стороне -- порт Enlive-а на ClojureScript, который называется Enfocus.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
В общем и целом -- впечатления очень положительные. Серьезных технологических затыков или багов нигде не обнаружил. Правда Enfocus сейчас активно обновляется до версии 1.0.0-SNAPSHOT, но этот снапшот глючный. Автор enfocus-а срочно правит баги, так что скоро увидим и стабильную версию. Версия Enfocus 0.9.1 -- достаточно стабильна и очень похожа на Enlive.</div>
</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com6tag:blogger.com,1999:blog-4007342736133040943.post-13407800535952389232012-07-13T11:57:00.001+03:002012-07-13T11:57:27.773+03:00MIT 6.001 читали не тем студентам...<div dir="ltr" style="text-align: left;" trbidi="on">
Интереснейшее <a href="http://www.quora.com/What-is-the-difference-between-MITs-new-6-01-course-based-on-Python-and-the-previous-6-001-course-based-on-Scheme">обсуждение</a> нашел по поводу замены 6.001 на 6.01 в MIT, связанный с отказом от Scheme и переходом на Python. Оказывается, дело не в устаревании курса, такое устареть не может. И не в запредельной хардкороности материала. Все банально и обидно: курс читали электронщикам, которые интересовались больше как построить роботов, нежели красивое и надежное ПО. В новом курсе 6.01, где уже используется Python, материал во многом излагается через применение программирования в робототехнике, что, конечно же, больше подходит для электронщиков.</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com5tag:blogger.com,1999:blog-4007342736133040943.post-47777009275938610402012-06-20T14:21:00.000+03:002012-06-20T14:21:50.573+03:00emacs-template.el<div dir="ltr" style="text-align: left;" trbidi="on">
На днях запилил для себя <a href="https://github.com/dbushenko/emacs-template">движок темплейтов</a> для emacs lisp-а в стиле <a href="http://mustache.github.com/">Mustache</a>. Использую его для генерации java-кода. Может кому еще пригодится, так что опубликовал его на GitHub.<br />
<br />
Велосипед решил пилить потому что CEDET/SRecode оказался для меня слишком сложным в освоении и использовании.</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com4tag:blogger.com,1999:blog-4007342736133040943.post-49682015499893025112012-06-12T15:05:00.000+03:002012-06-12T15:05:03.118+03:00Мой DSL для проектов на Android<div dir="ltr" style="text-align: left;" trbidi="on">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Пару месяцев назад
перепала мне задачка пилить в одиночку
довольно объемный проект на Android. Сам
по себе проект был несложный, но количество
повторяющегося кода зашкаливало. При
том самое обидное, что стандартными
средствами как-то избавиться от повторений
не представляется возможным. Тогда я и
наваял первоначальную версию небольшой
<a href="https://github.com/dbushenko/adsl">генерилки андроид-кода</a>.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Больше всего, конечно,
в андроиде меня раздражает xml. Логично,
что формочки лучше описывать в
декларативном стиле; понятно, что java
для этого не годится; разумеется, xml
здесь уместен. Но, блин, как же отстойно
все это выглядит! Элементарного цикла
в нем не сделать, а уж про реюзабельность
вообще молчу. Она там, конечно, есть в
виде подключаемых пользовательских
компонентов. Но все эти механизмы страшно
сосут даже по сравнению с инструментами
разработки под iPhone!</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Самое главное, чего я
хотел добиться созданием своего DSL, –
это избавиться от богомерзкого xml. Я
<span lang="ru-RU">хотел</span> описывать UI в
декларативном стиле на языке общего
назначения. Оказалось, что на Clojure – это
очень даже просто, достаточно
воспользоваться библиотекой hiccup.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Например, вместо этого </div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;"><</span>Button android<span style="color: purple;">:</span>id<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">@+id/helloWorldBtn</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>background<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">@drawable/blue_button_slt</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>layout_height<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">wrap_content</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>layout_width<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">wrap_content</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>text<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">Push me!</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>textColor<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">#fff</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>textSize<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">10dp</span><span style="color: maroon;">"</span>
<span style="color: #e34adc;"> android:</span>textStyle<span style="color: #808030;">=</span><span style="color: maroon;">"</span><span style="color: #0000e6;">bold</span><span style="color: maroon;">"</span><span style="color: #808030;">/</span><span style="color: #808030;">></span></pre>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
писать такой код: </div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<br /><pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">Button</span> <span style="color: #0000e6;">"helloWorldBtn"</span> wc wc
<span style="color: #808030;">[</span><span style="color: #400000;">:text</span> <span style="color: #0000e6;">"Push me!"</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#fff"</span>
<span style="color: #400000;">:textSize</span> <span style="color: #0000e6;">"10dp"</span>
<span style="color: #400000;">:textStyle</span> <span style="color: #0000e6;">"bold"</span>
<span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> <span style="color: #0000e6;">"blue_button_slt"</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span></pre>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
На данном этапе выигрыша
в коде почти никакого нет. Зато уже можно
сделать, например, так:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> large-button <span style="color: #808030;">[</span>id attributes image <span style="color: #808030;">&</span> <span style="color: #808030;">[</span> txt<span style="color: #808030;">]</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">VLayout</span> <span style="color: #7d0045;">nil</span> wc wc <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">conj</span> attributes <span style="color: #400000;">:gravity</span> <span style="color: #0000e6;">"center"</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">ImageButton</span> id <span style="color: #0000e6;">"60dp"</span> <span style="color: #0000e6;">"70dp"</span>
<span style="color: #808030;">[</span><span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> image<span style="color: #808030;">)</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">if-not</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">empty?</span> txt<span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">TextView</span> <span style="color: #7d0045;">nil</span> <span style="color: #0000e6;">"125dp"</span> <span style="color: #0000e6;">"18dp"</span>
<span style="color: #808030;">[</span><span style="color: #400000;">:text</span> txt
<span style="color: #400000;">:textSize</span> <span style="color: #0000e6;">"16dp"</span>
<span style="color: #400000;">:gravity</span> <span style="color: #0000e6;">"center"</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#0096c1"</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
</pre>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Как видите, введение
языка общего назначения вместо xml
позволяет легко создавать функции.
Напротив, создание реюзабельного
компонента под андроид – процедура
более долгая и трудоемкая. Еще очень
удобно использовать if-ы, loop-ы и прочие
возможности языка. Кроме того, код на
Clojure писать проще, ведь можно заранее
задать для компонентов сокращенные
варианты атрибутов либо рациональные
настройки. Например, в приведенном выше
исходнике wc означает "wrap_content".</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Кроме генерации xml на
основе приведенного выше примере, можно
сгенерировать и код на java. Это позволит
автоматизировать многие, часто повторяемые
операции.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Рассмотрим пример формы
с текстовой строкой и двумя кнопками.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> layout <span style="color: #808030;">[</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">html</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">VLayoutFP</span> <span style="color: #7d0045;">nil</span> <span style="color: #808030;">[</span><span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> <span style="color: #0000e6;">"home_bgr"</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">HLayoutFP</span> <span style="color: #7d0045;">nil</span> <span style="color: #808030;">[</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">VLayoutFP</span> <span style="color: #7d0045;">nil</span> <span style="color: #808030;">[</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">TextView</span> <span style="color: #7d0045;">nil</span> wc wc <span style="color: #808030;">[</span><span style="color: #400000;">:text</span> <span style="color: #0000e6;">"Demo text"</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#09bbaa"</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">Button</span> <span style="color: #0000e6;">"helloWorldBtn"</span> wc wc <span style="color: #808030;">[</span><span style="color: #400000;">:text</span> <span style="color: #0000e6;">"Push me!"</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#fff"</span>
<span style="color: #400000;">:textSize</span> <span style="color: #0000e6;">"10dp"</span>
<span style="color: #400000;">:textStyle</span> <span style="color: #0000e6;">"bold"</span>
<span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> <span style="color: #0000e6;">"blue_button_slt"</span><span style="color: #808030;">)</span>
<span style="color: #400000;">:code</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">->java</span> <span style="color: #808030;">{</span><span style="color: #808030;">}</span>
<span style="color: #808030;"> (</span><span style="color: maroon; font-weight: bold;">def</span> <span style="color: #808030;">^</span><span style="color: #400000;">:String</span> hello <span style="color: #0000e6;">"Hello"</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">def</span> world <span style="color: #0000e6;">"world"</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">System.out.println</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">+</span> hello <span style="color: #0000e6;">" "</span> world<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">Button</span> <span style="color: #0000e6;">"demoBtn"</span> wc wc <span style="color: #808030;">[</span><span style="color: #400000;">:text</span> <span style="color: #0000e6;">"Push me!"</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#fff"</span>
<span style="color: #400000;">:textSize</span> <span style="color: #0000e6;">"10dp"</span>
<span style="color: #400000;">:textStyle</span> <span style="color: #0000e6;">"bold"</span>
<span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> <span style="color: #0000e6;">"blue_button_slt"</span><span style="color: #808030;">)</span>
<span style="color: #400000;">:code</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">->java</span> <span style="color: #808030;">{</span><span style="color: #808030;">}</span> <span style="color: #808030;"> </span></pre>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: #808030;"> (</span><span style="color: maroon; font-weight: bold;">startActivity</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">new</span> android<span style="color: #808030;">.</span>content<span style="color: #808030;">.</span>Intent this OrderScreen<span style="color: #808030;">.</span>class<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span></pre>
<div style="margin-bottom: 0in;">
<br /></div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">generate-activity</span> layout <span style="color: #0000e6;">"com.adsl.demo"</span> <span style="color: #0000e6;">"MyDemoActivity"</span><span style="color: #808030;">)</span></pre>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Будет создана связка
файлов MVC. Генерятся классы
MyDemoActivityModel, MyDemoActivityView, MyDemoActivityController и
связываются таким образом, что они сразу
готовы к использованию как единая
сущность. Будет создана заготовка
метода, который вызывается во вьюхе
при изменении модели.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">public</span> void update(Observable modelObj, Object arg) <span style="color: purple;">{</span>
initialize<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
MyDemoActivityModel model <span style="color: #808030;">=</span> <span style="color: #808030;">(</span>MyDemoActivityModel<span style="color: #808030;">)</span>modelObj<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
В классе MyDemoActivityView
будут созданы атрибуты:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">private</span> Button _helloWorldBtn<span style="color: #808030;">;</span>
<span style="color: maroon; font-weight: bold;">private</span> Button _demoBtn<span style="color: #808030;">;</span></pre>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Позже они инициализируются
таким образом:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;">_helloWorldBtn = (Button) findViewById(R.id.helloWorldBtn)<span style="color: #808030;">;</span>
_demoBtn = (Button) findViewById(R.id.demoBtn)<span style="color: #808030;">;</span></pre>
<br />
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
Кнопкам назначается
листенер, который вызывает соответствующий
метод контроллера:<br />
<br />
<pre style="background: #ffffff; color: black;">View.OnClickListener listener = new View.OnClickListener() <span style="color: purple;">{</span>
<span style="color: #808030;">@</span>Override
<span style="color: maroon; font-weight: bold;">public</span> <span style="color: #bb7977;">void</span> onClick<span style="color: #808030;">(</span>View v<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>v<span style="color: #808030;">.</span>equals<span style="color: #808030;">(</span>_helloWorldBtn<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
controller<span style="color: #808030;">.</span>helloWorldBtnActionPerformed<span style="color: #808030;">(</span>MyDemoActivityView<span style="color: #808030;">.</span><span style="color: maroon; font-weight: bold;">this</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>v<span style="color: #808030;">.</span>equals<span style="color: #808030;">(</span>_demoBtn<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
controller<span style="color: #808030;">.</span>demoBtnActionPerformed<span style="color: #808030;">(</span>MyDemoActivityView<span style="color: #808030;">.</span><span style="color: maroon; font-weight: bold;">this</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span><span style="color: #808030;">;</span>
_helloWorldBtn.setOnClickListener(listener)<span style="color: #808030;">;</span>
_demoBtn.setOnClickListener(listener)<span style="color: #808030;">;</span> </pre>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Еще одна забавная штука,
которую я добавил скорее по приколу, –
это компиляция из моего псевдо-лиспа в
java. Кнопке можно задать атрибут :code и
прописать туда такое выражение:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">->java</span> <span style="color: #808030;">{</span><span style="color: #808030;">}</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">def</span> <span style="color: #808030;">^</span><span style="color: #400000;">:String</span> hello <span style="color: #0000e6;">"Hello"</span><span style="color: #808030;">)</span> <span style="color: dimgrey;">; Объявить стринговую переменную</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">def</span> world <span style="color: #0000e6;">"world"</span><span style="color: #808030;">)</span> <span style="color: dimgrey;">; Объявить нетипизированную переменную</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">System.out.println</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">+</span> hello <span style="color: #0000e6;">" "</span> world<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: dimgrey;">; Вызвать функцию с параметрами</span></pre>
<div style="margin-bottom: 0in;">
</div>
<br />
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Генератор преобразует
эти выражения в java-код и вставит его в
тело метода:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">public</span> void helloWorldBtnActionPerformed(MyDemoActivityView view) <span style="color: purple;">{</span>
<span style="color: #bb7977; font-weight: bold;">String</span> hello <span style="color: #808030;">=</span> <span style="color: #0000e6;">"Hello"</span><span style="color: purple;">;</span>
<span style="color: #bb7977; font-weight: bold;">Object</span> world <span style="color: #808030;">=</span> <span style="color: #0000e6;">"world"</span><span style="color: purple;">;</span>
<span style="color: #bb7977; font-weight: bold;">System</span><span style="color: #808030;">.</span>out<span style="color: #808030;">.</span>println<span style="color: #808030;">(</span>hello <span style="color: #808030;">+</span> <span style="color: #0000e6;">" "</span> <span style="color: #808030;">+</span> world<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: purple;"> </span></pre>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Вообще, такой транслятор
с псевдо-лиспа в java мне понадобился в
основном затем, чтобы можно было по
нажатию кнопки сделать простую
инициализацию объекта либо показать
другой активити.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Например:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">->java</span> <span style="color: #808030;">{</span><span style="color: #808030;">}</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">startActivity</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">new</span> android<span style="color: #808030;">.</span>content<span style="color: #808030;">.</span>Intent this OrderScreen<span style="color: #808030;">.</span>class<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span></pre>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: #808030;"> </span></pre>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
На выходе будет:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">public</span> void demoBtnActionPerformed(MyDemoActivityView view) <span style="color: purple;">{</span>
startActivity<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">new</span> android<span style="color: #808030;">.</span>content<span style="color: #808030;">.</span>Intent<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">this</span><span style="color: #808030;">,</span> OrderScreen<span style="color: #808030;">.</span>class<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span> </pre>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
Сам транслятор содержится
в файле clj2java.clj, а главная функция выглядит
так: <br />
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> process
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">[data</span> raw-code<span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">let</span> <span style="color: #808030;">[</span>code <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">enrich-with-data</span> data raw-code<span style="color: #808030;">)</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">list?</span> code<span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">let</span> <span style="color: #808030;">[</span>elem1 <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">first</span> code<span style="color: #808030;">)</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">cond</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'def</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-definition</span> code<span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'defn</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-func-definition</span> code<span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'do</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-command-sequence</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'set</span><span style="color: #808030;">!</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-assignment</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'if</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'return</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-return</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'-</span><span style="color: #808030;">></span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-method-call</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'lambda-interface</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-lambda-interface</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'lambda</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-lambda</span> code<span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'call</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-call</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'loop</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-loop</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> elem1 <span style="color: purple;">'new</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-new</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">next</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #400000;">:else</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-function-call</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process-single-value</span> code<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">[raw-code]</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">process</span> <span style="color: #808030;">{</span><span style="color: #808030;">}</span> raw-code<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span></pre>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"></pre>
<div style="margin-bottom: 0in;">
Как видите, транслятор
умеет не так уж и мало и позволяет писать
более краткий, по сравнению с java, код.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Сам же язык описания
интерфейсов построен с применением
барьеров абстракции, как об этом
рассказывается в SICP и On Lisp. Весь код
содержится в файле util.clj.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
На нижнем уровне
абстракции находятся различные служебные
функции и ядро всех будущих компонентов
– функция виджет: </div>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style><pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> widget <span style="color: #808030;">[</span>type id width height params-raw <span style="color: #808030;">&</span> more<span style="color: #808030;">]</span> <span style="color: #808030;">.</span> <span style="color: #808030;">.</span> <span style="color: #808030;">.</span> <span style="color: #808030;">)</span> </pre>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Все прочие виджеты
создаются на основе этой функции,
например, так:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> Button <span style="color: #808030;">[</span>id width height <span style="color: #808030;">&</span> <span style="color: #808030;">[</span>params<span style="color: #808030;">]</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">widget</span> <span style="color: #0000e6;">"Button"</span> id width height <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">apply</span> android params<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> HLayoutWC <span style="color: #808030;">[</span>id <span style="color: #808030;">&</span> <span style="color: #808030;">[</span>params <span style="color: #808030;">&</span> more<span style="color: #808030;">]</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">widget</span> <span style="color: #0000e6;">"LinearLayout"</span> id <span style="color: #0000e6;">"wrap_content"</span> <span style="color: #0000e6;">"wrap_content"</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">merge</span> <span style="color: #808030;">{</span><span style="color: #0000e6;">"xmlns:android"</span> <span style="color: #0000e6;">"http://schemas.android.com/apk/res/android"</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">andr-attr</span> <span style="color: #0000e6;">"orientation"</span><span style="color: #808030;">)</span> <span style="color: #0000e6;">"horizontal"</span><span style="color: #808030;">}</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">apply</span> android params<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
more<span style="color: #808030;">)</span><span style="color: #808030;">)</span></pre>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: #808030;"></span>
</pre>
<div style="margin-bottom: 0in;">
Верхний уровень
абстракции – кастомные виджеты и
хелперы, которые создаются на основе
стандартных виджетов. Например, синяя
кнопка выглядит так:<span style="color: #808030;"> </span></div>
<div style="margin-bottom: 0in;">
<span style="color: #808030;"> </span></div>
<div style="margin-bottom: 0in;">
</div>
<pre style="background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> BlueButton <span style="color: #808030;">[</span>id width height <span style="color: #808030;">&</span> <span style="color: #808030;">[</span>params<span style="color: #808030;">]</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">Button</span> id width height <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">flatten</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">conj</span> params <span style="color: #808030;">[</span><span style="color: #400000;">:background</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drawable</span> <span style="color: #0000e6;">"blue_button_slt"</span><span style="color: #808030;">)</span>
<span style="color: #400000;">:textColor</span> <span style="color: #0000e6;">"#fff"</span>
<span style="color: #400000;">:textSize</span> <span style="color: #0000e6;">"10dp"</span>
<span style="color: #400000;">:textStyle</span> <span style="color: #0000e6;">"bold"</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> </pre>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
А хелперы для отступа
выглядят так:</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> padding-top <span style="color: #808030;">[</span>top <span style="color: #808030;">&</span> more<span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">HLayout</span> <span style="color: #7d0045;">nil</span> wc wc <span style="color: #808030;">[</span><span style="color: #400000;">:paddingTop</span> top<span style="color: #808030;">]</span>
more<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> padding-left <span style="color: #808030;">[</span>left <span style="color: #808030;">&</span> more<span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">HLayout</span> <span style="color: #7d0045;">nil</span> wc wc <span style="color: #808030;">[</span><span style="color: #400000;">:paddingLeft</span> left<span style="color: #808030;">]</span>
more<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
</pre>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Саму генерилку описывать
особо нет смысла, т.к. она достаточно
простая.</div>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Плюсы подхода:</div>
<ol>
<li><div style="margin-bottom: 0in;">
Сокращение кода
описания активити в три-четыре раза.
Чем больше и сложнее описываемая форма
– тем больше выигрыш.</div>
</li>
<li><div style="margin-bottom: 0in;">
Гораздо более простой
способ создания абстракций при помощи
обыкновенных функций. В результате
значительно повышается реюзабельность
кода.</div>
</li>
<li><div style="margin-bottom: 0in;">
Значительно облегчается
чтение и модификация кода UI по сравнению
с использованием xml.</div>
</li>
</ol>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Минусы:</div>
<ol>
<li><div style="margin-bottom: 0in;">
Сгенеренный код
выглядит, порой, написанным copy-paste-ом.</div>
</li>
<li><div style="margin-bottom: 0in;">
При перегенерации
кода теряется код, написанный вручную
в сгенеренных ранее файлах.</div>
</li>
</ol>
<div style="margin-bottom: 0in;">
<br />
</div>
<div style="margin-bottom: 0in;">
Вообще, я с минусами
придумал бороться следующим образом.
Во-первых, не лазить в сгенеренные файлы
вообще. Если нужно добавить тело метода
или еще какую функциональность, то нужно
наследоваться от сгенеренных классов.
Это избавляет от необходимости переносить
вручную набитый код в новую версию
нагенеренных классов. Кроме того, раз
не нужно больше лазить в сгенеренные
классы, то отпадает требование о красоте
их кода, и автоматический copy-paste становится
допустимым. Главное, чтобы его не было
на уровне самого DSL.</div>
</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-79100712357751867982012-05-26T11:59:00.000+03:002012-05-26T12:03:59.838+03:00Впечатления о ClojureScript<div dir="ltr" style="text-align: left;" trbidi="on">
На прошлой неделе решил наконец ознакомиться с ClojureScript по-подробнее. До этого я смотрел презентацию Рича Хикки и некоторые другие скринкасты, успел "пощупать" ClojureScriptOne и почитать пару статей о том, как он (ClojureScript) крут. В общем, никаких особых сложностей с ним я не ожидал, поэтому начавшиеся приключения стали для меня откровенным сюрпризом.<br />
<br />
Во-первых, мне не удалось создать browser-connected repl, соединенный со своей html-иной. Я выполнял простые туториалы шаг за шагом, и на этапе соединения у меня ничего не получалось. Тогда я запустил проект samples/repl, входящий в поставку ClojureScript. И все было хорошо до тех пор, пока я не попытался соединиться с веб-страницей. В результате, при помощи Дэвида Нолена и еще нескольких разработчиков ClojureScript выяснилось, что документация чуть-чуть не соответствует. После моих гневных отзывов её (документацию) наконец поправили, а я напишу здесь русский вариант туториала о том, как начать работать с ClojureScript.<br />
<br />
1. Качаем с <a href="https://github.com/clojure/clojurescript">гитхаба</a> последнюю версию ClojureScript.<br />
2. Настраиваем emacs:<br />
a) Создаем файл browser-repl.sh с правами на выполнение:<br />
#!/bin/sh<br />
<br />
if [ "$CLOJURESCRIPT_HOME" = "" ]; then<br />
CLOJURESCRIPT_HOME="`dirname $0`/.."<br />
fi<br />
<br />
CLJSC_CP=''<br />
for next in lib/: lib/*: src/clj: src/cljs: test/cljs; do<br />
CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next<br />
done<br />
<br />
java -server -cp $CLJSC_CP clojure.main -e \<br />
"(require '[cljs.repl :as repl])<br />
(require '[cljs.repl.browser :as browser])<br />
(def env (browser/repl-env))<br />
(repl/repl env)"<br />
<br />
б) Прописываем в .emacs настройки для inferior-lisp'a:<br />
(setq inferior-lisp-program "~/clojure/clojurescript/browser-repl.sh")<br />
<br />
Здесь "~/clojure/clojurescript/browser-repl.sh" -- естственно, путь к созданному на шаге 2а файлу browser-repl.sh.<br />
<br />
3. Добавляем clojurescript/bin в $PATH. <br />
<br />
4. Заходим в каталог clojurescript/samples/repl, компилируем исходники: cljsc src/ > main.js<br />
<br />
Файл main.js будет позднее использоваться в index.html<br />
<br />
Обратите внимание на содержимое файла test.cljs. Он содержит такую строчку:<br />
(repl/connect "http://localhost:9000/repl")<br />
Именно она позволяет затем соединиться с Emacs-ом.<br />
<br />
5. Файлы index.html, main.js и каталог out необходимо захостить на каком-нибудь веб-сервере. Я, например, просто создал каталог Tomcat7/webapps/my и сложил туда все это барахло. В моем случае страница index.html стала доступна по адресу localhost:8080/my/index.html.<br />
<br />
Внимание! Хостить страницу обяхательно, просто открыв её как файл с диска вы не сможете к ней подконнектиться из Emacs-а!<br />
<br />
6. Запускаем Emacs, выполняем команду M-x inferior-lisp.<br />
<br />
7. Вводим следующие команды:<br />
(require '[cljs.repl :as repl])<br />
(require '[cljs.repl.browser :as browser])<br />
(def env (browser/repl-env))<br />
(repl/repl env)<br />
<br />
<br />
8. Загружаем/обновляем index.html в броузере. После этого repl в Emacs-е должен заработать. Например, вычисление (+ 1 1) должно выдать ответ 2. Если это произошло -- вуаля! у вас веб-консоль прямо в Emacs-е!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0jfoLSAXzO1vHbABt7_mcdk4FXlPlQLRsqV7ZCzWRQj37Q3EybFSYkQX7P0UdEnGSU6-48titBgwzOEaf3ZphoBJjZqlSkbKqA6TNyYd77Y5dESq33aYTH40gOXjrc68orOZokmjc0Y/s1600/clojurescript.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="473" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0jfoLSAXzO1vHbABt7_mcdk4FXlPlQLRsqV7ZCzWRQj37Q3EybFSYkQX7P0UdEnGSU6-48titBgwzOEaf3ZphoBJjZqlSkbKqA6TNyYd77Y5dESq33aYTH40gOXjrc68orOZokmjc0Y/s640/clojurescript.png" width="640" /></a></div>
<br />
В моем примере index.html содержит следующий фрагмент:<br />
<br />
<pre id="line1"> <<span class="start-tag">div</span> <span class="attribute-name">id</span>="<a class="attribute-value" href="http://www.blogger.com/blogger.g?blogID=4007342736133040943">dates</a>">
<<span class="start-tag">div</span>>23.05.2012</<span class="end-tag">div</span>>
<<span class="start-tag">div</span>>25.05.2012</<span class="end-tag">div</span>>
<<span class="start-tag">div</span>>27.05.2012</<span class="end-tag">div</span>>
<<span class="start-tag">div</span>>28.05.2012</<span class="end-tag">div</span>>
<<span class="start-tag">div</span>>29.05.2012</<span class="end-tag">div</span>>
</<span class="end-tag">div</span>></pre>
<pre id="line1"> </pre>
Таким образом, команды на скриншоте изменяют размер шрифта и цвет текста первой даты.<br />
<br />
Вообще, документация на ClojureScript достаточно бедная. Поэтому, вместо того, чтобы искать блог-посты и туториалы по использованию ClojureScript, я просто стал читать его исходники. Рекомендую для этого воспользоваться инструментом <a href="https://github.com/fogus/marginalia">Marginalia</a>. С Leiningen 2 это вообще просто.<br />
<br />
1. В .lein/profiles.clj добавьте плагин lein-marginalia<br />
<br />
{:user {:plugins [ [lein-marginalia "0.7.0"]<br />
[lein-pprint "1.1.1"]<br />
[lein-swank "1.4.4"]]}}<br />
<br />
2. Создайте новый проект: lein new clj-docs<br />
3. Скопируйте в катлог cljs-docs/src содержимое каталогоа cljorescript/src.<br />
4. Выполните команду lein marg, и в каталоге docs будет создан файл uberdoc.html, который слева содержит комментарии к исходнику, а справа -- сам исходник.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNktLAvU2Mdlvv5hAKkA0vGM1v-tTLw0TCvANvRM0IythnMbe-0vYusE7kMs38KoE_sHtVe_H7jJGKQGkPTdRMD_vVRR16rLA_jUmSCCIJV3-qY2RyUt92WP8TZTqc8m5A2i0EojwQOA/s1600/doc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNktLAvU2Mdlvv5hAKkA0vGM1v-tTLw0TCvANvRM0IythnMbe-0vYusE7kMs38KoE_sHtVe_H7jJGKQGkPTdRMD_vVRR16rLA_jUmSCCIJV3-qY2RyUt92WP8TZTqc8m5A2i0EojwQOA/s640/doc.png" width="640" /></a></div>
<br />
Еще стоит ознакомиться с <a href="http://closure-library.googlecode.com/svn/docs/index.html">Google Closure API</a> -- тоже полезное подспорье в работе с ClojureScript.<br />
<br />
Ну а в целом, ClojureScript мне очень понравился, и я надеюсь использовать его дальше в своих проектах.<br />
<br />
<pre id="line1"></pre>
</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com6tag:blogger.com,1999:blog-4007342736133040943.post-54603563220530068142012-05-08T16:55:00.001+03:002012-05-08T17:04:35.189+03:00Гусеничный Lisp-трактор<div dir="ltr" style="text-align: left;" trbidi="on">
Просматривая записи Казимира Майорника, нашел у него очень любопытную <a href="http://kazimirmajorinc.blogspot.com/2009/04/crawler-tractor.html">статью</a>. Это всего лишь забавный эксперимент, показывающий, как при помощи лисповой парадигмы "код-как-данные" можно реализовать циклическое выполнение кода без циклов и рекурсий. Никакой практической ценности в этом нет, просто красиво смотрится. Я написал что-то похожее на Clojure, но несколько усовершенствованное. В моей версии алгоритм выглядит так.<br />
<br />
1) Список f содержит всего одну функцию. Её и запускаем первой.<br />
2) Первая функция передает свой код в update.<br />
3) Update "препарирует" код, находит номер текущей итерации, инкрементирует его и собирает такую же функцию, как ей передали, но с новым значением итерации.<br />
4) Первая функция получает вторую функцию и добавляет её в конец списка f. Если список f содержит более трех элементов, то первый элемент списка f удаляется. Таким образом, в списке f содержится не более четырех элементов.<br />
5) Первая функция запускает вторую функцию и завершается.<br />
<br />
Выглядит и впрямь, как гусеничный трак :-)<br />
<br />
<div style="overflow: auto;">
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">defn</span> update <span style="color: #808030;">[</span>func<span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">let</span> <span style="color: #808030;">[</span>p <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">nth</span> func <span style="color: #008c00;">2</span><span style="color: #808030;">)</span> <span style="color: dimgrey;">;; Получить список (println (str "Hi for the " 1 " time!"))</span>
n <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">nth</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">second</span> p<span style="color: #808030;">)</span> <span style="color: #008c00;">2</span><span style="color: #808030;">)</span> <span style="color: dimgrey;">;; Получить 1 из списка</span>
<span style="color: dimgrey;">;; Создать список (println (str "Hi for the " 2 " time!"))</span>
new-p <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">cons</span> <span style="color: purple;">'println</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">list</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">map</span> #<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> n <span style="color: #808030;">%</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">inc</span> n<span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">identity</span> <span style="color: #808030;">%</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">second</span> p<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span>
<span style="color: dimgrey;">;; Заменить в теле функции (println (str "Hi for the " 1 " time!"))</span>
<span style="color: dimgrey;">;; на (println (str "Hi for the " 2 " time!"))</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">map</span> #<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">and</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">seq?</span> <span style="color: #808030;">%</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">first</span> <span style="color: #808030;">%</span><span style="color: #808030;">)</span> <span style="color: purple;">'println</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> new-p <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">identity</span> <span style="color: #808030;">%</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> func<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">def</span> f <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">ref</span> <span style="color: #808030;">[</span><span style="color: purple;">'</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">fn</span> <span style="color: #808030;">[</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">println</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">str</span> <span style="color: #0000e6;">"Hi for the "</span> <span style="color: #008c00;">1</span> <span style="color: #0000e6;">" time!"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">let</span> <span style="color: #808030;">[</span>func <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">last</span> @f<span style="color: #808030;">)</span>
<span style="color: dimgrey;">;; Обновить список функций f</span>
<span style="color: dimgrey;">;; Удалить первую функцию, если список f содержит больше трех элементов</span>
new-f <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">></span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">count</span> @f<span style="color: #808030;">)</span> <span style="color: #008c00;">3</span><span style="color: #808030;">)</span>
<span style="color: dimgrey;">;; Добавить обновленную функцию в конец списка</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">conj</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">vec</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">drop</span> <span style="color: #008c00;">1</span> @f<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">update</span> func<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">conj</span> @f <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">update</span> func<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">dosync</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">ref-set</span> f new-f<span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: dimgrey;">;; Выполнить последнюю функцию списка</span>
<span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">eval</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">last</span> @f<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">eval</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">last</span> @f<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
</pre>
</div>
</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com0tag:blogger.com,1999:blog-4007342736133040943.post-26868485775882139092012-05-07T15:03:00.002+03:002012-05-07T15:11:10.148+03:00CEDET 1.1, Emacs и Java<div dir="ltr" style="text-align: left;" trbidi="on">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
<br />
<div style="margin-bottom: 0in;">
Недавно вышедший Cedet
1.1 добавляет долгожданную поддержку
импорта символов из jar-ок. Теперь Emacs
предоставляет поддержку автодополнения
кода java примерно на том же уровне, что
и современные IDE.
</div>
<div style="margin-bottom: 0in;">
Например, у нас есть
класс My1:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0t5uJl7AClcQsxQEiJ4kpOj90k8OWhDhRpMVxkyKIvEOCb_iNKQgbHPcOsTpFqIsnZSam_1JLQB3JDik6gjv3YDHqCzYjO9nfei7ZrFH8RlqiWeOAevnObQZmKsX0NHk6jM2ZSAyLt-g/s1600/0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0t5uJl7AClcQsxQEiJ4kpOj90k8OWhDhRpMVxkyKIvEOCb_iNKQgbHPcOsTpFqIsnZSam_1JLQB3JDik6gjv3YDHqCzYjO9nfei7ZrFH8RlqiWeOAevnObQZmKsX0NHk6jM2ZSAyLt-g/s1600/0.png" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Он используется в My2:</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRv8c3jCCtO7330I6CpjeyfcHypQS5ME_pPSk3Sj_QbRBwDb49d2i2fde9dEQ2TozcbXSLjPwxbimq6-7gn38_eigeprXu9ytZU5c9Yr5N_BGtlmdk2RTeCV5FGefSqRqF4zlJ8V8L810/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRv8c3jCCtO7330I6CpjeyfcHypQS5ME_pPSk3Sj_QbRBwDb49d2i2fde9dEQ2TozcbXSLjPwxbimq6-7gn38_eigeprXu9ytZU5c9Yr5N_BGtlmdk2RTeCV5FGefSqRqF4zlJ8V8L810/s1600/1.png" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Обратите внимание, как
flymake параллельно скомпилировал файл и
подсветил ошибки. Если набрать “my.” и
выполнить функцию semantic-ia-complete-symbol-menu (у
меня она замаплена на Ctrl-Space), то Emacs
выдаст такую подсказку:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMpvMOLxHQWzluDevBvFbIwENCB7acLmhE0bKuLM52PoTuD_AXx3V8bzQHKdPB567PboMyl1fYo2GlGyvNVEEMLRkkAbEh2LFOXic-9N_JHOtrskN4pdEl5UGIOc4LKz1q44NUcyxxbLY/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMpvMOLxHQWzluDevBvFbIwENCB7acLmhE0bKuLM52PoTuD_AXx3V8bzQHKdPB567PboMyl1fYo2GlGyvNVEEMLRkkAbEh2LFOXic-9N_JHOtrskN4pdEl5UGIOc4LKz1q44NUcyxxbLY/s1600/2.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Аналогично можно
запрашивать подсказку по членам системных
классов. Например, для StringBuilder:</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2RC2nNcFtrbdaYAmmMaRAHwGxZaIT8m4_80IF6mSgkOPNBmRC5X4hivlBWzoXobeq0MEM5WwML0r2XJjONmCntfCcO7-UcD5E5Iova6NDktGD7P8bExg8O_StdMmFY5AlK0X_h-0VLUE/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2RC2nNcFtrbdaYAmmMaRAHwGxZaIT8m4_80IF6mSgkOPNBmRC5X4hivlBWzoXobeq0MEM5WwML0r2XJjONmCntfCcO7-UcD5E5Iova6NDktGD7P8bExg8O_StdMmFY5AlK0X_h-0VLUE/s1600/3.png" /></a></div>
<div style="margin-bottom: 0in;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</div>
<div style="margin-bottom: 0in;">
Правда надо учитывать,
что, во-первых, импорты “звёздочкой”
не срабатывают, системных классов
semanticdb-javap тоже не знает. Поэтому, если
необходимы подсказки, то импорт классов
из неймспейса java.lang нужно делать явно,
как в моем примере.</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
Для того, чтобы заработали
показанные выше механизмы, необходимо
следующее:</div>
<ol>
<li><div style="margin-bottom: 0in;">
установленный пакет
flymake;</div>
</li>
<li><div style="margin-bottom: 0in;">
установленный пакет
auto-complete;</div>
</li>
<li><div style="margin-bottom: 0in;">
скачанный CEDET 1.1;</div>
</li>
<li><div style="margin-bottom: 0in;">
Ну и конечно же,
последний Emacs и java.</div>
</li>
</ol>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
Конфигурация CEDET в
.emacs :</div>
<div style="margin-bottom: 0in;">
(load-file "~/cedet/common/cedet.el")
;; Подгрузить cedet</div>
<div style="margin-bottom: 0in;">
(global-semanticdb-minor-mode 1) ;;
Инициализировать semanticdb</div>
<div style="margin-bottom: 0in;">
;; Загрузить поддержку авто-дополнения </div>
<div style="margin-bottom: 0in;">
(semantic-load-enable-gaudy-code-helpers) </div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
(custom-set-variables ;;
Инициализация переменных, указывающих,
где </div>
<div style="margin-bottom: 0in;">
;; установлена java</div>
<div style="margin-bottom: 0in;">
. . .</div>
<div style="margin-bottom: 0in;">
'(cedet-java-jdk-root "/opt/jdk")</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
;; Сюда нужно
добавить все jar-ки, из</div>
<div style="margin-bottom: 0in;">
;; которых
необходимо импортировать символы</div>
<div style="margin-bottom: 0in;">
</div>
<div style="margin-bottom: 0in;">
'(semanticdb-javap-classpath
'("/opt/jdk/jre/lib/rt.jar"))</div>
. . .
<br />
<div style="margin-bottom: 0in;">
)</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
Конфигурация flymake:</div>
<div style="margin-bottom: 0in;">
(require 'flymake)</div>
<div style="margin-bottom: 0in;">
(add-hook 'java-mode-hook
'flymake-mode-on)</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
(defun my-java-flymake-init ()</div>
<div style="margin-bottom: 0in;">
(list "javac" (list
(flymake-init-create-temp-buffer-copy</div>
<div style="margin-bottom: 0in;">
'flymake-create-temp-with-folder-structure))))</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
(add-to-list
'flymake-allowed-file-name-masks '("\\.java$"
my-java-flymake-init flymake-simple-cleanup))</div>
<div style="margin-bottom: 0in;">
<br /></div>
<div style="margin-bottom: 0in;">
Конфигурация auto-complete:</div>
<div style="margin-bottom: 0in;">
(require 'auto-complete-config)</div>
<div style="margin-bottom: 0in;">
(add-to-list 'ac-dictionary-directories
"~/.emacs.d/ac-dict")</div>
<div style="margin-bottom: 0in;">
(ac-config-default)</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="margin-bottom: 0in;">
<br /></div>
</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com11tag:blogger.com,1999:blog-4007342736133040943.post-50413981397916535032012-03-29T11:59:00.000+03:002012-03-29T11:59:25.025+03:00Итоги за месяц<div dir="ltr" style="text-align: left;" trbidi="on">Давно не писал сюда из-за нехватки времени. Но вот, наконец, я достаточно освободился, чтобы набросать пару строк в блог.<br />
<br />
Со времени последней записи у меня произошло много интересных событий, и многое можно было бы описать здесь, т.к. почти все так или иначе связано с Clojure. Наиболее интересное, на мой взгляд, -- это первое в моей практике применение Clojure на коммерческом проекте. О нем вкратце и расскажу.<br />
<br />
Проект, который я делал с начала февраля по середину марта, -- андроид-приложение для планшетов. Оно предназначалось для дистанционного обучения какой-то фигне сотрудников какой-то компании. Технических сложностей там никаких нету, писать здесь не о чем, за исключением достаточного большого объема работы: необходимо было реализовать 22 формы со всякими разными украшениями. Дополнительная сложность -- необходимо написать еще и JSON веб-сервис, который может отдавать запрашиваемые данные и сохранять у себя ответы пользователей на опросы и тесты.<br />
<br />
Дедлайн был очень жесткий: 12 марта. На мой взгляд, при таких объемах и сроках проект должен делать не один человек, а как минимум трое.<br />
<br />
Во-первых, мне удалось уговорить заказчика разрешить писать веб-сервис на Clojure вместо java. По моим прикидкам, это сэкономило бы мне неделю работы. Для заказчика были крайне важны сроки, и он позитивно воспринял мое предложение, но поинтересовался: какие минусы у clojure? Я честно сказал, что этот язык просто менее распространен, чем java. Все остальное -- то же, что и у java. Например, готовый сервис мы захостим под Tomcat в виде war-файла. Заказчик согласился с этим риском, заметив, что если вдальнейшем будет необходимость, то тогда вебсервис можно будет переделать на java. Забегая вперед скажу, что необходимость не возникла :-)<br />
<br />
Я воспользовался библиотеками noir1.3-alpha10, cheshire, sqlkorma, lobos и очень быстро показал работающий сервак. Функциональность в сервак я добавлял в процессе работы над клиентской частью по мере необходимости. Поскольку я пользовался связкой Emacs+Slime, рабочий код я получал очень быстро. А время, которое я тратил на разработку очередной функции сервака, в среднем равнялось нескольким минутам. <br />
<br />
Основное время, конечно же, занимала Android-часть, причем в основном -- настройка внешнего вида формочек. Android -- неплохая платформа для пользователя, но очень сырая и недоделанная для разработчика. В отличие от ябловодов, нам, андроидам, нет возможности пользоваться WYSIWYG-редактором для построения GUI. А редактировать гигантские простыни xml-кода вручную -- сомнительное удовольствие...<br />
<br />
Решение нашлось довольно быстро. Я разработал небольшой DSL на основе Clojure и Hiccup. С применением этого DSL формочку я целиком описывал на Clojure, а потом генерил из неё связку xml и java-кода (при этом генерился весь MVC на форму). В среднем, количество кода в DSL и количесство сгенеренного xml+java кода соотносились как 1:3 или 1:4, что уже очень неплохо. Еще один важный бонус, который я получил от этого DSL -- возможность использования нормального вменяемого языка для описания UI вместо XML. Появилась возможность выносить общую функциональность в функции, делать циклы и т.д. Понятно, что большинство повторно используемых компонент оформлялись как отдельные виджеты; но там, где для этого не было необходимости -- я просто объявлял функцию на Clojure. Еще важное замечание: код в стиле Hiccup читается гораздо проще, чем xml.<br />
<br />
Дальше развивать свой DSL у меня уже не было времени, а повторяющийся код, который я не мог обобщить, все равно встречался. Например, код для работы с БД и вебсервисом состоял по большей части из извлечения-записи в хеш-таблицы по ключам, соответствующим именам полей в БД. Здесь мне на помощь пришел Emacs. Буквально пара десятков строк кода на elisp-е, и весь оставшийся boilerplate-код на java генерил Emacs.<br />
<br />
Надо сказать, что в сроки я уложился, но с учетом, что в конце проекта на 2 недели мне дали в помощь товарища. Думаю, получилось очень даже хорошо.</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-51893130456105439152012-02-19T11:35:00.000+03:002012-02-19T11:35:03.244+03:00Деплоймент Clojure под Tomcat<div dir="ltr" style="text-align: left;" trbidi="on">Не так давно я взялся за небольшой проект с клиент-серверной архитектурой. JSON веб-сервис мне позволили реализовать на clojure, что я очень быстро и сделал при помощи библиотек: Noir, Lobos, Korma и Cheshire. Но зато была масса непоняток с деплойментом этого готового сервиса на хостинг. Во-первых, по непонятной причине, оригинальный сервер, поработав какое-то время, попросту отваливается. Я до сих теряюсь в догадках, что заставляет его так капризничать. Понятное дело, что я решил задеплоить всё на Tomcat. <br />
<br />
Мне не удалось нигде найти нормальной инструкции о том, как деплоить noir-приложение под томкат, так что я решил восполнить этот пробел.<br />
<br />
Данная инструкция рассчитана на веб-сайт, разработанный на clojure при помощи фреймворка Noir (опробовано на noir 1.3.0-alpha10).<br />
<br />
1) Добавьте [lein-ring "0.5.4"] в :dev-dependencies вашего файла project.clj.<br />
Внимание! [uk.org.alienscience/leiningen-war "0.0.13"] – это не то же самое, что lein-ring. Плагин lein-ring умеет создавать ring-handler для noir-приложения и запаковать все в war-ку. А плагин leiningen-war только создает war-ку, а ring-handler-а не создает.<br />
<br />
2) В файле project.clj укажите ваш ring-handler и пространство имен для ahead-of-time компиляции.<br />
:ring {:handler MyProject.server/handler} <br />
:aot [MyProject.server]<br />
<br />
3) Создайте ring-handler в server.clj:<br />
(:require [noir.server :as server])<br />
. . .<br />
(def handler (server/gen-handler {:base-url "/myproject"}))<br />
Внимание! Обязательно нужно указать :base-url, иначе редиректы и линки задеплоенного в томкат приложения будут указывать не на localhost:8080/myproject/, а на localhost:8080/ . Эпик фейл будет фееричный!<br />
<br />
4) Уберите такую загрузку вьюх: (server/load-views "src/MyProject/views/") из файла server.clj. Вьюхи нужно загружать явно: <br />
(:require [noir.server :as server]<br />
. . .<br />
[MyProject.views.pages])<br />
Если этого не сделать, то после упаковки в war-ку и даже jar-ку, ни один ваш маршрут работать не будет.<br />
<br />
5) Все линки и формы, если вы их генерите из hiccup, нужно создавать функциями:<br />
(hiccup.form-helpers/form-to [:post "/myurl"] ...) <br />
(hiccup.page-helpers/link-to "/myurl" …)<br />
Если этого не сделать, то они будут вести на localhost:8080/ вместо localhost:8080/myproject.<br />
<br />
6) Создайте war-ку командой lein ring uberwar. <br />
Внимание! Команда lein ring uberwar поставляется с lein-ring-плагином, и в нашем случае она работает. Команда lein uberwar поставляется с плагином leiningen-war, и здесь она не работает. Так что этот плагин вообще лучше не использовать с noir.<br />
<br />
7) Скопируйте war-ку в webapps томката и переименуйте её в myproject.war. <br />
<br />
Теперь, после запуска томката, по адреусу localhost:8080/myproject будет ваше приложение. </div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com2tag:blogger.com,1999:blog-4007342736133040943.post-17033694176054515932012-01-22T18:01:00.000+03:002012-01-22T18:01:03.687+03:00Noir benchmark<div dir="ltr" style="text-align: left;" trbidi="on">Решил немного потестить простое веб-приложение, описанное в предыдущей статье. Тесты проводились на ноутбуке Acer Aspire 5745G (Intel Core i5 2.26Ghz, 4Gb). Обнаружил следующее.<br />
<br />
Во-первых, при запуске сайта командой lein run запускаются два java-процесса. Первый, занимающий 28.8Mb -- видимо служебный, т.к. никакие его характерстики за время работы приложения не менялись. Второй, сходу запросивший 76.7Mb, -- само веб-приложение. Я создал два типа сущностей: mypage и mypage2 с CRUD-действиями для них, не используя БД. Т.е. скорость обработки запроса целиком зависила от скорости работы java-машины и не зависела от скорости ввода-вывода или поиска данных в таблицах. При обращении к различным действиям сущности mypage объем потребляемой памяти возрос до 90.3Mb. При обращении к mypage2 -- до 93.5Mb. При повторном обращении к этим же сущностям потребление памяти никак не поменялось. При перезапуске приложения цифры изменились до 96Mb и 99Mb.<br />
<br />
Интересно взглянуть на производительность веб-приложения под нагрузкой. Я создал скрипт, делающий 70000 таких запросов:<br />
wget http://localhost:8080/mypage<br />
wget http://localhost:8080/mypage/848867<br />
wget http://localhost:8080/mypage/edit/996283<br />
wget http://localhost:8080/mypage/527997<br />
wget http://localhost:8080/mypage/edit/410389<br />
<br />
Сущности с указанными айдишниками, естественно, существовали в приложении. Эти 70тыс. запросов jetty обработал за полчаса, отдавая, таким образом, примерно 2333 динамических страницы в минуту или примерно 39 в секунду.<br />
<br />
Самым интересным для меня в этом эксперименте стало потребление памяти и процессорного времени.<br />
<br />
Сначала jetty затребовал 92% процессорного времени, но его запросы довольно быстро сократились до 40%. Затем, довольно медленно, и они стали снижаться. К концу эксперимента jetty потреблял всего 4% процессорного времени.<br />
<br />
Аналогично, запросы памяти сначала были немалыми. Довольно быстро jetty "откушал" 232.8Mb, после чего потребляемая память стала уверенно снижаться, остановившись на отметке 84.6Mb.<br />
<br />
Надо еще учитывать, что нагрузка на сервер была ограничена возможностями консоли, в которой работал wget, ведь его вывод на экран тоже занимает какое-то время. Но, по крайней мере, верхнюю границу запросов процессора и памяти мы получили. Нижняя, естественно, достоверной не является.<br />
<br />
Такое снижение потребляемой памяти и процессора стало возможно благодаря run-time оптимизациям java-машины (в Sun некогда здорово над этим поработали). Вот и скажи теперь, что java -- медленная...</div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com8tag:blogger.com,1999:blog-4007342736133040943.post-29503855325561776672012-01-08T12:22:00.001+03:002012-01-08T12:53:00.703+03:00CRUD на Clojure средствами Noir<div dir="ltr" style="text-align: left;" trbidi="on"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { color: #000080; so-language: zxx; text-decoration: underline }
-->
</style> <br />
<div style="font-weight: normal; margin-bottom: 0cm;">Давно собирался рассказать немного о Noir – классном веб-фреймворке для Clojure. Наконец, у меня появилась возможность написать большой туториал по применению Noir, что я с удовольствием и сделаю :-)</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Есть ли вообще необходимость в самоучителе по Noir? Не очень существенная, если честно. На сайте фреймворка (webnoir.org) более чем приличная документация: раздел для быстрого старта, несколько обучалок по использованию Noir и даже индекс API. Мой туториал, возможно, будет вам полезен, если вы, зная Clojure и разобравшись более-менее с Noir, все еще не очень представляете себе архитектуру своего будущего приложения.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>А задуматься над архитектурой придется. Noir очень сильно отличается от всех известных мне фреймворков. При своем минимализме он оставляет выбор конкретных библиотек за пользователем – такая свобода может привести к неверному их использованию. Насколько этот выбор широк можете судить хотя бы по <a href="http://brehaut.net/blog/2011/ring_introduction">статье</a> с обзором разных компонентов для каждого из уровней веб-стека на clojure. <br />
<div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Давайте посмотрим, на что похоже приложение Noir. Если вы проследовали инструкциям из Getting Started на сайте webnoir.org, то у вас уже установлен leiningen-плагин для Noir. Создадим новый проект командой lein noir new optimalist. У него очень простая структура. </div><div style="font-weight: normal; margin-bottom: 0cm;"><b>resources</b> – содержит каталог public со статическим контентом (css, javasript, images);</div><div style="font-weight: normal; margin-bottom: 0cm;"><b>src</b> – каталог с исходниками;</div><div style="font-weight: normal; margin-bottom: 0cm;"><b>test</b> – тесты;</div><div style="font-weight: normal; margin-bottom: 0cm;"><b>project</b>.clj – описание проекта и версий подключаемых библиотек.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Исходники содержаться в двух каталогах <b>src/optimalist/views </b>и<b> src/optimalist/models</b>. Никаких вопросов не вызывает? А как насчет <b>src/optimalist/controllers</b>? Такого каталога нету, т. к. самописных контроллеров здесь не предусмотрено. Это, конечно, у многих может вызвать разрыв шаблона: как вообще программировать MVC без C! </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Автор Noir Крис Гранжер очень подробно <a href="http://groups.google.com/group/clj-noir/browse_thread/thread/1718a9b1312156d3/502c3c7e24f7319c?hl=ru&lnk=gst&q=controller#502c3c7e24f7319c">обосновал</a> свое представление архитектуры. Исторически сложилось, что бизнес-логику обычно помещали в контроллер. Но со временем её все больше и больше выносили в отдельные компоненты, контроллер стал совсем тоненьким и, откровенно говоря, ненужным. Функции маршрутизации берет на себя фреймворк, так что в Noir (да и в других современных веб-фреймворках) строгой необходимости в отдельных контроллерах попросту нету. Единственное, что всё еще делает контроллер – это указывает, какую страницу рендерить в ответ на запрос пользователя. Раз уж код контроллера такой маленький, то Крис Гранжер попросту решил перенести функции контроллера на уровень View. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Если сравнивать такой подход с другими фреймворками, то, пожалуй, Крис – один из первых, кто открыто решился заявить о ненужности контроллеров. Например, в практике программирования Ruby on Rails всё еще считается правильным всю бизнес логику вынести на уровень модели, а контроллеры сделать маленькими и легковесными. Это очень упрощает тестирование. Но это половинчатый подход: выбросили бы контроллеры да и дело с концом! Скажем, в JSF2.0 примерно так и обстоят дела. Вся бизнес-логика находится в Java-бинах, вызываются они с уровня View напрямую, минуя слой контроллера. Маршруты привязываются к страницам обычно централизованно в специальном файле конфигурации. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Поскольку каталог models пустой, давайте взглянем повнимательнее на содержимое каталога views. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">В файле common.clj создается специальная функция layout. Она задает общий шаблон страницы, заголовки и структуру html-файла. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">В файле welcome.clj при помощи defpage задается маршрут «/welcome» и функция, которая сгенерирует ответ на запрос пользователя по этому маршруту. Как видно из кода, функция вернет строку приветствия. Шаблон html-страницы создается при помощи библиотеки Hiccup. Она позволяет писать html-код прямо на языке clojure при помощи родных для clojure структур данных: векторов и мапов.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Hiccup очень удобен тем, что позволяет производить любые манипуляции с шаблоном прямо при помощи Clojure. Заметьте, такой подход вообще не встречается среди мейнстримных фреймворков. Что в ASP.NET, что в J2EE для разметки страницы применяется свой собственный DSL, построенный на базе XHTML. Как, например, сделать повторяющийся фрагмент страницы на JSF2.0, на которой еще есть экшены? Правильно, с применением ломика и какой-то матери. В ASP.NET с этим, вроде, несколько проще, но подход такой же: xml-теги. Ну и скажите мне, как программисту Java, почему я не могу использовать Java для изменения шаблона веб-страницы? Как ни крути, но даже Java... хотя нет, уж лучше этот долбаный XML, чем веб-страницы на Java...</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Короче, в Noir используется hiccup, а hiccup позволяет писать html-страницы на clojure в декларативном стиле.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Вручную hiccup-код имеет смысл писать только для страниц очень небольшого размера. Если вам досталась от дизайнера монстрозная страница в десятки килобайт кода – конечно, такое поделие переписывать на hiccup не стоит. В этом вам поможет <a href="https://github.com/nathell/clj-tagsoup">clj-tagsoup</a>. Просто вызовите его функцию parse с указанием html-страницы и сохраните полученный hiccup-код.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Почти все руководства о веб-фреймворках в виде первого учебного приложения предлагают свой вариант блога. Давайте и мы сделаем свой простой блог при помощи Noir. Переименуем welcome.clj в article_pages.clj (а также namespace) и напишем код первой страницы блога – списка записей. Еще я немного изменил список импортируемых библиотек.</div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_pages
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:require</span> <span style="color: #308080;">[</span>optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>common <span style="color: #400000;">:as</span> common<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>response <span style="color: #400000;">:as</span> resp<span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:use</span> <span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>hiccup<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> articles <span style="color: #1060b6;">"/articles"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:p</span> <span style="color: #1060b6;">"List of articles"</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> </div><div style="font-weight: normal; margin-bottom: 0cm;">Обратите внимание, что маршрут «/articles» – именованный. В будущем я смогу получить его при помощи (url-for articles). </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Запустите веб-приложение командой lein run и откройте его по ссылке localhost:8080/articles. Разумеется, вы увидите текст «List of articles». Но пока что еще не работает корневой маршрут: localhost:8080/. Давайте добавим для него определение маршрута. </div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> <span style="color: #1060b6;">"/"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">resp/redirect</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> articles<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Как видите, здесь объявляется неименованный маршрут «/», и вместо ответа на него сервер редиректит на страницу articles.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Следующая проблема: как вывести список каких-либо элементов? Поскольку код шаблона мы пишем на clojure, то воспользуемся функцией for:</div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> articles <span style="color: #1060b6;">"/articles"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">let</span> <span style="color: #308080;">[</span>data <span style="color: #308080;">[</span><span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #008c00;">1</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"First title"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"First body"</span><span style="color: #308080;">}</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #008c00;">2</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"Second title"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Second body"</span><span style="color: #308080;">}</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #008c00;">3</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"Third title"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Third body"</span><span style="color: #308080;">}</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:p</span> <span style="color: #1060b6;">"List of articles"</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">[</span>item data<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Пока что все достаточно просто, не так ли? Страница articles покажет нам следующий текст:</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-style: normal; font-weight: normal;"><span style="color: black;"><span style="font-family: Times New Roman;"><span style="font-size: small;">List of articles</span></span></span></div><div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"><span style="color: black;"><span style="font-family: Times New Roman;"><span style="font-size: small;">First title<br />
First body</span></span></span></div><div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"><br />
</div><div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"><span style="color: black;"><span style="font-family: Times New Roman;"><span style="font-size: small;">Second title<br />
Second body</span></span></span></div><div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"><br />
</div><div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"><span style="color: black;"><span style="font-family: Times New Roman;"><span style="font-size: small;">Third title<br />
Third body</span></span></span></div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>Теперь данные нужно перенести в отдельный файл. Создадим в каталоге models файл article_model.clj, добавим туда код:<br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>models<span style="color: #308080;">.</span>article_model<span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">def</span> *data*
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ref</span>
<span style="color: #308080;">[</span><span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #1060b6;">"1"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"First article"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Lorem ipsum dolor sit amet."</span><span style="color: #308080;">}</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #1060b6;">"2"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"Second article"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Lorem ipsum dolor sit amet."</span><span style="color: #308080;">}</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #1060b6;">"3"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"Third article"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Lorem ipsum dolor sit amet. "</span><span style="color: #308080;">}</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #1060b6;">"4"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:title</span> <span style="color: #1060b6;">"Fourth article"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:body</span> <span style="color: #1060b6;">"Lorem ipsum dolor sit amet."</span><span style="color: #308080;">}</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> fetch-list <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
@*data*<span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Воспользуемся этой моделью. Чтобы импортировать модель, добавим в секцию require такую строку: [optimalist.models.article_model :as article-model]; а код функции articles перепишем таким образом: </div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> articles <span style="color: #1060b6;">"/articles"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:p</span> <span style="color: #1060b6;">"List of articles"</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">[</span>item <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch-list</span><span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Вообще такая организация кода всё еще не очень удобная. Что если будет несколько страниц с похожим функционалом? Нам придется выносить hiccup-код в отдельные модули чтобы сделать его reusable. Давайте сделаем это сразу. Создатим файл view/article_templates.clj и вынесем туда генерацию httml.</div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_templates
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:use</span> <span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>hiccup<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> show-list <span style="color: #308080;">[</span>articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:p</span> <span style="color: #1060b6;">"List of articles"</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">[</span>item articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> </div>В модуле article_pages.clj необходимо добавить строку [optimalist.views.article_templates :as article-view] в секцию :require. А страница articles тогда будет выглядеть так: <br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> articles <span style="color: #1060b6;">"/articles"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/show-list</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch-list</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;">Следующая наша задача – добавить к элементам списка ссылки на действия. Лично я – большой любитель технологии REST, о которой узнал из Ruby on Rails. Наши четыре действия над сущностью create, read, update, delete подразумевают наличие семи маршрутов:</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><table cellpadding="4" cellspacing="0" style="width: 642px;"><colgroup><col width="152"></col> <col width="138"></col> <col width="326"></col> </colgroup><tbody>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: 1px solid #000000; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0.1cm;" width="152"><b>Маршрут</b></td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: 1px solid #000000; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0.1cm;" width="138"><b>Метод</b></td> <td style="border: 1px solid #000000; padding: 0.1cm;" width="326"><b>Описание</b></td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/articles</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">GET</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Список записей</td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/:id</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">GET</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Просмотреть одну запись</td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/edit/:id</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">GET</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Показать форму редактирования <span style="font-weight: normal;">записи</span></td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/update/:id</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">POST</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Изменить запись в соответствии с указанными данными</td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/delete/:id</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">POST</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Удалить запись</td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/new</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138">GET</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Показать пустую форму для создания записи</td> </tr>
<tr valign="TOP"> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152">/article/create</td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"><div style="font-weight: normal;">POST</div></td> <td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326">Создать запись в соответствии с указанными данными</td> </tr>
</tbody></table><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Вообще в REST должны использоваться методы не только GET и POST, а также PUT (для создания новой записи) и DELETE (для удаления). Но то ли браузеры не понимают этих методов, то ли веб сервера, но факт остается фактом: использовать мы можем только GET и POST. В Ruby on Rails для обхода этого ограничения создается скрытое поле. И если указывается метод PUT или DELETE, то на самом деле используется POST с пометкой в скрытом поле, что это все-таки PUT/DELETE.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Разные методы отправки запроса нужно использовать не только для красоты. По сети ползают роботы-индексаторы, которые пытаются перейти по ссылкам на другие страницы. Представьте, например, что на вашем сайте кнопка DELETE сделана при помощи обыкновенной ссылки (запрос методом GET), и робот решит по ней перейти. Робот ведь не знает, что эта ссылка приводит к какому-то разрушительному действию, правда? Вот для этого-то все запросы, как-нибудь изменяющие сущность, должны быть отправлены методом POST.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Итак, первая ссылка, которую я хотел бы добавить к списку сущностей – маршрут /article/:id для просмотра записи.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>Вначале в article_pages добавим страницу: <br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> view-article <span style="color: #1060b6;">"/article/:id"</span> <span style="color: #308080;">{</span>id <span style="color: #400000;">:id</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #1060b6;">"View article"</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> <br />
<div style="font-weight: normal; margin-bottom: 0cm;">Она пока ничего не делает, но зато её сразу же можно протестить в браузере по маршруту /article/1. Заметьте, что в определении страницы задан хеш параметров {id :id}. Это деструктуризаця параметров запроса, и делает она именно то, на что похоже: создает локальную переменную id, значение которой будет получено из части маршрута :id.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Теперь добавим ссылку на эту страницу в функции show-list</div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> show-list <span style="color: #308080;">[</span>articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:p</span> <span style="color: #1060b6;">"List of articles"</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">[</span>item articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span> <span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">str</span> <span style="color: #1060b6;">"/article/"</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:id</span> item<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"View"</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span></pre>Функция url-for умеет создавать абсолютный URL по заданным параметрам. Параметром может быть как текстовая строка с маршрутом, так и сам именованный маршрут. И хотя предложенное выше решение – вполне работоспособное, но оно все-таки не оптимальное. Что если вы решите изменить URL в странице view-article? Тогда вам придется искать все места, где вы на него ссылаетесь, и править маршруты вручную. Лучше использовать именованый маршрут следующим образом: <br />
<div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">(url-for optimalist.views.article_pages/view-article {:id (:id item)})</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Здесь optimalist.views.article_pages/view-article – полное имя именованного маршрута. Необходимо писать его целиком, т. к. в файле article_templates.clj ничего не известно о aricle_pages.clj. И импортировать article_pages.clj мы тоже не можем, т. к. там уже есть импорт article_templates.clj, и мы получим циклическую зависимость. Поэтому, все-таки, придется писать целиком.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Второй параметр – хеш значений, которые будут переданы в генератор маршрута. Поскольку наш маршрут выглядит как «/article/:id», то ему нужно передать хеш {:id <значение>}. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Вообще, конечно же, такая запись жутко громоздкая и неудобная. Поэтому в начало файла article_templates.clj, после объявления неймспейса, добавим алиас:</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">(alias 'article 'optimalist.views.article_pages)</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Теперь генерацию ссылки можно переписать таким образом:</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">(url-for article/view-article {:id (:id item)})</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>Это уже значительно лучше, но все еще пока не идеально. Элементов типа «ссылка» у нас будет достаточно много, особенно, если много разных сущностей. Но типов ссылок всего несколько: new, view, edit, delete, back. Мы могли бы сделать более абстрактный механизм генерации ссылок, который инкапсулирует в себе всю внутреннюю структуру вектора [:a …], в том числе и надпись на ссылке. Создадим файл views/helpers/widget.clj: <br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>helpers<span style="color: #308080;">.</span>widget
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:require</span> <span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defmacro</span> view-link <span style="color: #308080;">[</span>page id<span style="color: #308080;">]</span>
<span style="color: #406080;">`</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">vec</span> <span style="color: #308080;">[</span><span style="color: #400000;">:a</span>
<span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">noir.core/url-for</span> <span style="color: #308080;">~</span>page <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #308080;">~</span>id<span style="color: #308080;">}</span><span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"View"</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Теперь его можно подключить в article_templates.clj, добавив строку [optimalist.views.helpers.widget] в секцию :use. А вместо записи [:a {href … } «View»] вставим вызов макроса: (view-link article/view-article (:id item)).</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Макрос здесь нужен из-за параметра page – символа, к которому привязан именованный маршрут. Конечно, его можно было бы передавать закавыченным, но это не очень-то красиво.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Сам макрос пока-что не слишком удачный. Что если нам понадобится добавить еще несколько похожих ссылок? Copy-paste – не наш метод, поэтому немного расширим наш язык для данного конкретного случая. </div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Это очень интересная технология, описанная Полом Грэмом в книге «On Lisp», и называется она «bottom-up programming». Идея заключается в том, что вы не только пишете свое приложение, используя Lisp, но и изменяете Lisp, чтобы легче было писать ваше предложение. Снизу, в язык вводятся новые конструкции, упрощающие реализацию приложения. Сверху, приложение описывается при помощи ваших новых конструкций, что делает код значительно короче. Отсюда и bottom-up programming.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>Новые конструкции, которые мы вводим в язык, называются утилитами. Давайте добавим две таких: <br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defmacro</span> common-url <span style="color: #308080;">[</span>page id<span style="color: #308080;">]</span>
<span style="color: #406080;">`</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">noir.core/url-for</span> <span style="color: #308080;">~</span>page <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #308080;">~</span>id<span style="color: #308080;">}</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defmacro</span> common-link <span style="color: #308080;">[</span>url text <span style="color: #308080;">&</span> params<span style="color: #308080;">]</span>
<span style="color: #406080;">`</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">vec</span> <span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">merge</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">~</span>url<span style="color: #308080;">}</span> <span style="color: #308080;">~</span>@params<span style="color: #308080;">)</span> <span style="color: #308080;">~</span>text<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Первый макрос просто инкапсулирует знание о том, как создавать URL. Теперь нам не надо заботиться о том, чтобы дописывать noir.core к url-for, а также создавать хеш для единственного занчения id.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;">Второй макрос создает html-ссылку с заданным текстом на указанный URL. Необязательный параметр params – это возможные параметры самой ссылки. Например, если нам потребуется задать обработчик onclick, то мы сможем сделать это через params.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div>Перепишем текст генерации ссылки с применением двух новых утилит: <br />
<pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defmacro</span> view-link <span style="color: #308080;">[</span>page id<span style="color: #308080;">]</span>
<span style="color: #406080;">`</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common-link</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common-url</span> <span style="color: #308080;">~</span>page <span style="color: #308080;">~</span>id<span style="color: #308080;">)</span> <span style="color: #1060b6;">"View"</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defmacro</span> edit-link <span style="color: #308080;">[</span>page id<span style="color: #308080;">]</span>
<span style="color: #406080;">`</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common-link</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common-url</span> <span style="color: #308080;">~</span>page <span style="color: #308080;">~</span>id<span style="color: #308080;">)</span> <span style="color: #1060b6;">"Edit"</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> </div><div style="font-weight: normal; margin-bottom: 0cm;"> Итак, на данный момент, у нас есть список записей и ссылка на просмотр каждой записи. Давайте теперь реализуем функцию view-article: </div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> view-article <span style="color: #1060b6;">"/article/:id"</span> <span style="color: #308080;">{</span>id <span style="color: #400000;">:id</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/show</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch</span> id<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> Чтобы она заработала, нужно в article_templates добавить функцию show: </div><pre style="background: none repeat scroll 0% 0% rgb(246, 248, 255); color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> show <span style="color: #308080;">[</span>article<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:h1</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> article<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> article<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> article<span style="color: #308080;">/</span>articles<span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"Back"</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">) </span></pre><div style="font-weight: normal; margin-bottom: 0cm;">В article_model необходимо добавить функцию fetch:<span style="color: #308080;"> </span></div><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"></div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;"></span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> fetch <span style="color: #308080;">[</span>id<span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">first</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">filter</span> #<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">=</span> id <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:id</span> <span style="color: #308080;">%</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span> @*data*<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"><style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style> </div><div style="font-weight: normal; margin-bottom: 0cm;">На этом, пожалуй, все сложности и закончились. Дальше, по накатанной. Проект целиком вы можете найти <a href="https://github.com/dbushenko/Simple-Noir-blog-example">здесь</a>, а вот как выглядят два основных файла проекта.</div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;"><b>article_pages.clj</b></div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;"></div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_pages
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:require</span> <span style="color: #308080;">[</span>optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>common <span style="color: #400000;">:as</span> common<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>response <span style="color: #400000;">:as</span> resp<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_templates <span style="color: #400000;">:as</span> article-view<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>optimalist<span style="color: #308080;">.</span>models<span style="color: #308080;">.</span>article_model <span style="color: #400000;">:as</span> article-model<span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:use</span> <span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> <span style="color: #1060b6;">"/"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">resp/redirect</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> articles<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> articles <span style="color: #1060b6;">"/articles"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/show-list</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch-list</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> view-article <span style="color: #1060b6;">"/article/:id"</span> <span style="color: #308080;">{</span>id <span style="color: #400000;">:id</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/show</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch</span> id<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> edit-article <span style="color: #1060b6;">"/article/edit/:id"</span> <span style="color: #308080;">{</span>id <span style="color: #400000;">:id</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">let</span> <span style="color: #308080;">[</span>item <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/fetch</span> id<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/form</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> update-article <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> id<span style="color: #308080;">}</span><span style="color: #308080;">)</span> <span style="color: #1060b6;">"Update"</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> update-article <span style="color: #308080;">[</span><span style="color: #400000;">:post</span> <span style="color: #1060b6;">"/article/update/:id"</span><span style="color: #308080;">]</span> <span style="color: #308080;">{</span><span style="color: #400000;">:keys</span> <span style="color: #308080;">[</span>id title body<span style="color: #308080;">]</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/update</span> id title body<span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">resp/redirect</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> view-article <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> id<span style="color: #308080;">}</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> new-article <span style="color: #1060b6;">"/article/new"</span> <span style="color: #308080;">[</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">common/layout</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-view/form</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> create-article<span style="color: #308080;">)</span> <span style="color: #1060b6;">"Create"</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> create-article <span style="color: #308080;">[</span><span style="color: #400000;">:post</span> <span style="color: #1060b6;">"/article/create"</span><span style="color: #308080;">]</span> <span style="color: #308080;">{</span><span style="color: #400000;">:keys</span> <span style="color: #308080;">[</span>title body<span style="color: #308080;">]</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/create</span> title body<span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">resp/redirect</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> articles<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defpage</span> delete-article <span style="color: #308080;">[</span><span style="color: #400000;">:post</span> <span style="color: #1060b6;">"/delete/:id"</span><span style="color: #308080;">]</span> <span style="color: #308080;">{</span>id <span style="color: #400000;">:id</span><span style="color: #308080;">}</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">article-model/delete</span> id<span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">resp/redirect</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> articles<span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
</pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;"><b>article_templates.clj</b></div><div style="font-weight: normal; margin-bottom: 0cm;"><br />
</div><div style="font-weight: normal; margin-bottom: 0cm;"></div><pre style="background: #f6f8ff; color: #000020;"><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">ns</span> optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_templates
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:require</span> <span style="color: #308080;">[</span>optimalist<span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>helpers<span style="color: #308080;">.</span>widget <span style="color: #400000;">:as</span> widget<span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:use</span> <span style="color: #308080;">[</span>noir<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span>hiccup<span style="color: #308080;">.</span>core<span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">alias</span> <span style="color: #406080;">'article</span> <span style="color: #406080;">'optimalist</span><span style="color: #308080;">.</span>views<span style="color: #308080;">.</span>article_pages<span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> show <span style="color: #308080;">[</span>article<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:h1</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> article<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> article<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> article<span style="color: #308080;">/</span>articles<span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"Back"</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> list-item <span style="color: #308080;">[</span>item<span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">let</span> <span style="color: #308080;">[</span>id <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:id</span> item<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span> <span style="color: #308080;">[</span><span style="color: #400000;">:div</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:title</span> item<span style="color: #308080;">)</span><span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">:body</span> item<span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:form</span> <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">str</span> <span style="color: #1060b6;">"form-"</span> id<span style="color: #308080;">)</span> <span style="color: #400000;">:method</span> <span style="color: #1060b6;">"POST"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:action</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> article<span style="color: #308080;">/</span>delete-article <span style="color: #308080;">{</span><span style="color: #400000;">:id</span> id<span style="color: #308080;">}</span><span style="color: #308080;">)</span><span style="color: #308080;">}</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">widget/view-link</span> article<span style="color: #308080;">/</span>view-article id<span style="color: #308080;">)</span> <span style="color: #1060b6;">" | "</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">widget/edit-link</span> article<span style="color: #308080;">/</span>edit-article id<span style="color: #308080;">)</span> <span style="color: #1060b6;">" | "</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">widget/delete-link</span> id<span style="color: #308080;">)</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> show-list <span style="color: #308080;">[</span>articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:div</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">[</span>item articles-list<span style="color: #308080;">]</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">list-item</span> item<span style="color: #308080;">)</span><span style="color: #308080;">)</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> article<span style="color: #308080;">/</span>new-article<span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"New"</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span>
<span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">defn</span> form <span style="color: #308080;">[</span>url button-text <span style="color: #308080;">&</span> <span style="color: #308080;">[</span>title body <span style="color: #308080;">&</span> rest<span style="color: #308080;">]</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:form</span> <span style="color: #308080;">{</span><span style="color: #400000;">:method</span> <span style="color: #1060b6;">"POST"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:action</span> url<span style="color: #308080;">}</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:input</span> <span style="color: #308080;">{</span><span style="color: #400000;">:name</span> <span style="color: #1060b6;">"title"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:type</span> <span style="color: #1060b6;">"text"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:value</span> title<span style="color: #308080;">}</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:textarea</span> <span style="color: #308080;">{</span><span style="color: #400000;">:name</span> <span style="color: #1060b6;">"body"</span><span style="color: #308080;">}</span> body<span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:input</span> <span style="color: #308080;">{</span><span style="color: #400000;">:type</span> <span style="color: #1060b6;">"submit"</span><span style="color: #0066ee;">,</span> <span style="color: #400000;">:value</span> button-text<span style="color: #308080;">}</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:br</span><span style="color: #308080;">]</span>
<span style="color: #308080;">[</span><span style="color: #400000;">:a</span> <span style="color: #308080;">{</span><span style="color: #400000;">:href</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">url-for</span> article<span style="color: #308080;">/</span>articles<span style="color: #308080;">)</span><span style="color: #308080;">}</span> <span style="color: #1060b6;">"Back"</span><span style="color: #308080;">]</span><span style="color: #308080;">]</span><span style="color: #308080;">)</span></pre><div style="font-weight: normal; margin-bottom: 0cm;"></div><div style="font-weight: normal; margin-bottom: 0cm;"></div></div>Dmitryhttp://www.blogger.com/profile/17434341697941378483noreply@blogger.com3