Clojure spec и конструкторы записей
Если бы я определил следующую запись:
(defrecord Person [name id])
и следующее:
(s/def ::name string?)
(s/def ::id int?)
(s/def ::person (s/keys :req-un [::name ::id]))
Как я могу гарантировать, что вы не можете создать Person, который не соответствует спецификации:: person? Другими словами, следующее должно вызвать исключение:
(->Person "Fred" "3")
Я старался:
(s/fdef ->Person :ret ::person)
но звонит:
(->Person "Fred" "3")
не вызывает исключения.
Тем не мение:
(s/conform ::person (->Person "Fred" "3"))
дает ожидаемое:
:clojure.spec/invalid
Спасибо
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}
Было бы не сложно создать макрос для комбинации defrecord и fdef конструктора, используя соответствующие спецификации.