Fork me on GitHub
#clojure-spec
<
2020-06-05
>
Felipe Marques14:06:01

@ikitommi Hi, a few days ago you mentioned some other libs to perform spec coercion, but I forgot to take notes on its names to check it later. Could you share the names again? Also, I'm having some trouble with coercing some data using spec-tools. I'm not sure if I got the logic right. Do you have any resource explaining how the coercion works in spec-tools? Thanks very much!

ikitommi14:06:47

Hi @marques.goncalves.fel! The slack history (& my message) is here: https://clojurians-log.clojureverse.org/clojure-spec/2020-05-19. I believe all spec-coercion libs are doing it in the same way, : given a spec, it's form is parsed and for all different specs (`or`, and, keys, integer?, any? etc.) a transforming function is selected and applied to the value. Corcion is recursive and best effort. With spec-tools:

(require '[clojure.spec.alpha :as s])
(require '[spec-tools.core :as st])

(s/def ::spec (s/nilable
                (s/nilable
                  (s/map-of
                    keyword?
                    (s/or :keys (s/keys :req-un [::c1])
                          :ks (s/coll-of (s/and int?) :into #{}))))))

(def value {"keys" {:c1 "1" ::c2 "kikka"}
            "keys2" {:c1 true}
            "ints" [1 "1" "invalid" "3"]})

(st/coerce ::spec value st/string-transformer)
;{:keys {:c1 "1", :test/c2 "kikka"}
; :keys2 {:c1 true}
; :ints #{1 "invalid" 3}}

(st/coerce ::spec value st/json-transformer)
;{:keys {:c1 "1", :test/c2 "kikka"}
; :keys2 {:c1 true}
; :ints #{"3" 1 "invalid" "1"}}

Felipe Marques14:06:33

Thanks for the links and explanation. Sorry for not finding the history. I looked only in slack! 😅

dev.4openID20:06:57

Learner here. Dumb Question maybe, maybe a mindset I have a map (ex JSON) in it exists the following: {:status {:timestamp "2020-04-09T15:27:00" :status "abc"} Yes, I suppose I could change the data so that the 1st :status has a different name but that is not very elegant. If the structure remains the same, I assume you cannot redefine the :status again within the spec Is there a way int spec (some technique) that deals with this type of anomaly? I have a data set exactly like that. Any thoughts?

Alex Miller (Clojure team)21:06:14

s/keys with :req-un will match the short name of the provided spec but can use different qualified specs

Alex Miller (Clojure team)21:06:10

(s/def :inner/status string?)
(s/def :inner/timestamp string?)
(s/def :outer/status (s/keys :req-un [:inner/status :inner/timestamp]))
(s/def :outer/map (s/keys :req-un [:outer/status]))

dev.4openID21:06:12

(s/def :inner/status string?) (s/def :inner/timestamp string?) (s/def :outer/status (s/keys :req-un [:inner/timestamp :inner/status])) (s/def :outer/map (s/keys :req-un [:inner/status])) (s/valid? :outer/map {:status {:timestamp "2020-09" :status "abc" }}) Hi, @alexmillerseems to fail against the data. Perhaps I miss it? (s/explain-str :outer/map {:status {:timestamp "2020-09" :status "abc" }}) ;; => "{:timestamp \"2020-09\", :status \"abc\"} - failed: string? in: [:status] at: [:status] spec: :inner/status\n"

seancorfield21:06:29

@dev.4openid That works for me:

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def :inner/status string?)
:inner/status
user=> (s/def :inner/timestamp string?)
:inner/timestamp
user=> (s/def :outer/status (s/keys :req-un [:inner/status :inner/timestamp]))
:outer/status
user=> (s/def :outer/map (s/keys :req-un [:outer/status]))
:outer/map
(s/valid? :outer/map {:status {:timestamp "2020-09"
                                                    :status "abc" }})
true
(s/explain-str :outer/map {:status {:timestamp "2020-09"
                               :status "abc" }})
"Success!\n"
user=> 
(you can use triple-backticks around your code to format it and make it easier to read)

dev.4openID21:06:13

Hi @alexmiller and @seancorfield - thanks for the help - I am wrong: my fat fingers and compiling versions of the trial and error - I screwed up! 😭😀 - along day!