If you always access *callbacks* as a thread-local, IE in the same thread, 
why do you need it to be an atom? You can just rebind it using set! instead 
of swap!.

Beyond that, I think that using thread locals this way is very useful when 
you want to limit the need for implementations to know your specific 
implementation. The other way would be to pass the transaction, or a 
generic environment, around, which tends to be ugly and sometimes confusing.

Just make sure not to use it a lot and to document well.

Disclaimer - while I have many years of development experience, I am 
relatively new to Clojure, but I read a lot of Clojure code at this point.

On Saturday, May 23, 2015 at 6:16:15 AM UTC-4, Colin Yates wrote:
>
> Hi,
>
> My use-case is that I need to have a bunch of state which differs for each 
> (web) request but is accessible via a var. Specifically I need to allow 
> code to access the current transaction and register one or callbacks that 
> are executed if/after that transaction is committed.
>
> In Java I would store these as thread locals as each web request comes in 
> as a new thread. In Clojure, the equivalent seems to be dynamic binding. 
>
> Am I right in thinking that the following infrastructure is correct, 
> specifically the use of thread-bound?. And when would I use bound? rather 
> than thread-bound?
>
> To be clear, the outer 'service' will call 'do-in-tx', code evaluated as a 
> result of 'do-in-tx' will access the current tx by calling 'with-tx'.
>
> (def ^:dynamic *tx*)
> (def ^:dynamic *callbacks*)
>
> (defn- execute-in-tx [db f]
>   (jdbc/with-db-transaction
>     [tx db]
>     (binding [*tx* tx]
>       (f tx))))
>
> (defn- execute-callbacks []
>   (doseq [[id cb] @*callbacks*] (cb)))
>
> (defn do-in-tx [db f]
>   (when (thread-bound? #'*tx*)
>     (throw (IllegalArgumentException. "Nested transactions are not 
> supported!.")))
>   (binding [*callbacks* (atom {})]
>     (execute-in-tx db f)
>     (execute-callbacks)))
>
> (defn with-tx
>   "Call back the specified function providing the current transaction.
>
>   If there is no current transaction then an (IllegalArgument)Exception is
>   thrown. "
>   [f]
>   (when-not (thread-bound? #'*tx*)
>     (throw (IllegalArgumentException. "No transaction found!")))
>   (f *tx*))
>
> (defn register-cb [id cb]
>   (when-not (thread-bound? #'*callbacks*)
>     (throw (IllegalArgumentException. "No transaction found!")))
>   (swap! *callbacks* assoc id cb))
>
>
>

-- 
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