When an object is built from a map, aggregating mixins is a trivial 
operation. But when a mixin needs to be closed, it is better to have a 
common mechanism to manage a stack of the close functions of all the 
aggregated mixins than to depend on application logic to do so. Case in 
point, I'd like to use an actor mixin or an alternative async channel mixin 
when composing a database. The async channel mixin will require a close but 
the actor mixin will not. So I developed the closer mixin to handle the 
calls to both the async channel and db file close functions. The database 
will only know the closer's do-close method. And it is the responsibility 
of the async channel and the db file mixins to register their close 
functions with the closer mixin.


My inspiration is Stuart Sierra's component library 
<https://github.com/stuartsierra/component>. But I am not handling 
dependencies per say, only managing a stack of close functions. And the 
"components" are only functions and atoms added to a common map structure, 
not records or deftypes. The result is a very lightweight pattern for 
components. The closer code itself is a lock-free mixin that gets added to 
the common map as needed when the on-close method is called.

For more information, see Closer 
<https://github.com/laforge49/aatree/wiki/Closer>.

(ns aatree.closer
  (:require [clojure.tools.logging :as log]))

(set! *warn-on-reflection* true)

(defn on-close [f opts]
  (let [fsa (:closer-fsa opts)]
    (if fsa
      (do
        (swap! fsa
               (fn [fs]
                 (if fs
                   (conj fs f)
                   (atom (list f)))))
        opts)
      (assoc opts :closer-fsa (atom (list f))))))

(defn- do-closer [fs opts]
  (when fs
    (try
      ((first fs) opts)
      (catch Exception e
        (log/warn e "exception on close")))
    (recur (next fs) opts)))

(defn do-close [opts]
  (let [fsa (:closer-fsa opts)]
    (if fsa
      (let [fs @fsa]
        (if fs
          (if (compare-and-set! fsa fs nil)
            (do-closer fs opts)
            (recur opts)))))))

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
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 clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to