(defun menu ()Т.е. ссылки жёстко задаются в теле программы. Поскольку здесь их всего 5 и они очень простые, то в данном случае это не создаёт больших проблем.
(list (list :link "/" :title "Главная")
(list :link "/about" :title "About")
(list :link "/articles" :title "Статьи")
(list :link "/resourses" :title "Ресурсы")
(list :link "/contacts" :title "Контакты")))
Однако, по мере роста приложения, а также в процессе изменения его структуры, проблема сохранения актуальности ссылок приобретает серьёзный характер.
Самый лучший способ решения данной проблемы использовать автоматическую генерацию ссылок. В RESTAS для этого есть специальная поддержка на базе функции restas:genurl. Например, с использованием данной функции вышеприведённый код можно было бы переписать следующим образом:
(defparameter *mainmenu*Здесь для генерации ссылок используется символ, связанный с конкретным маршрутом, а получающиеся ссылки будут учитывать базовый url, по которому подключается разрабатываемый модуль.
'((main . "Главная")
(about . "About")
(articles . "Статьи")
(resources . "Ресурсы")
(contacts . "Контакты")))
(defun menu ()
(iter (for (route . title) in *mainmenu*)
(collect (list :link (restas:genurl route)
:title title))))
Это очень простая ситуация, которая решается совершенно тривиальным образом. На сайте lisper.ru имеет место более сложный случай. Исходный код данного ресурса разбит на несколько совершенно независимых пакетов, которые объединяются в один сайт на основе механизма модулей. Простое использование restas:genurl здесь не подходит, поскольку маршруты, на которые ссылается главное меню, находятся в разных модулях. Для определения состава главного меню используется такое объявление:
(defparameter *mainmenu* `(("Главная" nil main)Здесь каждому элементу меню соответствует список, содержащий следующие элементы: заголовок, субмодуль (символ, который используется при вызове restas:mount-submodule), символ маршрута (указанный в restas:define-route) и возможно несколько ключевых параметров (параметров маршрута). А для непосредственной генерации ссылок используется такой код:
("Статьи" rulisp-articles restas.wiki:main-wiki-page)
("Планета" rulisp-planet restas.planet:planet-main)
("Форум" rulisp-forum restas.forum:list-forums)
("Сервисы" nil tools-list)
("Practical Common Lisp" rulisp-pcl rulisp.pcl:pcl-main)
("Wiki" rulisp-wiki restas.wiki:main-wiki-page)
("Файлы" rulisp-files restas.directory-publisher:route :path "")
("Поиск" nil google-search)))
(in-package #:rulisp)Наиболее интересной в данном коде является строка
(restas:with-submodule (restas:find-upper-submodule #.*package*)
(iter (for item in *mainmenu*)
(collect (list :href (apply #'restas:genurl-submodule
(second item)
(if (cdddr item)
(cddr item)
(last item)))
:name (first item)))))
(restas:with-submodule (restas:find-upper-submodule #.*package*)Дело в том, что генерация меню происходит каждый раз при генерации HTML-страницы для всех маршрутов, которые находятся в разных модулях и имеют различный контекст выполнения, а параметр *mainmenu* составлен с точки зрения самого верхнего модуля :rulisp, который используется для запуска приложения с помощью start.
Структура субмодулей в RESTAS образует иерархию и restas:find-upper-submodule позволяет найти нужный модуль выше по дереву, а макрос restas:with-submodule выполнить код в контексте найденного модуля. Таким образом, генерация ссылок работает всегда одинаково, не зависимо от контекста выполнения этого кода.
restas:find-upper-submodule и restas:with-submodule я добавил только сегодня, так что они пока есть только в git-версии RESTAS.
Комментариев нет:
Отправить комментарий