Clojureアプリ内で Stuart Sierraのコンポーネントライブラリ を使用する方法について頭を悩ませています。彼の Youtubeビデオ を見て、彼がライブラリを作成するに至った問題を十分に理解できたと思います。しかし、私はそれを新しい、かなり複雑なプロジェクトで実際に使用する方法を見つけるのに苦労しています。
これは非常に曖昧に聞こえますが、いくつかの重要な概念が欠けているように感じます。それを理解すれば、コンポーネントの使用方法をよく理解できます。別の言い方をすれば、Stuartのドキュメントとビデオでは、コンポーネントの内容と理由についてかなり詳しく説明していますが、方法がわかりません。
以下に入る詳細なチュートリアル/ウォークスルーはありますか?
前もって感謝します
つまり、Componentは特殊なDIフレームワークです。システムマップと依存関係マップの2つのマップを指定して、注入されたシステムをセットアップできます。
作成されたWebアプリを見てみましょう(免責事項、実際に実行せずにフォームに入力しました):
(ns myapp.system
(:require [com.stuartsierra.component :as component]
;; we'll talk about myapp.components later
[myapp.components :as app-components]))
(defn system-map [config] ;; it's conventional to have a config map, but it's optional
(component/system-map
;; construct all components + static config
{:db (app-components/map->Db (:db config))
:handler (app-components/map->AppHandler (:handler config))
:server (app-components/map->Server (:web-server config))}))
(defn dependency-map
;; list inter-dependencies in either:
;; {:key [:dependency1 :dependency2]} form or
;; {:key {:name-arg1 :dependency1
;; :name-arg2 :dependency2}} form
{:handler [:db]
:server {:app :handler})
;; calling this creates our system
(def create-system [& [config]]
(component/system-using
(system-map (or config {})
(dependency-map)))
これにより、(create-system)
を呼び出して、必要なときにアプリケーション全体の新しいインスタンスを作成できます。
(component/start created-system)
を使用して、システムが提供するサービスを実行できます。この場合、ポートと開いているデータベース接続をリッスンしているのはWebサーバーです。
最後に、(component/stop created-system)
で停止して、システムの実行を停止できます(たとえば、Webサーバーを停止し、dbから切断します)。
それでは、アプリのcomponents.clj
を見てみましょう。
(ns myapp.components
(:require [com.stuartsierra.component :as component]
;; lots of app requires would go here
;; I'm generalizing app-specific code to
;; this namespace
[myapp.stuff :as app]))
(defrecord Db [Host port]
component/Lifecycle
(start [c]
(let [conn (app/db-connect Host port)]
(app/db-migrate conn)
(assoc c :connection conn)))
(stop [c]
(when-let [conn (:connection c)]
(app/db-disconnect conn))
(dissoc c :connection)))
(defrecord AppHandler [db cookie-config]
component/Lifecycle
(start [c]
(assoc c :handler (app/create-handler cookie-config db)))
(stop [c] c))
;; you should probably use the jetty-component instead
;; https://github.com/weavejester/ring-jetty-component
(defrecord Server [app Host port]
component/Lifecycle
(start [c]
(assoc c :server (app/create-and-start-jetty-server
{:app (:handler app)
:Host host
:port port})))
(stop [c]
(when-let [server (:server c)]
(app/stop-jetty-server server)
(dissoc c :server)))
では、私たちは何をしたのでしょうか?再読み込み可能なシステムを手に入れました。 figwheel を使用している一部のclojurescript開発者は類似点を見始めていると思います。
これは、コードをリロードした後、システムを簡単に再起動できることを意味します。 user.clj
へ!
(ns user
(:require [myapp.system :as system]
[com.stuartsierra.component :as component]
[clojure.tools.namespace.repl :refer (refresh refresh-all)]
;; dev-system.clj only contains: (def the-system)
[dev-system :refer [the-system]])
(def system-config
{:web-server {:port 3000
:Host "localhost"}
:db {:Host 3456
:Host "localhost"}
:handler {cookie-config {}}}
(def the-system nil)
(defn init []
(alter-var-root #'the-system
(constantly system/create-system system-config)))
(defn start []
(alter-var-root #'the-system component/start))
(defn stop []
(alter-var-root #'the-system
#(when % (component/stop %))))
(defn go []
(init)
(start))
(defn reset []
(stop)
(refresh :after 'user/go))
システムを実行するには、次のようにreplに入力します。
(user)> (reset)
コードをリロードし、システム全体を再起動します。稼働中の既存のシステムが起動するとシャットダウンします。
その他のメリットもあります。
すべてが進行中であるため、Componentはフェイルオーバー、分散システム、または障害のあるコードに関連するものを処理しないことに注意してください;)
コンポーネントがサーバー内での管理に役立つ「リソース」(ステートフルオブジェクト)はたくさんあります。
Webサーバーとデータベースしかない場合、コンポーネントはやり過ぎのように見えることがあります。しかし、最近ではWebアプリはほとんどありません。
サイドノート:the-system
を別の名前空間に移動すると、開発時にthe-system
変数が更新される可能性が低くなります(例:代わりにrefresh
を呼び出す) reset
の)。