首先使用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")
考虑到您可能需要在运行时更改配置,您可以实现如下模式(粗略草图)
(def default-config {:k1 "v1"
:k2 2})
(def save-config (partial spit "config.edn"))
(def load-config #(from-edn "config.edn"))
(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)))
基本上,此代码包装了一个非全局的原子,并提供对其进行设置和获取访问的功能。您可以像使用 atom 一样读取其当前状态并更改它,比如使用 (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 等不同配置进行测试。
操作 userconfig 的函数(就像在用户面板中一样)将使用 get/alter..! 函数。
另外,上述 let 包装了一个 watch,自动更新 .edn 文件,每次更改 userconfig 时都会更新它。如果您不想这样做,可以添加一个 save-userconfig! 函数,将原子内容转存到 config.edn 中。但是,您可能希望创建一种方法来向原子添加更多 watch(例如在自定义字体大小更改后重新渲染 GUI),这在我看来会打破上述模式。
相反,如果你正在处理一个较大的应用程序,更好的方法是定义一个协议(具有类似 let 块中的函数),用文件、数据库、原子(或您需要测试/不同用例的任何内容)的各种构造函数来实现它,利用 reify 或 defrecord。该实例可以在应用程序中传递,并且每个状态操作/io 函数都应使用它,而不是使用任何全局变量。
clojure.edn/read-string
,而不是绑定*read-eval*
。 - Jeremy(slurp (io/resource "config.clj))
。当您的代码成为一个带有嵌入式配置的JAR包时,这也会有所帮助。 - noisesmith