Luminusを触ってみたのでメモ

Luminusを触ってみたのでメモ。

概要

  • 軽量ライブラリをベースにした小さなWEBアプリケーションフレームワーク
  • 以下を狙いとする
    • 堅牢
    • スケーラブル
    • プラットフォームの利用を簡単に

試用

Leiningen テンプレートLuminus-Templateを利用してテンプレートプロジェクトを作成。
なお、今回利用する Leiningen のバージョンは 2.1.3。

lein new luminus myapp
cd myapp
lein ring server
依存ライブラリ

上記で作成すると、project.clj(抜粋)は↓の感じに。

  :dependencies [[org.clojure/clojure "1.5.1"]
                 [lib-noir "0.7.1"]
                 [compojure "1.1.5"]
                 [ring-server "0.3.0"]
                 [selmer "0.4.3"]
                 [com.taoensso/timbre "2.6.2"]
                 [com.postspectacular/rotor "0.1.0"]
                 [com.taoensso/tower "1.7.1"]
                 [markdown-clj "0.9.33"]]
  :plugins [[lein-ring "0.8.7"]]

Ring, Compojure, lib-noir はいいとして、他を簡単に調べてみた。

  • Selmer: テンプレートシステム(Djangoにインスパイアされた)
  • Timbre: ロギング&プロファイリング ライブラリ
  • rotor: ログローテーションのアペンダ
  • Tower: 国際化のライブラリ
  • markdown-clj: マークダウンパーサ(ClojureおよびClojureScriptへコンパイルする)
生成されたソースの構成
src
└─myapp
    │  handler.clj …defines the base routes for the application, this is the entry point into the applicationand any pages
    │  repl.clj …provides functions to start and stop the application from the REPL
    │  util.clj
    │
    ├─routes …routes and controllers for our homepage are located
    │      home.clj …a namespace that defines the home and about pages of the application
    │
    └─views
        │  layout.clj …a namespace for the layout helpers
        │
        └─templates
                base.html …the base layout for the site
                home.html
                about.html

誤解を恐れず描くなら、リクエストの流れは概ね↓のイメージだろう。

チュートリアルをやってみる

Your first applicationに沿ってやってみる。
テンプレートを編集して、HOMEページをメッセージボードに置き換えるという内容。

プロジェクト新規作成
lein new luminus guestbook +h2

(H2の組み込みデータベースエンジンをサポートするテンプレートを作成)

(補足)+h2 オプションによって増えた依存ライブラリ
   [com.h2database/h2 "1.3.172"]
   [korma "0.3.0-RC5"]
   [log4j
    "1.2.17"
    :exclusions
    [javax.mail/mail
     javax.jms/jms
     com.sun.jdmk/jmxtools
     com.sun.jmx/jmxri]]]
(補足)生成されたソースの構成
src
│  ★+h2オプションによって増えた部分★
│  log4j.xml …logging configuration for Korma
│
└─guestbook
    │  handler.clj
    │  repl.clj
    │  util.clj
    │
    ├─models ★+h2オプションによって増えた部分★
    │      db.clj …used to house the functions for interacting with the database
    │      schema.clj …used to define the connection parameters and the database tables
    │
    ├─routes
    │      home.clj
    │
    └─views
        │  layout.clj
        │
        └─templates
                base.html
                home.html
                about.html
DB接続情報の編集

schema.clj の db-spec および db-store を編集する。
なお、create-tables関数が呼ばれたタイミングで、site.db.h2.dbファイルが resources フォルダ直下に生成される。

(def db-store "site.db")

(def db-spec {:classname "org.h2.Driver"
              :subprotocol "h2"
              :subname (str (io/resource-path) db-store)
              :user "sa"
              :password ""
              :naming {:keys clojure.string/lower-case
                       :fields clojure.string/upper-case}})
テーブル生成関数の編集

schema.clj に create-guestbook-table 関数を追加し、create-tables から呼び出すように編集する。

(defn create-guestbook-table []
  (sql/with-connection
    db-spec
    (sql/create-table
      :guestbook
      [:id "INTEGER PRIMARY KEY AUTO_INCREMENT"]
      [:timestamp :timestamp]
      [:name "varchar(30)"]
      [:message "varchar(200)"])
    (sql/do-commands
      "CREATE INDEX timestamp_index ON guestbook (timestamp)")))
(defn create-tables
  "creates the database tables used by the application"
  []
  (create-guestbook-table))
DBアクセス関数の編集

db.cljに以下を追記。

(defentity guestbook)

(defn save-message
  [name message]
  (insert guestbook
          (values {:name name
                   :message message
                   :timestamp (new java.util.Date)})))

(defn get-messages []
  (select guestbook))
アプリケーションの初期化処理の編集(DB初期化処理を追加)

handler.cljの関数 init(アプリケーション開始時に一度だけ呼ばれる関数)に、DB初期化処理(テーブル生成関数の呼び出し)を追加する。
requireに以下を追加。

[guestbook.models.schema :as schema]

init関数内の set-config! に続けて、以下を追加。

  ;;initialize the database if needed
  (if-not (schema/initialized?) (schema/create-tables))
コントローラの編集

home.clj の home-page 関数の定義を以下の通り置き換え(ゲストブック表示用のパラメータを追加)。

;(defn home-page []
;  (layout/render
;    "home.html" {:content (util/md->html "/md/docs.md")}))

(defn home-page [& [name message error]]
  (layout/render "home.html"
                 {:error    error
                  :name     name
                  :message  message
                  :messages (db/get-messages)}))

require に以下を追加。

[guestbook.models.db :as db]

また、ゲストブックへのメッセージ登録用のコントローラを追加する。

(defn save-message [name message]
  (cond
    (empty? name)
      (home-page name message "Somebody forgot to leave a name")
    (empty? message)
      (home-page name message "Don't you have something to say?")
    :else
      (do
        (db/save-message name message)
        (home-page))))

home-routes の定義に以下を追加する。

(POST "/" [name message] (save-message name message))
HTMLテンプレートの編集

home.html を以下の内容に置き換える。

{% extends "guestbook/views/templates/base.html" %}

{% block content %}
<ul>
{% for item in messages %}
  <li>
  	  <time>{{item.timestamp|date:"yyyy-MM-dd HH:mm"}}</time> 
      <p>{{item.message}}</p>
      <p> - {{item.name}}</p>      
  </li>
{% endfor %}
</ul>

{% if error %}
<p>{{error}}</p>
{% endif %}

<form action="/" method="POST">
    <p>
       Name: 
       <input type="text" name="name" value={{name}}>
    </p>
    <p>
       Message: 
       <textarea rows="4" cols="50" name="message">
           {{message}}
       </textarea>
    </p>
    <input type="submit" value="comment">
</form>
{% endblock %}
レイアウトを整える

resources/public/css/screen.css に以下を追記。

form {
    width: 200px;
    clear: both;
}
form input {
    width: 50%;
    clear: both;
}
パッケージング
  • 実行可能jar
lein ring uberjar

java -jar target/guestbook-0.1.0-SNAPSHOT-standalone.jar
  • war
lein ring uberwar