2017-10-13 40 views
2

我有地图,我想用它来验证在运行时我的要求是这样clojure.specs的:注册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])} 

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

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

我试图创建宏传递他们的S/DEF宏之前EVAL变量,而是一个失败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时EVAL变量,但失败规范验证。

(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)) 

有没有什么方法可以实现我想要做的?或者我误解了一些明显的东西?任何帮助表示赞赏!

回答

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"} 
+0

我想要达到的目标是重用无论是在应用程序的ClojureScript Clojure的和部分规格,我想有机会获得在运行,所以我可以做符合和验证。我也想重用它们来处理这些数据的函数。因此,将它们作为地图使用起来很方便,但我正在努力将它们从地图注册到全局注册表。 –

+0

此外,即使您放置了未命名空间的'req-un'键,您仍然需要在其中提供名称空间键,因此spec可以验证与键对应的值。 –

+3

@SergeyShvets查看[这篇文章](http://blog.cognitect.com/blog/2017/3/24/3xeif9bxaom78qyzwssgwz1leuorh4#sharing-specs-across-the-front-and-back-end)。我认为它确切地描述了你想要在CLJ/CLJS中重复使用规范的工作。另外,我不确定将规格存储在映射中与仅在命名空间中对它们进行定义相比具有什么优势。 –