diff options
author | scgilardi <scgilardi@gmail.com> | 2008-07-26 14:35:07 +0000 |
---|---|---|
committer | scgilardi <scgilardi@gmail.com> | 2008-07-26 14:35:07 +0000 |
commit | 4193d4c5b9e8ae17497261017ebfa5487732d58c (patch) | |
tree | bc69fcc1b9b37ad46ad7d1b27f298c76bb668bb3 /lib/lib.clj | |
parent | 07869a99545bf39334d800862d1f0288d38d346f (diff) |
updates for using the prefix idea and allowing large ns implementations
Diffstat (limited to 'lib/lib.clj')
-rw-r--r-- | lib/lib.clj | 334 |
1 files changed, 180 insertions, 154 deletions
diff --git a/lib/lib.clj b/lib/lib.clj index daa9da16..b855e53b 100644 --- a/lib/lib.clj +++ b/lib/lib.clj @@ -8,15 +8,66 @@ ;; ;; File: lib.clj ;; -;; lib.clj provides facilities for loading and managing libs. A lib is a -;; unit of Clojure code contained in a file or other resource within -;; classpath. Lib names must be valid Clojure symbol names. The name of a -;; lib's container is the lib name followed by ".clj". lib.clj also -;; provides general functions for finding and loading resources using the -;; class loaders visible to the Clojure runtime. +;; lib.clj provides functions for loading and managing Clojure source code +;; contained in Java resources. ;; -;; Here is a brief overview of what's in lib.clj. For detailed docs please -;; see the doc strings for the individual functions and macros. +;; lib.clj manages namespaces that are defined by loading Clojure source +;; from resources (libs) within classpath. Namespace names are Symbols. +;; Every namespace has an associated namespace directory in classpath +;; whose class-path-relative path is derived from the namespace name by +;; replacing any periods with slashes. +;; +;; A lib is a unit of Clojure code contained in a file or other resource +;; within classpath. A lib name is a Symbol whose name is a concatenation +;; of a namespace name, a period, and a namespace-relative name. A lib is +;; contained in a resource whose class-path-relative path is derived from +;; the lib name by replacing all periods with slashas and then appending +;; ".clj". +;; +;; To load a namespace definition, lib.clj loads the namespace's 'root +;; lib'. The root lib's name is derived from the namespace name by +;; repeating the "leaf" portion of the namespace name. For example, the +;; root lib for the namespace 'clojure-contrib.def' is the lib named +;; 'clojure-contrib.def.def'. It is contained in the resource: +;; +;; <classpath>/clojure-contrib/def/def.clj +;; +;; The following code ensures that 'clojure-contrib.def and +;; 'clojure-contrib.sql are loaded: +;; +;; (require '(clojure-contrib def sql)) +;; +;; In cases where the complete namespace is defined in more than one lib +;; all the libs should be contained within the hierarchy of directories +;; under the namespace directory and the root lib must (drectly or +;; indirectly) load the additional libs. For example, a hypothetical +;; namespace named 'clojure-contrib.math-funcs might be defined in +;; multiple libs contained in resources like these: +;; +;; <classpath>/clojure-contrib/math-funcs/math-funcs.clj +;; <classpath>/clojure-contrib/math-funcs/trig.clj +;; <classpath>/clojure-contrib/math-funcs/logarithms.clj +;; <classpath>/clojure-contrib/math-funcs/bessel.clj +;; <classpath>/clojure-contrib/math-funcs/inverses/trig.clj +;; <classpath>/clojure-contrib/math-funcs/inverses/logarithms.clj +;; +;; The following code ensures that 'clojure-contrib.math-funcs is loaded: +;; +;; (require '(clojure-contrib math-funcs)) +;; +;; This is the portion of math-funcs.clj that loads the remaining libs: +;; +;; (clojure/in-ns 'clojure-contrib.math-funcs) +;; +;; (clojure-contrib.lib/load-libs +;; '(clojure-contrib.math-funcs +;; trig logarithms bessel inverses.trig inverses.logarithms)) +;; +;; lib.clj also provides general functions for finding and loading +;; resources using the class loaders visible to the Clojure runtime. +;; +;; Here is a brief overview of what's in lib.clj. For detailed docs +;; please see the doc strings for the individual functions. ;; ;; Resources ;; @@ -26,39 +77,39 @@ ;; Function: load-resource ;; Loads Clojure source from an absolute path: URI, URL or String ;; -;; Core +;; Libs ;; ;; Function: load-libs -;; Loads lib(s) based libspecs and flags +;; Loads libs from arbitrary locations under classpath ;; -;; Function: libs -;; Returns a sorted set of symbols naming the currently loaded libs +;; Namespaces ;; -;; Convenience +;; Function: load-namespaces +;; Loads namespace definitions by loading namespace root libs ;; -;; Macro: require -;; Loads libs. By default doesn't reload any that are already loaded +;; Function: namespaces +;; Returns a sorted set of symbols naming the set of namespaces +;; that lib.clj has loaded and is tracking ;; -;; Macro: use -;; Loads libs like require and refers to each lib's namespace +;; Function: require +;; Loads namespace definitions that are not already loaded +;; +;; Function: use +;; Requires namespaces and refers to them using clojure/refer ;; ;; Examples ;; -;; (load-libs :require 'sql '(sql-test :in "private/unit-tests")) -;; (require sql (sql-test :in "private/unit-tests")) +;; (load-namespaces :require '(clojure-contrib sql sql.tests)) +;; (require '(clojure-contrib sql sql.tests)) ;; -;; (load-libs :require :use 'sql 'ns-utils :verbose) -;; (use sql ns-utils :verbose) +;; (load-namespaces :require :use '(clojure-contrib sql ns-utils) :verbose) +;; (use '(clojure-contrib sql ns-utils) :verbose) ;; ;; (use :reload-all :verbose -;; (sql :exclude '(get-connection) -;; :rename '{execute-commands do-commands}) -;; ns-utils) -;; -;; (use (sql)) -;; -;; (load-libs :require :use '(genclass :ns 'clojure)) -;; (use (genclass :ns 'clojure)) +;; '(clojure-contrib +;; (sql :exclude (get-connection) +;; :rename {execute-commands do-commands}) +;; ns-utils)) ;; ;; scgilardi (gmail) ;; Created 7 April 2008 @@ -88,8 +139,8 @@ (def #^{:private true :doc "A ref to a set of symbols representing loaded libs"} - *libs*) -(init-once *libs* (ref (sorted-set))) + *namespaces*) +(init-once *namespaces* (ref (sorted-set))) (def #^{:private true :doc @@ -137,58 +188,57 @@ (defn- load-one "Loads one lib from a resoure and ensures that namespace ns (if - specified) exists" - [sym url ns] + specified) exists. If require is true, also records the load so + a duplicate load will be skipped." + [sym url ns require] (load-resource url) (throw-if (and ns (not (find-ns ns))) "namespace '%s' not found after loading '%s'" ns url) - (dosync - (commute *libs* conj sym))) + (when require + (dosync + (commute *namespaces* conj sym)))) (defn- load-all "Loads a lib from a resource and forces a load of any libs which it - directly or indirectly loads via require/use/load-libs" - [sym url ns] + directly or indirectly loads via require/use/load-namespaces" + [sym url ns require] (dosync - (commute *libs* set/union - (binding [*libs* (ref (sorted-set))] - (load-one sym url ns) - @*libs*)))) - -(defn- sym-file - "Returns the implementation file path for a libspec sym" - [sym] - (let [n (name sym) - index (inc (.lastIndexOf n (int \.))) - leaf (.substring n index) - ns (if (zero? index) (name (ns-name *ns*)) n) - file-new (str (.replace ns \. \/) \/ leaf ".clj") - file-old (str sym ".clj")] - (if (find-resource file-new) - file-new - file-old))) + (commute *namespaces* set/union + (binding [*namespaces* (ref (sorted-set))] + (load-one sym url ns require) + @*namespaces*)))) + +(defn- lib-path + "Returns the resource path for a lib" + [lib-sym] + (str (.replace (name lib-sym) \. \/) ".clj")) + +(defn- root-lib-path + "Returns the resource path for a namespace root lib" + [ns-sym] + (let [n (name ns-sym) + index (inc (.lastIndexOf n (int \.))) + leaf (.substring n index)] + (str (.replace n \. \/) \/ leaf ".clj"))) (defn- load-lib - "Loads a lib with options: sequential keywords and arguments. The - arguments to all options are evaluated so literal symbols or lists must - be quoted" - [sym & options] - (let [raw-opts (apply hash-map options) - opts (zipmap (keys raw-opts) (map eval (vals raw-opts))) - in (:in opts) - ns (:ns opts) + "Loads a lib with options: sequential keywords and arguments." + [prefix sym & options] + (let [sym (symbol (str prefix \. sym)) + opts (apply hash-map options) + raw (:raw opts) reload (:reload opts) reload-all (:reload-all opts) require (:require opts) use (:use opts) verbose (:verbose opts) - loaded (contains? @*libs* sym) + loaded (contains? @*namespaces* sym) load (cond reload-all load-all (or reload (not require) (not loaded)) load-one) - namespace (when use (or ns sym)) - path (str (if in (str in \/)) (sym-file sym)) + namespace (when use sym) + path ((if raw lib-path root-lib-path) sym) url (find-resource path) filter-opts (select-keys opts *filter-keys*)] (binding [*verbose* (or *verbose* verbose)] @@ -197,7 +247,7 @@ (printf "(clojure-contrib.lib/load-resource \"%s\")\n" url) (flush)) (throw-if (not url) "'%s' not found in classpath" path) - (load sym url namespace)) + (load sym url namespace require)) (when namespace (when *verbose* (printf "(clojure/in-ns '%s)\n" (ns-name *ns*)) @@ -234,118 +284,94 @@ (.openStream url))) (.load Compiler reader (.getPath url) (.getFile url))))) -;; Core +;; Libs (defn load-libs - "Searches classpath for libs and loads them. 'load-libs' accepts zero or - more arguments where each argument is either a libspec that identifies a - lib to load, or a flag that modifies how all the identified libs are - loaded. + "Searches classpath for libs and loads them. Each argument is either a + libgroupspec that identifies a group of libs to load or a flag that + modifies how all the identified libs are loaded. - A libspec is either a symbol or a list containing a symbol followed by - zero or more options. Since the arguments to 'load-libs' are evaluated - before the call, any literal libspecs passed in must be quoted. + A libgroupspec is a list containing a prefix Symbol that identifies a + directory within classpath and libspecs that identify libs relative to + that directory. - The 'require' and 'use' macros offer a simpler interface to the - capabilities of load-libs and don't evaluate the libspecs they take as - arguments. - - An option is a keyword followed by an argument. - Recognized options: :in, :ns, :exclude, :only, :rename + A libspec is a Symbol. - All arguments to options within libspecs are evaluated so literal lists - and symbols appearing within libspecs must be quoted. - - The :in option's argument must evaluate to a string specifying the path - of the lib's parent directory relative to a location in classpath. - - The :ns options's argument must evaluate to a symbol specifying the - namespace to refer for this lib if the :use flag is present. When the - :ns option is not present the namespace defaults to the one with the - same name as the lib. - - The arguments and semantics for :exclude, :only, and :rename are those - documented for clojure/refer. + Periods in Symbol names are mapped to slashes in paths. A flag is a keyword. - Recognized flags: :require, :use, :reload, :reload-all, :verbose + Recognized flags: :reload-all, :verbose - :require indicates that any identified libs that are already loaded need - not be reloaded - :use triggers referring to each lib's namespace after loading - :reload forces loading of all the identified libs even if they were - loaded previously. :reload supersedes :require - :reload-all implies :reload and also forces loading of all libs that the - identified libs directly or indirectly load via load-libs/require/use + :reload-all forces loading of all namespaces that the identified libs + directly or indirectly load via load-namespaces/require/use :verbose triggers printing a message after loading each lib" [& args] - (let [libspecs (filter (complement keyword?) args) + (let [libgroupspecs (filter (complement keyword?) args) flags (filter keyword? args) flag-opts (interleave flags (repeat true))] - (doseq libspec libspecs - (let [combine (if (symbol? libspec) cons concat)] - (apply load-lib (combine libspec flag-opts)))))) + (doseq libgroupspec libgroupspecs + (let [[prefix & libspecs] libgroupspec] + (doseq libspec libspecs + (apply load-lib prefix libspec :raw true flag-opts)))))) -(defn libs - "Returns a sorted set of symbols naming loaded libs" - [] - @*libs*) +;; Namespaces -;; Convenience +(defn load-namespaces + "Searches classpath for namespaces and loads their definitions. Each + argument is either an nsgroupspec that identifies a group of namespaces + to load or a flag that modifies how all the identified namespaces are + loaded. -(defmacro require - "Searches classpath for libs and (by default) loads them if they are not - already loaded. 'require' accepts zero or more arguments where each - argument is either a libspec that identifies a lib to load, or a flag - that modifies how the identified libs are loaded. + An nsgroupspec is a list containing a prefix Symbol that identifies a + directory wihin classpath and nsspecs that identify namespace directories + relative to that directory and loading options. Periods in Symbol names + are mapped to slashes in paths. - A libspec is a symbol or a list containing a symbol followed by zero or - more options. 'require' does not evaluate its arguments so libspecs and - flags should not be quoted. + An nsspec is a Symbol or a list containing a Symbol and options. An option is a keyword followed by an argument. - Recognized options: :in - - All arguments to options within libspecs are evaluated so literal lists - and symbols appearing within libspecs must be quoted. + Recognized options: :exclude, :only, :rename - The :in option's argument must evaluate to a string specifying the path - of the lib's parent directory relative to a location in classpath. + The arguments and semantics for :exclude, :only, and :rename are those + documented for clojure/refer. They are effective only when the :use flag + is set. A flag is a keyword. - Recognized flags: :reload, :reload-all, :verbose + Recognized flags: :require, :use, :reload, :reload-all, :verbose - :reload forces loading of all the identified libs even if they were - loaded previously - :reload-all implies :reload and also forces loading of all libs that the - identified libs directly or indirectly load via load-libs/require/use. + :require indicates that any identified namespace definitions that are + already loaded need not be reloaded + :use triggers referring to each namespace with its options as filters + :reload forces loading of all the identified namespace definitions even + if they were loaded previously. :reload supersedes :require + :reload-all implies :reload and also forces loading of all namespace + definitions that the identified namespace definitions directly or + indirectly load via load-namespaces/require/use :verbose triggers printing a message after loading each lib" [& args] - `(apply load-libs :require '~args)) - -(defmacro use - "Searches classpath for libs and (by default) loads them if they are not - already loaded and refers to namespaces with the same name. 'use' accepts - zero or more arguments where each argument is either a libspec that - identifies a lib to load, or a flag that modifies how the identified libs - are loaded. - - A libspec is a symbol or a list containing a symbol followed by zero or - more options. 'use' does not evaluate its arguments so libspecs and flags - should not be quoted. - - 'use' recognizes all the libspecs, options and flags documented for - 'require' plus some additional options in libspecs. - - Additional options: :ns, :exclude, :only, :rename - - All arguments to options within libspecs are evaluated so literal lists - and symbols appearing within libspecs must be quoted. + (let [nsgroupspecs (filter (complement keyword?) args) + flags (filter keyword? args) + flag-opts (interleave flags (repeat true))] + (doseq nsgroupspec nsgroupspecs + (let [[prefix & nsspecs] nsgroupspec] + (doseq nsspec nsspecs + (let [combine (if (symbol? nsspec) cons concat)] + (apply load-lib prefix (combine nsspec flag-opts)))))))) + +(defn namespaces + "Returns a sorted set of symbols naming loaded namespaces" + [] + @*namespaces*) - The :ns options's argument must evaluate to a symbol specifying the - namespace to refer for this lib. +(defn require + "Loads namespace definitions if they are not already loaded. See doc for + load-namespaces (:require flag set) for further information." + [& args] + (apply load-namespaces :require args)) - The arguments and semantics for :exclude, :only, and :rename are those - documented for clojure/refer." +(defn use + "Loads namespace definitions if they are not already loaded and refers to + the namespaces. See doc for load-namespaces (:require and :use flags set + and nsspec options) for further information." [& args] - `(apply load-libs :require :use '~args)) + (apply load-namespaces :require :use args)) |