Fork me on GitHub
#malli
<
2021-11-19
>
Yehonathan Sharvit09:11:34

How would we write a schema for a string made of two components a and b separated by a / where the schema of b depends on the value of a. The valid values of a are known in advance. For instance: 1. When a is "ip" , b should be a valid ip 2. When a is "domain", b should be a valid domain Here are a few examples of valid and invalid data: • ip/127.0.0.1 is valid • ip/111 is not valid • domain/cnn.com is valid • domain/aa is not valid • kika/aaa is not valid

Ben Sless10:11:39

Add a decoder which splits the string at the separator. Then it's a multi schema for a tuple which dispatches of first

💡 1
Yehonathan Sharvit12:11:28

You mean a custom transformer like in https://github.com/metosin/malli/blob/master/docs/tips.md#trimming-strings ?

(require '[malli.transform :as mt])
(require '[malli.core :as m])
(require '[clojure.string :as str])

;; a decoding transformer, only mounting to :string schemas with truthy :string/trim property
(defn string-trimmer []
  (mt/transformer
    {:decoders
     {:string
      {:compile (fn [schema _]
                  (let [{:string/keys [trim]} (m/properties schema)]
                    (when trim #(cond-> % (string? %) str/trim))))}}}))

;; trim me please
(m/decode [:string {:string/trim true, :min 1}] " kikka  " string-trimmer)
; => "kikka"

Yehonathan Sharvit12:11:51

But then, it's the responsibility of the validate callers to explicitly mention my custom transformer. Is there a way to have the custom transformer somehow embedded in the schema?

Ben Sless12:11:55

(def Composite [:tuple {:decode/string #(str/split % #"/") A B])

Ben Sless12:11:22

You can do some sort of schema constructor, too: (def Composite (-simple-schema (fn [A B] [:tuple {:decode/string #(str/split % #"/") A B])))

Ben Sless12:11:49

Then use it like (Composite [:= domain] Domain)

Ben Sless12:11:36

sort of type-constructors

Yehonathan Sharvit12:11:57

Thanks @UK0810AQ2 I'll give it a try

Ben Sless13:11:29

The way I found works well for me is starting from an ideal representation then working backwards with transformers to get there

Yehonathan Sharvit09:11:07

It would be nice to be able to have a distinct error message when we explain why the data is invalid. The explanation could be either: 1. Unknown value of a 2. b is not a valid ip 3. b is not a valid domain

rovanion09:11:43

I don't know how malli works, but if you can use regexes to match strings you could write something like:

((ip)/(:ip-address:)|(domain)/(:domain-name:)) 

rovanion09:11:05

And then look at capture group 1 to see what a is and capture group 2 to find the value.

rovanion09:11:31

If regexes aren't available the logic that would be transferrable would be to group a and b together in pairs rather than to have separate specs for a and b.