h3. 问题
在运行时边界验证支持多种交换格式时使用{{clojure.spec}}很困难。
h3. 详细说明
目前在clojure.spec(alpha-14)中,符合器在创建Spec实例时附加到,并且在每个符合时被调用。这在对系统边界验证来说不是很有用,在边界验证中,符合/强制转换函数应该基于运行时数据选择,例如交换格式。
示例
* a {{keyword?}} spec
** EDN,不需要强制转换(它可以显示关键字)
** JSON,应用String->Keyword强制转换
** 基于字符串的格式(CSV、查询参数、...),应用String->Keyword强制转换
* a {{integer?}} spec
** EDN,不需要强制转换(它可以显示数字)
** JSON,不需要强制转换(它可以显示数字)
** 基于字符串的格式(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. 推荐
在Spec协议中支持选择性符合性,使用新的3个参数{{conform*}}和{{clojure.spec/conform}},都接受额外的用户提供的回调/访问函数。如果提供了回调,它将在Spec的{{conform*}}内部以当前规范作为参数调用,它将返回{{nil}}或一个2参数的符合器函数,该函数应该用于实际的符合。
实际的符合器实现可以由第三方库维护,如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)))
;; 字符串
(assert (= conformed-person (s/conform ::person string-person string-conforming-matcher)))
h3. 代替方案
支持这种功能的另一种方法是允许使用协议扩展Specs。第三方库可以有一个新的{{Conforming}}协议,带有3价{{conform}}函数,并且为所有当前Specs添加其实现。目前这不可能。
[1]
https://github.com/metosin/spec-tools