Fork me on GitHub
#malli
<
2023-04-10
>
victor ruotolo21:04:44

Hi everyone, I'm trying to define a custom transformer (not sure if necessary, but felt like what we needed) in 3 different ways. Only one of them works, and I don't really understand why the others don't, could anyone help me understand it a bit more?

(def something-schema [:map
                       [:something [:or :time/local-date :int]]])

(def transformer1
  (mt/transformer
   {:name            :my-transformer
    :default-decoder {:compile (fn [schema _]
                                 (fn [x]
                                   (case (m/form schema)
                                     :time/local-date
                                     (try (-> (time-format/formatter "MM/DD/YYYY")
                                              (time-format/parse x)
                                              (time-coerce/to-long))
                                          (catch Exception _ x))
                                     :int
                                     (try (Integer/parseInt x)
                                          (catch Exception _ x))
                                     x)))}}))
(def transformer2
  (mt/transformer
   {:name     :my-transformer
    :decoders {:int             (fn [x]
                                  (try (Integer/parseInt x) (catch Exception _ x)))
               :time/local-date (fn [x]
                                  (try (-> (time-format/formatter "MM/DD/YYYY")
                                           (time-format/parse x)
                                           (time-coerce/to-long)) (catch Exception _ x)))}}))

(def transformer3
  (mt/transformer
   {:name            :my-transformer
    :default-decoder {:compile (fn [schema _]
                                 (fn [x]
                                   (case (m/form schema)
                                     [:or :time/local-date :int] (try
                                                                   (Integer/parseInt x)
                                                                   (catch Exception _
                                                                     (-> (time-format/formatter "MM/DD/YYYY")
                                                                         (time-format/parse x)
                                                                         (time-coerce/to-long))))
                                     x)))}}))

(comment

 (m/decode
  something-schema
  {:something  "12/12/2012"}
  transformer3))

victor ruotolo21:04:31

Only transformer3 works the way I wanted it to, the other two just return the same string in the keyword :something

victor ruotolo21:04:44

Would anyone know why that's the case?

ikitommi13:04:41

@U01FK0L87DJ, root cause is that you are returning longs, not LocalDate instances as your schema defines. Decoding is a process that should always return values that are valid against the schemas. • in both 1 & 2: :or selects the first branch that returns valid value. Here, the LocalDate transformation is applied, but an int is returned -> :or ignores that branch as “invalid” and goes to check the next branch. None of the branches match, so original is returned. • how to fix? ◦ describe the field type as :int instead of :time/local-date ◦ mark it somehow as “can be decoded from string”, e.g. via a custom property ◦ something like:

(def something-schema
  [:map
   [:something [:int {::type :time/local-date}]]])

(def transformer1
  (mt/transformer
   {:name :my-transformer
    :default-decoder {:compile (fn [schema _]
                                 (when (= :time/local-date (::type (m/properties schema)))
                                   (fn [x] (try ...
                                                (catch Exception _ x)))))}}))

victor ruotolo13:04:36

Ooooooh I see, that makes sense. Thank you so much!