пятница, 19 февраля 2010 г.
Старый parenscript в gentoo-lisp-overlay
Внезапно обнаружил, что cl-closure-template не совместима с версией parenscript, представленной в gentoo-lisp-overlay. Пришлось добавить в свой форк более свежую версию parenscript и обновить зависимости для cl-closure-template.
суббота, 13 февраля 2010 г.
AJAX с cl-closure-template. Часть 2.
Тут хотел бы обратить внимание, что подобной красоты нельзя добиваться с помощью тем jquery UI, при чём, это принципиальный момент. jquery UI в некоторых случаях модифицирует оригинальную разметку, добавляя к ней несколько своих элементов, в других вообще её никак не изменяет, полагаясь полностью на использование CSS. Но для создания подобных красивых вещей необходимо создавать достаточно сложную разметку фактически ещё в момент генерации страницы. Таким образом нельзя на сервере создать каркас, а потом доработать его с помощью jquery UI до подобного уровня. Как бы факт, что приложения на базе jquery UI не хватает элегантности.
Вот, теперь я хочу иметь возможность создавать подобные панельки простым и эффективным способом, ведь ручное создание подобной разметки для каждой панели будет весьма трудоёмким. С помощью cl-closure-template проблема решается тривиально.
Сначала нужен шаблона для каркаса панели:
{template panel}
<table class="panel">
<tbody>
<tr>
<td class="TL"></td>
<td class="TM">
<span class="TML">{$title |noAutoescape}</span>
{if $close}
<div class="TRx"></div>
{/if}
</td>
<td class="TR"></td>
</tr>
<tr>
<td class="ML"></td>
<td class="MM">{$body |noAutoescape}</td>
<td class="MR"></td>
</tr>
<tr>
<td class="BL"></td>
<td class="BM"></td>
<td class="BR"></td>
</tr>
</tbody>
</table>
{/template}
Данный шаблон принимает три параметра:- close - признак того, надо ли создавать кнопку для закрытия панели.
- title - заголовок панели, в данном параметре можно передать не только текст, но и вообще произвольную разметку
- body - разметка для основного содержимое панели
CSS для данного шаблона можно посмотреть здесь (.panel). В оригинальном варианте автор использовал разметку на базе элементов div, но в этом случае требуется доработка панели после загрузки с помощью JavaScript-напильника, поэтому я выбрал вариант разметки на базе table.
В предыдущем сообщении я ввёл шаблон editable-text и с помощью него создавал пару простейших элементов управления. Использовался он следующим образом:
{call editable-text data="$name" /}Теперь, поместить результат работы этого шаблоны в красивую панельку можно следующим образом:{call panel}
{param title: 'Name' /}
{param close: true /}
{param body}
{call editable-text data="$name" /}
{/param}
{/call}Из чего видно, что результат вызова шаблона можно использовать как параметр для другого шаблона. Теперь можно не особо напрягаясь плодить подобные панельки в неограниченных колличествах :) Продолжение следует.
понедельник, 8 февраля 2010 г.
paredit
Попробовал использовать paredit, который все так хвалят, для редактирования лисп-кода: это ужасно - оно насильно поддерживает правильный баланс скобок, что полностью сбивает меня с толку. Я использую незакрытые s-выражения для выражения того, что мысль не закончена. Я ставлю последнюю скобку когда считаю, что выражение готово.
Наверное я просто недостаточно люблю скобочки (что правда, я их принял, но никогда не был в восторге от s-выражений) и поэтому идея "структурного редактирования" впечатления на меня не производит: предпочитаю иметь дело с текстом.
P.S. У меня есть схожая проблема с js2-mode, но там хоть можно спокойно удалять автоматически подставленные символы.
суббота, 6 февраля 2010 г.
AJAX с cl-closure-template. Часть 1.
В результате работы над текущим проектом графического редактора у меня зародилась идея, которая может быть применима к широкому кругу AJAX-приложений. Поскольку эта идея демонстрирует преимущества использования cl-closure-template, то решил рассказать об этом здесь. Ну и надеюсь, что будет целая серия постов, поэтому это "Часть 1". И да, в данный момент я не обладаю полным решением, поэтому оно будет изменяться от поста к посту.
Мне нравится как сделан github, поэтому я решил показать, как можно реализовать элементы управления в его стиле. Самый простой элемент (он встречается там достаточно часто) можно найти на странице проекта (если у вас есть проекты на github, то вы им безусловно пользовались), например строка с описанием проекта. По ней можно кликнуть мышкой, в результате чего появится форма, в которой можно редактировать это описание. Несколько обобщив концепцию данного элемента можно получить следующую схему:
Используемое мной решение заключается в том, что бы не заниматься "тонким редактированием" разметки, а производить полное её замещение на основе шаблонов, реализованных с помощью cl-closure-template: изменилась модель - создали новую разметку и подставили её вместо старой.
Для начала потребуются следующие шаблоны (которые в основном повторяют соответствующую разметку, используемую в github на момент написания данного поста):
Описываем маршрут для основной страницы:
Продолжение следует...
Мне нравится как сделан github, поэтому я решил показать, как можно реализовать элементы управления в его стиле. Самый простой элемент (он встречается там достаточно часто) можно найти на странице проекта (если у вас есть проекты на github, то вы им безусловно пользовались), например строка с описанием проекта. По ней можно кликнуть мышкой, в результате чего появится форма, в которой можно редактировать это описание. Несколько обобщив концепцию данного элемента можно получить следующую схему:
- Элемент модели данных (хранящийся на сервере) описывается с помощью фрагмента разметки
- Пользователь может активировать процесс редактирования этого элемента с помощью клика мышкой или иным образом
- В результате изначальная разметка скрывается и вместо неё появляется форма для ввода данных
- Пользователь может либо сохранить внесённые изменения, либо отказаться от них, в результате чего форма для ввода данных скрывается и снова появляется изначальная разметка, но модифицированная на основе введённых данных
Используемое мной решение заключается в том, что бы не заниматься "тонким редактированием" разметки, а производить полное её замещение на основе шаблонов, реализованных с помощью cl-closure-template: изменилась модель - создали новую разметку и подставили её вместо старой.
Для начала потребуются следующие шаблоны (которые в основном повторяют соответствующую разметку, используемую в github на момент написания данного поста):
{namespace example.githubway.view}
// Представление текстового элемента
{template editable-text}
<div class="editable-text" json="{$json}">
<p>
{$value}
<em class="edit-text">click to edit</em>
</p>
</div>
{/template}
// Форма для редактирование элемента
{template edit-text}
<form method="post" action="{$saveLink}" json="{$json}">
<input type="text" value="{$value}" name="value"></input>
<div class="form-actions">
<input type="submit" class="minibutton save" value="Save"></input>
<span class="fakelink cancel">cancel</span>
</div>
</form>
{/template}Эти шаблоны принимают следующие аргументы:- value - значение элемента
- saveLink - URL, который может использоваться для обновления значения элемента с помощью POST-запроса
- json - а вот это любопытно, это предыдущие аргументы в формате JSON. Зачем это надо? Напомню, что cl-closure-template позволяет создавать шаблоны, доступные как на сервеной, так и на клиентской стороне. Я хочу, что бы JavaScript-код получил полную информацию об элементе модели, которую он сможет в последующем использовать для передачи в эти же шаблоны для генерации новой разметки. Для этого я сохраняю эти данные в атрибуте json корневого элемента генерируемой разметки, что существенно упрощает последующую обработку.
function EObject (node) {
// node - узел DOM-дерева, представляющего элемент данных
if (node) {
this.node = node;
}
}
// Возвращает описание элемента модели в виде
// пригодном для генерации разметки с помощью шаблонов
EObject.prototype.modelData = function () {
var data = $.evalJSON(this.node.attr("json"))
data.json = $.toJSON(data);
return data;
};
// Метод для генерации основной разметки
EObject.prototype.toHTML = function (data) {
throw "Method toHTML not implemented";
};
// Метод для генерации формы редактирования
EObject.prototype.editForm = function (data) {
throw "Method editForm not implemented";
};
// Вспомогательный метод, позволяющий заменить разметку элемента
// таким образом, что бы в объекте осталась ссылка на актуальный
// элемент DOM-дерева
EObject.prototype.replaceHTML = function (html) {
this.node.after(html);
this.node = this.node.next();
this.node.prev().remove();
};
// Заменяет основное представление на форму редактирования
EObject.prototype.startEdit = function () {
this.replaceHTML(this.editForm(this.modelData()));
var obj = this;
$('.cancel:first', this.node).click(function (evt) { obj.endEdit(); });
this.node.ajaxForm({
dataType: 'json',
success: function (data) { obj.endEdit(data)},
error: function () { alert("Не удалось сохранить данные"); obj.endEdit() }
});
};
// Заменяет форму редактирования на основной вариант разметки
EObject.prototype.endEdit = function (data) {
this.replaceHTML(this.toHTML(data || this.modelData()));
// Похоже на хак, но мне кажется нормальным решением.
// Фактически, просто ре-инициализация объекта, которая,
// например, приведёт к активизации нужных обработчиков событий
this.constructor(this.node);
};Данный код использует библиотеке jquery и плагины jquery.form и jquery.json. Теперь, создать код для редактирования текстового элемента очень просто:function EditableText (node) {
if (node) {
// вызываем конструктор базового класса
EObject.prototype.constructor.call(this, node);
// Инициируем вызов процедуру редактирования по клику мышкой
var obj = this;
this.node.click(function (evt) { obj.startEdit(); });
}
}
// Инициализируем прототип
EditableText.prototype = new EObject;
// Необходимо, что бы иметь возможность создать новый класс, наследующий от EditableText
EditableText.prototype.constructor = EditableText;
// Генерируем разметку с помощью ранее описанного шаблона editable-text
EditableText.prototype.toHTML = example.githubway.view.editableText;
// Генерируем форму редактирования с помощью ранее описанного шаблона edit-text
EditableText.prototype.editForm = example.githubway.view.editText;И наконец инициализация при загрузке документа:$(document).ready(function () {
// Тоже довольно любопытно. Просто создаём новые объекты "в никуда".
// Но благодаря привязке этих объектов к DOM-дереву через обработчики
// событий они останутся "жить" и будут выполнять свою работу
$(".editable-text").each( function (i, node) { new EditableText($(node)); })
});Теперь серверная часть. Для демонстрации я использую очень простое приложение с одной страницей, на которой показывается имя и email некоего человека. Пользователь приложения может отредактировать их в стиле github. Код данного приложения зависит от:Сначала определим очень простую модель(defparameter *name* "Ivan Petrov")Здесь функции name-to-json и email-to-json генерируют plist, подходящий для использования с шаблонами, описанными в начале.
(defparameter *email* "Ivan.Petrov@example.com")
(defun with-json (&rest args)
(list* :json (json:encode-json-plist-to-string args)
args))
(defun name-to-json ()
(with-json :value *name*
:save-link (genurl 'save-name)))
(defun email-to-json ()
(with-json :value *email*
:save-link (genurl 'save-email)))
Описываем маршрут для основной страницы:
(define-route main ("")
(example.githubway.view:page (list :name (name-to-json)
:email (email-to-json))))Ради экономии места здесь я не буду приводить текст шаблона example.githubway.view:page, укажу лишь, что в нём есть следующие строчки:{call editable-text data="$name" /}
{call editable-text data="$email" /}Теперь определяем обработчики для обновления данных об имени и email, ради упрощения какая-либо обработка ошибок опущена:(define-route save-name ("api/name" :method :post :content-type "application/json")
(setf *name*
(hunchentoot:post-parameter "value"))
(json:encode-json-plist-to-string (name-to-json)))
(define-route save-email ("api/email" :method :post :content-type "application/json")
(setf *email*
(hunchentoot:post-parameter "value"))
(json:encode-json-plist-to-string (email-to-json)))Вот и всё. Полный код данного приложения я включил в состав closure-template и посмотреть его можно здесь.Продолжение следует...
среда, 3 февраля 2010 г.
Спасём Утриш!
Продолжается сбор подписей в защиту уникальной природной территории, которую хотят уничтожить ради строительства правительственной дачи. Заказник Утриш может погибнуть. Просьба ко всем неравнодушным людям: зайдите, почитайте, поставьте свою подпись под обращением к президенту и правительству, разошлите ссылку друзьям и знакомым, размещайте в блогах и форумах, помогите спасти Утриш!
http://save-utrish.spb.ru/vote/
группа «Спасём Утриш!» : http://vkontakte.ru/club1183453
http://save-utrish.spb.ru/vote/
группа «Спасём Утриш!» : http://vkontakte.ru/club1183453
P.S. Почему тэг lisp? Что бы больше людей прочитало, извиняюсь :)
вторник, 2 февраля 2010 г.
EAPI 3 в gentoo-lisp-overlay
Сегодня, после обновления gentoo-lisp-overlay вдруг обнаружил, что система хочет откатить мой SBCL-1.0.34 до версии 1.0.31, и это вместо того, что бы обновить его до версии 1.0.35! Как вскоре выяснилось, это связано с тем, что Stelian Ionescu перевёл пакеты для SBCL, ClozureCL и CMUCL на использование EAPI 3. Извините, я конечно понимаю, что переход на новые возможности portage обычно начинается с оверлеев, но я использую gentoo-lisp-overlay на нескольких машинах, в том числе и на рабочих серверах и переходить на использование portage-2.2, до тех пор пока он не попал в стабильную ветку, у меня никакого желания нет.
В общем, сделал revert для этих коммитов с EAPI 3, сам обновил ebuild для SBCL до версии 1.0.35 и залил в свой форк.
понедельник, 1 февраля 2010 г.
И ещё форум о Clojure
Ну и ещё один новый форум, о Clojure - http://lisper.ru/forum/clojure. Пожалуй на этом стоит пока остановиться :)
Подписаться на:
Сообщения (Atom)
