从clojure spec / keys重用结构定义代码

时间:2017-12-12 20:28:18

标签: clojure clojure.spec

我有一个规范定义,用于验证传入数据的内容。由于数据是字段映射,因此我使用spec/keys来验证它。例如:

(def person-data {:name "Jon Doe", :age 30})
(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person-info (s/keys :req-un [::name ::age])
...
;validate data via spec and make sure no additional keys are included
(s/valid? ::person-spec some-input)

但我还需要确保传入的数据只包含我想要的密钥。 (在这种情况下只有:name:age个键。为此,我执行以下操作:

(def permitted-keys [:age :name])
(select-keys some-input permitted-keys)

,确保只过滤掉这些密钥。

有没有办法可以在我的地图结构规范定义(s/keys)之间重用一些代码,以及我为过滤允许的密钥(permitted-keys)而采取的额外步骤?

或许可以从s/keys定义中提取密钥列表,或者将现有的密钥向量传递给s/keys

1 个答案:

答案 0 :(得分:1)

我建议查看this macro,因为它涵盖了所有基础,并提供了一个生成器,但这是另一个假设您的地图使用未命名空格键:

(defmacro keys-strict
  [& args]
  (let [{:keys [req opt req-un opt-un]} args
        ks (into #{} (->> (concat req opt req-un opt-un)
                          (map #(keyword (name %)))))] ;; strip namespaces from keywords
    `(s/and (s/keys ~@args) (s/map-of ~ks any?))))

重复使用密钥的相同真实来源的唯一技巧是您的密钥规范将被命名空间,但您的地图密钥不会。您可以在没有宏的情况下执行相同操作,只需s/and s/keys规范s/map-of规范或其他一些限制所允许密钥的规范。

  

有没有办法可以在地图结构的规范定义(s / keys)之间重用一些代码,以及我为过滤允许的密钥(allowed-keys)所采取的额外步骤?

是的,上面的示例通过将args拼接到s/keys调用来处理,并在此更完整的宏here中完成相同的操作。

注意:在某些情况下,您确实需要限制您接受的密钥,但我认为通常建议您定义可供以后扩展的地图规范。