在进入下单系统的实现之前,我们先来个小短篇,讨论一下配置分离和混合编程中的一些小东西。
前面的章节中我们反复提到,对数据库访问的连接配置 db-spec 应该分离出来。以便于隔离测试、开发和生产环境。现在是时候解决这个问题了。
Clojure 内置的 edn ,可以看作是面向配置的一个 DSL 机制。用这个机制,我们可以很方便的把 clojure 代码当作项目配置。
按说,正常项目么,配置用一种就行,比如就全用 edn 或者全用 typesafe 的 configuration (就是 akka 的 configuration)。学习项目,就没有那么严肃了,我们把 akka 的配置放到 conf 文件里,其它的扔进 edn。
Clojure edn 几乎就是简单的 clojure 语法,具体的语法参考见官方文档 [edn-format/edn] 。
我们先对 project.clj 做一些改动,把不同profile的配置文件分开:
...
:aot :all
:test-paths ["src/test/clojure" "src/test/java"]
:resource-paths ["resources/main"]
:junit ["src/test/java"]
:profiles {:server {:main liu.mars.market.App
:jvm-opts ["-Dconfig.resource=server.conf"]
:resource-paths ["resources/server"]}
:client {:main liu.mars.Client
:jvm-opts ["-Dconfig.resource=client.conf"]
:resource-paths ["resources/client"]}
:test {:dependencies [[junit/junit "4.12"]
[com.typesafe.akka/akka-testkit_2.12 "2.5.19"]]
:resource-paths ["resources/test"]
:java-source-paths ["src/test/java"]
:jvm-opts ["-Dconfig.resource=junit.conf"]}})
然后加入数据库配置,下面是 resources/server/config.edn (对应生产环境):
{:db-spec {:dbtype "postgresql"
:dbname "sequences"}}
然后是 resources/test/config.edn ,junit 配置也用它:
{:db-spec {:dbtype "postgresql"
:dbname "test"}}
如果不指明profile,说明我们在开发环境,使用默认配置。
我们直接把这部分配置信息写进 config.clj :
(nsliu.mars.market.config
(:require [clojure.java.io :refer [resource]])
(:require [clojure.edn :as edn]))
(defdefault-config
{:db-spec {:dbtype "postgresql"
:dbname "sequences"}})
(defconf (delay
(if-let[url (resource "config.edn")]
(->url
slurp
(edn/read-string)
(#(mergedefault-config %)))
default-config)))
(defndb-spec []
(:db-spec @conf))
Clojure 有一些强大的配置库,比如 [jarohen/nomad] 和 [yogthos/config] 。不过目前我们不打算在这个方向上走太远,先用尽可能少的依赖和逻辑,让我们的项目可以运行起来。
同样,在这里不过多讨论 delay 和 deref 的用法。这些东西很容易搜索到。
简单的说,conf的值会延迟到第一次执行 `@conf` 才被确定,我们将其封装在 db-spec 函数中,这样,如果程序启动后,成功的加载了 config.edn ,就会从中读取配置,否则 conf 就会设定为默认配置。
有兴趣的朋友可以尝试 `lein repl` 和 `lein +test repl` 之后,`@config/conf` 的值,或者 `config/db-spec` 是否一致。
=================================