Radianceは、Webアプリケーションの開発環境です。Webフレームワークのようなものですが、より汎用的で、自由にカスタマイズできます。特別な変更を加える必要なく、個人的なWebサイトやWebアプリケーションを簡単に書くことができます。
Radiance本体、関連するモジュールやアプリケーションは、Quicklispを通して配布されています。Radianceをインストールするには、次のようにします。
(ql-dist:install-dist "http://dist.shirakumo.org/shirakumo.txt")
(ql:quickload :radiance)
以上で、Quicklispのquickload
コマンドでPurplishなどのRadianceモジュールを利用できるようになります。
Radianceのチュートリアルはこちらです。チュートリアルを通して、Radianceの使い方に慣れることができます。Radianceの重要な概念やWebアプリケーションの書き方を紹介しています。特定の機能が必要なときに、どこを調べれば良いかが分かるようにしました。チュートリアルの最後では、Radianceの本番環境でのインストールとデプロイ方法について説明しています。
あなたが一番したいことはユーザにHTMLを配信することでしょう。その方向で進めながら、少しずつ拡張していきます。まずはRadianceの準備をします。
(ql:quickload :radiance)
(radiance:startup)
初めてRadianceを使う場合には、r-welcome
モジュールを使ってください。r-welcome
モジュールを使うと、Welcomeページへのリンクを取得してブラウザで開くことができます。まずは小さなページにリンクをはります。
(in-package :rad-user)
(define-page example "/example" ()
(setf (content-type *response*) "text/plain")
"Hi!")
localhost:8080/exampleにアクセスすると、"Hi"と表示されるはずです。これではかなり退屈ですね。次はcl-whoを用いてHTMLを出してみましょう。
(ql:quickload :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を追加しながらページのデザインを作っていくかと思います。HTMLにCSSを読み込ませることは可能ですが、長い目でみれば最良の方法とはいえません。
ここでは、モジュール(module)の作り方を紹介します。モジュールを使うことで、Webページを適切に整理することができます。モジュールに必要なファイルは手動で作れますが、ここでは雛形を用いてモジュールを自動生成します。
(create-module "example")
モジュールは自動生成されると、モジュールへのパスが返されます。生成されるものは、ASDFのシステムファイル、Lispのmainファイル、2つのフォルダ(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.")))))))
ページはシンボルの名前で特定されます。独自にモジュールを作成すると、新しいパッケージが生成されます。上の例において、シンボルexample
は一度も使われていないシンボルです。パッケージ内での名前衝突を避けるために、rad-userパッケージ
でページを消す必要があるかもしれません。
(remove-page 'rad-user::example)
次に簡単なCSSファイルを作りましょう。static
フォルダにexample.css
という名前で作成してください。CSSを書くのが面倒であれば、次のCSSコードを使ってください。
body{
font-family: sans-serif;
font-size: 12pt;
background: #EEE;
}
header{
text-align: center;
}
main{
width: 800px;
margin: 0 auto 0 auto;
background: #FFF;
padding: 10px;
border: 1px solid #BBB;
border-radius: 5px;
}
HTMLがこのCSSファイルにリンクするように修正しましょう。後では、アドレスをCSSファイルに対応づけるために、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
はRadianceの中心的な概念です。URIはオブジェクトであり、ドメイン
、ポート番号(オプション)
、パス
を含むリストです。
RadianceのURIは、URIから一般的な要素を抽出したもので、スキーマ、クエリ、フラグメント等は含みません。domains
のURIは、フレームワークにおいて複数の場所で使われる点が普通と違います。URIはlocation
を捕捉したりdispatch matching
を処理するために使われます。
URIは変更可能です。ただし、URIの修正は重要なパスに依存する場所で行われるはずなので、パフォーマンスの点で重要です。また、想定外の方法でURIを修正した場合、予想外の動作を引き起こす可能性があります。
URIは固有の文字列で表現されて、文字列にシリアライズすることができます。また、文字列をパーズして完全なURIオブジェクトに戻すことも可能です。URIはFASLファイルにリテラルで書き出されるので、マクロから吐き出すことも可能です。URIのシンタックスは、次の通りです。
URI ::= DOMAINS? (':' PORT)? '/' PATH?
DOMAINS ::= DOMAIN ('.' DOMAIN)*
DOMAIN ::= ('a'..'Z' | '0'..'9' | '-')
PORT ::= ('0'..'9'){1, 5}
PATH ::= .*
URIをURLに変換するにはuri-to-url
を使います。uri-to-url
を使うと、リバース(reversal)、エンコーディング、フォーマットの修正は自動で行われます。
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
オブジェクトを使います。request
オブジェクトは、リクエストがどこに向かうのかを表すURI、ヘッダ、POSTやGETのペイロード、クッキーなど、全ての情報を保持します。また、response
オブジェクトは、リターンコード、ヘッダー、クッキー、BODYのデータを保持します。
リクエストが行われている間、これらの2つのオブジェクトは必ず存在し、*request*
と*response*
に束縛されます。それらは、動的なページ生成のために必要な多くの情報を含みます。*request*
にはdata
テーブルがあり、任意のデータを保存することができます。これは、システム内の部品の間でリクエストの実行中に取得される情報を扱うときに役立ちます。
リクエストは、必ずしもHTTPサーバから来る必要はありません。動作をテストするために、プログラムからリクエストを送ることも可能です。どのような場合でも、リクエストをディスパッチするインターフェイスはrequest
と呼ばれます。この仕組みは、リクエストとレスポンスを構築して、URIを適切に処理します。もし独自にリクエストオブジェクトを送る場合はexecute-request
を使うこともできます。
リクエスト処理に関する詳しい情報は、dispatcher、pages、API endpointをご参照ください。
*request*
, *response*
, *default-external-format*
, *default-content-type*
, request
, uri
, http-method
, 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
も参考にしてしてください。
リクエストは、ルーティングシステムを通してディスパッチされます。他のフレームワークにおいてroutes
は、どのハンドラがリクエストを処理するかを指定しますが、Radianceではその方式とは違います。Radianceにおいてルート(route
)とは、URIを変換するフォームです。ルートは、2つの世界を作成して保持します。内部の世界と外部の世界です。
内部の世界は、Webアプリケーションが実際に動作する世界です。外部の世界は、HTTPサーバとWebサイトを利用するユーザが暮らす世界です。この区別は、サーバの潜在的な罠を避けてWebアプリケーションを書くために必要不可欠です。あなたは、アプリケーションを動作させるために、どのドメインやポートやパスが必要になるかを心配する必要はありません。また、Webの管理者として、システムをカスタマイズしながら壊さず運用することができます。
ルーティングには、Mapping
とReversal
の2種類があります。Mapping
は、外部の世界からくるURIを内部の世界のURIに変換します。たいていの場合、トップレベルのドメインを切り取り、サブドメインにマッピングします。一方、Reversal
は逆のことをします。内部の世界から外部の世界へと進みます。このことは、あなたが提供するWebページが、実際に外部からアクセス可能なリソースを参照できるようにするために必要です。
ルーティングは、任意の処理を行うことができます。基本的なレベルでは、何らかの方法で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は、それぞれのディスパッチャーに対応します。適合する1番目のディスパッチャーの関数が実行されます。
たったこれだけです。ディスパッチャー関数は、ページの内容を提供するために、必要な値を、レスポンスオブジェクトに設定する責任があります。そのためには、レスポンスオブジェクトのdata
のフィールドに直接設定するか、関数から適切な値を返します。Radianceは、4つの型のデータ(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ディスパッチャーであり、物事を簡単にするためのマクロをいくつか含みます。注目してもらいたいのは、拡張可能なオプションです。詳しくみていきましょう。
Radianceには、デフォルトでセットアップされるページがいくつかあります。例えば、favicon
とrobots
のページは、Radianceのstatic/
ディレクトリで配信されます。本番環境でも、自分のページの配信や更新をしたいことがあるかと思います。
そのような目的を果たすために、static
ページの仕組みがあります。static
ページは、静的コンテンツをWebアプリケーションとモジュールに渡します。static
ページは、そのドメインでも/static/...
のパスで有効であり、最初のディレクトリがモジュール名の形式である必要があります。残りは、モジュールのstatic/
ディレクトリの範囲にあるパスです。この仕組みにより、CSS、JavaScript、画像などの静的ファイルを参照することができます。
最後にapi
ページですが、APIエンドポイントのディスパッチを処理する役割があります。これについては次の章で説明します。ページは、静的ファイルの場合と同様に、どのドメインにおいても/api/...
のパスを捕捉して動作します。
page
, remove-page
, define-page
をご参照ください。
Radianceは、REST APIとの連携もサポートしています。これは取ってつけた機能ではありません。多くの現代のアプリケーションは、そのようなAPIを提供することが多いですが、RadianceでもAPIエンドポイントを含むWebアプリケーションを書くことができます。
概念を説明すると、APIエンドポイントは、ブラウザーのリクエストに応じて呼び出すことができる関数です。レスポンスは、リクエストの送り主が読める形式にシリアライズされます。重要な点は、APIエンドポイントはユーザからもプログラムからも利用可能であるべきということです。APIを通してプログラムから実行されるアクションは全てユーザによって実行されるので、Radianceでは両方から利用可能にするように推奨しています。重複を避けるために、これら2つを1つのものとして扱います。
全てのデータの更新は、APIエンドポイントを通して行われます。リクエスト主がユーザ
かプログラム
かを区別せずに処理されます。ユーザがリクエストを行う場合は、適切なページにリダイレクトします。プログラムからリクエストが行われた場合は、読み込めるフォーマットでデータのペイロードが提供されます。
全てのパートのうち、APIフォーマットのシステムですが、データを特別なフォーマットにシリアライズすることを担当します。デフォルトでは、S式を基本とするフォーマットが提供されていますが、JSON形式の出力も簡単にロードできます。
次に、browser
のPOST/GETパラメータの仕様をみましょう。そのパラメータが"true"
という文字列を含む場合、APIリクエストはユーザからであると扱われて、ペイロードデータは出力されずにリダイレクトされます。
あなたのアプリケーションは、統一されたAPIを提供するために、これらをうまく使う必要があります。今、実際のエンドポイントの定義は、名前、関数、引数を記述するラムダリスト、リクエストのパーズ関数で構成されます。引数の典型としては、必須の引数かオプショナル引数が理にかなっています。結局、HTTPリクエストは、キーワード引数しかもつことができません。キーワード引数は省略可能です。
APIエンドポイントの名前は、どこにリーチできるかを示す識別名として機能します。APIエンドポイントは、/api/
パスに存在し、エンドポイントの名前もそれに従います。エンドポイントを修正する際には、あなたのモジュールやアプリケーションが、他のエンドポイントをうっかり踏み外さないように気をつけなければいけません。これはURIディスパッチのときとは違いますが、理由は、APIエンドポイントは、曖昧さやパスのプロセスを許可しないからです。なので、全てのエンドポイントは、唯一のパスをもなければいけません。唯一のパスは、直接、名前としてサーブします。
生の(raw)関数は、APIがインターフェイスを提供する関数です。生の関数は、リクエストされたアクションを行い、適切なデータを上で述べた通りに返す役割を果たします。フォーマットされたAPIのデータを返すためには、api-output
を使います。ブラウザリクエストからリクエストを受けてリダイレクトをするには、redirect
を使います。
最後に、リクエストのパーズ関数は、リクエストオブジェクトを受け取り、関数が必要な実引数を抽出して、最終的に、可能であれば、適切な引数を適用させて関数を呼び出します。パーズ関数は、もし必要な引数が見当たらない場合、api-argument-missing
エラーの信号を送ります。無駄な実引数は無視されます。
call-api
を用いると、プログラムからAPIエンドポイントを呼び出すこともできます。call-api-request
を使うと、Requestをシュミレーションすることができます。どちらもURIディスパッチの仕組みを通る必要はありません。
ページと似ていますが、APIエンドポイントの定義は、定義を簡単にするために、拡張オプションも受け付けます。オプションについては、次で説明します。
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
のオプションを提供します。
それぞれのオプションは、オプションの型に応じて、名前と多くの引数を受け入れる関数のために、キーワードをもっています。定義名、ボディー部、最終にオプションに渡される値を含んだリストが、いつも引数として与えられます。
この拡張用(expansion)の関数は、定義マクロのボディーの式を変換します。環境の設定を許可するために、定義自体では含まれない式を吐き出すこともできます。
option
, option-type
, name
, expander
, option
, remove-option
, list-options
, define-option
, expand-options
をご参照ください。
モジュールの概念は、Radianceにおいて必要不可欠です。全体を構成する要素として働きます。技術レベルでいうと、モジュールは、特別なメタデータが与えられたパッケージです。モジュールは、modularize
システムによって提供され、フック、トリガー、インターフェイス等を使いやすくして、他の情報をトラッキングするために使われます。
defpackage
を使う代わりに、define-module
を使うようにしてください。シンタックスはdefpackage
ですが、:domain
のような特別なオプションを含んでいるので、モジュールが機能するプライマリ・ドメインを特定することができます。
モジュールのシステムでは、ASDFのシステムをモジュールに記すこともできます。もし記せば、そのASDFシステムは仮想のモジュールになります。このようにするためには、システムの定義にオプションを3つ追加する必要があります:
:defsystem-depends-on (:radiance)
:class "radiance:virtual-module"
:module-name "MY-MODULE"
こうすることで、Radianceは、ASDFのシステム情報を特定して、あなたのモジュールに関連づけることができます。新しいモジュールのために、必須のシステムとモジュールの定義を自動で行うには、create-module
をみてください。
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には、互いのモジュールを連携させるために、フック(hook)の仕組みがあります。フックを使うと、ある種のイベントに反応して、任意の関数を実行できます。例えば、意見を交換するソフトウェアでは、投稿が新たに作られる度にトリガーされるフックを設定することができます。拡張では、追加のタスクを実行するためにトリガーをフックに対して定義できます。
フックは、任意の数のトリガーをもつことができますが、トリガーは長すぎないようにしてください。フックをトリガーすることは、全てのトリガーが終了するまでブロックされる動作だからです。そのようなわけで、長い間続くようなトリガーの実行は、リクエストへのレスポンスを遅らせてしまう可能性があります。
フックは、長い間はonであり、後にoffになるスイッチにような関数であるべきです。また、新しいトリガーが実行中に定義された場合は、自動で呼び出されるべきです。これはdefine-hook-switch
により容易になります。define-hook-switch
は2つのフックを作ります。1つ目がトリガーされると、それに定義されているトリガーは、後に2つ目のフックがトリガーされたときに自動で呼び出されます。このおかげで、仮にトリガーがサーバの起動後に定義されたとしても、server-start
は適切に動作します。
list-hooks
, define-hook
, remove-hook
, define-trigger
, remove-trigger
, trigger
, define-hook-switch
をご参照ください。
システムが一枚岩になることを避けるために、またバックエンドも拡張できるように、Radianceにはインターフェイス(Interface)
の仕組みがあります。一般的にインターフェイスとは、関数、マクロ、変数がどのように動作するかについて約束事を書くものであり、実際の実装をそこに書かないことをいいます。つまり。インターフェイスがうまく動作するためには、実装を外側に置くことが大切です。このことにより、インターフェイスに対してコードを書くことができ、特定のバックエンドに結びつけることなく、与えられた機能を利用することができます。
具体例として、データベースのためのインターフェイスをみていきましょう。データベースには多くの種類があり、全ては違う方法でやりとりをしますが、データの保存、検索、修正等、どれにも共通する操作があるので、インターフェイスは実用的です。では、共通する操作を提供するインターフェイスを作ります。これは、特定の種類のデータベースが実際にすることは、それぞれの実装次第です。また、アプリケーションの作者として、データベース・インターフェイスを活用することができます。このインターフェイスを使えば、様々なデータベースに対して自動的にうまく動作するようにできるでしょう。
これはアプリケーションの作者に有利なだけではありません。インターフェイスで実装を分離することにより、システム管理者は、比較的容易に、特殊な機能を独自に実装することができます。インターフェイスが不透明であることにより、Lispのプロセスで動くものと、外部で動くプロセスを連携することができます。これは、Productionシステムの管理者が必要な情報を選び出すために、開かれた選択肢を多く与えます。
実際、インターフェイスは特別な種類のモジュールであり、特別な種類のパッケージです。定義の一部として、関数や変数などの束縛のために、一連の定義を含みます。インターフェイスはパッケージなので、あなたがコンポーテントで使えるユーザは、他のパケージにあるものも何でも使えます。違いはありません。実装の作者として、あなたはインターフェイスが示す定義を再定義することができます。
実際にインターフェイスを利用するモジュールを読み込むためには、インターフェイスの実装は、事前に読み込まれる必要があります。そうでなければ、マクロは適切に動作しません。あなたのASDFシステムの定義において、特定の実装を参照する必要なく、インターフェイスに依存することを許可するためには、Radianceは拡張されたASDFを提供します。この拡張を使うと、(:interface :foo)
のようなリストを:depends-on
に追加することができます。モジュールがロードされたときに、Radianceはインターフェイスを具体的な処理に分解します。
Radianceは、標準のインターフェイスを提供します。それぞれのインターフェイスは、radiance-contribsによって提供される標準実装を1つ以上もちます。インターフェイスは次の通りです:
admin
拡張可能な管理者ページを提供します。
auth
認証とログインに関することを全て処理します。
ban
IPアドレスによってユーザがサイトにアクセスすることを禁止します。
cache
キャッシュの仕組みを提供します。
database
データベースの柔軟なインターフェイスです。オブジェクトの保存、リレーショナルデータベースをバックエンドとして利用できます。
logger
ログ出力のためのインターフェイスです。デバッグやメッセージを出力できます。
mail
メールを送るための最小限のインターフェイスです。
profile
ユーザのプロフィールや属性を拡張するために使います。
rate
特定のリソースにrate limitationを許可します。
server
Radianceを外部の世界と結びつける架け橋のような役割を果たすインターフェイスです。
session
トラッキングするために、継続するセッションを保証します。
user
ユーザアカウントとパーミションの機能を提供します。
それぞれのインターフェイスについては、次の章で説明します。
interface
, interface-p
, implementation
, implements
, reset-interface
, define-interface-extension
, find-implementation
, load-implementation
, define-interface
, define-implement-trigger
をご参照ください。
同じマシン内の複数のRadianceインスタンスに対して異なる設定ができるように、Radianceでは環境(Environment)という仕組みを用意しています。基本的に環境とは、Radianceの設定、ランタイム・ファイル、読み込まれるモジュールの一式です。Radianceの設定は、インターフェイスの実装へのマッピングを含み、インターフェイスが求められたときに選ばれるようにします。
startup
が呼び出された時、どれだけ遅くとも、特定の環境が選択されます。早ければ、モジュールがロードされたときに選択されます。後者の場合は、環境を選ぶために、インタラクティブな再起動が可能です。これは必須の機能ですが、理由は、そうでなければ、Radianceがインターフェイスのマッピングを解決できないからです。
環境のシステムの一部として、Radianceは、あなたのアプリケーションで使える(使うべき)設定システムを提供します。設定システムを使うと、それぞれの環境ごとに、適切に設定することができます。その設定は、いつでも持続性があるうえに可読性にも優れたフォーマットで保存されるので、特別なツールで読み込んだり修正したりする必要はありません。
実際に設定手順でどのように保存処理がされているかを知りたい方は、ubiquitousを参考にしてください。 value
関数の代わりに、Radianceではconfig
関数を使えます。
設定ファイルとは別に、環境は、ユーザのアップロードやキャッシュなどのランタイムのデータのために、継続的な記憶場所を提供します。environment-module-directory
とenvironment-module-pathname
を使うことで、この場所から情報を取り出すことができます。アップロードやキャッシュを保存するとき、モジュールはこれらのパス(path)を利用して、継続的なインターフェイスを管理者に与えます。デプロイされたシステムでは、環境の記憶場所を変更することが望ましいかもしれません。そのような場合、管理者は、想定通りに動作させるためにenvironment-directory
と environment-module-directory
の上で新たなメソッドを与えることをおすすめします。詳しい動作については、関連するドキュメントを参照してください。
環境は、管理者の上書きを許可します。environment-module-directory
に:static
と:template
を使うことで、モジュールの標準テンプレートや静的なファイルへのパスをえることができます。各々のディレクトリ中でのパスは、モジュール自体のソースファイルと対応する必要があります。
実際にモジュールが利用する静的ファイルとテンプレートは、ロード時にキャッシュされることに注意してください。したがって、Lispイメージが再起動されるか、モジュールのソースファイルが再読みされない限り、変更は有効になりません。
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
によりRadianceのインスタンスを管理する必要があります。startup
とshutdown
のドキュメントには、どのフックが、どの順番で呼び出されているかが説明されています。実装では、シンボルがexportされていない限り、追加で特定されない定義をインターフェオスのパッケージのシンボルに加えることができます。
*startup-time*
, uptime
, server-start
, server-ready
, server-stop
, server-shutdown
, startup
, startup-done
, shutdown
, shutdown-done
, started-p
をご参照ください。
標準のインターフェイスは、Radianceとcore packageと一緒に配布されています。ライブラリは、追加のインターフェイスを提供することも可能です。インターフェイスの実装ですが、インターフェイス定義では、次の制限の緩和が許可されています:
&key
引数を含むラムダリストは、実装依存のキーワード引数を使って拡張できます。&optional
引数を含み、&key
か&rest
を含まないラムダリストは、オプショナル引数で拡張できます。必須の引数しか含まないラムダリストは、オプショナル引数かキーワード引数で拡張できます。
adminインターフェイスは、管理者ページを作成するためのものです。ユーザ構成の設定やシステム情報の表示のために使えます。"管理(administration)"という名前ですが、システムの管理者だけに限ったものではありません。どのユーザにも利用できることができます。
管理者ページは、カテゴリー分けされたメニュやパネルを表示できなければいけません。パネル群は、他のモジュールによって提供されており、admin:define-panel
で追加できます。秘密情報を含むようなパネルにアクセスするパネルは、:access
オプションでアクセスを禁止させるようにしてください。パーミッションについては、 userインターフェイスを参照してください。
管理者ページや特定のパネルにリンクをはるには、page
リソースを使ってください。
admin:list-panels
, admin:remove-panel
, admin:define-panel
, admin:panel
をご覧ください。
authインターフェイスは、ユーザをリクエストと結びつけます。そのために、ユーザがシステムに対して自分自身を認証させる方法を提供しなければいけません。どのように実現するかは、実装次第です。実装は、認証のプロセスが初期化するためのページを提供しなければいけません。page
のソースを通してURIを認証プロセスに渡して、"login"
を引数として渡します。
現在リクエストに結び付けられているユーザをauth:current
コマンドで調べることができます。ユーザが"anonymous"
と解釈される場合は、NIL
を返します。詳しくはuserインターフェイスを参照ください。
auth:*login-timeout*
, auth:page
, auth:current
, auth:associate
もご覧ください。
banインターフェイスは、IPによるアクセス制限を提供します。IP BANされたクライアントのIPアドレスからは、リクエストするページに対してアクセス出来なくなります。BANは、タイムアウトの後、手動と自動いずれでも、離す(lift)することができます。実装として、ユーザのIPを監視するために、追加の労力を割くことは想定していません。
ban:jail
, ban:list
, ban:jail-time
, ban:release
をご参照ください。
cacheインターフェイスは、一般的なキャッシュの仕組みを提供します。カスタマイズ可能で無効化のテストをもっています。キャッシュを明示的に新しくするには、cache:renew
をつかいます。cache:with-cache
を使うと、キャッシュの部品を構築することができ、テストのformがtrueと評価されたときに、BODYのキャッシュ値が返されるようにできます。
キャッシュ値が保存される方法は実装によります。 cache:get
かcache:with-cache
を使うと、キャッシュ値は文字列かバイト列に強制変換されます。実装では、キャッシュに格納できる typeには制限がありませんが、少なくとも、文字列とバイト列はサポートします。
キャッシュ値の名前は、名前とパッケージ名が、次の文字を含まないシンボルでなければいけません:<>:"/\|?*.
キャッシュ値の変形は、princ
によって表示される表現とは区別されるオブジェクトである必要があります。その際には、先ほどと同じ文字の制限が適用されます。
cache:get
, cache:renew
, cache:with-cache
をご参照ください。
databaseインターフェイスは、データを持続させるためのレイヤーを提供します。通常は、データベースと呼ばれるレイヤーです。リレーショナル型のデータベースである必要はありませんが、そうであってもいいです。実装の変数を保持するために、基本的なデータベースの機能しかサポートされません。(joinsやtriggers等はありません)データ型も、整数、float、文字列に限定されます。これらの制限にも関わらず、多くのアプリケーションにおいて、データベースインターフェイスはとても役に立ちます。
伝統的なRDMBの用語と区別するために、特別な用語が使われます。schema
は"structure"、table
は"collection"、row
は"record"とします。
データベースに接続する前に、データベース関連の命令を実行すると、未定義の動作を招きます。Radianceでは、Radianceが動作している間はデータベースが接続されることを保障しているので、どのページ、どのAPI、どのURIディスパッチャーの定義において、データベースインターフェイスを問題なく使えます。
実際にデータ保存を行うための関数は、db:insert
、db:remove
、db:update
、db:select
、db:iterate
です。
それらは、あなたが期待するように動作するはずです。
詳しくは、それぞれの関数に書かれているコメントを読んでください。コレクションの作り方、どのような制限があるかを知りたい方は、db:create
のコメントも参考にしてください。
データベースは、データ操作が完了すれば、Radianceを再起動したり、Lispのイメージや機械がクラッシュしたとしても、データの変更は存続されなければいけません。
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
を参照してください。
loggerインターフェースは、ログの関数を提供します。システムの中で関連して起きていることについて、ログのメッセージを出すことができます。ログの出力内容と出力方法に関しては、実装とシステムの管理者次第です。
logger:log
, logger:trace
, logger:debug
, logger:info
, logger:warn
, logger:error
, logger:severe
, logger:fatal
をご覧ください。
mailインターフェースは、メールを送る仕組みを組み込むことができます。様々なコンポーネントが、Webサイトの外からユーザとつながるために、メールのアクセスが必要になるかもしれません。リモートサーバ、ローカル環境でのメール送信等、メールの送信方法の設定は、処理系依存です。mail:send
のフックを使うと、メールが送られる前に、メールに反応することができます。
mail:send
をご覧ください。
profileインターフェイスを使うと、ユーザが存在するアプリケーションにおいて、ユーザへのインターフェイスを拡張することができます。このインターフェイスは、機能の一部として、ユーザのプロフィールが表示されるページを提供する必要があります。そのプロフィールは、数種類のパネルを表示しなければいけません。パネルは、他のモジュールによって提供されており、profile:define-panel
で追加できます。
page
リソースの型情報により、ユーザのプロフィールページへのURIを得ることができます。
このインターフェイスを使うと、視覚的にユーザを特定させるために、profile:avatar
でアバター画像にアクセスすることができます。また、profile:name
を使うとユーザ名をカスタマイズできます。profile:fields
、profile:add-field
、profile:remove-field
を使うと、どのようなデータをユーザの属性に含むか、それを公に(public)表示させるかどうか指定できます。
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
をご参照ください。
rateインターフェースは、Rate limitationの仕組みを提供します。秘密情報や負荷が高いリソースへのアクセスを防ぐことができます。2つの段階があります。第1段階は、rate:define-limit
により特定のリソースに対してRate limitationの動作を定義します。その後、リソースがrate:with-limitation
マクロにより、リソースが保護されます。もし、特定のユーザからblockへのアクセスがあまりに頻繁にされる場合、ブロック(block)は呼び出されません。制限定義内のコード(the code in the limit definition)が、代わりに実行されます。
Rate limitation
は、各クライアント、各ユーザ、各セッションごとで実装に依存しますが、グローバルではないことに注意してください。
rate:define-limit
, rate:left
, rate:with-limitation
をご参照ください。
serverインターフェイスとloggerインターフェイスは、Radianceが起動時に順番通りに読み込まれます。HTTPリクエストに応答する責任があります。実装では、リクエストを受け入れて、Radianceのrequest
関数に渡す必要があります。その後、response
はリクエスト主に戻されます。
リスナーの動作を特定する引数は実装によることに注意してください。しかし、実装は、(mconfig :radiance :port)
で設定されたlocalhost
とポートからでアクセスできる標準のリスナーを提供して、radiance:startup
で起動できるようにする必要があります。
server:start
, server:stop
, server:listeners
, server:started
, server:stopped
をご参照ください。
sessionインターフェースは、あるクライアントが行う複数のリクエストを追跡します。クライアントによっては情報を隠蔽したり偽装している場合があるので、完全にはクライアントを追跡できるとはいえません。しかし、多くのユーザに対しては、うまく動作するはずです。セッション・インターフェイスは、他のインターフェイスや低レイヤーのライブラリの中で使われます。ユーザ認証にような一貫性を保つために使われます。
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
をご参照ください。
userインターフェースは、ユーザオブジェクトを永続させ、パーミションの仕組みを組み込めます。ユーザ認証、ユーザの特定、トラッキング等は扱いません。このインターフェイスでは、ユーザオブジェクトを提供するのみであり、パーミション情報が管理されます。
パーミションに関する詳細は、user:user
を参照してください。
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
もご覧ください。
-
issue #28のなかで、
*environment-root*
は削除されました。#29で問題点が修正されました。*environment-root*
は、environment-directory
関数に統合されることにより、より汎用的なメカニズムに置き換えられました。もし*environment-root*
をカスタマイズして利用していた場合、§1.11
を参考にenvironment-directory
を変更してください。 -
issue #30で、モジュールのバージョンを変更するために、自動でマイグレーションができる仕組みを入れました。#31で修正を加えました。この機能は、すでにRediance自体とモジュールで、新しい環境ディレクトリ(environment directories)を構成するために使われており、自動で以前のデータを現在の新たな場所に移動してくれます。詳しくは
§1.12
を参考にしてください。 -
issue #26の後、モジュールのソースを変更することなく、管理者がモジュールのテンプレートや静的ファイルを上書きできるようになりました。環境に関連するドキュメントは、
§1.11
を参照してください。 -
ユーザ・インターフェイスは、整数のuser:idが、各々のユーザ・オブジェクトをサポートすることを求められます。これにより、データベースでユーザを参照できて、より効率的に記録できるようになりました。
-
データベース・インターフェイスは、
:unique
、db:select
、db:iterate
のサポートを求められます。
- modularize :パッケージのメタシステム
- modularize-interfaces :インターフェイスと実装の拡張
- modularize-hooks :フックとトリガーの仕組み
- ubiquitous: 環境設定
- radiance-contribs :インターフェイスの実装、他便利な機能