I came up with this solution:
(ns spec-test.core
(:require [clojure.spec :as s]))
(s/def :user/name string?)
(s/def :common/user (s/keys :req [:user/name]))
;; with this little helper function...
(defn check [type data]
(if (s/valid? type data)
true
(throw (AssertionError. (s/explain type data)))))
;; I can use it in my :pre condition
(defn aname [user]
{:pre [(check :common/user user)]}
(-> user :user/name))
;; when I call name with an illegal arguement...
(aname {:x "Elon"})
;; ...it not fails and returns a better error message:
CompilerException java.lang.AssertionError: null,
compiling:(/Users/joakimtengstrand/IdeaProjects/spec-test/src/spec_test/core.clj:19:1)
val: {:x "Elon"} fails spec: :common/user predicate: (contains? % :user/name)
With this solution I don't need to enable assertions, and the code is neat
and less verbose!
/Joakim
On Thursday, September 15, 2016 at 3:11:32 PM UTC+2, Shantanu Kumar wrote:
>
> Hi Joakim,
>
> You might be interested in Paul Stadig's library
> https://github.com/pjstadig/assertions that leverages Java's `-ea`
> (enable-assertions, which you may want to keep enabled in dev) command-line
> flag. If you have a bunch of things together to assert, you may want to use
> the `when-assert` macro for wholesale optimization:
> https://github.com/pjstadig/assertions/blob/0.2.0/src/pjstadig/assertions.clj#L13
>
>
> Shantanu
>
> On Thursday, 15 September 2016 16:50:17 UTC+5:30, [email protected]
> wrote:
>>
>> Ok, thanks!
>>
>> In the Java world, the assertions is also something that need to be turn
>> on explicitly.
>> In that sence, they are kind of not mandatory to be executed (or at least
>> signals that to the reader of the code).
>>
>> I would be happier if you guys could add another method, that I can use
>> in my :pre conditions, that leverage
>> the same amount of details in the error messages, but that is always
>> "turned on".
>>
>> In the meanwhile, I will use s/assert ;-)
>>
>> BR,
>> Joakim Tengstrand
>>
>>
>> On Wednesday, 14 September 2016 15:59:09 UTC+2, Alex Miller wrote:
>>>
>>> Another option that has been added since the guide was written is
>>> s/assert which seems closer to what you're suggesting.
>>>
>>> (defn name [user]
>>> {:pre [(s/assert :common/user user)]}
>>> (-> user :user/name))
>>>
>>> ;; need to enable assertion checking - this can also be enabled globally
>>> with system property clojure.spec.check-asserts
>>> (s/check-asserts true)
>>>
>>> (name {:user/name "Elon"})
>>> "Elon"
>>>
>>> (name {:x "Elon"})
>>> ExceptionInfo Spec assertion failed
>>> val: {:x "Elon"} fails predicate: (contains? % :user/name)
>>> :clojure.spec/failure :assertion-failed
>>> clojure.core/ex-info (core.clj:4725)
>>>
>>> Rather than use it in a precondition, you can also use s/assert directly
>>> in the code.
>>>
>>> On Wednesday, September 14, 2016 at 7:37:24 AM UTC-5,
>>> [email protected] wrote:
>>>>
>>>> (ns spec-test.core
>>>> (:require [clojure.spec :as s]))
>>>>
>>>> (s/def :user/name string?)
>>>> (s/def :common/user (s/keys :req [:user/name]))
>>>>
>>>> ; first version of name (using :pre)
>>>> (defn name [user]
>>>> {:pre [(s/valid? :common/user user)]}
>>>> (-> user :user/name))
>>>>
>>>> ; This statement works ok and returns "Elon":
>>>> (name {:user/name "Elon"})
>>>>
>>>> ; but this statement...
>>>> (name {:x "Elon"})
>>>>
>>>> ;...will throw:
>>>> CompilerException java.lang.AssertionError:
>>>> Assert failed: (s/valid? :common/user user)
>>>>
>>>> ; ...but then I don't get as much information
>>>> ; about the error as if I would have called:
>>>> (s/explain :common/user {:x "Elon"})
>>>>
>>>> ;...which also contains the predicate:
>>>> val: {:x "Elon"} fails spec: :common/user
>>>> predicate: (contains? % :user/name)
>>>>
>>>> ; (second version of name - more verbose)
>>>> ; or do I need to wite it like this:
>>>> (defn name [user]
>>>> (let [parsed (s/conform :common/user user)]
>>>> (if (= parsed ::s/invalid)
>>>> (throw (ex-info "Invalid input" (s/explain-data :common/user user)))
>>>> (-> user :user/name))))
>>>>
>>>> ; so that:
>>>> (name {:x "Elon"})
>>>>
>>>> ; ...will return:
>>>> CompilerException clojure.lang.ExceptionInfo:
>>>> Invalid input #:clojure.spec{:problems}
>>>> ({:path [], :pred (contains? % :user/name),
>>>> :val {:x "Elon"}, :via [:common/user], :in []})
>>>>
>>>> ; It should be nice if I could be able to write it like this
>>>> ; (or similar, to get a better error message):
>>>> (defn name [user]
>>>> {:pre [(s/explain :common/user user)]}
>>>> (-> user :user/name))
>>>>
>>>>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.