Skip to content

Instantly share code, notes, and snippets.

@camsaul
Created January 27, 2022 02:53
Show Gist options
  • Save camsaul/79c5f3bfb86b028e15f2c859b4a0fdc8 to your computer and use it in GitHub Desktop.
Save camsaul/79c5f3bfb86b028e15f2c859b4a0fdc8 to your computer and use it in GitHub Desktop.
Reify all/sneaky proxy
;; we have to do a bit of magic to make the [[ConnectionDataSource]] work -- normally if someone were to use it with
;; `with-open` then the original connection would get closed when someone called `.close` on the one from the
;; `DataSource`. So we need to actually have [[ConnectionDataSource]] return a sneaky special proxy Connection that
;; forwards ALL of its other methods to the original Connection EXCEPT for `.close`, which should just no-op.
;;
;; This is all a little hacky for my tastes and it would be better if we could just eliminate [[ConnectionDataSource]]
;; entirely so we didn't need to use this anymore. It's only used in two places anyway.
(defn- reify-all-method [^java.lang.reflect.Method method original-symb]
(let [method-name (-> (symbol (.getName method))
(vary-meta assoc :tag (.. method getReturnType getTypeName)))
params (for [^java.lang.reflect.Parameter param (.getParameters method)]
(-> (symbol (.getName param))
(vary-meta assoc :tag (.. param getType getName))))]
(list
method-name
(into ['_] params)
(list* (symbol (str "." (.getName method)))
original-symb
(for [param params] (with-meta param nil))))))
(defn- reify-all*
[^Class klass original-symb & {:as method->override}]
(let [original-symb (vary-meta original-symb assoc :tag (.getCanonicalName klass))]
(list
`fn [original-symb]
(list*
`reify
(symbol (.getCanonicalName klass))
(for [method (sort-by #(.getName ^java.lang.reflect.Method %) (.getDeclaredMethods klass))]
(get method->override method (reify-all-method method original-symb)))))))
(defmacro ^:private reify-all
"Like [[reify]] but also adds a default implementation for EVERY declared method of `class`, calling the same method
on `original`.
(reify-all java.sql.Connection conn)
;; ->
(reify java.sql.Connection
(close [_] (.close conn))
...)
Override individual methods by passing in `Method->override-form`. (Pass in the
matching [[java.lang.reflect.Method]] because in some cases you can have multiple matching methods and it's easier
if you just tell us the exact method you want to override than us trying to guess which one you mean based on type
hints.) See [[non-closing-connection]] below for an example."
{:style/indent :defn}
[klass original & Method->override-form]
(eval (list* reify-all* klass
(list 'quote original)
Method->override-form)))
(defn- non-closing-connection
"Returns a wrapped [[java.sql.Connection]] that doesn't close when you call `.close`. Used to
implement [[ConnectionDataSource]] below."
^java.sql.Connection [conn]
(reify-all java.sql.Connection conn
(.getDeclaredMethod java.sql.Connection "close" nil)
'(close [_])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment