I’m writing a small application that includes a password change function with validators for password quality. Currently the validators are specified in a map like so:
(def validations
{:min-length 6
:max-length 32})
The validations map is defined in the validations namespace, but I plan to move it to a configuration namespace later on. My decision to use a map in this way was to make configuration straight-forward for non-programmers.
There are a number of validation functions in the validations namespace that usually take the form:
(defn min-length [n s]
{:req (str "be at least " n " characters long")
:pass? (>= (.length (or s "")) n)})
So with the function above (min-length 3 "clojure") would return {:req "be at least 3 characters long", :pass? true}.
I can validate a password with this function in the validation namespace with this function:
(defn validate-new-password [password]
(into {} (for [[k v] validations]
[k (eval (list (-> k name symbol) v password))])))
The result being something like:
>(validate-new-password "clojure")
{:min-length {:req "be at least 6 characters long", :pass? true},
:max-length {:req "be no longer than 32 characters long", :pass? true},
:min-digits {:req "have at least 1 digit", :pass? false},
:allow-whitespace {:req "not contain spaces or tabs", :pass? true},
:allow-dict-words {:req "not be a dictionary word", :pass? false}}
What is the most practical way to resolve the validation functions when the validate-new-password function is called from outside the validation namespace?
I’ve tried a number of approaches over the past weeks but I’ve never been happy with the resulting form (and none of them have worked!).
Generally I guess the question is “how are symbols in a :require’d namespace resolved when called by functions within that namespace?”
I’m also interested on any general comments about my implementation.
There is no need for eval, as in 98% of the cases.