【问题标题】:Clojure Spec Not Validating Data ProperlyClojure Spec 未正确验证数据
【发布时间】:2017-12-15 23:29:18
【问题描述】:

我对 Clojure Spec 感到非常困惑。当我通过输入在 repl 中运行时:

(require '[clojure.spec.alpha :as s])

然后加上:

(s/valid? even? 10)

我得到 //true。当我跑步时:

(s/valid? even? 11)

//错误。好的,这样就可以了。然后,当我在 core.clj 中需要规范时:

(ns spam-problem.core
    (:require [clojure.spec.alpha :as s]
              [clojure.spec.gen.alpha :as gen]))

并尝试一个简单的验证让它抛出一个错误,没有任何反应:

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (s/valid? even? 11))

我不知道我在这里做错了什么,并且对规范应该如何工作感到非常困惑。我正在使用命令 lein run 来运行它。是否有其他方式运行它?

【问题讨论】:

  • valid 不会抛出。它返回一个布尔值,你没有做任何事情。
  • 好的,但也不适用于conform。我应该在我的示例中使用它。
  • Conform 也不会抛出。您可能想改用 s/assert。

标签: clojure clojure.spec


【解决方案1】:

我理解你的感受,因为一旦我进入 Spec,它就让我产生了同样的想法。真正帮助我解决我心中的问题的是考虑到 Spec 不是最终的库,而是一个框架。在我的项目中,通常我有一个特殊的模块,其中包含高于基本规范功能的高级包装器。我相信,您可能会这样做:定义一个函数,该函数接受数据、规范并根据您的业务逻辑提出您希望拥有的错误消息。这是我的代码的一个小例子:

(ns project.spec
  (:require [clojure.spec.alpha :as s]))

;; it's better to define that value is a constant
(def invalid :clojure.spec.alpha/invalid)

(defn validate
  "Either returns coerced data or nil in case of error."
  [spec value]
  (let [result (s/conform spec value)]
    (if (= result invalid)
      nil
      result)))

(defn spec-error
  "Returns an error map for data structure that does not fit spec." 
  [spec data]
  (s/explain-data spec data))

现在,让我们准备一些规格:

(defn x-integer? [x]
  (if (integer? x)
    x
    (if (string? x)
      (try
        (Integer/parseInt x)
        (catch Exception e
          invalid))
      invalid)))

(def ->int (s/conformer x-integer?))

(s/def :opt.visits/fromDate ->int)
(s/def :opt.visits/toDate ->int)
(s/def :opt.visits/country string?)
(s/def :opt.visits/toDistance ->int)

(s/def :opt.visits/params
  (s/keys :opt-un [:opt.visits/fromDate
                   :opt.visits/toDate
                   :opt.visits/country
                   :opt.visits/toDistance]))

以下是一些用法示例:

(let [spec :opt.visits/params
      data {:some :map :goes :here}]
  (if-let [cleaned-data (validate spec data)]
    ;; cleaned-data has values coerced from strings to integers,
    ;; quite useful for POST parameters
    (positive-logic cleaned-data)
    ;; error values holds a map that describes an error
    (let [error (spec-error spec data)]
      (error-logic-goes-here error))))

这里可能需要改进的是具有validateerror 功能的组合功能。这样的函数可以返回两个值的向量:成功标志和结果或错误数据结构,如下所示:

[true {:foo 42}] ;; good result
[false {:error :map}] ;; bad result

Spec 库并不规定单一的数据处理方式;这就是为什么它非常好和灵活。

【讨论】:

  • 谢谢,我会调查的。我认为我遇到的问题是所有在线示例都显示在 repl 中使用规范,除了在进入我正在工作的命名空间后将这些测试复制并粘贴到 repl 中,我不知道如何运行这些测试它们是否是在我的编辑器中编写的。我习惯了 Ruby,您只需在单独的 rspec 文档中编写测试,要求它,然后运行。如果有任何关于如何在 Clojure 中执行此操作的文档,我很乐意看到它。
【解决方案2】:

valid? 是一个返回真或假的谓词。你的程序没有对返回值做任何事情。如果您想抛出异常,请尝试将其打印到控制台或使用s/assert

如果 (check-asserts?) 在运行时为 false,则始终返回 x。默认为 'clojure.spec.check-asserts' 系统属性的值,否则为 false 放。您可以切换检查断言吗? with (check-asserts bool)。

所以你可能需要设置(s/check-asserts true)s/assert 抛出异常:

(clojure.spec.alpha/assert even? 3)
=> 3
(clojure.spec.alpha/check-asserts?)
=> false
(clojure.spec.alpha/check-asserts true)
=> true
(clojure.spec.alpha/assert even? 3)
ExceptionInfo Spec assertion failed
val: 3 fails predicate: :clojure.spec.alpha/unknown
clojure.core/ex-info (core.clj:4739)

【讨论】:

  • s/assert 也不起作用,s/conform 也不起作用。
  • @JessieRichardson“不会”还是“不会”? conformvalid? 的原因不同;既不投掷。你试过assert吗?来自文档:“如果 x 有效则返回 x?根据规范,否则会抛出一个 ex-info,其中包含 explain-data 加上 ::failure of :assertion-failed。”
  • spam-problem.core=> (require '[clojure.spec.alpha :as s]) nil spam-problem.core=> (s/conform even? 1000) 1000 垃圾邮件问题。 core=> (s/conform even?1001) :clojure.spec.alpha/invalid spam-problem.core=> (s/assert even?1000) 1000 spam-problem.core=> (s/assert even?1001) 1001
  • 实际编辑器运行lein run时无反馈。只是:Java HotSpot(TM) 64-Bit Server VM warning: Unable to open cgroup memory limit file /sys/fs/cgroup/memory/memory.limit_in_bytes (No such file or directory)
  • 我不确定您是否有规格问题。尝试只在你的 main 中抛出一个异常,而不是别的:(throw (ex-info "test" {}))。我觉得您启动它的方式是一种不会从程序中打印出错误的方法。
猜你喜欢
  • 1970-01-01
  • 2019-08-16
  • 1970-01-01
  • 2019-12-11
  • 2014-01-08
  • 1970-01-01
  • 2021-08-25
  • 2020-08-31
  • 1970-01-01
相关资源
最近更新 更多