Radiance是一个Web应用程序环境,有点像Web框架,但更一般,更灵活。它应该让您可以轻松地编写个人网站,并以一种可以在任何设置上使用,而无需进行特殊适应的方式,并且可以轻松地进行部署应用程序。
辐射和相关的模块和应用程序通过QuickLisp在单独的区域中分布。要安装辐射,请执行:
(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")
(ql:quickload :radiance)
从那里开始,您应该能够通过QuickLisp的quickload直接加载并使用任何类型的辐射模块。
您可以在此处找到一个介绍辐射率和大多数重要概念的教程,并探讨如何在此处编写Web应用程序。它应该给您一种很好的感觉,并为您提供有关在哪里需要特定功能的指示。在最后一部分中,它也将进入生产服务器上的辐射安装的实际设置和部署。
您最有可能要做的最基本的事情是提供某种HTML。因此,让我们努力做到这一点,并逐渐扩展它。在开始之前,我们需要启动辐射。
( ql :quickload :radiance )
( radiance :startup)如果这是您第一次设置Radiance,您将使用r-welcome模块获得有关它的注释。它还应该为您提供一个可以在浏览器中打开的链接,以查看一些问候页面。现在,我们只想将自己的小页面与之一起。
( in-package :rad-user )
(define-page example " /example " ()
( setf (content-type *response* ) " text/plain " )
" Hi! " )访问Local主机:8080/示例现在应该显示“ HI”。确实很无聊。因此,让我们吐出一些HTML。目前,我们将使用Cl- 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业务的解释。完整解释它是由此后的部分来处理的,但是它的要旨是确保在任何设置下正确解决静态文件的链接。
Radiance中最中心的概念之一是URI。 URI是由域列表,可选端口号和路径列表组成的对象(请参阅uri )。从本质上讲,它是一般URI的脱衣舞版本,因此不包括模式,查询或片段部分。另一个重要的区别是,在整个框架中,乌里斯的domains都在捕获位置和处理调度匹配的整个框架中使用。
请注意,乌里斯是可变的。这对于性能很重要,因为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有效负载中包含的所有数据,例如帖子,GET,HEAD,HEADER和COOKIE变量。响应对象包含返回代码,标题,cookie和实际数据。
在处理请求期间,这两个对象必须始终存在并绑定到*request*和*response*变量。它们封装了生成动态页面所需的许多非常重要的信息。此外,请求包含一个不透明的data表,您可以在其中存储任意数据。当您需要在请求执行期间可以到达的系统各个部分之间交换信息时,这很有用。
请求不一定来自HTTP服务器。为了测试事项,您还可以自己构建请求并以编程方式发送。无论如何,调用请求的主要接口称为request 。这将为您构建一个请求和响应对象,并适当处理URI。如果您想自己执行此操作,并且实际上只需发送完整的请求对象,则可以使用execute-request 。
有关请求的实际处理,请参阅调度程序,页面和API端点。
See *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 , content-type , external-format , headers , cookies , cookie , name , 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
在派遣请求之前,它会经过所谓的路由系统。与其他框架不同,在其他框架中,“路由”指定了处理请求的内容,在Radiance A路线( route )是URI转换器的一种形式。系统的这一部分是负责创建和维护两个“宇宙”,一个内部和外部的部分。
内部宇宙是一个实际的Web应用程序。外部宇宙是HTTP服务器和网站的用户居住的一个。为了让您一只手,一只手编写Web应用程序,而不必担心服务器上的潜在设置可能会像某个时候看起来如何。您不必担心运行应用程序可能需要使用哪种域,端口,路径设置。另一方面,它使您可以作为一个网络介绍,可以自定义并运行系统,以确切的需求,而不必担心破坏事情。
这一切都是通过路线来促进的,其中有两种:映射和逆转路线。映射路线负责将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的一个子类,它也带有名称,功能和优先级。 LIVE在优先级列表中,每当请求到达时都会处理。请求的URI与每个调度程序匹配。然后执行匹配的第一个调度程序的功能。
就是这样。调度程序的函数负责在响应对象中设置必要的值以交付页面内容。为了做到这一点,它可以直接设置响应对象的data字段,也可以从函数返回适当的值。辐射仅接受四种类型的值: 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,在定义宏中具有一些额外功能,使您更容易。最值得注意的是可扩展的选项,您可以在下面找到一个说明。
Radiance本身设置了几个默认页面。首先是favicon和robots页面,它们只是为Radiance的static/目录提供各个文件。您可能需要为其提供自己的页面,或更新生产服务器上的文件。
然后是static页面,该页面负责为所有Web应用程序和模块提供静态内容。它应在任何域上都处于活动状态,并且始终在路径/static/...中始终具有...必须具有一个形式,其中第一个目录是模块的名称,其余的是该模块static/目录中的路径。这使您始终能够通过通用路径参考CSS,JS和图像等静态文件。
最后,有一个api页面,该页面负责处理API端点的调度,这将在以下部分中进行了解释。通过捕获所有域上的/api/...路径,该页面的作用与静态的页面相似。
请参阅page , remove-page , define-page
Radiance为REST API定义提供了集成支持。这不仅是一个已解决的功能,而且是因为大多数现代应用程序都希望提供某种API,并且因为Radiance建议某种撰写您的应用程序的方法,而该应用程序必然涉及API端点。
从概念上讲,API端点是通过浏览器请求可调用的函数。然后将它们的响应序列化为请求者可读的格式,无论是什么。重要的是要记住的是,API端点应该由用户和程序都可以使用。 Radiance鼓励这一点,因为通常可以通过API通过API执行的任何类型的操作也必须以某种方式执行。为了避免重复,可以将两者混合在一起。
因此,通常应通过API端点提供任何类型的数据修改操作,该操作根据用户还是应用程序请求的,其反应略有不同。对于用户,通常应将其重定向到适当的页面,在应用程序的情况下,应以可读格式提供数据有效负载。
所有这些的第一部分是API格式系统,该系统负责将数据序列化为某些指定格式。默认情况下,仅提供基于S-表达的格式,但是可以轻松加载获得JSON输出的贡献。
第二部分是browser帖子/获取参数的规范。如果该参数包含确切的字符串"true" ,则将API请求视为来自用户的,因此应输出重定向而不是数据有效负载。
您的应用程序应利用这些东西来提供正确集成的API。现在,一个实际的端点定义由名称,原始函数,一个描述该函数参数的lambda列表和请求解析函数组成。通常,对于您的论点,仅必需的论证和可选论点是有道理的。毕竟,HTTP请求仅具有可以提供的“关键字参数”,并且可以存在或丢失。
API端点的名称也用作标识符,告诉您可以在哪里到达。 API端点活在/api/路径上,然后是端点的名称。因此,您负责将端点用模块或应用程序的名称前缀,以免意外地绊倒其他端点。这与URI调度程序不同,因为API端点必须完全匹配,并且不允许对路径进行任何歧义或处理。因此,每个端点都必须具有独特的路径,也可以立即用作名称。
原始函数是API为接口提供的函数。它负责执行请求的操作并如上所述返回适当的数据。有关返回格式化的API数据,请参见api-output 。有关浏览器请求的重定向,请参见redirect 。
最后,请求解析函数负责取一个请求对象,从中提取函数所需的参数,最后用适当的参数调用该函数 - 如果可能的话。如果缺少所需的参数,解析功能可能会api-argument-missing错误发出信号。多余的参数应被忽略。
您还可以使用call-api编程地调用API端点,也可以使用call-api-request模拟请求调用,而无需浏览整个URI调度机制。
与页面类似,API端点定义还接受可扩展的选项,使定义更简单。有关选项的说明,请参见以下部分。
See api , *default-api-format* , *serialize-fallback* , api-format , remove-api-format , list-api-formats , define-api-format , api-output , api-serialize , api-endpoint , remove-api-endpoint , list-api-endpoints , api-endpoint , name , handler , argslist , request-handler , 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系统提供,用于促进钩子和触发器,接口以及其他一些信息的跟踪。
这对您意味着的是,您应该使用define-module表单来定义主包,而不是标准的defpackage 。语法与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
为了避免变得整体,为了允许可扩展的后端,Radiance包括一个接口系统。从最一般的意义上讲,接口提供了关于某些功能,宏,变量等如何工作的希望,但实际上并不能实际实现它们。使界面概述工作的所有内容的实际功能都将其推向实现。这使用户可以对接口进行编码并使用其提供的功能,而无需将自己绑在任何特定的后端。
对于具体示例,假设数据库有一个接口。这是明智的,因为有许多不同类型的数据库,它们都提供了许多不同的交互方式,但仍然提供了一些非常普遍的操作:存储数据,检索数据并修改数据。因此,我们创建了一个提供这些常见操作的界面。然后,它可以由特定类型的数据库实现,以使实际操作起作用。作为应用程序作者,您可以使用数据库界面,然后使您的应用程序自动在许多不同的数据库中使用。
除了给应用程序作者一个优势外,界面提供的解耦还意味着系统管理员可以相对轻松地编写自己的实现,如果现有实现不满足其特定要求。由于界面的不透明,实现都可以为在LISP过程中运行的东西提供桥梁,也可以提供完全外部的东西。这为生产系统的管理员提供了很多选择,使他们可以准确选择所需的东西。
实际上,界面是特殊的模块,因此是特殊的包装。作为其定义的一部分,它们包括针对其他绑定,例如函数,变量等的一系列定义。由于它是一个软件包,作为用户,您可以使用接口的组件,就像您在任何其他软件包中使用其他任何内容一样。没有区别。作为实现作者,您只需重新定义接口概述的所有定义即可。
为了实际加载使用接口的模块,必须事先加载接口的实现。否则,宏无法正常工作。因此,为了允许根据ASDF系统定义中的接口而无需参考特定的实现,Radiance提供了ASDF扩展。此扩展程序使得可以在您的:depends-on列表中添加(:interface :foo)之类的列表。然后,当装载模块时,辐射将使接口解析到其混凝土实现。
Radiance提供了许多标准接口。这些接口中的每一个至少具有辐射范围提供的一个标准实现。接口是:
adminauthbancachedatabaseloggermailprofilerateserversessionuser接口在下面深入描述。
请参阅interface , interface-p , implementation , implements , reset-interface , define-interface-extension , find-implementation , load-implementation , define-interface , define-implement-trigger
为了允许在同一台计算机上使用不同的设置运行多个辐射实例,Radiance提供了所谓的环境系统。环境基本上是辐射本身和所有加载模块的配置和运行时文件集。辐射配置还包括将接口映射到所选的实现,因此决定如果请求接口应该选择什么。
当startup启动时,最早选择使用的特定环境,而最早是加载模块时的。在后一种情况下,提供了交互式重新启动以允许您选择环境。这是必要的,因为否则Radiance将无法解析接口映射。
作为环境系统的一部分,Radiance为您提供了一个配置系统,您可能(可能应该)将其用于应用程序。它确保为每个环境正确地将设置正确地复用,并且设置始终是持久的。它还使用人类可读的存储格式,以便可以在不需要任何特殊工具的情况下读取和修改文件。
有关配置存储的实际处理和使用实体,请参见普遍存在。只需注意,Radiance而不是value函数,而是提供config功能。
除了配置文件外,环境还为运行时数据文件(例如用户上传,缓存文件等)提供一致的存储位置。您可以使用environment-module-directory和environment-module-pathname来检索此位置。存储上传和缓存时,模块应使用这些路径向管理员提供一致的接口。
在部署的系统上,可能希望更改环境存储路径的位置,在这种情况下,鼓励管理员提供有关environment-directory和environment-module-directory的新方法,以根据需要自定义行为。另请参见关联的文档字符串,以获取更多详细信息和默认操作。
环境还允许管理员覆盖。使用:static和:template类型用于environment-module-directory模板类型为您提供了存储文件的路径,该文件应覆盖模块的标准模板和静态文件。相应目录中的路径必须与模块自己的源文件的路径匹配。请注意,模块实际使用的静态和模板文件是在模块加载时缓存的,因此除非重新启动LISP映像,否则将不会更改,或者重新加载了模块的源文件。
See environment-change , environment , environment-directory , environment-module-directory , environment-module-pathname , check-environment , mconfig-pathname , mconfig-storage , mconfig , defaulted-mconfig , config , defaulted-config , template-file , @template , static-file , @static
有时系统以不相容的方式向后发展。在这种情况下,要使现有的设置继续使用新版本,运行时数据迁移是必要的。 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
这些接口以辐射率分布,是核心软件包的一部分。但是,库可以提供其他接口。对于标准接口的实现,允许接口定义约束的以下放宽:
包含&key参数的lambda列表可以通过进一步的依赖于实现的关键词参数来扩展。包含&optional &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: