2017-10-13 43 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中重複使用規範的工作。另外,我不確定將規格存儲在映射中與僅在命名空間中對它們進行定義相比具有什麼優勢。 –