Clojure Spec не проверяет данные должным образом

Я очень смущен Clojure Spec. Когда я запускаю в repl, введя:

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

И затем добавьте:

(s/valid? even? 10)

Я получаю правду. И когда я бегу:

(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. Есть ли другой способ, которым вы должны его запустить?

2 ответа

Я понимаю, что вы чувствуете, потому что, как только я попал в 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 не определяет единого способа обработки данных; Вот почему это действительно хорошо и гибко.

valid? предикат, который возвращает истину или ложь. Ваша программа ничего не делает с возвращаемым значением. Попробуйте распечатать его на консоли или используя s/assert если вы хотите выбросить исключение:

Если (check-asserts?) Имеет значение false во время выполнения, всегда возвращает x. По умолчанию используется значение системного свойства clojure.spec.check-asserts или false, если не установлено. Вы можете переключать чек-утверждения? с (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)
Другие вопросы по тегам