I'm not caching the values, only the mapping from keywords to methods (the
caching is per class, not per bean). It's just saving the
introspection/reflection on each call. The values themselves are always
obtained directly from the bean.
On Thursday, January 30, 2014 4:12:31 PM UTC+2, Jozef Wagner wrote:
>
> Hi,
>
> Not every bean is immutable, so you cannot cache its values by default. If
> reading from the bean is expensive for you, either create a normal hash-map
> with into {} and select-keys, or use memoize.
>
> user=> (def b (bean (java.util.Date.)))
> #'user/b
> user=> (def hm (into {} (select-keys b [:year :month :date])))
> #'user/hm
> user=> hm
> {:date 30, :month 0, :year 114}
> user=> (def mb (memoize b))
> #'user/mb
> user=> (mb :day)
> 4
>
> JW
>
>
>
> On Thu, Jan 30, 2014 at 2:35 PM, pron <[email protected]
> <javascript:>>wrote:
>
>> The bean function is a very useful Java interop feature that provides a
>> read-only view of a Java Bean as a Clojure map.
>> As it stands, the function performs introspection on the bean's class
>> whenever the function is called:
>>
>>
>> (defn bean
>> "Takes a Java object and returns a read-only implementation of the
>>
>> map abstraction based upon its JavaBean properties."
>> {:added "1.0"}
>>
>> [^Object x]
>> (let [c (. x (getClass))
>>
>> pmap (reduce1 (fn [m ^java.beans.PropertyDescriptor pd]
>>
>> (let [name (. pd (getName))
>>
>> method (. pd (getReadMethod))]
>>
>> (if (and method (zero? (alength (. method
>> (getParameterTypes)))))
>>
>> (assoc m (keyword name) (fn []
>> (clojure.lang.Reflector/prepRet (.getPropertyType pd) (. method (invoke x
>> nil)))))
>>
>> m)))
>> {}
>> (seq (.. java.beans.Introspector
>>
>> (getBeanInfo c)
>>
>>
>> (getPropertyDescriptors))))
>> v (fn [k] ((pmap k)))
>>
>> snapshot (fn []
>> (reduce1 (fn [m e]
>>
>> (assoc m (key e) ((val e))))
>>
>> {} (seq pmap)))]
>>
>>
>> (proxy [clojure.lang.APersistentMap]
>> []
>>
>> (containsKey [k] (contains? pmap k))
>>
>> (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v
>> k))))
>>
>> (valAt ([k] (when (contains? pmap k) (v k)))
>>
>> ([k default] (if (contains? pmap k) (v k) default)))
>>
>> (cons [m] (conj (snapshot) m))
>>
>> (count [] (count pmap))
>>
>> (assoc [k v] (assoc (snapshot) k v))
>>
>> (without [k] (dissoc (snapshot) k))
>>
>> (seq [] ((fn thisfn [plseq]
>>
>> (lazy-seq
>> (when-let [pseq (seq plseq)]
>>
>> (cons (new clojure.lang.MapEntry (first pseq) (v (first
>> pseq)))
>>
>> (thisfn (rest pseq)))))) (keys pmap))))))
>>
>>
>>
>>
>> I propose to cache the pmap value for each class using JDK 7's
>> ClassValue<http://docs.oracle.com/javase/7/docs/api/>.
>> Here's a proposed implementation:
>>
>> (def ^:private ^java.lang.ClassValue bean-class-value
>>
>> (proxy [java.lang.ClassValue]
>> []
>> (computeValue [c]
>>
>> (reduce (fn [m ^java.beans.PropertyDescriptor pd]
>>
>> (let [name (. pd (getName))
>>
>> method (. pd (getReadMethod))
>>
>> type (.getPropertyType pd)]
>> (if (and method (zero? (alength (. method
>> (getParameterTypes)))))
>>
>> (assoc m (keyword name) (fn [x]
>> (clojure.lang.Reflector/prepRet type (. method (invoke x nil)))))
>>
>> m)))
>> {}
>> (seq (.. java.beans.Introspector
>>
>> (getBeanInfo c)
>> (getPropertyDescriptors)))))))
>>
>>
>> (defn bean
>> "Takes a Java object and returns a read-only implementation of the
>>
>> map abstraction based upon its JavaBean properties."
>> {:added "1.0"}
>>
>> [^Object x]
>> (let [c (. x (getClass))
>>
>> pmap (.get bean-class-value c)
>>
>> v (fn [k] ((pmap k) x))
>>
>> snapshot (fn []
>> (reduce (fn [m e]
>>
>> (assoc m (key e) ((val e) x)))
>>
>> {} (seq pmap)))]
>> (proxy [clojure.lang.APersistentMap]
>>
>> []
>> (containsKey [k] (contains? pmap k))
>>
>> (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v
>> k))))
>>
>> (valAt ([k] (when (contains? pmap k) (v k)))
>>
>> ([k default] (if (contains? pmap k) (v k) default)))
>>
>> (cons [m] (conj (snapshot) m))
>>
>> (count [] (count pmap))
>> (assoc [k v] (assoc (snapshot) k v))
>>
>> (without [k] (dissoc (snapshot) k))
>>
>> (seq [] ((fn thisfn [plseq]
>>
>> (lazy-seq
>> (when-let [pseq (seq plseq)]
>>
>> (cons (new clojure.lang.MapEntry (first pseq) (v (first
>> pseq)))
>>
>> (thisfn (rest pseq)))))) (keys pmap))))))
>>
>>
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to [email protected]<javascript:>
>> Note that posts from new members are moderated - please be patient with
>> your first post.
>> To unsubscribe from this group, send email to
>> [email protected] <javascript:>
>> For more options, visit this group at
>> http://groups.google.com/group/clojure?hl=en
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>
>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.