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

Reuse structure definition code from clojure spec/keys

我有一个验证传入数据内容的规范定义。由于数据是字段映射,因此我使用 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?

我建议查看 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 规范或其他一些限制允许键的规范。

Is there a way I can reuse some code between my spec definition for the map structure (s/keys) and this additional step I take for filtering the allowed keys (permitted-keys)?

是的,在上面的示例中,这是通过将 args 拼接到 s/keys 调用来处理的,并且在这个更完整的宏 here.

中也以类似方式完成

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