はじめに
この記事は、一般的なWeb開発タスクへのCommon Lisp機能の適用を説明するために書かれています。
テンプレート、ルーティング、キャッシュ-Webプログラミングで使用される主なものがどのようにLispに実装されているかを示します。 マクロ用のスペースも少し残しました。
この記事は非常に教育的ですが、完全に機能するWebサイト-rigidus.ruです。
テンプレートとそのコンパイル
{namespace tpl} {template root} <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">{\n} <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">{\n} <head>{\n} <title>{$headtitle}</title>{\n} <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />{\n} <link rel="stylesheet" type="text/css" media="screen" href="/style.css" />{\n} <link rel="Shortcut Icon" type="image/x-icon" href="/favicon.ico" />{\n} </head>{\n} <body id="top">{\n} {$content | noAutoescape}{\n} </body>{\n} </html>{\n} {/template}
CL-CLOSURE-TEMPLATEライブラリを使用して、この単純なテンプレートはtplパッケージのルート関数のマシンコードにオンザフライでコンパイルされます。 したがって、データをテンプレートに適用することは、コンパイルされた関数の呼び出しです。
(tpl:root (list :headtitle " " :content "Hello world"))
「Hello world」の代わりに、サイトに最小限のグリッドを提供する「ベース」テンプレートなど、別のテンプレートをコンパイルする関数呼び出しを代用できます。
{template base} <div id="center"> <div class="col1 left"> <a id="logo" href="index.html"> <img src="http://www.gravatar.com/avatar/d8a986606b9d5e4769ba062779e95d9f?s=45" style="border: 1px solid #7F7F7F"/> </a> <ul id="nav"> {foreach $elt in $navpoints} {call navelt data="$elt" /} {/foreach} </ul> </div> {$content |noAutoescape} <div class="clear">.</div> </div> <div id="footer"> <p> <a href="/about">About</a> | <a href="/contacts">Contacts</a> </p> </div> {/template}
ここで、入門記事habrahabr.ru/blogs/webdev/111365の例を使用して、新しく作成したテンプレートを使用して、次のように1ページからサイトのリクエストディスパッチャを作成できます。
(defun request-dispatcher (request) (tpl:root (list :headtitle "My home page" :content (tpl:base (list :navpoints ..-.. :content ..-..)))))
RESTASルート
RESTASライブラリは、ディスパッチャの魅力的な記述から私たちを解放します。
これで、設定したルート(ルート)に基づいてディスパッチャが作成され、次のように定義されます。
(restas:define-route main ("") (tpl:main (list :headtitle "My main page" :content "Hello! <a href=\"/articles\">Articles</a>"))) (restas:define-route css ("/css/:cssfile") (hunchentoot:handle-static-file (format nil "~a/css/~a" *base-dir* cssfile)))
-このナンセンスは何ですか? -洗練されたWeb開発者に依頼してください。 -cssファイルごとに独自のルートを尋ねる必要がありますか?
「まったくない!」 -答えます。 lambda:要件を指定できます。これは、ルートが適切かどうかを決定します。 サイトディレクトリ内のディスクでファイルを見つけた場合にファイルを提供する更新されたコードは次のとおりです。
(restas:define-route static ("/:staticfile" :requirement (lambda () (let ((request-file (pathname (format nil "~a/~a" *base-dir* (hunchentoot:request-uri hunchentoot:*request*)))) (files (directory (format nil "~a/*.*" *base-dir*)))) (not (null (find request-file files :test #'equal)))))) (hunchentoot:handle-static-file (format nil "~a/~a" *base-dir* staticfile)))
ここで、メインページとcssファイルのリターンのルートを定義しました-ご覧のとおり、次を使用できます:: wildcards
マクロを使用する
org-modeを使用してサイトの記事を準備します。org-modeは、レイアウトの容易さ(wikiなど)と折りたたみセクションなどのさまざまな便利なツールを組み合わせた便利なEmaxモードです。 org-to-html関数を作成しましたが、
org-mode形式で記事のテキストを送信すると、記事で直接指定されたメタデータから抽出されたヘッダーを使用してhtmlが自動的に作成され、セクションとサブセクションに関する情報も返されます。
この関数がファイルを処理した後、いくつかのヘッダーを変更し、呼び出しを単純にするために、デフォルトページマクロを使用する必要がある場合があります。
(defmacro default-page (menu file-path &optional (body nil)) `(let ((menu-memo ,menu)) (multiple-value-bind (content sections directives) (org-to-html (alexandria:read-file-into-string ,file-path)) (let ((title (getf directives :title))) ,body (page title menu-memo (tpl:default (list :title title :navpoints menu-memo :sections (loop :for i :from 1 :for section :in sections :collect (list :anchor (format nil "anchor-~a" i) :level (format nil "level-~a" (car section)) :title (cadr section))) :content content)))))))
これで、クライアントコードの複雑な呼び出しを取り除くだけでなく、たとえば次のように、デフォルトページ内のコードの「インジェクション」を行うこともできます。
(restas:define-route about ("/about") (default-page (menu) (base-path "about.org") ;; - (let ((cnt (length sections))) ;; e (setf title (format nil "~a — ~a " title cnt)))))
次のセクションでは、このアプローチをより有意義に使用します。
キャッシング
私の記事は、見出しとカテゴリのメタ情報を含むファイルにあります。 「/記事」ページを作成するには、ファイルを調べますが、これには時間がかかり、システムをロードできます。 ただし、このデータは
クロージャーで覚えておいてください、これがこのコードが行うことです:
(let ((memo)) (restas:define-route articles ("/articles") (when (null memo) (setf memo (default-page (menu) (base-path "articles.org") (setf content ;; , ;; ( ) )))) memo))
キャッシュを時間の経過とともに廃止したい場合、これも実装が非常に簡単であることは明らかです。 私がスライムに入るのは簡単ですが、このコードの最後の行でCtrl + X、Ctrl + Eを実行すると、再度実行され、キャッシュがゼロになります。 新しい記事のアップロード(何が起こるか
あまりにも頻繁に)私はこれを行います-これはすぐにいくつかの機能を追加する良い理由です。
詳細に興味のある方へ:
ソースコードをgithub.com/rigidus/rigidus.ruに投稿しました
そして、サイト自体はrigidus.ruにあります
彼がどのようにhabraeffectに対処するかを見てみましょう。