Brad
Brad

Reputation: 715

Can the dispatch values of a clojure multimethod be queried?

I'm writing some code that looks like this:

(def
  authorized-access-levels
  {:sales-rep-manager (fn [{{user :user} :session}]

                        )
   :regional-sales-manager (fn [{{user :user} :session}]

                             )
   :vp-of-sales (fn [{{user :user} :session}]

                  )
   })

Later in the code:

(defn
  get-my-housing
  [{{user :user} :session :as request}]
  (let [data-fn (authorized-access-levels (user :access-level))]
    (data-fn request)))

This at surface level seems like a great use case for multi methods where the defmulti would look like this:

(defmulti get-my-housing (fn [{{{access-level :access-level} :user} :session}] access-level))
(defmethod get-my-housing :vp-of-sales [{{user :user} :session}]

  )

but I have another need that looks like this:

:auth-fn (fn [user] (contains? authorized-access-levels (user :access-level)))

So (long story short) I need the keys to determine if a user is authorized to get data but then I use the key to dispatch to a function via a map.

Can I query a multimethod to see what it's dispatch values are? If so then I can write this as a multimethod and then query it for authorization. Any other ideas?

Upvotes: 3

Views: 1063

Answers (2)

Alex
Alex

Reputation: 13941

Can I query a multimethod to see what it's dispatch values are?

Yes, you can do introspection on a multimethod using the methods function to get the dispatch table for a multimethod, and the get-method to look up the method for a given dispatch value.

user=> (defmulti authorized? :access-level)
user=> (defmethod authorized? :admin [_] true)
user=> (defmethod authorized? :user [_] false)

user=> (keys (methods authorized?))
(:user :admin)

user=> ((get-method authorized? :admin) {:access-level :admin})
true

Upvotes: 9

sbensu
sbensu

Reputation: 1511

I'm not sure if I understood correctly but let me try. You have three different concepts that we want to model as three different functions: get a users access-level, get a users authorization from the access level, and finally get the housing. I would model it like this:

(defn get-access-level [user]
  (:access-level user))

(defn authorized? [user]
  (contains? authorized-access-levels (get-access-level user)))

(defmulti get-my-housing (fn [req] 
                           (get-access-level (get-in req [:session :user]))

(defmethod get-my-housing :vp-of-sales
  [req]
  ((:vp-of-sales authorized-access-levels) req))

This is worth it if each defmethod has a different behavior. If they all access the same map with a different key the extra indirection is probably not worth it.

Upvotes: 0

Related Questions