Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Add scopes for hooks #10

Merged
merged 1 commit into from

3 participants

@hugoduncan

Adds a hook-scope macro that provides a scope which records any change to
hooks during the dynamic scope of its body, and restores hooks to their
original state on exit of the scope.

This has only been lightly tested, as I wanted to get feedback as to whether this was a welcome addition or not. The motivation for this is to allow lein sub to apply plugins properly to sub-projects, without having their hooks spill over between projects.

@hugoduncan hugoduncan Add scopes for hooks
Adds a hook-scope macro that provides a scope which records any change to
hooks during the dynamic scope of its body, and restores hooks to their
original state on exit of the scope.
f0f992d
@hugoduncan hugoduncan referenced this pull request in kumarshantanu/lein-sub
Merged

Init sub-projects, so their plugins are applied #8

@joegallo
Collaborator

Doesn't the use of alter-var-root here mean that all hook-scopes are shared, and that two threads both using hook-scope simultaneously would clobber each other? (If not, then I need to read it much closer, maybe it's more subtle than my initial read of things revealed.)

@hugoduncan

Threads can indeed interfere with each other, as they share any scope.

This is essentially the minimal change to allow scopes in line with the global nature of the current implementation. Supporting thread bindings would be a wider reaching change, and requiring a change in the implementation details of how hooks are added, removed and used, and the current semantics of hooks.

My driving use case for this was single threaded, but I hoped this issue would be brought up and discussed.

@technomancy
Owner

Great idea; let's get some docs in for it and I'll cut a release. Thanks.

@technomancy technomancy merged commit e0d1148 into technomancy:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 27, 2012
  1. @hugoduncan

    Add scopes for hooks

    hugoduncan authored
    Adds a hook-scope macro that provides a scope which records any change to
    hooks during the dynamic scope of its body, and restores hooks to their
    original state on exit of the scope.
This page is out of date. Refresh to see the latest.
Showing with 42 additions and 0 deletions.
  1. +35 −0 src/robert/hooke.clj
  2. +7 −0 test/robert/test_hooke.clj
View
35 src/robert/hooke.clj
@@ -55,6 +55,40 @@
::hooks hooks
::original original)))))))
+(defonce hook-scopes [])
+
+(defn start-scope []
+ (locking hook-scopes
+ (alter-var-root #'hook-scopes conj {})))
+
+(defn- scope-update-fn
+ [scopes target-var]
+ (conj
+ (pop scopes)
+ (update-in (peek scopes) [target-var] #(if % % @(hooks target-var)))))
+
+(defn- possibly-record-in-scope
+ [target-var]
+ (locking hook-scopes
+ (when (seq hook-scopes)
+ (alter-var-root #'hook-scopes scope-update-fn target-var))))
+
+(defn end-scope []
+ (locking hook-scopes
+ (let [head (peek hook-scopes)]
+ (alter-var-root #'hook-scopes pop)
+ (doseq [[var old-hooks] head]
+ (reset! (hooks var) old-hooks)))))
+
+(defmacro hook-scope
+ "Defines a scope which records any change to hooks during the dynamic scope of
+its body, and restores hooks to their original state on exit of the scope."
+ [& body]
+ `(try
+ (start-scope)
+ ~@body
+ (finally (end-scope))))
+
(defn add-hook
"Add a hook function f to target-var. Hook functions are passed the
target function and all their arguments and must apply the target to
@@ -63,6 +97,7 @@
(add-hook target-var f f))
([target-var key f]
(prepare-for-hooks target-var)
+ (possibly-record-in-scope target-var)
(swap! (hooks target-var) assoc key f)))
(defn- clear-hook-mechanism [target-var]
View
7 test/robert/test_hooke.clj
@@ -83,3 +83,10 @@
(is (= (keyed 1) 4))
(clear-hooks #'keyed)
(is (= (keyed 1) 1)))
+
+(deftest hook-scope-test
+ (is (hooked))
+ (hook-scope
+ (add-hook #'hooked asplode)
+ (is (thrown? Exception (hooked))))
+ (is (hooked)))
Something went wrong with that request. Please try again.