Skip to content

Commit 4bf9867

Browse files
authored
Add support for require like import-vars (#78)
* Support require like import-vars * Add require like examples * Fix typos
1 parent f21d5fd commit 4bf9867

File tree

4 files changed

+123
-76
lines changed

4 files changed

+123
-76
lines changed

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ The former approach places an onus on the creator of the library; the various or
3333
diff])
3434
```
3535

36+
`import-vars` supports the `require` syntax of Clojure:
37+
38+
```clojure
39+
(import-vars
40+
[clojure.walk :refer [prewalk postwalk]])
41+
```
42+
43+
Renaming is also supported with the `require` syntax:
44+
45+
```clojure
46+
(import-vars
47+
[clojure.walk :refer [prewalk postwalk] :rename {prewalk pre-walk}])
48+
```
49+
3650
### `def-map-type`
3751

3852
A Clojure map implements the following interfaces: `clojure.lang.IPersistentCollection`, `clojure.lang.IPersistentMap`, `clojure.lang.Counted`, `clojure.lang.Seqable`, `clojure.lang.ILookup`, `clojure.lang.Associative`, `clojure.lang.IObj`, `java.lang.Object`, `java.util.Map`, `java.util.concurrent.Callable`, `java.lang.Runnable`, and `clojure.lang.IFn`. Between them, there's a few dozen functions, many with overlapping functionality, all of which need to be correctly implemented.
@@ -106,11 +120,11 @@ This abstract type may be used within the body of `deftype+`, which is just like
106120
A drop in replacement for `defprotocol` that is more REPL-friendly.
107121

108122
A protocol created with Clojure's `defprotocol` always creates new instance at load time.
109-
If a protocol is reloaded, a `defrecord` in another namespace that is referencing the procotol will not automatically be updated to the new protocol instance.
123+
If a protocol is reloaded, a `defrecord` in another namespace that is referencing the protocol will not automatically be updated to the new protocol instance.
110124

111125
One telltale symptom of this disconnect can be a `No implementation of method` exception when calling record methods.
112126

113-
Potemkin's `defprotocol+` improves the REPL experience by only creating a new instance of a protocol if the procotol body has changed.
127+
Potemkin's `defprotocol+` improves the REPL experience by only creating a new instance of a protocol if the protocol body has changed.
114128

115129
### `definterface+`
116130

src/potemkin/namespaces.clj

+79-66
Original file line numberDiff line numberDiff line change
@@ -4,100 +4,113 @@
44
"Makes sure that all changes to `src` are reflected in `dst`."
55
[src dst]
66
(add-watch src dst
7-
(fn [_ src old new]
8-
(alter-var-root dst (constantly @src))
9-
(alter-meta! dst merge (dissoc (meta src) :name)))))
7+
(fn [_ src old new]
8+
(alter-var-root dst (constantly @src))
9+
(alter-meta! dst merge (dissoc (meta src) :name)))))
1010

1111
(defmacro import-fn
1212
"Given a function in another namespace, defines a function with the
1313
same name in the current namespace. Argument lists, doc-strings,
1414
and original line-numbers are preserved."
1515
([sym]
16-
`(import-fn ~sym nil))
16+
`(import-fn ~sym nil))
1717
([sym name]
18-
(let [vr (resolve sym)
19-
m (meta vr)
20-
n (or name (:name m))
21-
arglists (:arglists m)
22-
protocol (:protocol m)]
23-
(when-not vr
24-
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
25-
(when (:macro m)
26-
(throw (IllegalArgumentException.
27-
(str "Calling import-fn on a macro: " sym))))
18+
(let [vr (resolve sym)
19+
m (meta vr)
20+
n (or name (:name m))
21+
arglists (:arglists m)
22+
protocol (:protocol m)]
23+
(when-not vr
24+
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
25+
(when (:macro m)
26+
(throw (IllegalArgumentException.
27+
(str "Calling import-fn on a macro: " sym))))
2828

29-
`(let [vr# (resolve '~sym)]
30-
(def ~(with-meta n {:protocol protocol}) (deref vr#))
31-
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
32-
(link-vars vr# (var ~n))
33-
vr#))))
29+
`(let [vr# (resolve '~sym)]
30+
(def ~(with-meta n {:protocol protocol}) (deref vr#))
31+
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
32+
(link-vars vr# (var ~n))
33+
vr#))))
3434

3535
(defmacro import-macro
3636
"Given a macro in another namespace, defines a macro with the same
3737
name in the current namespace. Argument lists, doc-strings, and
3838
original line-numbers are preserved."
3939
([sym]
40-
`(import-macro ~sym nil))
40+
`(import-macro ~sym nil))
4141
([sym name]
42-
(let [vr (resolve sym)
43-
m (meta vr)
44-
n (or name (with-meta (:name m) {}))
45-
arglists (:arglists m)]
46-
(when-not vr
47-
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
48-
(when-not (:macro m)
49-
(throw (IllegalArgumentException.
50-
(str "Calling import-macro on a non-macro: " sym))))
51-
`(let [vr# (resolve '~sym)]
52-
(def ~n (deref vr#))
53-
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
54-
(.setMacro (var ~n))
55-
(link-vars vr# (var ~n))
56-
vr#))))
42+
(let [vr (resolve sym)
43+
m (meta vr)
44+
n (or name (with-meta (:name m) {}))
45+
arglists (:arglists m)]
46+
(when-not vr
47+
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
48+
(when-not (:macro m)
49+
(throw (IllegalArgumentException.
50+
(str "Calling import-macro on a non-macro: " sym))))
51+
`(let [vr# (resolve '~sym)]
52+
(def ~n (deref vr#))
53+
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
54+
(.setMacro (var ~n))
55+
(link-vars vr# (var ~n))
56+
vr#))))
5757

5858
(defmacro import-def
5959
"Given a regular def'd var from another namespace, defined a new var with the
6060
same name in the current namespace."
6161
([sym]
62-
`(import-def ~sym nil))
62+
`(import-def ~sym nil))
6363
([sym name]
64-
(let [vr (resolve sym)
65-
m (meta vr)
66-
n (or name (:name m))
67-
n (with-meta n (if (:dynamic m) {:dynamic true} {}))
68-
nspace (:ns m)]
69-
(when-not vr
70-
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
71-
`(let [vr# (resolve '~sym)]
72-
(def ~n (deref vr#))
73-
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
74-
(link-vars vr# (var ~n))
75-
vr#))))
64+
(let [vr (resolve sym)
65+
m (meta vr)
66+
n (or name (:name m))
67+
n (with-meta n (if (:dynamic m) {:dynamic true} {}))
68+
nspace (:ns m)]
69+
(when-not vr
70+
(throw (IllegalArgumentException. (str "Don't recognize " sym))))
71+
`(let [vr# (resolve '~sym)]
72+
(def ~n (deref vr#))
73+
(alter-meta! (var ~n) merge (dissoc (meta vr#) :name))
74+
(link-vars vr# (var ~n))
75+
vr#))))
76+
77+
(defn- unravel* [x]
78+
(if (sequential? x)
79+
(->> x
80+
rest
81+
(mapcat unravel*)
82+
(map
83+
#(symbol
84+
(str (first x)
85+
(when-let [n (namespace %)]
86+
(str "." n)))
87+
(name %))))
88+
[x]))
89+
90+
(defn- unravel [x]
91+
(or (and (sequential? x)
92+
(let [[ns refer-kw refers rename-kw renames] x]
93+
(when (and (= :refer refer-kw)
94+
(sequential? refers)
95+
(or (and (nil? rename-kw) (nil? renames))
96+
(and (= :rename rename-kw) (map? renames))))
97+
(map #(with-meta (symbol (str ns) (name %)) {:name (get renames %)})
98+
refers))))
99+
(unravel* x)))
76100

77101
(defmacro import-vars
78102
"Imports a list of vars from other namespaces."
79103
[& syms]
80-
(let [unravel (fn unravel [x]
81-
(if (sequential? x)
82-
(->> x
83-
rest
84-
(mapcat unravel)
85-
(map
86-
#(symbol
87-
(str (first x)
88-
(when-let [n (namespace %)]
89-
(str "." n)))
90-
(name %))))
91-
[x]))
92-
syms (mapcat unravel syms)]
104+
(let [syms (mapcat unravel syms)]
93105
`(do
94106
~@(map
95107
(fn [sym]
96108
(let [vr (resolve sym)
97-
m (meta vr)]
109+
m (meta vr)
110+
?name (-> sym meta :name)]
98111
(cond
99-
(nil? vr) `(throw (ex-info (format "`%s` does not exist" '~sym) {}))
100-
(:macro m) `(import-macro ~sym)
101-
(:arglists m) `(import-fn ~sym)
102-
:else `(import-def ~sym))))
112+
(nil? vr) `(throw (ex-info (format "`%s` does not exist" '~sym) {}))
113+
(:macro m) `(import-macro ~sym ~?name)
114+
(:arglists m) `(import-fn ~sym ~?name)
115+
:else `(import-def ~sym ~?name))))
103116
syms))))

test/potemkin/imports_test.clj

+4
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@
2323

2424
(defn ^clojure.lang.ExceptionInfo ex-info-2 [msg data]
2525
(ex-info msg data))
26+
27+
(def some-other-value 2)
28+
29+
(defn some-other-fn [] 1)

test/potemkin/namespaces_test.clj

+24-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
(ns potemkin.namespaces-test
22
(:require
3-
[clojure.repl :as repl :refer :all]
4-
[clojure.string :as str]
5-
[clojure.test :refer :all]
6-
[potemkin :refer :all]
7-
[potemkin.imports-test :as i]))
3+
[clojure.repl :as repl :refer :all]
4+
[clojure.string :as str]
5+
[clojure.test :refer :all]
6+
[potemkin :refer :all]
7+
[potemkin.imports-test :as i]))
88

99
(import-macro i/multi-arity-macro)
1010
(import-macro i/multi-arity-macro alt-macro-name)
@@ -13,7 +13,12 @@
1313
(import-fn i/protocol-function)
1414
(import-fn i/inlined-fn)
1515
(import-def i/some-value)
16-
16+
(import-vars [i :refer [some-other-fn]])
17+
(import-vars [i
18+
:refer [multi-arity-macro multi-arity-fn some-value some-other-value]
19+
:rename {multi-arity-macro renamed-multi-arity-macro
20+
multi-arity-fn renamed-multi-arity-fn
21+
some-value renamed-some-value}])
1722

1823
(defn drop-lines [n s]
1924
(->> s str/split-lines (drop n) (interpose "\n") (apply str)))
@@ -48,8 +53,19 @@
4853
(try
4954
(import-vars [clojure.set union onion-misspelled])
5055
(is false "`import-vars` should have thrown an exception")
51-
(catch Exception ex
52-
(is "`clojure.set/onion-misspelled` does not exist" (.getMessage ex)))))
56+
(catch Exception ex
57+
(is "`clojure.set/onion-misspelled` does not exist" (.getMessage ex)))))
58+
59+
(deftest import-vars-supports-refers
60+
(is (fn? some-other-fn)))
61+
62+
(deftest import-vars-allows-renaming
63+
(is (= 1 renamed-some-value))
64+
(is (= 2 some-other-value))
65+
(is (out= (source i/multi-arity-fn) (source renamed-multi-arity-fn)))
66+
(is (rest-out= (doc i/multi-arity-fn) (doc renamed-multi-arity-fn)))
67+
(is (out= (source i/multi-arity-macro) (source renamed-multi-arity-macro)))
68+
(is (rest-out= (doc i/multi-arity-macro) (doc renamed-multi-arity-macro))))
5369

5470
;; This is the whole test for CLJ-1929
5571
(import-vars [potemkin.imports-test ex-info-2])

0 commit comments

Comments
 (0)