1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
;; Copyright (c) Stephen C. Gilardi. All rights reserved.
;; The use and distribution terms for this software are covered by the
;; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
;; which can be found in the file CPL.TXT at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
;;
;; lib.clj
;;
;; A 'lib' is a Clojure source file that follows these conventions:
;;
;; - has a basename that is a valid symbol name in Clojure
;; - has the extension ".clj"
;;
;; A lib will typically also contain forms that provide something useful
;; for a Clojure program to use. It may also define a namespace with the
;; same name as its basename.
;;
;; This file is an example of a lib. It follows the naming conventions
;; and provides these functions in the 'lib' namespace that make it
;; convenient to use libs from Clojure source code and the Clojure repl:
;;
;; 'require' searches classpath for libs and loads them if
;; they are not already loaded
;;
;; 'use' requires libs and refers to their namespaces
;;
;; 'libs' returns a sorted list of loaded libs
;;
;; 'load-uri' loads Clojure source from a location
;;
;; 'load-system-resource' loads Clojure source from a resource in
;; classpath
;;
;; 'require' and 'use' have additional options as described in their docs.
;;
;; scgilardi (gmail)
;; 8 April 2008
;;
;; Thanks to Stuart Sierra for providing many useful ideas, discussions
;; and code contributions for lib.clj.
(clojure/in-ns 'lib)
(clojure/refer 'clojure)
(import '(java.io BufferedReader InputStreamReader))
;; Private
(defmacro init-once
"Initializes a var exactly once. The var must already exist."
{:private true}
[var init]
`(let [v# (resolve '~var)]
(when-not (. v# (isBound))
(. v# (bindRoot ~init)))))
(def
#^{:private true :doc
"A ref to a set of symbols representing loaded libs"}
*libs*)
(init-once *libs* (ref #{}))
(def
#^{:private true :doc
"True while a verbose require is pending"}
*verbose*)
(init-once *verbose* false)
(def load-system-resource)
(defn- load-one
"Loads a lib from <classpath>/in/"
[sym in need-ns]
(let [res (str sym ".clj")]
(load-system-resource res in)
(when (and need-ns (not (find-ns sym)))
(throw (new Exception (str "namespace '" sym "' not found after "
"loading resource '" res "'")))))
(dosync
(commute *libs* conj sym))
(when *verbose*
(println "loaded lib" sym)))
(defn- load-all
"Loads a lib and any libs on which it directly or indirectly
depends even if already loaded."
[sym in need-ns]
(dosync
(commute *libs* set/union
(binding [*libs* (ref #{})]
(load-one sym in need-ns)
@*libs*))))
(defn- require-one
"Single-argument version of 'require'"
[sym & options]
(let [opts (apply hash-map options)
in (:in opts)
need-ns (:need-ns opts)
reload (:reload opts)
reload-all (:reload-all opts)
verbose (:verbose opts)]
(binding [*verbose* (or *verbose* verbose)]
(cond reload-all
(load-all sym in need-ns)
(or reload (not (contains? @*libs* sym)))
(load-one sym in need-ns)))))
(defn- use-one
"Single-argument version of 'use'"
[sym & options]
(apply require-one sym :need-ns true options)
(apply refer sym options))
;; Public
(defn require
"Declares that subsequent code requires the capabilities
provided by the named libs. Each argument is a quoted
symbol or a quoted list of the form (symbol & options...).
If the lib named by the symbol is not yet loaded, searches
for it and loads it. The default search is in the locations
in classpath. Options may include at most one each of the
following:
:in string
:reload boolean
:reload-all boolean
:verbose boolean
An argument to :in specifies the path to the lib's parent
directory relative to a location in classpath.
When :reload is true, the lib is reloaded if already loaded.
When :reload-all is true, the lib and all libs on which
it directly or indirectly depends are reloaded.
When :verbose is true, prints a message after each load."
[& args]
(doseq arg args
(if (symbol? arg)
(require-one arg)
(apply require-one arg))))
(defn use
"Requires and 'refer's the named libs. Arguments are like
those of 'require', with additional options which are filters
for 'refer'."
[& args]
(doseq arg args
(if (symbol? arg)
(use-one arg)
(apply use-one arg))))
(defn libs
"Returns a sorted sequence of symbols naming loaded libs"
[]
(sort @*libs*))
(defn load-uri
"Loads Clojure source from a URI, which may be a java.net.URI
java.net.URL, or String. Accepts any URI scheme supported by
java.net.URLConnection (http and jar), plus file URIs."
[uri]
(let [url (cond ; coerce argument into java.net.URL
(instance? java.net.URL uri) uri
(instance? java.net.URI uri) (. uri (toURL))
(string? uri) (new java.net.URL uri)
:else (throw (new Exception
(str "Cannot coerce "
(class uri)
" into java.net.URL."))))]
(if (= "file" (. url (getProtocol)))
(load-file (. url (getFile)))
(with-open reader
(new BufferedReader
(new InputStreamReader
(. url (openStream))))
(load reader)))))
(defn load-system-resource
"Loads Clojure source from a resource within classpath"
([res]
(let [url (. ClassLoader (getSystemResource res))]
(when-not url
(throw (new Exception (str "resource '" res
"' not found in classpath"))))
(load-uri url)))
([res in]
(load-system-resource (if in (str in \/ res) res))))
|