This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-11
Channels
- # adventofcode (52)
- # announcements (3)
- # aws (2)
- # babashka (36)
- # babashka-sci-dev (4)
- # beginners (69)
- # biff (45)
- # calva (9)
- # cider (3)
- # clara (8)
- # clj-kondo (24)
- # clojure (20)
- # clojure-dev (12)
- # clojure-europe (12)
- # clojurescript (2)
- # conjure (1)
- # emacs (17)
- # lsp (69)
- # malli (12)
- # off-topic (32)
- # polylith (2)
- # re-frame (4)
- # releases (2)
- # scittle (6)
- # shadow-cljs (21)
- # tools-deps (10)
- # vim (11)
- # xtdb (11)
hi. I am trying out malli. What is the usual approach to validate a date string with malli?
(def ^:private iso8601-message "is not in ISO DATE TIME format, e.g. 2020-07-03T10:15:30+01:00 or 2020-07-03T10:15:30Z")
;; ISO_OFFSET_DATE_TIME format, e.g., 2011-12-03T10:15:30+01:00 or 2011-12-03T10:15:30Z
(defn ^:private date-time-parser
[date-time]
(try
(.parse DateTimeFormatter/ISO_OFFSET_DATE_TIME date-time)
(catch DateTimeParseException _)))
(def date-range [:map
[:from {:optional true} [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]
[:to {:optional true} [:fn {:swagger/type "string" :swagger/format "date-time" :error/message iso8601-message} date-time-parser]]])
It's a bit draconian but I came to adopt the approach of never using a :fn
schema if I can help it. Instead, all data coming into the process should go through a coercer. In reitit coercion is built in for you, otherwise just roll your own:
https://clojurians.slack.com/archives/CLDK6MFMK/p1636145457067000?thread_ts=1636057867.062800&cid=CLDK6MFMK
Then just say your date is an inst?
I used to do what @U11EL3P9U does and it works well enough IMO. While not immediately apparent, I also created https://github.com/esuomi/muotti/ partly because of the complexities related to this problem, and in fact its source has a tidbit which may also be inspiration, if nothing else: https://github.com/esuomi/muotti/blob/d6e4e3df5f70bf1d26d035b034d9306445aea423/src/main/clj/muotti/malli.clj#L181-L199
thanks for the examples. I am going to use @U11EL3P9U’s approach on this. @UK0810AQ2 what is the reason you prefer not to use :fn
?
• fn schemas are opaque
• values can't be generated
• they hide the real type you're interested in. For example, if you have a time stamp, you might want to compare it against things (such as other dates), meaning you'll have to parse it anyway. You want the real type on your hands
• Schemas specify ought, not is. you use decoding to make the best effort to bring what is to ought, then validate. You can often find an actual type which is closer to what you want than a fn
I can give you a real example - I have code which takes date over as string and needs to calculate N days ago from it, an API which returns numbers in JSON as strings. I don't want to do manual parsing. Once everything is past the gate of coercion and validation, I want my data to be the types I actually use, not what some random stressed out developer at $COMPANY crammed over a poorly thought out API
@UK0810AQ2 That seems to make sense. Thanks for your explanation!