K2NR.ME

このエントリーをはてなブックマークに追加 Tweet

HerokuでClojureアプリをCompojureを使ってデプロイしてみよう その1

Hi, 今日はいい天気だったね!みんな元気にしてるかな?

さて、今日は前回インストールしたClojureを使って、実際にHerokuでアプリをデプロイしてみましょう。

Hello Worldアプリでもいいかと思ったんですが、さすがに簡単すぎるしチュートリアルも巷に溢れかえってるので少しだけ難易度を上げてPostgresqlを使ったアプリを開発してみることにします。

とりあえず、教材に使うアプリはこれにします。テキストエリアに愚痴を入力して送信すると愚痴が表示されるという、Twitter超簡易版アプリ・「ぐちってー」です。Twitter Bootstrapとか勉強しながら作ってたら丸一日かかってしまいました。

ソースコードはこちらです。

目的

少し長くなるので、数回に分けて連載方式でいこうと思います。

登場人物の説明

たくさんのフレームワークやライブラリが登場するので、簡単に紹介しておきます。

Compojure

Clojureで今もっとも広く使われているであろうWebフレームワークです。ルーティングなどが楽に実装できます。すごくシンプルな作りになってて、初学者が混乱することは少ないだろうと思います。その他のWebフレームワークにNoirなどありますが、今回は登場しません。使い方わからないし。

Ring

Clojureで作られたWebアプリケーションライブラリです。CompojureはこのRingをベースにして開発されています。Compojureでは実装されていない機能などはRingのAPIを使用します。

Hiccup

いわゆるHTMLテンプレートエンジンです。Hiccupの他にもEnlive,Fleetというエンジンがありますが、Hiccupが最もシンプルなので今回はこれを使います。

Postgresql

ご存知リレーショナルデータベース管理システムの雄。postgresを選んだ理由はただ一つ、HerokuではPostgresしか使えないからです。 今回はローカルでpostgresqlサーバーを動かして開発するのでインストールされてない方はインストールしておきましょう。

brew install postgresql

Heroku

Webアプリホスティングサービスですね。Paasって言うんでしょうか。最近のバズワード難しくてわかりません><

HerokuでClojureが運用できるということで、どうしても試してみたくなったというのが今回の連載の動機です。

project.clj

依存ライブラリはproject.cljに書きます。特に難しい話は無いのでコピペすればいいじゃない!

(defproject guchitter "1.0.0-SNAPSHOT"
  :description "guchitter"
  :dependencies [[org.clojure/clojure "1.3.0"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [postgresql "9.1-901.jdbc4"]
                 [org.clojure/java.jdbc "0.1.1"]
                 [compojure "1.0.1"]
                 [hiccup "0.3.8"]
                 [ring/ring-jetty-adapter "1.1.0-SNAPSHOT"]
                 [ring/ring-devel "1.1.0-SNAPSHOT"]
                ]
  :main guchitter.core)

ちなみに、ライブラリの検索、バージョンの確認にはClojarsというサイトを使います。

Compojureのキホン

早速Compojureの使い方を見ていきましょう。 src/guchitter/core.cljは名前のとおりアプリの核となるベースの処理を書いています。

(ns guchitter.core
  (:use
    [compojure.core :only [defroutes]]
    [compojure.route :only  [not-found files resources]]
    [ring.adapter.jetty :only [run-jetty]]
    [ring.middleware.reload]
    )
  (require [compojure.handler :as handler]
           [guchitter.views.layout :as layout]
           [guchitter.controllers.guchi :as controller]))

(defroutes main-routes
  controller/routes
  (files "/")
  (resources "/")
  (not-found (layout/not-found))) ;not-foundを通すと文字化けする

(defn start [port app]
  (run-jetty app {:port port :join? false}))

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

(defn -main []
  (let [port (Integer/parseInt (System/getenv "PORT"))]
    (start port application)))

(defn -dev-main []
  (let [dev-app (-> application
                  (wrap-reload '[guchitter.core
                                 guchitter.views
                                 guchitter.models
                                 guchitter.controllers]))
        port 8080]
    (start port dev-app)))

ns,use,requireとかは他のサイトなどで使い方を確認しておいて下さい。

defroutes

defroutesマクロでルーティングを定義します。

(defroutes main-routes
  controller/routes
  (files "/")
  (resources "/")
  (not-found (layout/not-found))) ;not-foundを通すと文字化けする

先頭のcontroller/routescontroller/routesのルーティングを参照することを示しています。該当のコードを引用すると clj (defroutes routes (GET "/" [] (index)) (POST "/" {params :params} (create params)))

最初に、/にGETリクエストがきた場合、index関数が呼ばれて(index関数の詳細は後で登場します)、index関数は表示する完全なhtmlを返します。 同様に/にPOSTリクエストがきた場合、POSTのパラメータがparamsに入り、それをcreate関数に渡し、create関数は送られた愚痴をDBに登録した後にこのときに表示すべきHTMLを返します。(厳密にはウソです。実際にはHTMLではなく/にリダイレクトするHTTPのレスポンスを返します)

静的ファイルの取り扱い

core.cljに話を戻して、(files "/")は何をしているかというと、静的なファイルの取り扱いを指定しています。この例では例えば/A.htmlのGETリクエストがきたときに[プロジェクトのルート]/public/A.htmlというファイルを返すことになります。

(resources "/")も同様なのですが、こちらはリソースファイルです。デフォルトでは[プロジェクトのルート]/resources/public/のファイルが該当します。

存在しないURL

(not-found (layout/not-found))の部分は404を表示する場合にlayout/not-found関数の返すHTMLを表示します。コメントのとおり、not-found関数を通すと文字化けしてしまうようで、日本語が使えません。バグなのか使い方が悪いのか。。。

ひとつ注意点として、not-founddefroutesの末尾に書かなければなりません。ルーティングの際にはdefroutesで定義された順に評価されるからです。

サーバーの開始

(run-jetty app {:port port :join? false}))これでサーバー開始です。JettyというHTTPサーバが動き始めます。:portはポート番号、:join?はブロッキング・ノンブロッキングの指定です。

ポート番号を環境変数PORTから取得していますが、これはHerokuで動作させるためです。ローカルではPORT環境変数は存在しないので、ローカルで動かす場合は環境変数を定義する必要があります。

export PORT=8080

実際に動かしてみよう

全部のファイルについて解説したわけじゃないですが、第一回の締めにここで一旦動かしてみましょう。動かすためにはまずDBを作成しなきゃなりません。

DBの作成

$ initdb pg
$ postgres -D pg &
$ createdb guchi
$ export PORT=8080
$ export DATABASE_URL=postgres://localhost:5432/guchi

これでデータベースguchitterが作成されました。ちなみにHeroku上でデプロイする際は既に存在する共有DBを使用するので、DB作成は必要ありません。次にテーブルを作成します。

$ lein run -m guchitter.models.migration

これでguchi.models.migration/-main関数が実行されます。migration.cljの解説は省略します。これはテーブルを作成するためのスクリプトです。コードは以下のとおり。

(ns guchitter.models.migration
  (:use [guchitter.models.db :only [my-db]])
  (require [clojure.java.jdbc :as sql]))

(defn create-guchi []
  (sql/with-connection my-db
    (sql/create-table :guchi
      [:id :serial "PRIMARY KEY"]
      [:body :varchar "NOT NULL"]
      [:created_at :timestamp "NOT NULL" "DEFAULT CURRENT_TIMESTAMP"])))

(defn -main []
  (print "Migrating database...") (flush)
  (create-guchi)
  (println " done"))

migrationで使用されているSQL関連は次回解説しますね。

サーバー起動

1行実行するだけ。

$ lein run

動きましたか?

続きはまた次回にしましょう。

最後に

最後に感謝の意を述べなければなりません。前回のエントリで乞食をしたところ、早速2人の方から品物が届きました。

こんなどうしようもないニートのためにお金を使ってくれる人がいるなんて、涙が出るほど嬉しいです。ほんとにありがとうございます。

もしまだ寄付してもいいよ!という人がいればこちらから商品を送ってくだしあ!!!

参考サイト

comments powered by Disqus