aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscgilardi <scgilardi@gmail.com>2008-04-03 02:03:39 +0000
committerscgilardi <scgilardi@gmail.com>2008-04-03 02:03:39 +0000
commit1aac8b2c8c8df2dfb44163ac5cade8eef6c4c366 (patch)
tree04941cc6b531e5faca4f87c61f6c3e68d622c631
parent08e4ae8796699e1bdbfc3bf3ea3133a7a1c25380 (diff)
initial import of pkg.clj
-rw-r--r--pkg.clj133
1 files changed, 133 insertions, 0 deletions
diff --git a/pkg.clj b/pkg.clj
new file mode 100644
index 00000000..456a6db6
--- /dev/null
+++ b/pkg.clj
@@ -0,0 +1,133 @@
+;; pkg.clj
+;;
+;; Clojure package loading and dependency via require/provide.
+;;
+;; A 'package' is a named set of capabilities represented by a symbol.
+;; Its capabilities are expected to be defined in an implementation
+;; file somewhere in CLASSPATH whose name is the package name followed
+;; by ".clj". The implementation file may be in the filesystem, in a
+;; jar file, or at any other valid CLASSPATH URL.
+;;
+;; A call to 'require' indicates that subsequent code depends on
+;; capabilities provided by one or more packages and loads any of those
+;; packages that have not already been provided.
+;;
+;; A call to 'provide' indicates that a package's capabilities have been
+;; successfully loaded. A package's implementation file will generally
+;; end with a call to provide.
+;;
+;; pkg.clj provides variations of 'require' that:
+;;
+;; - require a package and automatically 'refer' to a namespace of the
+;; same name. This is for the common case of a package defining its
+;; own namespace which dependent code wants to use without namespace
+;; qualifiers. 'require-ns'
+;;
+;; - force the loading of all packages and dependencies regardless of
+;; whether they have already been provided. 'require-force'
+;;
+;; - both refer to the package's namespace and force a reload of the
+;; the package and its dependencies. 'require-ns-force'
+;;
+;; The "force" variations are useful during development.
+;;
+;; 'provide' is made available as a separate function (rather than being
+;; done automatically after loading an implementation file) to allow
+;; flexibility in providing a package's capabilities by some means other
+;; than loading an implementation file using 'require'.
+;;
+;; scgilardi (gmail)
+;; 2 April 2008
+;;
+;; load-resource adapted from Stuart Sierra's public domain require.clj
+;;
+;; 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.
+
+(clojure/in-ns 'pkg)
+(clojure/refer 'clojure)
+
+;; Private
+
+(def
+ #^{:doc "A ref to a set of symbols representing provided packages"
+ :private true}
+ *packages* (ref #{}))
+
+(defn- load-resource
+ "Sequentially read and evaluate forms from a resource within CLASSPATH"
+ [name]
+ (let [url (. ClassLoader (getSystemResource name))]
+ (when-not url
+ (throw (new Exception (str \" name \" " not found in CLASSPATH"))))
+ (if (= "file" (. url (getProtocol)))
+ (load-file (. url (getFile)))
+ (with-open reader (new java.io.BufferedReader
+ (new java.io.InputStreamReader
+ (. url (openStream))))
+ (load reader)))))
+
+;; Public
+
+(defn packages
+ "Returns a set of symbols representing provided packages"
+ []
+ @*packages*)
+
+(defn provided?
+ "Returns true if package has been provided, else false"
+ [package]
+ (contains? @*packages* package))
+
+(defn provide
+ "Marks a package as provided"
+ [package]
+ (dosync
+ (commute *packages* conj package)
+ nil))
+
+(defn require
+ "Indicates that subsequent code depends on capabilities provided by
+ the specified packages. Loads any of those packages that have not
+ yet been provided."
+ [& packages]
+ (doseq package packages
+ (when-not (provided? package)
+ (let [resource (str package ".clj")]
+ (load-resource resource)
+ (when-not (provided? package)
+ (throw (new Exception (str \" resource \"
+ " did not provide " package))))))))
+
+(defn require-ns
+ "Requires a package and then refers to the namespace of the same name
+ with filters"
+ [package & filters]
+ (require package)
+ (apply refer package filters))
+
+(defn require-forced
+ "Like 'require' but will reload packages (and their dependencies) that
+ have already been provided"
+ [& packages]
+ (let [forced-packages
+ (binding [*packages* (ref #{})]
+ (apply require packages)
+ @*packages*)]
+ (dosync
+ (commute *packages* set/union forced-packages)))
+ nil)
+
+(defn require-ns-forced
+ "Like 'require-ns' but will reload packages (and their dependencies)
+ that have already been provided"
+ [package & filters]
+ (require-forced package)
+ (apply refer package filters))
+
+(provide 'pkg)