this is my clojure-ring handler
(defn handler [req]
(let [distr (get-in req [:query-params "dist"])]
(def sortie (describe (eval ((resolve (symbol distr)) 1 3))
0.5 0.25 3))
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "<p>" (print-str sortie) "<p>")}
))
(def app
(-> #'handler
(ring.middleware.stacktrace/wrap-stacktrace)
(wrap-spy)
(wrap-params)
))
the "dist" key maps to a string.
:query-params {"dist" "gaussian-dist"}
I want to use it as a function name. The whole
(eval ((resolve (symbol distr)) 1 3))
returns a map
When I replace distr by the string, it works as desired.
=> (eval ((resolve (symbol "gaussian-dist")) 1 3))
{:Distribution :actuarial.distributions/Gaussian, :Parameters {:sigma 3, :mu 1}}
Edit:
This is how I ended up solving my problem: Thanks again, I'm a beginner at Clojure
I got through it using a macro
(defmacro get-map [map name]
`(get ~map ~name))
(defn handler [req]
(let [distr-name (get-in req [:query-params "dist"])
distr-map ((get-map distributions-map distr-name) 1 3)
sortie (describe distr-map 0.5 0.25 3)
]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "<p>" sortie "<p>")}))
It seems a bit of a risky proposition from a security and maintainability standpoint to let the Internet call any symbol in your namespace as a function, so my thought would be to instead do something like:
Prepare a white-list of functions users should be able to request, in the form of a map from query parameter strings to functions.
(def distributions {"gaussian-dist" gaussian-dist})
Make a default function which either makes an error message or just picks some default distribution.
When you get a query, just look up the function you need in distributions
using get
and the default function to handle the case of query strings not found.
(let [distr (get-in req [:query-params "dist"])
sortie ((get distributions distr default-fn) 1 3)]
It's also kinda risky to def
a var like sortie
every request, as that opens you up to race conditions. That's why it seemed more natural to let
that name too unless there's more to the handler than I'm seeing here. You might also be able to do what you want with (def ^:dynamic *sortie*)
and binding
, which isolates the bindings of different threads from one another to prevent that.