Fork me on GitHub
#malli
<
2022-02-07
>
Ben Sless14:02:24

Where does it make sense in your opinion to specify keywords renaming for a transformer? in a map schema's property or on its entry?

Ben Sless14:02:10

[:map {:rename {:a :b}} [:b int?]] vs [:map [:b {:rename :a} int?]]

ikitommi16:02:05

My intuition says it belongs to the map. But I guess, it depends. Being part of the entry-tuple, you could for example merge two maps with mapping to same domain and the mappings would be merged automatically

ikitommi16:02:34

e.g.

[:merge
 [:map [:a {:rename/SAP "SAP_A", :rename/SALESFORCE "sf_a"} int?]]
 [:map [:b {:rename/SAP "SAP_B"} int?]]]

ikitommi16:02:53

not actually sure if the entry properties get merged correctly here, just guessing 🙂

Ben Sless17:02:17

Followup harder question - if I rename in decode it reports error on the wrong key path!

Ben Sless17:02:35

I dug myself a deeper hole than expected 😄

Ben Sless17:02:42

How do I get out?

Ben Sless06:02:22

The only way I see around it is attaching as metadata the rename mappings and encode the error report on response

ikitommi19:02:11

could you gist a minimal case, I can try to figure out howto

Ben Sless19:02:06

Sure, I'll send it over later today / tomorrow morning

Ben Sless09:02:15

(defn key-renamer
  "Take a tuple of keys [`k1` `k2`] and return a function of a map `m`
  which will replace `k1` with `k2` if it exists in `m`"
  [[k1 k2]]
  (fn -rename [m]
    (if-let [[_ v] (find m k1)]
      (dissoc (assoc m k2 v) k1)
      m)))

(defn- -compile-rename-keys-transformer
  "Takes a map of keys->keys and returns a function which will rename the
  keys in LHS to RHS if they exist in a map."
  [m]
  (if (seq m)
    (reduce comp (map key-renamer m))
    identity))

(def rename-keys-transformer
  (mt/transformer
   {:decoders
    {:map
     {:compile
      (fn [schema _]
        (-> schema m/properties :rename -compile-rename-keys-transformer))}}}))

(comment
  (m/decode
   [:map
    {:rename {:a :b :c :d :x :y}}
    [:b int?]
    [:c int?]
    [:y int?]]
   {:a 1
    :c 2
    :x 3}
   (mt/transformer rename-keys-transformer)))
Given that an input is incorrect post transformation, how do I report the original field name as invalid?

ikitommi10:02:29

had time to look at this. Thing is, you should describe the resulting schema, not the orginal.

ikitommi10:02:37

so, it should be:

[:map 
 {:rename {:a :b, :c :d, :x :y}}
 [:b int?]
 [:d int?]
 [:y int?]]

Ben Sless10:02:41

The problem is that schemas, being a description of "ought", can't report back errors about "is" for any lossy transformation

Ben Sless10:02:52

I had a typo in the last entry which I can spot now

Ben Sless10:02:12

Anyway, schema validates b, user gave me a, I want to report back "a should be an integer" if I was given a, even thought I treat it as b. Some sort of alias mechanism

ikitommi10:02:31

ok, that :thinking_face:

ikitommi10:02:18

what if the rename-map is used to create the target (or source) schema?

ikitommi10:02:21

e.g. transform keys.

ikitommi10:02:37

then, you could validate them separately.

ikitommi10:02:34

(def Target
  (m/schema
   [:map
    [:b int?]
    [:d int?]
    [:y int?]]))

(def mappings {:b :a, :d :c, :y :x})

(def Source
  (mu/transform-entries 
   Target 
   (partial map (fn [[k p v]] [(mappings k k) p v]))))

Source
; => [:map [:a int?] [:c int?] [:x int?]]

ikitommi10:02:07

something like that, explicit schemas for both Source and Target.

ikitommi10:02:10

I would like to see someone cook Meander and Malli together, so that one could write a Malli schema and a Meander transformation for it and infer the target Malli schema from those.

ikitommi10:02:22

might be the ultimate data transformation tool 😎

Ben Sless10:02:17

I started off with a separate schema, I wanted to be clever and unify

Ben Sless10:02:08

Regarding meander, I also thought on using it for the complex unification problems I made for myself

Ben Sless10:02:29

Add a "match" property to schema which binds, then compile that to meander pattern and match on it

awesome 1
Ben Sless10:02:30

Or going the other way, write a meander to malli compiler

awesome 1
Ben Sless10:02:51

Then schemas reflect how data looks

Ben Sless10:02:01

And you can go crazy with unification

AJ Jaro16:02:05

What is the best way to setup the schema to work with a protocol? We could potentially use the https://github.com/metosin/malli#fn-schemas to work through instance?, but maybe there’s a better solution

AJ Jaro15:02:36

Thanks. We’ll probably implement some fn schema to check for satisfies or instance for now I guess!

Ben Wadsworth20:02:04

It is not clear to me what is going on here… its stumped me…

(comment
  (require '[malli.core :as m]
           '[malli.util :as mu])

  (def zip
    [:re #"\d{5}"])

  (def some-schema
    [:map
     [:zipcode zip]])

  (m/validate some-schema {:zipcode "12345\t22"}) => true

  (m/validate (mu/merge some-schema nil) "12345\t22") => false

  some-schema => [:map [:zipcode [:re #"\d{5}"]]]

  (mu/merge some-schema nil) => [:map [:zipcode [:re #"\d{5}"]]]
  )

Ben Wadsworth20:02:57

doh’ i mean the obvious here is wrong…one sec..

Ben Wadsworth20:02:32

(being the map in one call and string in another) lol…

Ben Wadsworth20:02:54

I compounded a couple of issues but I dont think its Malli…. I think it might be coercion from reitit. Sorry bout that, another pair of eyes found the painfully obvious string where a map should have been in my example 😐

Ben Wadsworth21:02:28

yeah no issue… happy monday