3

我有一个 clojure.specs 的地图,我想用它来在运行时验证我的请求,如下所示:

{::num-id int?
 :project-spec/id ::num-id
 :project-spec/name (s/and string? #((< (count %) 24)))
 :project-spec/project (s/keys :req-un [:project-spec/id :project-spec/name])}

我可以在运行时使用这些规范中的任何一个来验证 using s/valid?,除了那个project-spec/project。那一个要求所有其他人都在全球规范注册表中注册才能正常工作。当我尝试使用简单doseq函数注册规范时,如果失败,因为我将局部变量传递给s/def宏并且在扩展宏之前它不会将变量解析为值。

(doseq [[name spec] spec-map]
    (s/def name spec))

在将变量传递给 s/def 宏之前,我尝试创建宏来评估变量,但是那个失败并带有CompilerException java.lang.UnsupportedOperationException: Can't eval locals.

(defmacro reg-spec
  [name spec]
  `(s/def ~(eval name) ~(eval spec)))

(doseq [[name spec] spec-map]
    (reg-spec name spec))

我尝试的最后一件事是在传递给时评估变量,s/def但规范验证失败。

(s/def (eval spec-name) (eval spec-spec))
 CompilerException java.lang.AssertionError: Assert failed: k must be 
 namespaced keyword or resolvable symbol (c/and (ident? k) (namespace k))

有什么方法可以实现我想做的事情吗?还是我误解了一些明显的东西?任何帮助表示赞赏!

4

1 回答 1

3

您是否有理由不想通过 给规格命名s/def?规范的一个重要方面是强/命名空间名称。您的示例在某种意义上为它们命名,但仅作为该映射中的键。我s/def都给了。我修复了上面示例中的一些错误。您的地图键是命名空间的,所以s/keys应该使用:req而不是:req-un.

(s/def ::num-id integer?)
(s/def :project-spec/id ::num-id)
(s/def :project-spec/name (s/and string? #(< (count %) 24)))
(s/def :project-spec/project (s/keys :req [:project-spec/id :project-spec/name]))

如果您愿意,您仍然可以构建您的规格图,但键/值将是相同的。

(s/conform :project-spec/project
           {:project-spec/id 1, :project-spec/name "123"})
;;=> {:project-spec/id 1, :project-spec/name "123"}
于 2017-10-13T07:06:12.033 回答