首页 > 代码库 > LightTable的结构(二)

LightTable的结构(二)

这节主要研究下object的一个属性,behaviors

定义一个behavior需要提供name,trigger,reaction

(behavior ::on-close-destroy          :triggers #{:close}          :reaction (fn [this]                      (object/raise this :destroy)))

在创建object的时候传入

(object/object* ::user.hello                :tags [:user.hello]                :behaviors [::on-close-destroy]                :init (fn [this]                        (hello-panel this)))

在object/object*和object/create的时候都可以传入behavior

 

那么怎么触发behavior呢

(object/raise app :closing)

 

(defn raise*  ([obj reactions args] (raise* obj reactions args nil))  ([obj reactions args trigger]   (doseq [r reactions           :let [func (:reaction (->behavior r))                 args (if (coll? r)                        (concat (rest r) args)                        args)                 meta (if (coll? r)                        (meta r)                        {})]           :when func]     (try     (with-time       (binding [*behavior-meta* meta]         (apply func obj args))       (when-not (= trigger :object.behavior.time)         (raise obj :object.behavior.time r time trigger)))       (catch js/Error e         (safe-report-error (str "Invalid behavior: " (-> (->behavior r) :name)))         (safe-report-error e)         )       (catch js/global.Error e         (safe-report-error (str "Invalid behavior: " (-> (->behavior r) :name)))         (safe-report-error e)         )))))(defn raise [obj k & args]  (let [reactions (-> @obj :listeners k)]    (raise* obj reactions args k)))

可以看出,object/raise会从obj的:listeners中获取对应trigger的reactions

object/raise*中,对这些reactions进行执行,那么behavior是如何变成:listener的呢,注意到 object/handle-redef 会使用update-listeners

(defn handle-redef [odef]  (let [id (::type odef)]    (doseq [o (instances-by-type id)            :let [o (deref o)                  args (:args o)                  old (:content o)                  behs (set (:behaviors o))                  inst (@instances (->id o))                  neue (when (:init odef)                         (apply (:init odef) inst args))                  neue (if (vector? neue)                         (crate/html neue)                         neue)]]      (merge! inst {:tags (set/union (:tags o) (:tags odef))                                      :behaviors (set/union behs (set (:behaviors odef)))                                      :content neue})      (merge! inst (update-listeners inst))      (when (and old neue)        (replace-with old neue))      (raise inst :redef))    id))(defn object* [name & r]  (-> (apply make-object* name r)      (store-object*)      (handle-redef)))

update-listeners利用->triggers将behavior转换成对应的 trigger,存入:listeners

(defn update-listeners  ([obj] (update-listeners obj nil))  ([obj instants]   (let [cur @obj         behs (set (concat (:behaviors cur) (tags->behaviors (:tags cur))))         trigs (->triggers behs)         ;;We need to load new JS files here because they may define the behaviors that we‘re meant to         ;;capture. If we have a load, then load and recalculate the triggers to pick up those newly         ;;defined behaviors         trigs (if (:object.instant-load trigs)                 (do                   (raise* obj (:object.instant-load trigs) nil :object.instant-load)                   (->triggers behs))                 trigs)         trigs (if instants                 trigs                 (dissoc trigs :object.instant :object.instant-load))]     ;;deref again in case :object.instant-load made any updates     (assoc @obj :listeners trigs))))
(defn ->triggers [behs]  (let [result (atom (transient {}))]    (doseq [beh behs            t (:triggers (->behavior beh))]      (swap! result assoc! t (conj (or (get @result t) ‘()) beh)))    (persistent! @result)))

  

 

 

--------------------------------------

注:

获取Ref, Atom 和Agent对应的value @ref (deref ref)  

LightTable的结构(二)