【问题标题】:How to define specs dynamically?如何动态定义规格?
【发布时间】:2021-09-15 18:11:23
【问题描述】:

我希望根据我从请求中提取的一些数据生成一组规范。我想根据收到的数据动态定义一些规范。

(def my-key :frame-data/pretty_name) ;;imagine this and the validator aren't hardcoded
(def validator string?)
(s/def my-key validator?) ;; defines my-ns/my-key, instead of `:frame-data/pretty_name.
(s/describe my-key) ;; string? ;;which sorta works, but its looking up `my-ns/my-key` instead of :frame-data/pretty_name

我的目标是有一个看起来像我写的规范:

(s/def :frame-data/pretty_name string?)

我是 clojure 的新手,所以我不知道如何完成它,但我尝试了一些方法:

(s/def (eval my-key) validator) ;;Assert Failed: k must be a namespaced keyword or resolveable symbol

(definemacro def-spec [key validator]
  '(s/def ~key ~validator))
(def-spec my-key validator) ;; my-ns/my-key ;; returns the same as earlier

还有很多变化,但我不确定如何动态定义规范,但感觉应该是这样。

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    您可以对 clojure.spec.alpha/def 表单进行宏扩展,以查看其扩展为:

    (macroexpand `(s/def :frame-data/pretty_name string?))
    ;; => (clojure.spec.alpha/def-impl (quote :frame-data/pretty_name) (quote clojure.core/string?) string?)
    

    或者看看clojure.spec.alpha/defsource code

    然后编写您自己的不引用规范键的宏,以便对其进行评估:

    (defmacro defspec [k spec-form]
      `(s/def-impl ~k (quote ~spec-form) ~spec-form))
    

    例子:

    (defspec my-key validator)
    
    (s/valid? my-key "abc")
    ;; => true
    
    (s/valid? my-key 123)
    ;; => false
    
    (s/describe my-key)
    ;; => validator
    

    如果您不喜欢 (s/describe my-key) 返回 validator,请尝试将宏定义中的 (quote ~spec-form) 替换为 ~spec-form,也许您会更喜欢它。

    【讨论】:

    • 这似乎更适合我的需要,谢谢。替换为~spec-form,而不是打印“验证器”,而是打印#object[clojure.core#string_QMARK...]。可读性差很多,但也许这是我唯一的两个选择。
    【解决方案2】:

    我很想有一个更好的答案,但似乎这有效,只有s/describe 返回validator 而不是string?

    (eval `(s/def ~my-key validator)) ;; :frame-data/pretty_name
    (s/describe my-key) ;; validator
    (s/valid? my-key "some string") ;; true
    (s/valid? my-key 123) ;; false
    

    另外一个看起来很奇怪的宏也可以工作,但是双波浪号看起来很奇怪,使用 eval 感觉很奇怪:

    (defmacro define-spec [spec-key validator]
      `(eval `(s/def ~~spec-key validator)))
    
    (define-spec my-key validator)  ;; :frame-data/pretty_name
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 2018-06-28
      • 2014-12-14
      相关资源
      最近更新 更多