Help us understand the problem. What is going on with this article?

DuctでHello World

More than 1 year has passed since last update.

これは Clojure アドベントカレンダー 2017 5日目の記事です。

lein new duct <duct-app> してみたけど、そのあとどうすれば良いのか分からないといった方の参考になればと思い、書かせていただきます。

対象読者: これからDuctを使い始めたい方

なお、私の手元の環境は Windows10 Pro です。

それでは始めます。

lein new duct <app-name> はダメ!ゼッタイ!

まず、Ductのlein-templateですが、使わないことをお勧めします。
生成されるファイルが多く混乱のもととなるため、最初は1からプロジェクトを作りましょう。

フォルダ構成

lein-templateは使わずにファイルとフォルダを手動で作成します。
以下の構成でファイルとフォルダを作成してください。

foo-clj
├── project.clj
├── resources
│   └── config.edn
└── src
    ├── foo_clj
    │   └── endpoint
    │       └── example.clj
    └── user.clj

Ductのlein-templateは推奨しませんが、素のlein-templateであればOKです。lein new <app-name> でプロジェクトを作成していただいてもかまいません。

project.cljの中身

project.cljを以下のようにします。ちょっとDuctなどのバージョンが古いですが、私の手元で確認したものとなります。気になる方はぜひ最新バージョンでお試しください。

project.clj
(defproject foo-clj "0.0.1"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [duct/core "0.3.3"]
                 [duct/module.logging "0.2.0"]
                 [duct/module.web "0.5.0"]
                 [integrant/repl "0.2.0"]])

user.cljの中身

次にuser.cljを以下のようにします。これにより、replから前処理(prep)や初期化処理(init)、終了処理(halt)などが行えるようになります。

src/user.clj
(ns user
  (:require [integrant.repl :refer [clear halt go init prep reset]]
            [duct.core :as duct]
            [clojure.java.io :as io]
            [integrant.repl.state :refer [config system]]))      

(defn read-config []
  (duct/read-config (io/resource "config.edn")))

(integrant.repl/set-prep! (comp duct/prep read-config))

config.ednの中身

次にconfig.ednを以下のようにします。今後の設定に関してはこちらのファイルに色々書いていく形となります。

resources/config.edn
{:duct.core/project-ns foo-clj
 :duct.core/environment :development
 :duct.module/logging 
 {}
 :duct.module.web/site
 {}
 :duct.router/cascading
 [#ig/ref :foo-clj.endpoint/example]
 :foo-clj.endpoint/example
 {}}

example.cljの中身

さいごにexample.cljを以下のようにします。こちらはCompojureを使って書いていく形となります。

src/foo_clj/endpoint/example.clj
(ns foo-clj.endpoint.example
  (:require [compojure.core :refer :all]
            [integrant.core :as ig]))

(defn example-endpoint [options]
  (context "/example" []
    (GET "/" [] "<b>Hello, World!</b>")))

(defmethod ig/init-key :foo-clj.endpoint/example [_ options]
  (example-endpoint options))

defmethodの部分はintegrantによるものです。

REPLによる動作確認

Ductを動かすための最低限の準備は整いました。REPLから下記コマンドを実行して、jetty-serverを起動します。

PS D:\dev\foo-clj> lein repl
...
user=> (prep)  # 前処理を実行
:prepped
user=> (init)  # 初期化処理を実行
:duct.server.http.jetty/starting-server {:port 3000}
:initiated

以上で、サーバの起動は完了です。Webブラウザからhttp://localhost:3000/exampleにアクセスしてみましょう。

foo-clj.PNG

正常にページが表示されればOKです!

あとはページを増やすなり、Hiccupやenliveを導入するなり好きにカスタマイズしてください。

応用1: endpointの追加

endpointの追加はconfig.ednに記載します。

resources/config.edn
{:duct.core/project-ns foo-clj
 :duct.core/environment :development
 :duct.module/logging 
 {}
 :duct.module.web/site
 {}
 :duct.router/cascading
 [#ig/ref :foo-clj.endpoint/example
  #ig/ref :foo-clj.endpoint/example2]  # new!
 :foo-clj.endpoint/example
 {}
 :foo-clj.endpoint/example2            # new!
 {}}

:duct.router/cascadingへの登録と空のendpointの定義がセットで必要となります。
設定を変更したらreplから(reset)を実行してください。

user=> (reset)
:reloading ()

user=> java.lang.IllegalArgumentException: No method in multimethod 'init-key' for dispatch value: :foo-clj.endpoint/example2
        clojure.lang.ExceptionInfo: Error on key :foo-clj.endpoint/example2 when building system

errorが発生しますが、これはendpointの実態を作成していないためです。exampleを参考にexample2を作成して(reset)すれば無事新しいendpointが公開されます。

応用2: ポート番号の変更

jetty-serverのデフォルトポートが3000番ですが、別のポート番号に変更してみます。config.ednに以下の行を追加します。

resources/config.edn
{:duct.core/project-ns foo-clj
 :duct.core/environment :development
 :duct.module/logging 
 {}
 :duct.module.web/site
 {}
 :duct.router/cascading
 [#ig/ref :foo-clj.endpoint/example
  #ig/ref :foo-clj.endpoint/example2]
 :foo-clj.endpoint/example
 {}
 :foo-clj.endpoint/example2
 {}
 :duct.server.http/jetty    # new!
 {:port    3001}}           # new!

変更後は(reset)すれば反映されます。

Ductを使う場合ソースコードに変更を加えて(reset)で反映というサイクルで開発を進めていくことができます。

参考情報

Ductを使うにはintegrantは欠かせないものとなりますので、まずはintegrantを習得しましょう。
ぜひ、作成者である@weavejester (James Reeves)さんのskillcastをご覧になってください。

Enter Integrant: a micro-framework for data-driven architecture with James Reeves

[追加1]
Duct Frameworkを使用する場合、個々のmoduleの設定を理解しておく必要もあるため、そちらのリンクも追加しておきます。

server.http.jetty
module.web
module.logging

[追加2]
Ductテンプレートを推奨しないと書きましたが、構造を理解すればdev環境とprod環境に分けた設計を行えるようになるため、慣れてくればテンプレートを活用すると良いでしょう。そのときは作者である@weavejester (James Reeves)さんのブログをご参照ください。

Advancing Duct
Building Web Services with Duct

上記のブログでduct-alphaのテンプレートを使用しておりますが、12/5現時点ではductで大丈夫かと思います。(すべて試してませんが) duct-alphaにてプロジェクトを作成したところ少し古いバージョンでproject.cljが生成されてしまいました。

おわり

以上です。12/4に Clojure アドベントカレンダー 2017 を見たらたまたま12/5が空いていたため、即興で書かせていただきました。今後も加筆修正は行っていく予定ですので、誤っている点や改善点などございましたらコメントいただければ幸いです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away