我对Clojure Spec感到非常困惑。当我通过输入以下命令运行时:
(require '[clojure.spec.alpha :as s])
然后添加:
(s/valid? even? 10)
我知道//真的。当我跑:
(s/valid? even? 11)
//假。好的,这样才行。然后当我在core.clj中需要spec时:
(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来运行它。还有另外一种方法可以运行吗?
答案 0 :(得分:0)
valid?
是一个返回true或false的谓词。您的程序没有使用返回值执行任何操作。如果要抛出异常,请尝试将其打印到控制台或使用s/assert
:
如果(check-asserts?)在运行时为false,则始终返回x。默认为 'clojure.spec.check-asserts'系统属性的值,否则返回false 组。你可以切换check-asserts? 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)
答案 1 :(得分:0)
我理解你的感受,因为一旦我进入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))))
这里可以改进的是具有validate
和error
功能的组合功能。这样的函数可以返回两个值的向量:成功标志和结果或错误数据结构如下:
[true {:foo 42}] ;; good result
[false {:error :map}] ;; bad result
Spec库没有规定处理数据的单一方式;这就是它真正优秀和灵活的原因。