2024 年 Clojure 状况调查! 分享您的看法。

欢迎!请参阅 关于 页面了解更多有关此工作方式的信息。

0
Spec
h3. 问题

在运行时边界验证中支持多种交换格式时使用 {{clojure.spec}} 是困难的。

h3. 详细信息

当前在 clojure.spec (alpha-14) 中,符合者是在创建 Spec 实例时附加的,并且每次符合时都会调用它们。这在系统边界验证中并不很实用,在系统中,应该根据运行时数据选择符合/强制函数,例如交换格式。

示例

* a {{keyword?}} spec
** 使用 EDN,不应执行强制转换(它可以展示 Keywords)
** 使用 JSON,应用 String->Keyword 强制转换
** 使用基于 String 的格式(CSV,查询参数等),应用 String->Keyword 强制转换

* a {{integer?}} spec
** 使用 EDN,不应执行强制转换(它可以展示数字)
** 使用 JSON,不应执行强制转换(它可以展示数字)
** 使用基于 String 的格式(CSV,查询参数等),应用 String->Long 强制转换

以下是一个更完整的示例


(s/def ::id integer?)
(s/def ::name string?)
(s/def ::title keyword?)
(s/def ::person (s/keys :opt [::id], :req-un [::name ::title]))

;; 这就是我们如何在不同的交换格式中查看数据的方式
(def edn-person {::id 1, :name "Tiina", :title :boss})
(def json-person {::id 1, :name "Tiina", :title "boss"})
(def string-person {::id "1", :name "Tiina", :title "boss"})

;; 这是我们所希望的
(def conformed-person edn-person)


要使用此功能,您需要手动创建针对所有不同交换格式的新边界规格,并为每个规格指定不同的符合者。非限定关键词可以通过在 {{s/keys}} 中进行映射来工作(例如,{{::title}} => {{::title$JSON}}),但如果在边界上公开了完全限定的键(如示例中的 {{::id}}),则不起作用 - 您不能将具有相同名称的不同符合版本的规格进行注册。

h3. 建议

支持通过具有新 3-arity 的 Spec 协议中的选择性符合,其中包含额外的用户提供的回调/访问者函数的 {{conform*}} 和 {{clojure.spec/conform}}。如果提供了回调,它将在 Spec 的 {{conform*}} 中调用,并将当前规范作为参数传递,并返回一个 2-arity 的符合函数,该函数应用于实际符合。

实际的符合-matcher 实现可以由第三方库维护,例如 spec-tools[1]。

使用它的外观如下


;; edn
(assert (= conformed-person (s/conform ::person edn-person)))
(assert (= conformed-person (s/conform ::person edn-person nil)))

;; json
(assert (= conformed-person (s/conform ::person json-person json-conforming-matcher)))

;; string
(assert (= conformed-person (s/conform ::person string-person string-conforming-matcher)))


h3. 替代方案

支持此功能的另一种方法允许使用协议扩展Spec。第三方库可以拥有一个具有3-arity conform的新{{Conforming}}协议,并在所有当前spec中添加其实现。目前这是不可能的。

[1] https://github.com/metosin/spec-tools

16 个解答

0
by
参考:https://clojure.atlassian.net/browse/CLJ-2116(由ikitommi报告)
...