Kuchitama Tech Note

はてな記法がいつまでたっても覚えられないので、はてなダイアリーからマークダウンが使えるこっちに引っ越してきました。

clojureで変態webシステムを作ってみた

このエントリは変態アドベントカレンダー11/15です

今日は変態のためのWebサイトを、clojureというJVM上で動く関数型言語を使って開発してみようと思います。

今回の開発にあたって、次のライブラリを用いています。
このライブラリの使い方をざっくりと追いかけながら、hentaiwebサイトを作っていこうと思います。

  • webフレームワーク compojure
  • テンプレートエンジン fleet

Compojure

compojureはclojureで書かれたwebフレームワークです。
sinatraの影響が強く反映されており、シンプルで最小限の機能を提供しています。
どうも、clojure界隈ではデファクトスタンダードらしく、資料の少ないclojureにしてはそこそこ情報が見つかります。

というわけで、こいつとRingで動くwebアプリはこんな感じです。

hentai.core.clj
(ns hentai.core
  (:use compojure.core compojure.response
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))


(defroutes main-routes
  (GET "/" _ "You are hentai")
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

このサンプルを実行すると、デフォルトルートでは、"You are hentai"、 /ikemen にアクセスすると "Your are ikemen" と表示されます。

とりあえず、ルーティングができたのでここにテンプレートエンジンを組み込もうと思います。

Fleet

今回はFleetというテンプレートエンジンを使います。

テンプレートとなるhtml中に<( hogehoge )>といった具合に、<( )>で囲んだ中にclojureのコードを埋め込むことが出来ます。
他にも、テンプレートエンジンはいくつもあるのですが、使い慣れているVelocityに近い書き方ができるので、今回はこれを採用しました。

早速使ってみます

hentai.core.clj
(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

(fleet-ns templates "templates" [:fleet :bypass])

(defroutes main-routes
  (GET "/" _ (templates/hentai {}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

ちなみにテンプレートは、hoge.html.fleetの様に最後に.fleetの拡張子をつけます

templates/hentaii.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>This is Hentai web</title>
</head>
<body>
<h1>あなたは変態です</h1>
</body>
</html>

これで、デフォルトルートでhtmlがレンダリングされるはずです

が、なんかエラーが出ました

どうやら、日本語を扱うには、次の表記をcore.cljに追記する必要があるそうです

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

ということで、core.cljはこのように

core.clj
(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

(fleet-ns templates "templates" [:fleet :bypass])

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

(defroutes main-routes
  (GET "/" _ (templates/hentai {}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

もう一息

最後に、fleetに動的に値を渡してみます
core.cljの次の一文は最後に {}が入ってます。

(GET "/" _ (templates/hentai {}))

これは、clojureではMapとして扱われます。
ここに値を追加することで、テンプレートを動的に変更出来ます。
ということで、こんな感じに変更してみます

(GET "/" _ (templates/hentai {:link (get-img)}))

これで、:linkというキーに対して、関数 get-imgの結果が保持されます。
そして、それはそのままテンプレートから呼び出すことができます。

関数get-imgを定義しましょう。
こんな感じに、core.cljに書き足します。

(def hentai-link ["http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854674_photo0.jpg" "http://yipev.com/data/img2/20110408084405/thumb_e1r73nh7fv301cd1uweinzjw.jpg" "http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854662_photo0.jpg"])
(defn get-img []  (get hentai-link (rand-int 3)))

defは定数の定義、defnは関数の定義です。
これで、ベクタhentai-linkからランダムに値を取得する関数get-imgが出来ました。

最終的に、core.cljはこんな感じ

(ns hentai.core
  (:use compojure.core compojure.response fleet
        [ring.util.response :only (response content-type)])
  (:require [compojure.route :as route]
            [compojure.handler :as handler]))

;テンプレートの読み込み
(fleet-ns templates "templates" [:fleet :bypass])

(extend-protocol Renderable fleet.util.CljString 
    (render [this _] (-> (response (.toString this))
                       (content-type "text-html; charset=UTF-8"))))

;ベクタ定義
(def hentai-link ["http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854674_photo0.jpg" "http://yipev.com/data/img2/20110408084405/thumb_e1r73nh7fv301cd1uweinzjw.jpg" "http://fs.plaync.jp/UF/image/6344/01/23/13/842/seigetsu_32446/854662_photo0.jpg"])
;関数定義
(defn get-img []  (get hentai-link (rand-int 3)))

;ルーティングの設定
(defroutes main-routes
  (GET "/" _ (templates/hentai {:link (get-img)}))
  (GET "/ikemen" _ "You are ikemen")
  (route/resources "/")
  (route/not-found "Page not found"))

(def app
  (handler/site main-routes))

templateから、Mapで渡された値を取得するには、dataを使います。

templates/hentai.html.fleet
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Index Fleet</title>
</head>
<body>
<h1>あなたは変態です。</h1>
<img src="<(data :link)>" />
</body>
</html>

これで、ランダムに変態のための画像が表示されるはずです。

まとめ

まとめると、今回の内容は次の通りです。

  • clojureでwebシステム構築してみた
  • compojureを使ってみた
  • fleetを使ってみた

今後は、formに入力してpost取得などをやってみようと思います。
それから、もしfleetに自作関数を食わせる方法をご存知の方がいらっしゃれば
ぜひご連絡ください。
今のところ、値は渡せるようになりましたが変数の渡し方がよくわかっていません・・・


次のリンクで今回作ったhentaiWebをしばらく見れるようにしておくので、
我こそは、という変態の方はぜひご覧ください。
http://kuchitama.ddo.jp

と思ったら、うまく動きませんでした…orz
ローカル環境ではちゃんと見えるので、サーバの設定?
近いうちに直します・・・

直しました。