18

clojureでアプリケーション構成を処理する慣用的な方法は何ですか?

これまでのところ、私はこの環境を使用しています:

;; config.clj
{:k1 "v1"
 :k2 2}

;; core.clj
(defn config []
  (let [content (slurp "config.clj")]
    (binding [*read-eval* false]
      (read-string content))))

(defn -main []
  (let [config (config)]
    ...))

これには多くの欠点があります:

  • へのパスconfig.cljが常に正しく解決されるとは限りません
  • 使用されているライブラリ/フレームワークの構成セクションを構造化する明確な方法がない
  • グローバルにアクセスできない ( @app/config) (もちろん、これは優れた機能スタイルの方法と見なすことができますが、ソース ファイル全体の構成へのアクセスが面倒になります。

ストームのような大規模なオープンソース プロジェクトは、Clojure の代わりに YAML を使用しているようで、少し醜いハックを介して構成をグローバルにアクセスできるようにします。(eval ``(def ~(symbol new-name) (. Config ~(symbol name)))).

4

3 に答える 3

12

最初に clojure.edn を使用し、特に clojure.edn/read を使用します。例)

(use '(clojure.java [io :as io]))
(defn from-edn
  [fname]    
  (with-open [rdr (-> (io/resource fname)
                      io/reader
                      java.io.PushbackReader.)]
    (clojure.edn/read rdr)))

io/resource を使用した config.edn のパスに関しては、これに対処する唯一の方法です。おそらく実行時に変更された config.edn を保存したいので、ファイル リーダーとライターのパスが次のような修飾されていないファイル名で構成されているという事実に依存したい場合があります。

(io/reader "where-am-i.edn")

デフォルトは

(System/getProperty "user.dir")

実行時に構成を変更したいという事実を考慮すると、このようなパターンを実装できます (ラフスケッチ)

;; myapp.userconfig
(def default-config {:k1 "v1"
                     :k2 2})
(def save-config (partial spit "config.edn"))
(def load-config #(from-edn "config.edn")) ;; see from-edn above

(let [cfg-state (atom (load-config))]
  (add-watch cfg-state :cfg-state-watch
    (fn [_ _ _ new-state]
      (save-config new-state)))
  (def get-userconfig #(deref cfg-state))
  (def alter-userconfig! (partial swap! cfg-state))
  (def reset-userconfig! #(reset! cfg-state default-config)))

基本的に、このコードはグローバルではないアトムをラップし、それへの set および get アクセスを提供します。現在の状態を読み取り、sth を使用してアトムのように変更できます。のように(alter-userconfig! assoc :k2 3)。グローバル テストでは、リセットできます。userconfig を変更し、さまざまな userconfig をアプリケーションに挿入します(alter-userconfig! (constantly {:k1 300, :k2 212}))

userconfig を必要とする関数は (defn do-sth [cfg arg1 arg2 arg3] ...) のように記述でき、default-userconfig、testconfig1,2,3 などのさまざまな構成でテストできます。 user-panel は get/alter..! を使用します。機能。

また、上記の let は、userconfig が変更されるたびに .edn ファイルを自動的に更新するウォッチを userconfig にラップします。これをしたくない場合は、save-userconfig を追加できます。アトムの内容を config.edn に吐き出す関数。ただし、私の意見では、上記のパターンの型を破る (カスタム フォント サイズが変更された後に GUI を再レンダリングするなど) アトムにさらにウォッチを追加する方法を作成することもできます。

代わりに、より大きなアプリケーションを扱っている場合、より良いアプローチは、userconfig のプロトコルを (let ブロックのような同様の関数で) 定義し、ファイル、データベース、atom (または任意のもの) のさまざまなコンストラクターで実装することです。テスト/さまざまな使用シナリオの必要性) reify または defrecord を利用します。このインスタンスはアプリケーション内で渡される可能性があり、すべての状態操作/io 関数は、グローバルなものの代わりにそれを使用する必要があります。

于 2013-07-20T15:24:48.430 に答える
1

この 1 か月間、仕事のためにかなりの量の作業を行いました。構成を渡すことが受け入れられない場合のために、atom でグローバル構成マップを使用しました。アプリケーションの起動の早い段階で、構成変数はswap!ロードされた構成で編集され、その後はそのままになります。これは、アプリケーションの存続期間中は事実上不変であるため、実際には機能します。ただし、このアプローチはライブラリではうまく機能しない場合があります。

「使用されているライブラリ/フレームワークの構成セクションを構造化する明確な方法がない」という意味がわかりません。ライブラリに構成へのアクセスを許可しますか? とにかく、起動時に構成をセットアップする関数に与えられる構成ローダーのパイプラインを作成しました。これにより、ライブラリとソースに基づいて構成を分離できます。

于 2013-07-18T14:57:02.480 に答える
1

構成マップを (環境ごとに) 別のファイルにリソースとして保持することさえ気にしません。Confijulate ( https://github.com/bbbates/confijulate、はい - これは個人的なプロジェクトです) を使用すると、単一の名前空間内で各環境のすべての構成を定義し、システム プロパティを介してそれらを切り替えることができます。しかし、再構築せずにオンザフライで値を変更する必要がある場合は、Confijulate でそれも可能です。

于 2014-01-01T23:45:13.573 に答える