Radiance - это среда веб -приложений, которая похожа на веб -структуру, но более общая, более гибкая. Это должно позволить вам писать личные веб -сайты и, как правило, легко развернутые приложения и таким образом, что их можно использовать практически на любой настройке без необходимости подвергать особой адаптации.
Сияние и связанные с ними модули и приложения распределены через QuickLisp в отдельном расстоянии. Чтобы установить сияние, делай:
(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")
(ql:quickload :radiance)
Оттуда вы сможете загружать и использовать любой вид модуля сияния, как Purplish непосредственно через Quicklisp quickload .
Вы можете найти учебник, который вводит сияние и большинство важных концепций, и исследует, как написать веб -приложение в целом. Это должно дать вам хорошее представление о том, как делать вещи, и дать вам указатели о том, где смотреть, если вам нужна конкретная функция. В последней части он также вступит в фактическую настройку и развертывание установки Radiance на производственном сервере.
Самое основное, что вы, скорее всего, хотите сделать, это подавать какой -то HTML. Итак, давайте поработаем к этому и постепенно расширим это. Прежде чем мы сможем начать, нам нужно запустить сияние.
( ql :quickload :radiance )
( radiance :startup) Если это ваша первая настройка сияния, вы получите примечание об этом с помощью модуля r-welcome . Это также должно дать вам ссылку, которую вы можете открыть в своем браузере, чтобы увидеть небольшую страницу приветствия. Пока мы просто захотим поставить нашу собственную маленькую страницу вместе с ней.
( in-package :rad-user )
(define-page example " /example " ()
( setf (content-type *response* ) " text/plain " )
" Hi! " )Посещение Localhost: 8080/Пример теперь должен просто показать «Привет». Действительно, довольно скучно. Итак, давайте выплюнем немного HTML. На данный момент мы будем использовать CL-W-WHO, так как это очень просто. Сначала загрузите его, затем запустите следующее:
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " ))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " )))))))Через перекомпилируйте и обновляется позже, и у нас происходит немного стиля шрифта. Далее мы, вероятно, захотим добавить в него файл CSS, чтобы правильно его стилизовать. Мы могли бы обслуживать CSS, используя и другую страницу, но это не лучший способ сделать вещи в долгосрочной перспективе.
Вместо этого давайте посмотрим, как создать модуль, который позволит нам организовать вещи более упорядоченным образом. Вы можете создать файлы для модуля вручную, но сейчас мы рассмотрим автоматически сгенерированный скелет, который может предоставить вам Radiance.
(create-module " example " ) Это должно вернуть вам путь, по которому проживает модуль. Он должен содержать систему ASDF, основной файл LISP и две папки, static и template . Удивительно, но static папка - это то, где статически обслуживаются файлы, а template предназначен для документов шаблона, если вы используете систему шаблонов.
Давайте откроем example.lisp Лип и перенесем наш пример страницы с нее.
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " ))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " ))))))) Страницы идентифицированы символом имени. Поскольку у нас теперь есть свой собственный модуль, и, следовательно, наш собственный пакет, примерный символ примера не будет таким же, как и тот, который мы использовали ранее. Нам просто нужно удалить страницу в пакете rad-user чтобы избежать столкновения.
(remove-page ' rad-user::example) Обязательно загрузите пример файла всякий раз, когда вы меняете его сейчас для вступления в силу. Далее давайте создадим простой файл CSS, чтобы немного пообщаться. Файл будет example.css размещен в static папке. Вот образец CSS, если вы не хотите писать свой собственный.
body {
font-family : sans-serif;
font-size : 12 pt ;
background : # EEE ;
}
header {
text-align : center;
}
main {
width : 800 px ;
margin : 0 auto 0 auto;
background : # FFF ;
padding : 10 px ;
border : 1 px solid # BBB ;
border-radius : 5 px ;
}Затем нам нужно изменить наш HTML, чтобы на самом деле ссылаться на лист стилей. Чтобы получить адрес на таблицу стилей, нам придется использовать систему маршрутизации Radiance. Не волнуйтесь, это не очень хлопот.
(define-page example " /example " ()
( cl-who :with-html-output-to-string (o)
( cl-who :htm
( :html
( :head ( :title " Example Page " )
( :link :rel " stylesheet " :type " text/css "
:href (uri-to-url " /static/example/example.css " :representation :external )))
( :body ( :header ( :h1 " Couldn't Be Simpler. " ))
( :main ( :p " Trust me on this one. " ))))))) Обновите страницу, и вуаля, теперь у нее есть немного пицца. Вы, вероятно, понадобитесь объяснением всего бизнеса uri-to-url . Объяснение его в полном объеме обрабатывается секциями, следующих за этим, но суть его в том, что он гарантирует, что ссылка на статический файл правильно разрешена при любой настройке.
Одна из самых центральных концепций в сиянии - это URI. URI - это объект, который состоит из списка доменов, дополнительного номера порта и пути (см. uri ). По сути, это урезанная версия общего URI, и поэтому не включает схему, запрос или фрагментную часть. Другое важное отличие состоит в том, что domains URI используются в нескольких точках по всей структуре, как для захвата мест, так и для обработки сопоставления диспетчеры.
Обратите внимание, что Uris изменяемы. Это важно для производительности, поскольку модификации URI должны происходить в нескольких частях, которые лежат на критическом пути. Однако в обычном случае не ожидается, что URI изменяются за пределами нескольких избранных функций. Модификация части URI неожиданным образом может привести к странному поведению.
У URI есть уникальное представление строк и может быть сериализовано на строку и снова проанализирована обратно в полный объект URI. URIS также может быть сброшен в файлы FASL в качестве литералов, поэтому излучение их от макросов в порядке. Синтаксис для URI заключается в следующем:
URI ::= DOMAINS? (':' PORT)? '/' PATH?
DOMAINS ::= DOMAIN ('.' DOMAIN)*
DOMAIN ::= ('a'..'Z' | '0'..'9' | '-')
PORT ::= ('0'..'9'){1, 5}
PATH ::= .*
Вы можете использовать uri-to-url чтобы превратить URI в конкретный URL. Отмена, кодирование и правильное форматирование всех деталей обрабатываются для вас автоматически.
См. uri , domains , port , path , matcher , uri-string , make-uri , make-url , ensure-uri , copy-uri , parse-uri , uri< , uri> , uri= , uri-matches , merge-uris , represent-uri , uri-to-url .
Чтобы инкапсулировать данные, которые отправляются в и обратно, у нас есть идея объекта запроса ( request ) и ответа ( response ). Объект запроса содержит URI, который представляет, в какое место проходит запрос, и все данные, содержащиеся в полезной нагрузке HTTP, такие как Post, Get, заголовок и переменные cookie. Объект ответа содержит код возврата, заголовки, файлы cookie и фактические данные тела.
Во время обработки запроса эти два объекта должны всегда присутствовать и связаны с *request* и *response* . Они инкапсулируют много очень важной информации, которая необходима для создания динамических страниц. Кроме того, запрос содержит непрозрачную таблицу data , в которой вы можете хранить произвольные данные. Это полезно, когда вам нужно обмениваться фрагментами информации между отдельными частями системы, которые могут быть достигнуты во время выполнения запроса.
Запросы не обязательно должны поступать с HTTP -сервера. Чтобы проверить вещи, вы также можете построить запрос самостоятельно и программно отправлять его. Как бы то ни было, основной интерфейс для отправки запроса называется request . Это будет построить объект запроса и ответа для вас и соответствующим образом обрабатывать URI. Если вы хотите сделать это самостоятельно и действительно просто отправить полный объект запроса, вы можете использовать execute-request .
Для фактической обработки запроса см. Диспетчеры, страницы и конечные точки API.
См. *request* , *response* , *default-external-format* , *default-content-type* , request , uri , http-method , body-stream , headers , post-data , get-data , cookies , user-agent , referer , domain , remote , data , issue-time , response , data return-code , external-format , Externa- content-type , Cookie, Cookie, Cookie, Cookie, Cookie, Velecties, Cookie, Cookie, name , headers , cookies , Cookie, Velecties, cookie , Cookie, Cookie, value . domain , path , expires , http-only , secure , cookie-header , cookie , get-var , post-var , post/get , header , file , redirect , serve-file , request-run-time , *debugger* , handle-condition , render-error-page , execute-request , set-data , запрос запрос, request
Перед тем, как запрос может быть отправлен, он проходит через то, что называется система маршрутизации. В отличие от других рамок, где «маршруты» определяют, что обрабатывает запрос, в сиянии маршрут ( route ) является формой переводчика URI. Эта часть системы является то, что отвечает за создание и поддержку двух «вселенных», внутренней и внешней.
Внутренняя вселенная - это единственные фактические веб -приложения, в которых живут. Внешняя вселенная - это та, в которой живет HTTP -сервер и пользователь веб -сайта. Это различие необходимо для того, чтобы в какой -то момент он позволил вам в какой -то момент писать веб -приложения, не беспокоясь о том, как потенциальная настройка на сервере может выглядеть в какой -то момент. Вам не нужно беспокоиться о том, какой домен, порт, настройка пути может потребоваться для запуска вашего приложения. С другой стороны, это позволяет вам, как Webadmin, настраивать и запустить систему в ваши точные желания, не опасаясь разорвать вещи.
Все это облегчено по маршрутам, из которых есть два вида: картирование и маршруты изменения. Картовые маршруты отвечают за превращение URI из внешней вселенной в одну из внутренней вселенной. Обычно это включает в себя отключение домена верхнего уровня и, возможно, выполнение картирования субдоменов. Маршруты реверса делают наоборот- они переходят от внутренней вселенной к внешней. Это необходимо для того, чтобы сделать ссылки на ваших обслуживаемых страницах, относящихся к ресурсам, которые фактически доступны извне. Обычно это включает в себя изменение отображения субдомена и снова добавление домена верхнего уровня.
Маршруты могут выполнять произвольную работу. На самом базовом уровне это просто функции, которые каким -то образом изменяют URI. Это позволяет вам создавать очень гибкую систему, которая должна быть достаточно мощной, чтобы удовлетворить все ваши потребности в качестве администратора. Как писатель приложений, вам просто нужно убедиться, что использовать external-uri или uri-to-url на всех ссылках, которые вы вкладываете в свои страницы.
См. route , name , direction , priority , translator , route , remove-route , list-routes , define-route define-matching-route , define-target-route define-string-route internal-uri external-uri
Наконец, мы приходим к части, которая на самом деле генерирует контент для запроса. Диспетчеры URI - это подкласс URI, который также несет имя, функцию и приоритет. Жизнь в списке с приоритетом, который обрабатывается всякий раз, когда прибывает запрос. URI запроса соответствует каждому диспетчеру. Функция первого диспетчера, который соответствует, затем выполняется.
И это все. Функция диспетчера отвечает за установление необходимых значений в объекте ответа для доставки содержания страницы. Для этого он может непосредственно установить поле data объекта ответа, либо вы можете вернуть соответствующее значение из функции. Radiance принимает только четыре типа значений: stream , pathname , string и (array (unsigned-byte 8)) .
Если диспетчер URI не имеет явного приоритетного номера, его приоритет над другими определяется специфичностью URI. Смотрите функцию сортировки URI uri> для объяснения того, как именно это рассчитано.
См. uri-dispatcher , name , dispatch-function , priority , uri-dispatcher , remove-uri-dispatcher , list-uri-dispatchers , uri-dispatcher> , define-uri-dispatcher , dispatch
Страницы - это то, что вы, вероятно, будете использовать для определения ваших фактических функций обслуживания контента. Тем не менее, страница-это всего лишь URI-Dispatcher с некоторой дополнительной функциональностью в макросе определения, что облегчает вам ситуацию. В частности, это расширяемые варианты, для которых вы можете найти объяснение ниже.
Есть пара страниц по умолчанию, созданные самим сиянием. Во -первых, есть страницы favicon и robots , которые просто обслуживают соответствующие файлы из static/ каталога Radiance. Вы, вероятно, захотите либо предоставить свои собственные страницы для этого, либо обновить файлы на вашем производственном сервере.
Тогда есть static страница, которая отвечает за обслуживание статического содержимого для всех веб -приложений и модулей. Он должен быть активным в любом домене и всегда на пути /static/... где ... должна иметь форму, в которой первый каталог - это имя модуля, а остальное - это путь в static/ каталоге этого модуля. Это позволяет вам всегда иметь возможность ссылаться на статические файлы, такие как CSS, JS и изображения по общему пути.
Наконец, есть страница api , которая отвечает за обработку отправки конечных точек API, которые объясняются в следующем разделе. Страница действует аналогично статичному, захватив путь /api/... на всех доменах.
См. page , remove-page , define-page
Radiance обеспечивает интегрированную поддержку для определения API REST. Это не просто привязанная функция, а скорее потому, что большинство современных приложений хотят предоставить какой-либо API, и потому что Radiance консультирует определенный способ написания ваших приложений, которые обязательно включают конечные точки API.
Концептуально, конечные точки API - это функции, которые можно вывести по запросу браузера. Их ответ затем сериализуется на формат, который читается запрашивателем, каким бы он ни был. Однако важно помнить, что конечные точки API должны быть использованы как пользователями, так и программами. Radiance поощряет это, потому что, как правило, любое действие, которое можно выполнять программно через API, также должен быть выполнен пользователем каким -то образом. Чтобы избежать дублирования, они могут быть связаны.
Таким образом, обычно любое действие модификации данных должно обеспечиваться через конечную точку API, которая реагирует немного по -разному в зависимости от того, запрашивает ли его пользователь или приложение. В случае с пользователем он обычно должен перенаправить обратно на соответствующую страницу, а в случае приложения он должен предоставить полезную нагрузку данных в читаемом формате.
Первой частью всего этого является система форматов API, которая отвечает за сериализацию данных в некотором указанном формате. По умолчанию предоставляется только формат на основе S-экспрессии, но можно легко загрузить выход JSON.
Вторая часть - это спецификация параметра Post/Get browser . Если этот параметр содержит точную строку "true" , то запрос API рассматривается как исходящий от пользователя, и, следовательно, перенаправление, а не полезную нагрузку данных.
Ваше приложение должно использовать эти вещи, чтобы обеспечить правильно интегрированный API. Теперь фактическое определение конечной точки состоит из имени, необработанной функции, списка Lambda, описывающего аргументы функции, и функцию анализа запроса. Как правило, для ваших аргументов, только необходимые и дополнительные аргументы имеют смысл. В конце концов, HTTP -запрос имеет только «аргументы ключевых слов», которые он может предоставить, и они могут либо присутствовать, либо отсутствовать.
Название конечной точки API также служит идентификатором, который сообщает вам, где вы можете его достичь. Конечные точки API живут на пути /api/ , за которым следует имя конечной точки. Таким образом, вы несете ответственность за префикс ваших конечных точек с именем вашего модуля или приложения, чтобы не случайно спотыкаться о других конечных точках. Это отличается от диспетчеров URI, потому что конечные точки API должны точно соответствовать и не допускают никакой двусмысленности или обработки пути. Таким образом, каждая конечная точка должна иметь уникальный путь, который также может немедленно служить именем.
Необработанная функция - это функция, для которой API обеспечивает интерфейс. Он отвечает за выполнение запрошенного действия и возврат соответствующих данных, как описано выше. Для возврата форматированных данных API см. api-output . Для перенаправления в случае запроса браузера см. redirect .
Наконец, функция анализа запроса отвечает за принятие объекта запроса, извлечение аргументов, которые нужна функции, и, наконец, вызов этой функции с соответствующими аргументами- если это возможно. Функция анализа может сигнализировать об ошибке api-argument-missing если отсутствует необходимый аргумент. Излишные аргументы следует игнорировать.
Вы также можете программно вызвать конечную точку API с помощью call-api или имитировать звонок с запросом с помощью call-api-request без необходимости проходить весь механизм отправки URI.
Подобно страницам, определения конечных точек API также принимают расширяемые параметры, которые упрощают определение. См. Следующий раздел для объяснения вариантов.
См. api , *default-api-format* , handler serialize request-handler *serialize-fallback* , api-format list-api-formats remove-api-format remove-api-endpoint , list-api-endpoints define-api-format name api-serialize api-endpoint api-output argslist api-endpoint call-api-request call-api , define-api
Варианты - это способ предоставления расширяемого макроса определения. Это полезно, когда структура обеспечивает общий способ определения чего -либо, но другие части могут захотеть обеспечить расширения, чтобы сделать общие операции короче. Например, общей задачей является ограничение конечной точки страницы или API людьми, которые имеют необходимые учетные данные доступа.
Чтобы облегчить это, Radiance предоставляет довольно общий механизм опций. Параметры разделены на тип опции, который обозначает, к каким определению макрос принадлежит опция. Radiance предоставляет типы опций api и page из коробки.
Каждый вариант имеет ключевое слово для имени и функцию расширителя, которая должна принять ряд аргументов, в зависимости от типа опции. В качестве аргументов всегда представлено название определяемой вещи, список форм тела определения и окончательное, необязательное значение, которое было предоставлено опции в списке опций, если оно было упомянуто вообще. Эта функция расширения затем отвечает за преобразование форм тела макроса определения. Он также может излучать вторую форму, которая находится за пределами самого определения, чтобы позволить настройку среды каким -либо образом.
См. option , option-type , name , expander , option , remove-option , list-options , define-option , expand-options
Концепция модуля необходима для сияния. Это служит представлением «части» целого. На техническом уровне модуль - это пакет с специальными метаданными, прикрепленными к нему. Он обеспечивается modularize системой и используется для облегчения крючков и триггеров, интерфейсов и отслеживания нескольких других деталей информации.
Для вас это означает, что вместо стандартного defpackage вы должны использовать форму define-module для определения вашего основного пакета. Синтаксис такой же, как defpackage , но включает в себя некоторые дополнительные параметры, такие как :domain , который позволяет указать основной домен, в котором должен работать этот модуль (если есть).
Система модуля также позволяет связывать систему ASDF к модулю. Если это сделано, то система ASDF становится «виртуальным модулем». Чтобы сделать это, вы должны добавить три варианта в определение вашей системы:
:defsystem-depends-on (:radiance)
:class "radiance:virtual-module"
:module-name "MY-MODULE"
Это позволяет сиянию идентифицировать и связывать информацию о системе ASDF с модулем. Для автоматического создания необходимых определений системы и модулей для нового create-module см.
See virtual-module , virtual-module-name , define-module , define-module-extension , delete-module , module , module-p , module-storage , module-storage-remove , module-identifier , module-name , current-module , module-domain , module-permissions , module-dependencies , module-required-interfaces , module-required-systems , module-pages , module-api-endpoints , describe-module , find-modules-directory , *modules-directory* , create-module
Одним из механизмов, которые обеспечивает Radiance, позволяет интегрировать модули друг в друга, является крючки. Крюки позволяют выполнять произвольную функцию в ответ на какое -то событие. Например, программное обеспечение для форума может настроить крючок, который запускается всякий раз, когда создается новый пост. Затем расширение может определить триггер на этом крючке, который выполняет дополнительные задачи.
Крюк может иметь определенное количество триггеров, определенных на нем, но вы должны убедиться, что триггер не займет слишком много времени, так как запуск крюка - это операция блокировки, которая не завершится, пока все триггеры не завершены. Таким образом, долгосрочная операция запуска может задержать ответ на запрос слишком долго.
Иногда крючки должны функционировать больше как переключатели, где они могут быть «включены» в течение долгого времени, пока они снова не выключат «отключены». Если новые триггеры определяются в течение этого времени, их следует называть автоматически. Это то, что облегчает define-hook-switch . Он производит два крючка. После того, как первый был вызван, любой триггер, который определяется на нем позже, автоматически называется до тех пор, пока второй крючок не будет запущен. Это позволяет правильному функционировать триггеры, такие как server-start даже если триггер определяется только после того, как сервер уже запускается.
См. list-hooks , define-hook , remove-hook , define-trigger , remove-trigger , trigger , define-hook-switch
Чтобы не стать монолитным, и для того, чтобы разрешить расширяемые бэкэнды, сияние включает в себя систему интерфейса. В наиболее общем смысле интерфейс дает перспективу относительно того, как некоторые функции, макросы, переменные и т. Д. должны работать, но на самом деле не реализует их. Фактическая функциональность, которая делает все, что интерфейс, описывает работу, отталкивается к реализации. Это позволяет пользователям кодироваться против интерфейса и использовать его предоставленную функциональность, не привязавшись к какому -либо конкретному бэкэну.
Для конкретного примера, допустим, есть интерфейс для базы данных. Это разумно, поскольку существует много различных видов баз данных, которые предлагают много разных способов взаимодействия, но все же все также предлагают некоторые очень распространенные операции: хранение данных, получение данных и изменение данных. Таким образом, мы создаем интерфейс, который предлагает эти общие операции. Затем он зависит от конкретного вида базы данных, чтобы сделать фактическую работу. Как писатель приложений, вы можете использовать интерфейс базы данных, и вместе с ним он автоматически работает с множеством различных баз данных.
Помимо того, что писатели приложений является преимуществом, развязка, которую предоставляют интерфейсы, также означает, что системный администратор может написать свою собственную реализацию с относительной легкостью, если их конкретные требования не будут выполнены существующими реализациями. Благодаря непрозрачности интерфейсов, реализация может обеспечить мост к чему -то, что работает в процессе LISP, и что -то, что является совершенно внешним. Это оставляет большой выбор открытым для администратора производственной системы, чтобы они могли выбрать именно то, что им нужно.
На практике интерфейсы являются специальными видами модулей и, следовательно, специальными видами пакетов. В рамках своего определения они включают серию определений для других привязков, таких как функции, переменные и т. Д. Поскольку это пакет, в качестве пользователя вы можете использовать компоненты интерфейса так же, как вы бы использовали что -либо еще в любом другом пакете. Там нет разницы. Как писатель реализации, вы просто переопределяете все определения, которые обрисовывает интерфейс.
Чтобы фактически загрузить модуль, который использует интерфейс, реализация для интерфейса должна быть загружена заранее. В противном случае макросы не могли работать должным образом. Таким образом, чтобы разрешить в зависимости от интерфейсов в определении системы ASDF без необходимости ссылаться на конкретную реализацию, Radiance обеспечивает расширение ASDF. Это расширение позволяет добавить список как (:interface :foo) в ваш список :depends-on . Затем Radiance разрешит интерфейс к конкретной реализации при его загрузке модуля.
Radiance предоставляет кучу стандартных интерфейсов. Каждый из этих интерфейсов имеет по крайней мере одну стандартную реализацию, предоставленную контрольными сияниями. Интерфейсы:
adminauthbancachedatabaseloggermailprofilerateserversessionuserИнтерфейсы описаны подробно ниже.
См. interface , interface-p , implementation , implements , reset-interface , define-interface-extension , find-implementation , load-implementation , define-interface , define-implement-trigger
Чтобы позволить запустить несколько экземпляров сияния с различными настройками на одной и той же машине, Radiance обеспечивает то, что он называет системой среды. Среда - это в основном набор файлов конфигурации и времени выполнения для самого сияния и всех загруженных модулей. Конфигурация Radiance также включает в себя отображение интерфейса с выбранной реализацией и, таким образом, решает, что следует выбрать, если запрашивается интерфейс.
Конкретная среда, которая используется, выбирается не позднее, когда называется startup , и самая ранняя, когда модуль загружается. В последнем случае предоставляются интерактивные перезагрузки, чтобы позволить вам выбрать среду. Это необходимо, так как в противном случае сияние не сможет разрешить картирование интерфейса.
В рамках системы окружающей среды Radiance предоставляет вам систему конфигурации, которую вы можете-и, вероятно, должны-использовать для вашего приложения. Это гарантирует, что настройки должным образом мультиплексированы для каждой среды, и что настройки всегда постоянны. В нем также используется формат хранения, читаемый на человеке, так что файлы могут быть прочитаны и изменены, не требуя каких-либо специальных инструментов.
Смотрите вездесущему для фактической обработки и инструктирование использования конфигурационного хранилища. Просто обратите внимание, что вместо функций value Radiance предоставляет функции config .
Помимо файлов конфигурации, среда также предоставляет последовательные местоположения хранилища для файлов данных времени выполнения, таких как загрузка пользователей, файлы кэша и т. Д. Вы можете получить это место, используя environment-module-directory и environment-module-pathname . При хранении загрузок и кэша модуль должен использовать эти пути, чтобы представить последовательный интерфейс администратору.
В развернутой системе может быть желательно, чтобы изменить местоположение путей хранения окружающей среды, и в этом случае администратору рекомендуется предоставить новые методы на environment-directory и environment-module-directory для настройки поведения по желаемому. См. Также связанные строки документации для получения дополнительной информации и действий по умолчанию.
Окружающая среда также позволяет администратору переопределять. Использование :static и :template для environment-module-directory дает вам путь к хранению файлов, которые должны переопределить стандартный шаблон модуля и статические файлы. Пути внутри соответствующих каталогов должны соответствовать путям собственных исходных файлов модуля. Обратите внимание, что статические и шаблонные файлы, которые модуль фактически использует, кэшируются во время загрузки модуля и, следовательно, не будут изменяться, если изображение LISP не будет перезагружено, или исходные файлы модуля не будут перезагружены.
См. environment-change , environment , environment-directory environment-module-directory окружающей @static , mconfig-pathname environment-module-pathname , check-environment , mconfig-storage , defaulted-mconfig mconfig @template config defaulted-config static-file template-file
Иногда системы развиваются в обратном порядке несовместимыми. В этом случае, чтобы существующие настройки продолжали функционировать с новой версией, необходима миграция данных времени выполнения. Radiance предлагает систему для автоматизации этого процесса и обеспечения плавного обновления.
Миграция между версиями должна происходить автоматически во время последовательности запуска Radiance. Как администратор или автор, вам не нужно выполнять какие -либо дополнительные шаги для миграции. Однако, как автор модуля, вам, естественно, придется предоставить код для выполнения необходимых шагов миграции данных для вашего модуля.
Для того, чтобы модуль был мигрируется, его необходимо загрузить системой ASDF, которая имеет спецификацию версии. Версия должна следовать стандартной схеме пунктирных чисел с дополнительной хэш -версией, которую можно добавить в конце. Затем вы можете определить этапы миграции между отдельными версиями, используя define-version-migration . После определения Radiance автоматически заберут конкретные версии и выполнит необходимые миграции в последовательности, чтобы достичь текущей целевой версии. Для получения дополнительной информации о точной процедуре и о том, что вы можете сделать, см. migrate и migrate-versions .
См. last-known-system-version , migrate-versions , define-version-migration , ready-dependency-for-migration , ensure-dependencies-ready , versions , мигрируйте, migrate
Наконец, Radiance обеспечивает стандартную последовательность запуска и выключения, которая должна обеспечить надлежащую настройку и подготовку ситуации, а затем снова хорошо очищена. Большая часть этой последовательности просто гарантирует, что определенные крючки вызываются в правильном порядке и в подходящее время.
Хотя вы можете запустить сервер вручную, используя соответствующую функцию интерфейса, вы не должны ожидать, что приложения будут работать должным образом, если вы сделаете это таким образом. Многие из них ожидают, что определенные крючки будут вызваны, чтобы правильно работать. Вот почему вы всегда должны, если только вы точно не знаете, что делаете, используйте startup и shutdown для управления экземпляром сияния. Документация этих двух функций должна объяснить, какие именно запускаются крючки, а в каком порядке. Реализация может предоставить дополнительные, неопределенные определения на символах в пакете интерфейса, если указанные символы не экспортируются.
См. *startup-time* , uptime , server-start , server-ready , server-stop , server-shutdown , startup , startup-done , shutdown , shutdown-done , started-p
Эти интерфейсы распространяются с сиянием и являются частью основного пакета. Однако библиотеки могут предоставить дополнительные интерфейсы. Для реализаций стандартных интерфейсов разрешены следующие релаксации ограничений определения интерфейса:
Списки Lambda, которые содержат &key аргументы, могут быть расширены путем дальнейших аргументов ключевых слов, зависимыми от реализации. Lambda-Lists, которые содержат &optional , но No &key или &rest могут быть расширены дополнительными дополнительными аргументами. Списки Lambda, которые содержат только требуемые аргументы, могут быть расширены дополнительными дополнительными аргументами или ключевыми словами.
Этот интерфейс предусматривает страницу администрирования. Он должен использоваться для любого вида настроек пользователя, или отображения системной информации. Обратите внимание, что, несмотря на то, что его называют «администрацией», это не предназначено исключительно для администраторов системы. Страница должна быть использована для любого пользователя.
Страница администрирования должна иметь возможность отобразить категоризированное меню и панель. Панели предоставляются другими модулями и могут быть добавлены через admin:define-panel . Панели, которые дают доступ к конфиденциальным операциям, должны быть надлежащим образом ограничены с помощью опции :access и разрешения без декорации. Смотрите пользовательский интерфейс для объяснения разрешений.
Чтобы ссылаться на страницу администрирования или на определенную панель на ней, используйте тип ресурса page .
См. admin:list-panels , admin:remove-panel , admin:define-panel , admin:panel
Интерфейс аутентификации отвечает за привлечение пользователя к запросу. По этой причине он должен обеспечить некоторую способ, с помощью которого пользователь может аутентифицировать себя против системы. Как это сделано точно, зависит от реализации. Однако реализация должна предоставить страницу, на которой инициируется процесс аутентификации. You can get a URI to it through the page resource and passing "login" as argument.
You can test for the user currently tied to the request by auth:current . This may also return NIL , in which case the user should be interpreted as being "anonymous" . See the user interface for more information.
See auth:*login-timeout* , auth:page , auth:current , auth:associate
This interface provides for IP-banning. It must prevent any client connecting through a banned IP from seeing the content of the actual page they're requesting. Bans can be lifted manually or automatically after a timeout. The implementation may or may not exert additional effort to track users across IPs.
See ban:jail , ban:list , ban:jail-time , ban:release
The cache interface provides for a generic caching mechanism with a customisable invalidation test. You can explicitly renew the cache by cache:renew . To define a cached block, simply use cache:with-cache , which will cause the cached value of the body to be returned if the test form evaluates to true.
The exact manner by which the cached value is stored is up to the implementation and cache:get or cache:with-cache may coerce the cached value to a string or byte array. The implementation may support any number of types of values to cache, but must in the very least support strings and byte arrays.
The name for a cached value must be a symbol whose name and package name do not contain any of the following characters: <>:"/|?*. The variant for a cached value must be an object that can be discriminated by its printed (as by princ ) representation. The same character constraints as for the name apply.
See cache:get , cache:renew , cache:with-cache
This interface provides you with a data persistence layer, usually called a database. This does not have to be a relational database, but may be one. In order to preserve implementation variance, only basic database operations are supported (no joins, triggers, etc). Data types are also restricted to integers, floats, and strings. Despite these constraints, the database interface is sufficiently useful for most applications.
Note that particular terminology is used to distance from traditional RDBMS terms: a schema is called a "structure". A table is called a "collection". A row is called a "record".
Performing database operations before the database is connected results in undefined behaviour. Thus, you should put your collection creation forms ( db:create ) within a trigger on db:connected . Radiance ensures that the database is connected while Radiance is running, so using the database interface in any page, api, or uri dispatcher definitions is completely fine.
The functions for actually performing data storage are, intuitively enough, called db:insert , db:remove , db:update , db:select , and db:iterate . The behaviour thereof should be pretty much what you'd expect. See the respective docstrings for a close inspection. Also see the docstring of db:create for a lengthy explanation on how to create a collection and what kind of restrictions are imposed.
The database must ensure that once a data manipulation operation has completed, the changes caused by it will be persisted across a restart of Radiance, the lisp image, or the machine, even in the case of an unforeseen crash.
See database:condition , database:connection-failed , database:connection-already-open , database:collection-condition , database:invalid-collection , database:collection-already-exists , database:invalid-field , database:id , database:ensure-id , database:connect , database:disconnect , database:connected-p , database:collections , database:collection-exists-p , database:create , database:structure , database:empty , database:drop , database:iterate , database:select , database:count , database:insert , database:remove , database:update , database:with-transaction , database:query , database:connected , database:disconnected
This interface provides primitive logging functions so that you can log messages about relevant happenings in the system. The actual configuration of what gets logged where and how is up to the implementation and the administrator of the system.
See logger:log , logger:trace , logger:debug , logger:info , logger:warn , logger:error , logger:severe , logger:fatal
With the mail interface you get a very minimal facility to send emails. A variety of components might need email access, in order to reach users outside of the website itself. The configuration of the way the emails are sent --remote server, local sendmail, etc.-- is implementation dependant.
The mail:send hook provided by the interface allows you to react to outgoing emails before they are sent.
See mail:send
The profile interface provides extensions to the user interface that are commonly used in applications that want users to have some kind of presence. As part of this, the interface must provide for a page on which a user's "profile" can be displayed. The profile must show panels of some kind. The panels are provided by other modules and can be added by profile:define-panel .
You can get a URI pointing to the profile page of a user through the page resource type.
The interface also provides access to an "avatar image" to visually identify the user ( profile:avatar ), a customisable name that the user can change ( profile:name ), and field types to what kind of data is contained in a user's field and whether it should be public information or not ( profile:fields profile:add-field profile:remove-field ).
See profile:page , profile:avatar , profile:name , profile:fields , profile:add-field , profile:remove-field , profile:list-panels , profile:remove-panel , profile:define-panel , profile:panel
This interface provides for a rate limitation mechanism to prevent spamming or overly eager access to potentially sensitive or costly resources. This happens in two steps. First, the behaviour of the rate limitation is defined for a particular resource by rate:define-limit . Then the resource is protected through the rate:with-limitation macro. If the access to the block by a certain user is too frequent, the block is not called, and the code in the limit definition is evaluated instead.
Note that rate limitation is per-client, -user, or -session depending on the implementation, but certainly not global.
See rate:define-limit , rate:left , rate:with-limitation
This and the logger interface are the only interfaces Radiance requires an implementation for in order to start. It is responsible for accepting and replying to HTTP requests in some manner. The implementation must accept requests and relay them to the Radiance request function, and then relay the returned response back to the requester.
Note that the actual arguments that specify the listener behaviour are implementation-dependant, as is configuration thereof. However, if applicable, the implementation must provide for a standard listener that is accessible on localhost on the port configured in (mconfig :radiance :port) and is started when radiance:startup is called.
See server:start , server:stop , server:listeners , server:started , server:stopped
The session interface provides for tracking a client over the course of multiple requests. It however cannot guarantee to track clients perfectly, as they may do several things in order to cloak or mask themselves or falsify information. Still, for most users, the session tracking should work fine enough.
The session interface is usually used by other interfaces or lower-lying libraries in order to provide persistence of information such as user authentication.
See session:*default-timeout* , session:session , session:= , session:start , session:get , session:list , session:id , session:field , session:timeout , session:end , session:active-p , session:create
This interface provides for persistent user objects and a permissions system. It does not take care of authentication, identification, tracking, or anything of the sort. It merely provides a user object upon which to build and with which permissions can be managed.
See user:user for a description of permissions and their behaviour.
See user:condition , user:not-found , user:user , user:= , user:list , user:get , user:id , user:username , user:fields , user:field , user:remove-field , user:remove , user:check , user:grant , user:revoke , user:add-default-permissions , user:create , user:remove , user:action , user:ready , user:unready
This is an extension of the database interface. Any module implementing this interface must also implement the database interface. This interface provides some extensions to allow more expressive database operations that are only directly supported by relational database systems.
See relational-database:join , relational-database:sql
*environment-root* has been removed as per issue #28 and fix #29. It has instead been replaced by a more generic mechanism for environment directories, incorporated by the function environment-directory . If you previously customised *environment-root* , please now change environment-directory instead, as described in §1.11.template-file and static-file .user:id identifier for each user object, allowing you to reference users in databases and records more efficiently.:unique on db:select and db:iterate . If you'd like to support the continued development of Radiance, please consider becoming a backer on Patreon: