Спецификация Clojure и конструкторы записей

Если я определил следующую запись:

(defrecord Person [name id])

и следующее:

(s/def ::name string?)
(s/def ::id int?)
(s/def ::person (s/keys :req-un [::name ::id]))

Как я могу гарантировать, что вы не сможете создать человека, который не соответствует спецификации ::person? Другими словами, следующее должно вызвать исключение:

(->Person "Fred" "3")

Я старался:

(s/fdef ->Person :ret ::person)

но звоню:

(->Person "Fred" "3")

не вызывает исключения.

Однако:

(s/conform ::person (->Person "Fred" "3"))

дает ожидаемое:

:clojure.spec/invalid

Спасибо


person Tim Stewart    schedule 22.07.2016    source источник


Ответы (1)


Спецификации fdef :ret и :fn проверяются только во время clojure.spec.test/check тестов, но вы можете использовать спецификацию fdef :args для проверки входных данных функции-конструктора при инструментировании.

(s/fdef ->Person
  :args (s/cat :name ::name :id ::id)
  :ret ::person)

(require '[clojure.spec.test :as stest])
(stest/instrument `->Person)

(->Person "Fred" "3")

=> CompilerException clojure.lang.ExceptionInfo: Call to #'spec.examples.guide/->Person did not conform to spec:
In: [1] val: "3" fails spec: :spec.examples.guide/id at: [:args :id] predicate: int?
:clojure.spec/args  ("Fred" "3")
:clojure.spec/failure  :instrument
:clojure.spec.test/caller  {:file "guide.clj", :line 709, :var-scope spec.examples.guide/eval3771}

Было бы несложно макросировать комбинацию derecord и fdef конструктора, используя соответствующие спецификации.

person Alex Miller    schedule 22.07.2016