pax_global_header00006660000000000000000000000064125410674610014520gustar00rootroot0000000000000052 comment=f3f5b29689c2bda53b4977cf97f5588f82c9bd00 tools.namespace-tools.namespace-0.2.11/000077500000000000000000000000001254106746100177655ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/.gitignore000066400000000000000000000000071254106746100217520ustar00rootroot00000000000000target tools.namespace-tools.namespace-0.2.11/CONTRIBUTING.md000066400000000000000000000012171254106746100222170ustar00rootroot00000000000000This is a [Clojure contrib] project. Under the Clojure contrib [guidelines], this project cannot accept pull requests. All patches must be submitted via [JIRA]. See [Contributing] and the [FAQ] on the Clojure development [wiki] for more information on how to contribute. [Clojure contrib]: http://dev.clojure.org/display/doc/Clojure+Contrib [Contributing]: http://dev.clojure.org/display/community/Contributing [FAQ]: http://dev.clojure.org/display/community/Contributing+FAQ [JIRA]: http://dev.clojure.org/jira/browse/TNS [guidelines]: http://dev.clojure.org/display/community/Guidelines+for+Clojure+Contrib+committers [wiki]: http://dev.clojure.org/ tools.namespace-tools.namespace-0.2.11/README.md000066400000000000000000000576221254106746100212600ustar00rootroot00000000000000clojure.tools.namespace ======================================== Tools for managing namespaces in Clojure. Parse `ns` declarations from source files, extract their dependencies, build a graph of namespace dependencies within a project, update that graph as files change, and reload files in the correct order. This is only about namespace dependencies **within** a single project. It has nothing to do with Leiningen, Maven, JAR files, or repositories. Releases and Dependency Information ---------------------------------------- * [Latest stable release is 0.2.11](https://github.com/clojure/tools.namespace/tree/tools.namespace-0.2.11) * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.namespace%22) [Leiningen](http://leiningen.org/) dependency information: [org.clojure/tools.namespace "0.2.11"] [Maven](http://maven.apache.org/) dependency information: org.clojure tools.namespace 0.2.11 ### Development Snapshots ### * Git master branch is at **0.2.12-SNAPSHOT** * [All Snapshot Versions](https://oss.sonatype.org/content/groups/public/org/clojure/tools.namespace/) Leiningen dependency information for development snapshots: :dependencies [[org.clojure/tools.namespace "0.2.12-SNAPSHOT"]] :repositories {"sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"} See also [Maven Settings and Repositories](http://dev.clojure.org/display/doc/Maven+Settings+and+Repositories) on dev.clojure.org. Overview ---------------------------------------- [API Documentation](http://clojure.github.com/tools.namespace/) tools.namespace consists of several parts: **clojure.tools.namespace.parse:** A parser for namespace declarations in Clojure source files. Given a stream of characters from a Clojure source file, it can find the `ns` declaration and parse the `:require` and `:use` clauses to find the names of other namespaces that file depends on. This is all syntactic analysis: it does not evaluate any code. **clojure.tools.namespace.find:** Utilities to search for Clojure namespaces on the filesystem, in directories or JAR files. Combined with [java.classpath](http://clojure.github.com/java.classpath/), it can search for namespaces on the Java classpath. This namespace contains most of the functions in clojure.tools.namespace version 0.1.x. **clojure.tools.namespace.repl:** Utilities to load and reload code based on the namespace dependency graph. This takes some explaining, see below. c.t.n.repl is built out of smaller parts: * c.t.n.dependency - generic dependency graph data structure * c.t.n.track - namespace dependency tracker * c.t.n.file - file-reader extension to tracker * c.t.n.dir - directory-scanner extension to tracker * c.t.n.reload - namespace-reloading extension to tracker You can recombine these parts in other ways, but c.t.n.repl is the primary public entry-point to their functionality. **clojure.tools.namespace.move:** Utilities to aid in moving and renaming Clojure namespaces. This code is still ALPHA, and it modifies your source files, so be careful. Reloading Code: Motivation ---------------------------- c.t.n.repl is a smarter way to reload code. The traditional way to reload Clojure code without restarting the JVM is `(require ... :reload)` or `:reload-all` or an editor/IDE feature that does the same thing. This has several problems: * If you modify two namespaces which depend on each other, you must remember to reload them in the correct order to avoid compilation errors. * If you remove definitions from a source file and then reload it, those definitions are still available in memory. If other code depends on those definitions, it will continue to work but will break the next time you restart the JVM. * If the reloaded namespace contains `defmulti`, you must also reload all of the associated `defmethod` expressions. * If the reloaded namespace contains `defprotocol`, you must also reload any records or types implementing that protocol and replace any existing instances of those records/types with new instances. * If the reloaded namespace contains macros, you must also reload any namespaces which use those macros. * If the running program contains functions which close over values in the reloaded namespace, those closed-over values are not updated. (This is common in web applications which construct the "handler stack" as a composition of functions.) Often the only surefire way to reload Clojure code is to restart the JVM. A large Clojure application can take 20 seconds or more just to compile. I wrote tools.namespace to help speed up this development cycle. For more detail on how I use tools.namespace in my development workflow, see the article [My Clojure Workflow, Reloaded](http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded). Reloading Code: Usage ----------------------- There's only one important function, `refresh`: user=> (require '[clojure.tools.namespace.repl :refer [refresh]]) nil user=> (refresh) :reloading (com.example.util com.example.app com.example.app-test) :ok The `refresh` function will scan all the directories on the classpath for Clojure source files, read their `ns` declarations, build a graph of their dependencies, and load them in dependency order. (You can change the directories it scans with `set-refresh-dirs`.) Later on, after you have changed and saved a few files in your editor, run it again: user=> (refresh) :reloading (com.example.app com.example.app-test) :ok Based on file modification timestamps and the graph of dependencies, the `refresh` function will reload *only* the namespaces that have changed, in dependency order. But first, it will *unload* (remove) the namespaces that changed to clear out any old definitions. This is quite unlike `(require ... :reload)`. Calling `refresh` will *blow away your old code*. Sometimes this is helpful: it can catch trivial mistakes like deleting a function that another piece of code depends on. But sometimes it hurts when you have built-up application state stored in a Var that got deleted by `refresh`. This brings us to the next section: Reloading Code: Preparing Your Application -------------------------------------------- Being able to safely destroy and reload namespaces without breaking your application requires some discipline and careful design. It won't "just work" on any Clojure project. ### No Global State The first rule for making your application reload-safe is **no global state**. That means you should avoid things like this: (def state-of-world (ref {})) (def object-handle (atom nil)) c.t.n.repl/refresh will destroy those Vars when it reloads the namespace (even if you used `defonce`). Instead of storing your state in global Vars, store it *locally* in an object that represents the running state of your application. Then provide a constructor function to initialize that state: (defn create-application [] {:state-of-world (ref {}) :object-handle (atom nil)}) You can choose what representation works best for your application: map, vector, record, or even just a single Ref by itself. Typically you'll still need one global `def` somewhere, perhaps in the REPL itself, to hold the current application instance. See the next section. ### Managed Lifecycle The second rule for making your application reload-safe is to have a consistent way to **start and stop the entire system**. The "start" function should: - Acquire stateful resources such as sockets, files, and database connections - Start threads or background processes - Initialize application state such as caches or counters - Return an object encapsulating the state of the application The "stop" function should take the state returned by "start" as an argument and do the opposite: - Close or release stateful resourecs - Stop all background processes - Clear out application state It might take a few tries to get it right, but once you have working start and stop functions you can have a workflow like this: Step 1. Start up a REPL. Step 2. Load the app: user=> (require '[clojure.tools.namespace.repl :refer [refresh]]) user=> (refresh) user=> (def my-app (start-my-app)) Step 3. Test it out. Step 4. Modify some source files. Step 5. Restart: user=> (stop-my-app my-app) user=> (refresh) user=> (def my-app (start-my-app)) (You could also combine all those steps in a single utility function, but see warnings below.) After that, you've got a squeaky-clean new instance of your app running, in a fraction of the time it takes to restart the JVM. ### Handling Errors If an exception is thrown while loading a namespace, `refresh` stops, prints the namespace that caused the exception, and returns the exception. You can print the rest of the stacktrace with `clojure.repl/pst`; the exception itself is bound to `*e`. user=> (refresh) :reloading (com.example.app com.example.app-test) :error-while-loading com.example.app # user=> (clojure.repl/pst) IllegalArgumentException Parameter declaration cond should be a vector clojure.core/assert-valid-fdecl (core.clj:6567) clojure.core/sigs (core.clj:220) clojure.core/defn (core.clj:294) clojure.lang.Var.invoke (Var.java:427) ... Remember that any namespaces which depend on the namespace that caused the exception **do not exist** at this point: they have been removed but not yet reloaded. After you fix the problem, call `refresh` again and it will resume reloading where it left off. **NOTE:** If your current REPL namespace is one of those that has not yet been reloaded, then none of the functions you defined in that namespace will exist! Starting with version 0.2.8, tools.namespace will *attempt* to restore aliases to the namespaces which were successfully loaded. So, for example, if your current REPL namespace is named `dev` and contains this ns declaration: (ns dev (:require [com.example.foo :as foo] [com.example.bar :as bar] [clojure.tools.namespace.repl :as tns])) And you get an error on refresh like this: dev=> (tns/refresh) :reloading (com.example.bar dev) :error-while-loading com.example.bar # Then the functions in `com.example.foo` should still be available in the `dev` namespace via the alias `foo`. Warnings and Potential Problems ------------------------------- **`ns` syntax:** Clojure's `ns` macro is notoriously lax in what syntax it accepts. tools.namespace.parse is somewhat liberal, but it cannot handle every possible variation of syntax that `ns` does. Stick to the docstrings of [ns] and [require] and everything should be fine. **AOT-compilation:** Reloading code does not work in the presence of [AOT-compiled] namespaces. If you are using AOT-compilation in your project, make sure it is disabled and you have deleted any AOT-compiled `.class` files before starting a REPL development session. (In Leiningen, run `lein clean`.) Note that the presence of `:main` in project.clj triggers AOT-compilation in some versions of Leiningen. **Conflicts:** Other libraries which also do code-reloading may conflict with tools.namespace. One known example is ring-devel (as of [Ring] version 1.1.6) which uses [ns-tracker], which uses an older version of tools.namespace. **REPL namespace:** Be careful when reloading the namespace in which you run your REPL. Because namespaces are removed when reloading, all your past definitions are lost. Either keep your REPL in a namespace which has no file associated with it, such as `user`, or put all your REPL definitions in a file so that they can be reloaded. **Fully-qualified names:** Be careful when using fully-qualified symbol names without namespace aliases (`require` with no `:as`). If the namespace happens to be loaded already, it will not necessarily cause an error if you forget to `require` it, but the dependency graph of namespaces will be incorrect. **Old definitions:** Beware of code which has references to old definitions, especially references to things you created in the REPL. **Rolling your own:** If you create your own instance of the dependency tracker, do not store it in a namespace which gets reloaded. [AOT-compiled]: http://clojure.org/compilation [Ring]: https://github.com/ring-clojure/ring [ns-tracker]: https://github.com/weavejester/ns-tracker [ns]: http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/ns [require]: http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/require ### Warnings for Helper Functions Be careful defining a helper function in a namespace which calls `refresh` if that namespace also could get reloaded. For example, you might try to combine the stop-refresh-start code from the "Managed Lifecycle" section into a single function: (def my-app nil) (defn restart [] (stop-my-app my-app) (refresh) (alter-var-root #'my-app (constantly (start-my-app)))) This won't work if the namespace containing `restart` could get reloaded. After `refresh`, the namespace containing `restart` has been dropped, but the function continues to run in the *old* namespace and refer to old Vars. If you want to run some code after `refresh`, you can pass an option naming a function you want to run *after* a successful reload. The value of this option must be a symbol, and it must be fully namespace-qualified. The previous example could be correctly written (assuming these functions are defined in the `dev` namespace): (def my-app nil) (defn start [] (alter-var-root #'my-app (constantly (start-my-app)))) (defn restart [] (stop-my-app my-app) (refresh :after 'dev/start)) ### Warnings for Aliases Namespace aliases created at the REPL will still refer to the *old* namespace after `refresh`. For example: user=> (require '[com.example.foo :as foo]) user=> foo/bar user=> (refresh) :reloading (com.example.foo) :ok user=> foo/bar ; this is the *old* foo/bar If you try to recreate the alias with the new namespace, you will get an error: user=> (require '[com.example.foo :as foo]) IllegalStateException Alias foo already exists in namespace user, aliasing com.example.foo clojure.lang.Namespace.addAlias (Namespace.java:224) The only way out is to remove the alias before recreating it: user=> (ns-unalias *ns* 'foo) nil user=> (alias 'foo 'com.example.foo) ### Warnings for Protocols When reloading namespaces which contain protocols, be careful that you do not leave any old instances of records or types implementing those protocols. For example, if you have a namespace like this: (ns com.example.foo) (defprotocol IFoo (foo [this])) (defrecord FooRecord [] IFoo (foo [this] nil)) And you do something like the following at the REPL: user=> (def my-foo (->FooRecord)) user=> (clojure.tools.namespace.repl/refresh) user=> (foo my-foo) You will get a confusing error message like this: IllegalArgumentException No implementation of method: :foo of protocol: #'com.example.foo/IFoo found for class: com.example.foo.FooRecord clojure.core/-cache-protocol-fn (core_deftype.clj:527) That's because `my-foo` is an **instance** of the **old** version of `FooRecord`, implementing the **old** version of `IFoo`. As far as the JVM is concerned, the old `IFoo` and the new `IFoo` are completely different classes. To avoid this problem, always create new instances of records after a refresh. ### Warnings for Multimethods Calling `prefer-method` is a global side-effect. If you modify a call to `prefer-method` and reload the namespace containing it, Clojure may throw "java.lang.IllegalStateException: Preference conflict in multimethod." The workaround is to call `remove-method` before reloading. tools.namespace cannot detect this situation automatically. See [TNS-23]. ### Heap Usage and PermGen (JDK 1.7 and before) In rare cases, reloading a lot of code may lead to out-of-memory errors from the JVM like `java.lang.OutOfMemoryError: PermGen space`. You may be able to mitigate this by increasing the size of the "Permanent Generation" where the JVM stores compiled classes. To do this, add the following command-line argument to your JVM startup: -XX:MaxPermSize= where `` is a number with a suffix like `m` for megabytes. To find the default MaxPermSize for your JDK, run `java -XX:+PrintFlagsFinal` and search the results for "MaxPermSize". Try doubling it. The Permanent Generation was removed in JDK 1.8 ([JEP 122]) so this section no longer applies. In some older JDKs (1.5) the default garbage collector did not collect the Permanent Generation at all unless it was explicitly enabled with `-XX:+CMSPermGenSweepingEnabled`. Disabling Refresh In a Namespace -------------------------------- Some projects have a "project REPL" or a "scratch" namespace where you want keep state during development. You can use the functions `disable-unload!` and `disable-reload!` in `clojure.tools.namespace.repl` to prevent `refresh` from automatically un/reloading those namespaces. Use this feature sparingly: it exists as a development-time convenience, not a work-around for code that is not reload-safe. Also, see the warnings about aliases, below. Aliases to reloaded namespaces will break if the namespace *containing* the alias is not reloaded also. After an error, `refresh` will **not** attempt to recover symbol mappings and aliases for namespaces with `disable-unload!` or `disable-reload!` set. Developer Information ---------------------------------------- * [GitHub project](https://github.com/clojure/tools.namespace) * [How to contribute](http://dev.clojure.org/display/community/Contributing) * [Bug Tracker](http://dev.clojure.org/jira/browse/TNS) * [Continuous Integration](http://build.clojure.org/job/tools.namespace/) * [Compatibility Test Matrix](http://build.clojure.org/job/tools.namespace-test-matrix/) Change Log ---------------------------------------- ### Version 0.2.12-SNAPSHOT * In development, current Git master branch ### Version 0.2.11 on 19-Jun-2015 * [TNS-34] Allow reader conditionals in parsed source files ### Version 0.2.10 on 26-Feb-2015 * Widen existing functions to handle both clj and cljc files in advance of reader conditional support in Clojure 1.7. ### Version 0.2.9 on 31-Jan-2015 * Fix [TNS-20]: Undefined 'unload' order after namespaces are first added to an new, empty tracker. * Improvement [TNS-21]: Support `ns` clauses which use vectors instead of lists for clauses, contrary to docs. * Improvement [TNS-32]: Support `ns` clauses which use symbols as clause heads instead of keywords, contrary to docs. ### Version 0.2.8 on 19-Dec-2014 * Improvement [TNS-31]: Specific error message when `:after` symbol passed to `refresh` cannot be resolved. * Fix [TNS-26]: namespace alias recovery after failed reload did not work due to local binding shadowing global Var ### Version 0.2.7 on 19-Sept-2014 * [Revert bad commit](https://github.com/clojure/tools.namespace/commit/27194f2edfe3f5f9e1343f993beca4b43f0bafe8) mistakenly included in 0.2.6 which could cause the tracker's 'unload' order to be incorrect. See discussion at [TNS-20]. ### **BROKEN** Version 0.2.6 on 7-Sept-2014 **DO NOT USE** * `clojure.tools.namespace.parse/read-ns-decl` asserts that its argument is a PushbackReader, instead of silently returning nil * Fix [TNS-22]: broken `clojure.string/replace` with Windows path separator ### Version 0.2.5 on 15-Jul-2014 * New `clojure.tools.namespace.repl/clear` empties the state of the REPL dependency tracker. This can help repair the dependency tracker after a failed load or a circular dependency error. * Enhancement [TNS-19]: `deps-from-ns-decl` should return an empty set instead of nil. This may be a breaking change for some but is consistent with the original docstring. * Enhancement [TNS-18]: Compute transitive dependencies in linear time. * Enhancement [TNS-17]: The `ns` form doesn't need to be the first top-level form in a file. * Fix [TNS-16]: Don't depend on specific hash ordering in tests. Exposed by new hash functions in Clojure 1.6.0. * Fix [TNS-15]: Handle spaces in classpath directories (old `clojure.tools.namespace`) * Fix [TNS-12]: Duplicate definition of `jar-file?` ### Version 0.2.4 on 19-Jul-2013 * Fix [TNS-10]: Forbid circular dependency when a namespace depends on itself * Fix [TNS-9] and [TNS-11]: support other prefix-list forms * Fix [TNS-8]: In `move-ns`, do not modify files whose contents does not change ### Version 0.2.3 on 01-Apr-2013 * New: Attempt recovery of aliases/refers in REPL after error ### Version 0.2.2 on 14-Dec-2012 * New: Add `:after` option to `refresh` * New: Add `clojure.tools.namespace.move` * Fix [TNS-4], reflection warnings ### Version 0.2.1 on 26-Oct-2012 * Fix: Restore deprecated 0.1.x APIs in `clojure.tools.namespace` * Fix [TNS-3], actually use `refresh-dirs` ### Version 0.2.0 on 05-Oct-2012 * **Not recommended for use**: this release introduced breaking API changes (renaming core namespaces and functions) without backwards-compatibility. Applications with dependencies on both the 0.2.x and 0.1.x APIs cannot use this version. * New dependency tracking & reloading features * Eliminate dependency on [java.classpath] ### Version 0.1.3 on 24-Apr-2012 * Fix [TNS-1] Workaround for Clojure 1.2 reader bug ### Version 0.1.2 on 10-Feb-2012 * Fix: Eliminate reflection warnings ### Version 0.1.1 on 18-May-2011 ### Version 0.1.0 on 24-Apr-2011 * Source-compatible with clojure.contrib.find-namespaces in old clojure-contrib 1.2.0 [TNS-1]: http://dev.clojure.org/jira/browse/TNS-1 [TNS-3]: http://dev.clojure.org/jira/browse/TNS-3 [TNS-4]: http://dev.clojure.org/jira/browse/TNS-4 [TNS-8]: http://dev.clojure.org/jira/browse/TNS-8 [TNS-9]: http://dev.clojure.org/jira/browse/TNS-9 [TNS-10]: http://dev.clojure.org/jira/browse/TNS-10 [TNS-11]: http://dev.clojure.org/jira/browse/TNS-11 [TNS-12]: http://dev.clojure.org/jira/browse/TNS-12 [TNS-15]: http://dev.clojure.org/jira/browse/TNS-15 [TNS-16]: http://dev.clojure.org/jira/browse/TNS-16 [TNS-17]: http://dev.clojure.org/jira/browse/TNS-17 [TNS-18]: http://dev.clojure.org/jira/browse/TNS-18 [TNS-19]: http://dev.clojure.org/jira/browse/TNS-19 [TNS-20]: http://dev.clojure.org/jira/browse/TNS-20 [TNS-21]: http://dev.clojure.org/jira/browse/TNS-21 [TNS-22]: http://dev.clojure.org/jira/browse/TNS-22 [TNS-23]: http://dev.clojure.org/jira/browse/TNS-23 [TNS-24]: http://dev.clojure.org/jira/browse/TNS-24 [TNS-25]: http://dev.clojure.org/jira/browse/TNS-25 [TNS-26]: http://dev.clojure.org/jira/browse/TNS-26 [TNS-27]: http://dev.clojure.org/jira/browse/TNS-27 [TNS-28]: http://dev.clojure.org/jira/browse/TNS-28 [TNS-29]: http://dev.clojure.org/jira/browse/TNS-29 [TNS-30]: http://dev.clojure.org/jira/browse/TNS-30 [TNS-31]: http://dev.clojure.org/jira/browse/TNS-31 [TNS-32]: http://dev.clojure.org/jira/browse/TNS-32 [TNS-33]: http://dev.clojure.org/jira/browse/TNS-33 [TNS-34]: http://dev.clojure.org/jira/browse/TNS-34 [java.classpath]: https://github.com/clojure/java.classpath [JEP 122]: http://openjdk.java.net/jeps/122 Copyright and License ---------------------------------------- Copyright © 2012 Stuart Sierra All rights reserved. The use and distribution terms for this software are covered by the [Eclipse Public License 1.0] which can be found in the file epl-v10.html 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. [Eclipse Public License 1.0]: http://opensource.org/licenses/eclipse-1.0.php tools.namespace-tools.namespace-0.2.11/epl.html000066400000000000000000000305361254106746100214420ustar00rootroot00000000000000 Eclipse Public License - Version 1.0

Eclipse Public License - v 1.0

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

"Contribution" means:

a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and

b) in the case of each subsequent Contributor:

i) changes to the Program, and

ii) additions to the Program;

where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.

"Contributor" means any person or entity that distributes the Program.

"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.

"Program" means the Contributions distributed in accordance with this Agreement.

"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.

2. GRANT OF RIGHTS

a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.

b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.

c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.

d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.

3. REQUIREMENTS

A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:

a) it complies with the terms and conditions of this Agreement; and

b) its license agreement:

i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;

ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;

iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and

iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.

When the Program is made available in source code form:

a) it must be made available under this Agreement; and

b) a copy of this Agreement must be included with each copy of the Program.

Contributors may not remove or alter any copyright notices contained within the Program.

Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.

4. COMMERCIAL DISTRIBUTION

Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.

For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.

5. NO WARRANTY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.

6. DISCLAIMER OF LIABILITY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. GENERAL

If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.

All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.

Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.

This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.

tools.namespace-tools.namespace-0.2.11/pom.xml000066400000000000000000000017061254106746100213060ustar00rootroot00000000000000 4.0.0 tools.namespace 0.2.11 ${artifactId} org.clojure pom.contrib 0.1.2 true Stuart Sierra scm:git:git@github.com:clojure/tools.namespace.git scm:git:git@github.com:clojure/tools.namespace.git git@github.com:clojure/tools.namespace.git tools.namespace-0.2.11 tools.namespace-tools.namespace-0.2.11/src/000077500000000000000000000000001254106746100205545ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/000077500000000000000000000000001254106746100215005ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/clojure/000077500000000000000000000000001254106746100231435ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/000077500000000000000000000000001254106746100246065ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/000077500000000000000000000000001254106746100257465ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace.clj000066400000000000000000000154711254106746100304040ustar00rootroot00000000000000 ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use ;; and distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra", :doc "This namespace is DEPRECATED; most functions have been moved to other namespaces"} clojure.tools.namespace (:require [clojure.java.io :as io]) (:import (java.io File FileReader BufferedReader PushbackReader InputStreamReader) (java.util.jar JarFile JarEntry))) ;;; Finding namespaces in a directory tree (defn clojure-source-file? "DEPRECATED; trivial to implement locally Returns true if file is a normal file with a .clj or .cljc extension." [^File file] (and (.isFile file) (or (.endsWith (.getName file) ".clj") (.endsWith (.getName file) ".cljc")))) (defn find-clojure-sources-in-dir "DEPRECATED; moved to clojure.tools.namespace.find Searches recursively under dir for Clojure source files (.clj, .cljc). Returns a sequence of File objects, in breadth-first sort order." [^File dir] ;; Use sort by absolute path to get breadth-first search. (sort-by #(.getAbsolutePath ^File %) (filter clojure-source-file? (file-seq dir)))) (defn comment? "DEPRECATED; moved to clojure.tools.namespace.parse Returns true if form is a (comment ...)" [form] (and (list? form) (= 'comment (first form)))) (defn ns-decl? "DEPRECATED; moved to clojure.tools.namespace.parse Returns true if form is a (ns ...) declaration." [form] (and (list? form) (= 'ns (first form)))) (defn read-ns-decl "DEPRECATED; moved to clojure.tools.namespace.parse Attempts to read a (ns ...) declaration from rdr, and returns the unevaluated form. Returns nil if read fails or if a ns declaration cannot be found. The ns declaration must be the first Clojure form in the file, except for (comment ...) forms." [^PushbackReader rdr] (try (loop [] (let [form (doto (read rdr) str)] (cond (ns-decl? form) form (comment? form) (recur) :else nil))) (catch Exception e nil))) (defn read-file-ns-decl "DEPRECATED; moved to clojure.tools.namespace.file Attempts to read a (ns ...) declaration from file, and returns the unevaluated form. Returns nil if read fails, or if the first form is not a ns declaration." [^File file] (with-open [rdr (PushbackReader. (BufferedReader. (FileReader. file)))] (read-ns-decl rdr))) (defn find-ns-decls-in-dir "DEPRECATED; moved to clojure.tools.namespace.find Searches dir recursively for (ns ...) declarations in Clojure source files; returns the unevaluated ns declarations." [^File dir] (filter identity (map read-file-ns-decl (find-clojure-sources-in-dir dir)))) (defn find-namespaces-in-dir "DEPRECATED; moved to clojure.tools.namespace.find Searches dir recursively for (ns ...) declarations in Clojure source files; returns the symbol names of the declared namespaces." [^File dir] (map second (find-ns-decls-in-dir dir))) ;;; copied from clojure.java.classpath to preserve deprecated API ;;; without an explicit dependency (defn- loader-classpath [loader] (when (instance? java.net.URLClassLoader loader) (map #(java.io.File. (.toURI ^java.net.URL %)) (.getURLs ^java.net.URLClassLoader loader)))) (defn- classpath ([classloader] (distinct (mapcat loader-classpath (take-while identity (iterate #(.getParent ^ClassLoader %) classloader))))) ([] (classpath (clojure.lang.RT/baseLoader)))) (defn- classpath-directories [] (filter #(.isDirectory ^File %) (classpath))) (defn- jar-file? [f] (let [file (io/file f)] (and (.isFile file) (or (.endsWith (.getName file) ".jar") (.endsWith (.getName file) ".JAR"))))) (defn- classpath-jarfiles [] (map #(JarFile. ^File %) (filter jar-file? (classpath)))) (defn- filenames-in-jar [^JarFile jar-file] (map #(.getName ^JarEntry %) (filter #(not (.isDirectory ^JarEntry %)) (enumeration-seq (.entries jar-file))))) ;;; Finding namespaces in JAR files (defn clojure-sources-in-jar "DEPRECATED; moved to clojure.tools.namespace.find Returns a sequence of filenames ending in .clj or .cljc found in the JAR file." [^JarFile jar-file] (filter #(or (.endsWith ^String % ".clj") (.endsWith ^String % ".cljc")) (filenames-in-jar jar-file))) (defn read-ns-decl-from-jarfile-entry "DEPRECATED; moved to clojure.tools.namespace.find Attempts to read a (ns ...) declaration from the named entry in the JAR file, and returns the unevaluated form. Returns nil if the read fails, or if the first form is not a ns declaration." [^JarFile jarfile ^String entry-name] (with-open [rdr (PushbackReader. (BufferedReader. (InputStreamReader. (.getInputStream jarfile (.getEntry jarfile entry-name)))))] (read-ns-decl rdr))) (defn find-ns-decls-in-jarfile "DEPRECATED; moved to clojure.tools.namespace.find Searches the JAR file for Clojure source files containing (ns ...) declarations; returns the unevaluated ns declarations." [^JarFile jarfile] (filter identity (map #(read-ns-decl-from-jarfile-entry jarfile %) (clojure-sources-in-jar jarfile)))) (defn find-namespaces-in-jarfile "DEPRECATED; moved to clojure.tools.namespace.find Searches the JAR file for Clojure source files containing (ns ...) declarations. Returns a sequence of the symbol names of the declared namespaces." [^JarFile jarfile] (map second (find-ns-decls-in-jarfile jarfile))) ;;; Finding namespaces anywhere on CLASSPATH (defn find-ns-decls-on-classpath "DEPRECATED; use clojure.tools.namespace.find/find-ns-decls and clojure.java.classpath/classpath from http://github.com/clojure/java.classpath Searches CLASSPATH (both directories and JAR files) for Clojure source files containing (ns ...) declarations. Returns a sequence of the unevaluated ns declaration forms." [] (concat (mapcat find-ns-decls-in-dir (classpath-directories)) (mapcat find-ns-decls-in-jarfile (classpath-jarfiles)))) (defn find-namespaces-on-classpath "DEPRECATED; use clojure.tools.namespace.find/find-namespaces and clojure.java.classpath/classpath from http://github.com/clojure/java.classpath Searches CLASSPATH (both directories and JAR files) for Clojure source files containing (ns ...) declarations. Returns a sequence of the symbol names of the declared namespaces." [] (map second (find-ns-decls-on-classpath))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/000077500000000000000000000000001254106746100277025ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/dependency.clj000066400000000000000000000131561254106746100325200ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Bidirectional graphs of dependencies and dependent objects."} clojure.tools.namespace.dependency (:require [clojure.set :as set])) (defprotocol DependencyGraph (immediate-dependencies [graph node] "Returns the set of immediate dependencies of node.") (immediate-dependents [graph node] "Returns the set of immediate dependents of node.") (transitive-dependencies [graph node] "Returns the set of all things which node depends on, directly or transitively.") (transitive-dependencies-set [graph node-set] "Returns the set of all things which any node in node-set depends on, directly or transitively.") (transitive-dependents [graph node] "Returns the set of all things which depend upon node, directly or transitively.") (transitive-dependents-set [graph node-set] "Returns the set of all things which depend upon any node in node-set, directly or transitively.") (nodes [graph] "Returns the set of all nodes in graph.")) (defprotocol DependencyGraphUpdate (depend [graph node dep] "Returns a new graph with a dependency from node to dep (\"node depends on dep\"). Forbids circular dependencies.") (remove-edge [graph node dep] "Returns a new graph with the dependency from node to dep removed.") (remove-all [graph node] "Returns a new dependency graph with all references to node removed.") (remove-node [graph node] "Removes the node from the dependency graph without removing it as a dependency of other nodes. That is, removes all outgoing edges from node.")) (defn- remove-from-map [amap x] (reduce (fn [m [k vs]] (assoc m k (disj vs x))) {} (dissoc amap x))) (defn- transitive "Recursively expands the set of dependency relationships starting at (get neighbors x), for each x in node-set" [neighbors node-set] (loop [unexpanded (mapcat neighbors node-set) expanded #{}] (if-let [[node & more] (seq unexpanded)] (if (contains? expanded node) (recur more expanded) (recur (concat more (neighbors node)) (conj expanded node))) expanded))) (declare depends?) (def set-conj (fnil conj #{})) (defrecord MapDependencyGraph [dependencies dependents] DependencyGraph (immediate-dependencies [graph node] (get dependencies node #{})) (immediate-dependents [graph node] (get dependents node #{})) (transitive-dependencies [graph node] (transitive dependencies #{node})) (transitive-dependencies-set [graph node-set] (transitive dependencies node-set)) (transitive-dependents [graph node] (transitive dependents #{node})) (transitive-dependents-set [graph node-set] (transitive dependents node-set)) (nodes [graph] (clojure.set/union (set (keys dependencies)) (set (keys dependents)))) DependencyGraphUpdate (depend [graph node dep] (when (or (= node dep) (depends? graph dep node)) (throw (Exception. (str "Circular dependency between " (pr-str node) " and " (pr-str dep))))) (MapDependencyGraph. (update-in dependencies [node] set-conj dep) (update-in dependents [dep] set-conj node))) (remove-edge [graph node dep] (MapDependencyGraph. (update-in dependencies [node] disj dep) (update-in dependents [dep] disj node))) (remove-all [graph node] (MapDependencyGraph. (remove-from-map dependencies node) (remove-from-map dependents node))) (remove-node [graph node] (MapDependencyGraph. (dissoc dependencies node) dependents))) (defn graph "Returns a new, empty, dependency graph." [] (->MapDependencyGraph {} {})) (defn depends? "True if x is directly or transitively dependent on y." [graph x y] (contains? (transitive-dependencies graph x) y)) (defn dependent? "True if y is a dependent of x." [graph x y] (contains? (transitive-dependents graph x) y)) (defn topo-sort "Returns a topologically-sorted list of nodes in graph." [graph] (loop [sorted () g graph todo (set (filter #(empty? (immediate-dependents graph %)) (nodes graph)))] (if (empty? todo) sorted (let [[node & more] (seq todo) deps (immediate-dependencies g node) [add g'] (loop [deps deps g g add #{}] (if (seq deps) (let [d (first deps) g' (remove-edge g node d)] (if (empty? (immediate-dependents g' d)) (recur (rest deps) g' (conj add d)) (recur (rest deps) g' add))) [add g]))] (recur (cons node sorted) (remove-node g' node) (clojure.set/union (set more) (set add))))))) (defn topo-comparator "Returns a comparator fn which produces a topological sort based on the dependencies in graph. Nodes not present in the graph will sort after nodes in the graph." [graph] (let [pos (zipmap (topo-sort graph) (range))] (fn [a b] (compare (get pos a Long/MAX_VALUE) (get pos b Long/MAX_VALUE))))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/dir.clj000066400000000000000000000053161254106746100311570ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Track namespace dependencies and changes by monitoring file-modification timestamps"} clojure.tools.namespace.dir (:require [clojure.tools.namespace.file :as file] [clojure.tools.namespace.track :as track] [clojure.java.io :as io] [clojure.set :as set] [clojure.string :as string]) (:import (java.io File) (java.util.regex Pattern))) (defn- find-files [dirs] (->> dirs (map io/file) (filter #(.exists ^File %)) (mapcat file-seq) (filter file/clojure-file?) (map #(.getCanonicalFile ^File %)))) (defn- modified-files [tracker files] (filter #(< (::time tracker 0) (.lastModified ^File %)) files)) (defn- deleted-files [tracker files] (set/difference (::files tracker #{}) (set files))) (defn- update-files [tracker deleted modified] (let [now (System/currentTimeMillis)] (-> tracker (update-in [::files] #(if % (apply disj % deleted) #{})) (file/remove-files deleted) (update-in [::files] into modified) (file/add-files modified) (assoc ::time now)))) (defn- dirs-on-classpath [] (filter #(.isDirectory ^File %) (map #(File. ^String %) (string/split (System/getProperty "java.class.path") (Pattern/compile (Pattern/quote File/pathSeparator)))))) (defn scan "Scans directories for files which have changed since the last time 'scan' was run; update the dependency tracker with new/changed/deleted files. If no dirs given, defaults to all directories on the classpath." [tracker & dirs] (let [ds (or (seq dirs) (dirs-on-classpath)) files (find-files ds) deleted (seq (deleted-files tracker files)) modified (seq (modified-files tracker files))] (if (or deleted modified) (update-files tracker deleted modified) tracker))) (defn scan-all "Scans directories for all Clojure source files and updates the dependency tracker to reload files. If no dirs given, defaults to all directories on the classpath." [tracker & dirs] (let [ds (or (seq dirs) (dirs-on-classpath)) files (find-files ds) deleted (seq (deleted-files tracker files))] (update-files tracker deleted files))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/file.clj000066400000000000000000000044201254106746100313130ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Read and track namespace information from files"} clojure.tools.namespace.file (:require [clojure.java.io :as io] [clojure.tools.namespace.parse :as parse] [clojure.tools.namespace.track :as track]) (:import (java.io PushbackReader))) (defn read-file-ns-decl "Attempts to read a (ns ...) declaration from file, and returns the unevaluated form. Returns nil if read fails, or if the first form is not a ns declaration." [file] (with-open [rdr (PushbackReader. (io/reader file))] (parse/read-ns-decl rdr))) (defn clojure-file? "Returns true if the java.io.File represents a normal Clojure source file." [^java.io.File file] (and (.isFile file) (or (.endsWith (.getName file) ".clj") (.endsWith (.getName file) ".cljc")))) ;;; Dependency tracker (defn- files-and-deps [files] (reduce (fn [m file] (if-let [decl (read-file-ns-decl file)] (let [deps (parse/deps-from-ns-decl decl) name (second decl)] (-> m (assoc-in [:depmap name] deps) (assoc-in [:filemap file] name))) m)) {} files)) (def ^:private merge-map (fnil merge {})) (defn add-files "Reads ns declarations from files; returns an updated dependency tracker with those files added." [tracker files] (let [{:keys [depmap filemap]} (files-and-deps files)] (-> tracker (track/add depmap) (update-in [::filemap] merge-map filemap)))) (defn remove-files "Returns an updated dependency tracker with files removed. The files must have been previously added with add-files." [tracker files] (-> tracker (track/remove (keep (::filemap tracker {}) files)) (update-in [::filemap] #(apply dissoc % files)))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/find.clj000066400000000000000000000111311254106746100313110ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra", :doc "Search for namespace declarations in directories and JAR files."} clojure.tools.namespace.find (:require [clojure.java.io :as io] [clojure.set :as set] [clojure.tools.namespace.file :as file] [clojure.tools.namespace.parse :as parse]) (:import (java.io File FileReader BufferedReader PushbackReader InputStreamReader) (java.util.jar JarFile JarEntry))) ;;; JAR-file utilities, adapted from clojure.java.classpath (defn- jar-file? "Returns true if file is a normal file with a .jar or .JAR extension." [f] (let [file (io/file f)] (and (.isFile file) (or (.endsWith (.getName file) ".jar") (.endsWith (.getName file) ".JAR"))))) (defn- jar-files "Given a sequence of File objects, filters it for JAR files, returns a sequence of java.util.jar.JarFile objects." [files] (map #(JarFile. ^File %) (filter jar-file? files))) (defn- filenames-in-jar "Returns a sequence of Strings naming the non-directory entries in the JAR file." [^JarFile jar-file] (map #(.getName ^JarEntry %) (filter #(not (.isDirectory ^JarEntry %)) (enumeration-seq (.entries jar-file))))) ;;; Finding namespaces in a directory tree (defn find-clojure-sources-in-dir "Searches recursively under dir for Clojure source files (.clj, .cljc). Returns a sequence of File objects, in breadth-first sort order." [^File dir] ;; Use sort by absolute path to get breadth-first search. (sort-by #(.getAbsolutePath ^File %) (filter file/clojure-file? (file-seq dir)))) (defn find-ns-decls-in-dir "Searches dir recursively for (ns ...) declarations in Clojure source files; returns the unevaluated ns declarations." [^File dir] (keep file/read-file-ns-decl (find-clojure-sources-in-dir dir))) (defn find-namespaces-in-dir "Searches dir recursively for (ns ...) declarations in Clojure source files; returns the symbol names of the declared namespaces." [^File dir] (map second (find-ns-decls-in-dir dir))) ;;; Finding namespaces in JAR files (defn clojure-sources-in-jar "Returns a sequence of filenames ending in .clj or .cljc found in the JAR file." [^JarFile jar-file] (filter #(or (.endsWith ^String % ".clj") (.endsWith ^String % ".cljc")) (filenames-in-jar jar-file))) (defn read-ns-decl-from-jarfile-entry "Attempts to read a (ns ...) declaration from the named entry in the JAR file, and returns the unevaluated form. Returns nil if the read fails, or if the first form is not a ns declaration." [^JarFile jarfile ^String entry-name] (with-open [rdr (PushbackReader. (io/reader (.getInputStream jarfile (.getEntry jarfile entry-name))))] (parse/read-ns-decl rdr))) (defn find-ns-decls-in-jarfile "Searches the JAR file for Clojure source files containing (ns ...) declarations; returns the unevaluated ns declarations." [^JarFile jarfile] (filter identity (map #(read-ns-decl-from-jarfile-entry jarfile %) (clojure-sources-in-jar jarfile)))) (defn find-namespaces-in-jarfile "Searches the JAR file for Clojure source files containing (ns ...) declarations. Returns a sequence of the symbol names of the declared namespaces." [^JarFile jarfile] (map second (find-ns-decls-in-jarfile jarfile))) ;;; Finding namespaces (defn find-ns-decls "Searches a sequence of java.io.File objects (both directories and JAR files) for .clj or .cljc source files containing (ns...) declarations. Returns a sequence of the unevaluated ns declaration forms. Use with clojure.java.classpath to search Clojure's classpath." [files] (concat (mapcat find-ns-decls-in-dir (filter #(.isDirectory ^File %) files)) (mapcat find-ns-decls-in-jarfile (jar-files files)))) (defn find-namespaces "Searches a sequence of java.io.File objects (both directories and JAR files) for .clj or .cljc source files containing (ns...) declarations. Returns a sequence of the symbol names of the declared namespaces. Use with clojure.java.classpath to search Clojure's classpath." [files] (map second (find-ns-decls files))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/move.clj000066400000000000000000000074651254106746100313560ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Refactoring tool to move a Clojure namespace from one name/file to another, and update all references to that namespace in your other Clojure source files. WARNING: This code is ALPHA and subject to change. It also modifies and deletes your source files! Make sure you have a backup or version control."} clojure.tools.namespace.move (:require [clojure.string :as str] [clojure.java.io :as io]) (:import (java.io File))) (defn- update-file "Reads file as a string, calls f on the string plus any args, then writes out return value of f as the new contents of file. Does not modify file if the content is unchanged." [file f & args] (let [old (slurp file) new (str (apply f old args))] (when-not (= old new) (spit file new)))) (defn- ns-file-name [sym] (str (-> (name sym) (str/replace "-" "_") (str/replace "." File/separator)) ".clj")) (defn- clojure-source-files [dirs] (->> dirs (map io/file) (filter #(.exists ^File %)) (mapcat file-seq) (filter (fn [^File file] (and (.isFile file) (.endsWith (.getName file) ".clj")))) (map #(.getCanonicalFile ^File %)))) (def ^:private symbol-regex ;; LispReader.java uses #"[:]?([\D&&[^/]].*/)?([\D&&[^/]][^/]*)" but ;; that's too broad; we don't want a whole namespace-qualified symbol, ;; just each part individually. #"[a-zA-Z0-9$%*+=?!<>_-]['.a-zA-Z0-9$%*+=?!<>_-]*") (defn replace-ns-symbol "ALPHA: subject to change. Given Clojure source as a string, replaces all occurrences of the namespace name old-sym with new-sym and returns modified source as a string." [source old-sym new-sym] (let [old-name (name old-sym) new-name (name new-sym)] ;; A lossless parser would be better, but this is adequate (str/replace source symbol-regex (fn [match] (if (= match old-name) new-name match))))) (defn move-ns-file "ALPHA: subject to change. Moves the .clj source file (found relative to source-path) for the namespace named old-sym to a file for a namespace named new-sym. WARNING: This function moves and deletes your source files! Make sure you have a backup or version control." [old-sym new-sym source-path] (let [old-file (io/file source-path (ns-file-name old-sym)) new-file (io/file source-path (ns-file-name new-sym))] (.mkdirs (.getParentFile new-file)) (io/copy old-file new-file) (.delete old-file) (loop [dir (.getParentFile old-file)] (when (empty? (.listFiles dir)) (.delete dir) (recur (.getParentFile dir)))))) (defn move-ns "ALPHA: subject to change. Moves the .clj source file (found relative to source-path) for the namespace named old-sym to new-sym and replace all occurrences of the old name with the new name in all Clojure source files found in dirs. This is a purely textual transformation. It does not work on namespaces require'd or use'd from a prefix list. WARNING: This function modifies and deletes your source files! Make sure you have a backup or version control." [old-sym new-sym source-path dirs] (move-ns-file old-sym new-sym source-path) (doseq [file (clojure-source-files dirs)] (update-file file replace-ns-symbol old-sym new-sym))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/parse.clj000066400000000000000000000070761254106746100315200ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Parse Clojure namespace (ns) declarations and extract dependencies."} clojure.tools.namespace.parse (:require [clojure.set :as set])) (defn- read-clj "Calls clojure.core/read. If reader conditionals are supported (Clojure 1.7) then adds options {:read-cond :allow}." [rdr] (if (resolve 'clojure.core/reader-conditional?) (read {:read-cond :allow} rdr) (read rdr))) (defn comment? "Returns true if form is a (comment ...)" [form] (and (list? form) (= 'comment (first form)))) (defn ns-decl? "Returns true if form is a (ns ...) declaration." [form] (and (list? form) (= 'ns (first form)))) (defn read-ns-decl "Attempts to read a (ns ...) declaration from a java.io.PushbackReader, and returns the unevaluated form. Returns the first top-level ns form found. Returns nil if read fails or if a ns declaration cannot be found. Note that read can execute code (controlled by *read-eval*), and as such should be used only with trusted sources." [rdr] {:pre [(instance? java.io.PushbackReader rdr)]} (try (loop [] (let [form (doto (read-clj rdr) str)] ; str forces errors, see TNS-1 (if (ns-decl? form) form (recur)))) (catch Exception e nil))) ;;; Parsing dependencies (defn- prefix-spec? "Returns true if form represents a libspec prefix list like (prefix name1 name1) or [com.example.prefix [name1 :as name1]]" [form] (and (sequential? form) ; should be a list, but often is not (symbol? (first form)) (not-any? keyword? form) (< 1 (count form)))) ; not a bare vector like [foo] (defn- option-spec? "Returns true if form represents a libspec vector containing optional keyword arguments like [namespace :as alias] or [namespace :refer (x y)] or just [namespace]" [form] (and (sequential? form) ; should be a vector, but often is not (symbol? (first form)) (or (keyword? (second form)) ; vector like [foo :as f] (= 1 (count form))))) ; bare vector like [foo] (defn- deps-from-libspec [prefix form] (cond (prefix-spec? form) (mapcat (fn [f] (deps-from-libspec (symbol (str (when prefix (str prefix ".")) (first form))) f)) (rest form)) (option-spec? form) (deps-from-libspec prefix (first form)) (symbol? form) (list (symbol (str (when prefix (str prefix ".")) form))) (keyword? form) ; Some people write (:require ... :reload-all) nil :else (throw (IllegalArgumentException. (pr-str "Unparsable namespace form:" form))))) (defn- deps-from-ns-form [form] (when (and (sequential? form) ; should be list but sometimes is not (contains? #{:use :require 'use 'require} (first form))) (mapcat #(deps-from-libspec nil %) (rest form)))) (defn deps-from-ns-decl "Given an (ns...) declaration form (unevaluated), returns a set of symbols naming the dependencies of that namespace. Handles :use and :require clauses but not :load." [decl] (set (mapcat deps-from-ns-form decl))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/reload.clj000066400000000000000000000040311254106746100316400ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Force reloading namespaces on demand or through a dependency tracker"} clojure.tools.namespace.reload (:require [clojure.tools.namespace.track :as track])) (defn remove-lib "Remove lib's namespace and remove lib from the set of loaded libs." [lib] (remove-ns lib) (dosync (alter @#'clojure.core/*loaded-libs* disj lib))) (defn track-reload-one "Executes the next pending unload/reload operation in the dependency tracker. Returns the updated dependency tracker. If reloading caused an error, it is captured as ::error and the namespace which caused the error is ::error-ns." [tracker] (let [{unload ::track/unload, load ::track/load} tracker] (cond (seq unload) (let [n (first unload)] (remove-lib n) (update-in tracker [::track/unload] rest)) (seq load) (let [n (first load)] (try (require n :reload) (update-in tracker [::track/load] rest) (catch Throwable t (assoc tracker ::error t ::error-ns n ::track/unload load)))) :else tracker))) (defn track-reload "Executes all pending unload/reload operations on dependency tracker until either an error is encountered or there are no more pending operations." [tracker] (loop [tracker (dissoc tracker ::error ::error-ns)] (let [{error ::error, unload ::track/unload, load ::track/load} tracker] (if (and (not error) (or (seq load) (seq unload))) (recur (track-reload-one tracker)) tracker)))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/repl.clj000066400000000000000000000150421254106746100313400ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "REPL utilities for working with namespaces"} clojure.tools.namespace.repl (:require [clojure.tools.namespace.track :as track] [clojure.tools.namespace.dir :as dir] [clojure.tools.namespace.reload :as reload])) (defonce ^:private refresh-tracker (track/tracker)) (defonce ^:private refresh-dirs []) (defn- print-and-return [tracker] (if-let [e (::reload/error tracker)] (do (when (thread-bound? #'*e) (set! *e e)) (prn :error-while-loading (::reload/error-ns tracker)) e) :ok)) (defn- print-pending-reloads [tracker] (prn :reloading (::track/load tracker))) (defn- load-disabled? [sym] (false? (::load (meta (find-ns sym))))) (defn- unload-disabled? [sym] (or (false? (::unload (meta (find-ns sym)))) (load-disabled? sym))) (defn- remove-disabled [tracker] (-> tracker (update-in [::track/unload] #(remove unload-disabled? %)) (update-in [::track/load] #(remove load-disabled? %)))) (defn- referred "Given a Namespace object, returns a map of symbols describing the Vars it refers from other namespaces, in the following form: {other-namespace-name {symbol-in-other-ns symbol-in-this-ns}}" [ns] (reduce (fn [m [sym var]] (let [ns-name (ns-name (:ns (meta var))) var-name (:name (meta var))] (assoc-in m [ns-name var-name] sym))) {} (ns-refers ns))) (defn- aliased "Given a namespace object, returns a map of symbols describing its aliases, in the following form: {alias-symbol namespace-name}" [ns] (reduce (fn [m [alias n]] (assoc m alias (ns-name n))) {} (ns-aliases ns))) (defn- recover-ns "Given the maps returned by 'referred' and 'aliased', attempts to restore as many bindings as possible into the current namespace. Any bindings to namespaces or Vars which do not currently exist will be ignored." [refers aliases] (doseq [[ns-name symbol-map] refers] (when-let [ns (find-ns ns-name)] (doseq [[source-name target-name] symbol-map] (when (ns-resolve ns source-name) (if (= source-name target-name) (refer ns-name :only (list source-name)) (refer ns-name :only () :rename {source-name target-name})))))) (doseq [[alias-sym ns-name] aliases] (when (find-ns ns-name) (alias alias-sym ns-name)))) (defn- do-refresh [scan-fn after-sym] (when after-sym (assert (symbol? after-sym) ":after value must be a symbol") (assert (namespace after-sym) ":after value must be a namespace-qualified symbol")) (let [current-ns-name (ns-name *ns*) current-ns-refers (referred *ns*) current-ns-aliases (aliased *ns*)] (alter-var-root #'refresh-tracker #(apply scan-fn % refresh-dirs)) (alter-var-root #'refresh-tracker remove-disabled) (print-pending-reloads refresh-tracker) (alter-var-root #'refresh-tracker reload/track-reload) (in-ns current-ns-name) (let [result (print-and-return refresh-tracker)] (if (= :ok result) (if after-sym (if-let [after (ns-resolve *ns* after-sym)] (after) (throw (Exception. (str "Cannot resolve :after symbol " after-sym)))) result) ;; There was an error, recover as much as we can: (do (when-not (or (false? (::unload (meta *ns*))) (false? (::load (meta *ns*)))) (recover-ns current-ns-refers current-ns-aliases)) ;; Return the Exception to the REPL: result))))) (defn disable-unload! "Adds metadata to namespace (or *ns* if unspecified) telling 'refresh' not to unload it. The namespace may still be reloaded, it just won't be removed first. Warning: Aliases to reloaded namespaces will break." ([] (disable-unload! *ns*)) ([namespace] (alter-meta! namespace assoc ::unload false))) (defn disable-reload! "Adds metadata to namespace (or *ns* if unspecified) telling 'refresh' not to load it. Implies disable-unload! also. Warning: Aliases to reloaded namespaces will break." ([] (disable-reload! *ns*)) ([namespace] (alter-meta! namespace assoc ::load false))) (defn refresh "Scans source code directories for files which have changed (since the last time this function was run) and reloads them in dependency order. Returns :ok or an error; sets the latest exception to clojure.core/*e (if *e is thread-bound). The directories to be scanned are controlled by 'set-refresh-dirs'; defaults to all directories on the Java classpath. Options are key-value pairs. Valid options are: :after Namespace-qualified symbol naming a zero-argument function to be invoked after a successful refresh. This symbol will be resolved *after* all namespaces have been reloaded." [& options] (let [{:keys [after]} options] (do-refresh dir/scan after))) (defn refresh-all "Scans source code directories for all Clojure source files and reloads them in dependency order. The directories to be scanned are controlled by 'set-refresh-dirs'; defaults to all directories on the Java classpath. Options are key-value pairs. Valid options are: :after Namespace-qualified symbol naming a zero-argument function to be invoked after a successful refresh. This symbol will be resolved *after* all namespaces have been reloaded." [& options] (let [{:keys [after]} options] (do-refresh dir/scan-all after))) (defn set-refresh-dirs "Sets the directories which are scanned by 'refresh'. Supports the same types as clojure.java.io/file." [& dirs] (alter-var-root #'refresh-dirs (constantly dirs))) (defn clear "Clears all state from the namespace/file tracker. This may help repair the namespace tracker when it gets into an inconsistent state, without restarting the Clojure process. The next call to 'refresh' will reload all source files, but may not completely remove stale code from deleted files." [] (alter-var-root #'refresh-tracker (constantly (track/tracker)))) tools.namespace-tools.namespace-0.2.11/src/main/clojure/clojure/tools/namespace/track.clj000066400000000000000000000126141254106746100315040ustar00rootroot00000000000000;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and ;; distribution terms for this software are covered by the Eclipse ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ;; which can be found in the file epl-v10.html 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. (ns ^{:author "Stuart Sierra" :doc "Dependency tracker which can compute which namespaces need to be reloaded after files have changed. This is the low-level implementation that requires you to find the namespace dependencies yourself: most uses will interact with the wrappers in clojure.tools.namespace.file and clojure.tools.namespace.dir or the public API in clojure.tools.namespace.repl."} clojure.tools.namespace.track (:refer-clojure :exclude (remove)) (:require [clojure.set :as set] [clojure.tools.namespace.dependency :as dep])) (defn- remove-deps [deps names] (reduce dep/remove-node deps names)) (defn- add-deps [deps depmap] (reduce (fn [ds [name dependencies]] (reduce (fn [g dep] (dep/depend g name dep)) ds dependencies)) deps depmap)) (defn- update-deps [deps depmap] (-> deps (remove-deps (keys depmap)) (add-deps depmap))) (defn- affected-namespaces [deps names] (set/union (set names) (dep/transitive-dependents-set deps names))) (defn add "Returns an updated dependency tracker with new/updated namespaces. Depmap is a map describing the new or modified namespaces. Keys in the map are namespace names (symbols). Values in the map are sets of symbols naming the direct dependencies of each namespace. For example, assuming these ns declarations: (ns alpha (:require beta)) (ns beta (:require gamma delta)) the depmap would look like this: {alpha #{beta} beta #{gamma delta}} After adding new/updated namespaces, the dependency tracker will have two lists associated with the following keys: :clojure.tools.namespace.track/unload is the list of namespaces that need to be removed :clojure.tools.namespace.track/load is the list of namespaces that need to be reloaded To reload namespaces in the correct order, first remove/unload all namespaces in the 'unload' list, then (re)load all namespaces in the 'load' list. The clojure.tools.namespace.reload namespace has functions to do this." [tracker depmap] (let [{load ::load unload ::unload deps ::deps :or {load (), unload (), deps (dep/graph)}} tracker new-deps (update-deps deps depmap) changed (affected-namespaces new-deps (keys depmap)) ;; With a new tracker, old dependencies are empty, so ;; unload order will be undefined unless we use new ;; dependencies (TNS-20). old-deps (if (empty? (dep/nodes deps)) new-deps deps)] (assoc tracker ::deps new-deps ::unload (distinct (concat (reverse (sort (dep/topo-comparator old-deps) changed)) unload)) ::load (distinct (concat (sort (dep/topo-comparator new-deps) changed) load))))) (defn remove "Returns an updated dependency tracker from which the namespaces (symbols) have been removed. The ::unload and ::load lists are populated as with 'add'." [tracker names] (let [{load ::load unload ::unload deps ::deps :or {load (), unload (), deps (dep/graph)}} tracker known (set (dep/nodes deps)) removed-names (filter known names) new-deps (remove-deps deps removed-names) changed (affected-namespaces deps removed-names)] (assoc tracker ::deps new-deps ::unload (distinct (concat (reverse (sort (dep/topo-comparator deps) changed)) unload)) ::load (distinct (filter (complement (set removed-names)) (concat (sort (dep/topo-comparator new-deps) changed) load)))))) (defn tracker "Returns a new, empty dependency tracker" [] {}) (comment ;; Structure of the namespace tracker map {;; Dependency graph of namespace names (symbols) as defined in ;; clojure.tools.namespace.dependency/graph :clojure.tools.namespace.track/deps {} ;; Ordered list of namespace names (symbols) that need to be ;; removed to bring the running system into agreement with the ;; source files. :clojure.tools.namespace.track/unload () ;; Ordered list of namespace names (symbols) that need to be ;; (re)loaded to bring the running system into agreement with the ;; source files. :clojure.tools.namespace.track/load () ;; Added by clojure.tools.namespace.file: Map from source files ;; (java.io.File) to the names (symbols) of namespaces they ;; represent. :clojure.tools.namespace.file/filemap {} ;; Added by clojure.tools.namespace.dir: Set of source files ;; (java.io.File) which have been seen by this dependency tracker; ;; used to determine when files have been deleted. :clojure.tools.namespace.dir/files #{} ;; Added by clojure.tools.namespace.dir: Instant when the ;; directories were last scanned, as returned by ;; System/currentTimeMillis. :clojure.tools.namespace.dir/time 1405201862262}) tools.namespace-tools.namespace-0.2.11/src/test/000077500000000000000000000000001254106746100215335ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/test/clojure/000077500000000000000000000000001254106746100231765ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/000077500000000000000000000000001254106746100246415ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/tools/000077500000000000000000000000001254106746100260015ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/tools/namespace/000077500000000000000000000000001254106746100277355ustar00rootroot00000000000000tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/tools/namespace/dependency_test.clj000066400000000000000000000226021254106746100336060ustar00rootroot00000000000000(ns clojure.tools.namespace.dependency-test (:use clojure.test clojure.tools.namespace.dependency)) ;; building a graph like: ;; ;; :a ;; / | ;; :b | ;; \ | ;; :c ;; | ;; :d ;; (def g1 (-> (graph) (depend :b :a) ; "B depends on A" (depend :c :b) ; "C depends on B" (depend :c :a) ; "C depends on A" (depend :d :c))) ; "D depends on C" ;; 'one 'five ;; | | ;; 'two | ;; / \ | ;; / \ | ;; / \ / ;; 'three 'four ;; | / ;; 'six / ;; | / ;; | / ;; | / ;; 'seven ;; (def g2 (-> (graph) (depend 'two 'one) (depend 'three 'two) (depend 'four 'two) (depend 'four 'five) (depend 'six 'three) (depend 'seven 'six) (depend 'seven 'four))) ;; :level0 ;; / | | \ ;; ----- | | ----- ;; / | | \ ;; :level1a :level1b :level1c :level1d ;; \ | | / ;; ----- | | ----- ;; \ | | / ;; :level2 ;; / | | \ ;; ----- | | ----- ;; / | | \ ;; :level3a :level3b :level3c :level3d ;; \ | | / ;; ----- | | ----- ;; \ | | / ;; :level4 ;; ;; ... and so on in a repeating pattern like that, up to :level26 (def g3 (-> (graph) (depend :level1a :level0) (depend :level1b :level0) (depend :level1c :level0) (depend :level1d :level0) (depend :level2 :level1a) (depend :level2 :level1b) (depend :level2 :level1c) (depend :level2 :level1d) (depend :level3a :level2) (depend :level3b :level2) (depend :level3c :level2) (depend :level3d :level2) (depend :level4 :level3a) (depend :level4 :level3b) (depend :level4 :level3c) (depend :level4 :level3d) (depend :level5a :level4) (depend :level5b :level4) (depend :level5c :level4) (depend :level5d :level4) (depend :level6 :level5a) (depend :level6 :level5b) (depend :level6 :level5c) (depend :level6 :level5d) (depend :level7a :level6) (depend :level7b :level6) (depend :level7c :level6) (depend :level7d :level6) (depend :level8 :level7a) (depend :level8 :level7b) (depend :level8 :level7c) (depend :level8 :level7d) (depend :level9a :level8) (depend :level9b :level8) (depend :level9c :level8) (depend :level9d :level8) (depend :level10 :level9a) (depend :level10 :level9b) (depend :level10 :level9c) (depend :level10 :level9d) (depend :level11a :level10) (depend :level11b :level10) (depend :level11c :level10) (depend :level11d :level10) (depend :level12 :level11a) (depend :level12 :level11b) (depend :level12 :level11c) (depend :level12 :level11d) (depend :level13a :level12) (depend :level13b :level12) (depend :level13c :level12) (depend :level13d :level12) (depend :level14 :level13a) (depend :level14 :level13b) (depend :level14 :level13c) (depend :level14 :level13d) (depend :level15a :level14) (depend :level15b :level14) (depend :level15c :level14) (depend :level15d :level14) (depend :level16 :level15a) (depend :level16 :level15b) (depend :level16 :level15c) (depend :level16 :level15d) (depend :level17a :level16) (depend :level17b :level16) (depend :level17c :level16) (depend :level17d :level16) (depend :level18 :level17a) (depend :level18 :level17b) (depend :level18 :level17c) (depend :level18 :level17d) (depend :level19a :level18) (depend :level19b :level18) (depend :level19c :level18) (depend :level19d :level18) (depend :level20 :level19a) (depend :level20 :level19b) (depend :level20 :level19c) (depend :level20 :level19d) (depend :level21a :level20) (depend :level21b :level20) (depend :level21c :level20) (depend :level21d :level20) (depend :level22 :level21a) (depend :level22 :level21b) (depend :level22 :level21c) (depend :level22 :level21d) (depend :level23a :level22) (depend :level23b :level22) (depend :level23c :level22) (depend :level23d :level22) (depend :level24 :level23a) (depend :level24 :level23b) (depend :level24 :level23c) (depend :level24 :level23d) (depend :level25a :level24) (depend :level25b :level24) (depend :level25c :level24) (depend :level25d :level24) (depend :level26 :level25a) (depend :level26 :level25b) (depend :level26 :level25c) (depend :level26 :level25d))) (deftest t-transitive-dependencies (is (= #{:a :c :b} (transitive-dependencies g1 :d))) (is (= '#{two four six one five three} (transitive-dependencies g2 'seven)))) (deftest t-transitive-dependencies-deep (is (= #{:level0 :level1a :level1b :level1c :level1d :level2 :level3a :level3b :level3c :level3d :level4 :level5a :level5b :level5c :level5d :level6 :level7a :level7b :level7c :level7d :level8 :level9a :level9b :level9c :level9d :level10 :level11a :level11b :level11c :level11d :level12 :level13a :level13b :level13c :level13d :level14 :level15a :level15b :level15c :level15d :level16 :level17a :level17b :level17c :level17d :level18 :level19a :level19b :level19c :level19d :level20 :level21a :level21b :level21c :level21d :level22 :level23a :level23b :level23c :level23d} (transitive-dependencies g3 :level24))) (is (= #{:level0 :level1a :level1b :level1c :level1d :level2 :level3a :level3b :level3c :level3d :level4 :level5a :level5b :level5c :level5d :level6 :level7a :level7b :level7c :level7d :level8 :level9a :level9b :level9c :level9d :level10 :level11a :level11b :level11c :level11d :level12 :level13a :level13b :level13c :level13d :level14 :level15a :level15b :level15c :level15d :level16 :level17a :level17b :level17c :level17d :level18 :level19a :level19b :level19c :level19d :level20 :level21a :level21b :level21c :level21d :level22 :level23a :level23b :level23c :level23d :level24 :level25a :level25b :level25c :level25d} (transitive-dependencies g3 :level26)))) (deftest t-transitive-dependents (is (= '#{four seven} (transitive-dependents g2 'five))) (is (= '#{four seven six three} (transitive-dependents g2 'two)))) (defn- before? "True if x comes before y in an ordered collection." [coll x y] (loop [[item & more] (seq coll)] (cond (nil? item) true ; end of the seq (= x item) true ; x comes first (= y item) false :else (recur more)))) (deftest t-before (is (true? (before? [:a :b :c :d] :a :b))) (is (true? (before? [:a :b :c :d] :b :c))) (is (false? (before? [:a :b :c :d] :d :c))) (is (false? (before? [:a :b :c :d] :c :a)))) (deftest t-topo-comparator-1 (let [sorted (sort (topo-comparator g1) [:d :a :b :foo])] (are [x y] (before? sorted x y) :a :b :a :d :a :foo :b :d :b :foo :d :foo))) (deftest t-topo-comparator-2 (let [sorted (sort (topo-comparator g2) '[three seven nine eight five])] (are [x y] (before? sorted x y) 'three 'seven 'three 'eight 'three 'nine 'five 'eight 'five 'nine 'seven 'eight 'seven 'nine))) (deftest t-topo-sort (let [sorted (topo-sort g2)] (are [x y] (before? sorted x y) 'one 'two 'one 'three 'one 'four 'one 'six 'one 'seven 'two 'three 'two 'four 'two 'six 'two 'seven 'three 'six 'three 'seven 'four 'seven 'five 'four 'five 'seven 'six 'seven))) (deftest t-no-cycles (is (thrown? Exception (-> (graph) (depend :a :b) (depend :b :c) (depend :c :a))))) (deftest t-no-self-cycles (is (thrown? Exception (-> (graph) (depend :a :b) (depend :a :a))))) tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/tools/namespace/move_test.clj000066400000000000000000000045601254106746100324410ustar00rootroot00000000000000(ns clojure.tools.namespace.move-test (:use [clojure.java.io :as io] [clojure.test :only (deftest is)] [clojure.tools.namespace.move :only (move-ns)]) (:import (java.io File))) (defn- create-temp-dir "Creates and returns a new temporary directory java.io.File." [name] (let [temp-file (File/createTempFile name nil)] (.delete temp-file) (.mkdirs temp-file) temp-file)) (def ^:private file-one-contents " (ns com.example.one (:require [com.example.two :as two] [com.example.three :as three])) (defn foo [] (com.example.a.four/foo)) ") (def ^:private file-two-contents " (ns com.example.two (:require [com.example.three :as three] [com.example.a.four :as four])) ") (def ^:private file-three-contents " (ns com.example.three (:use com.example.five)) ") (def ^:private file-four-contents " (ns com.example.a.four) (defn foo [] \"foo\") ") (deftest t-move-ns (let [temp-dir (create-temp-dir "tools-namespace-t-move-ns") src-dir (io/file temp-dir "src") example-dir (io/file temp-dir "src" "com" "example") file-one (io/file example-dir "one.clj") file-two (io/file example-dir "two.clj") file-three (io/file example-dir "three.clj") old-file-four (io/file example-dir "a" "four.clj") new-file-four (io/file example-dir "b" "four.clj")] (.mkdirs (io/file example-dir "a")) (spit file-one file-one-contents) (spit file-two file-two-contents) (spit file-three file-three-contents) (spit old-file-four file-four-contents) (Thread/sleep 1500) ;; ensure file timestamps are different (let [file-three-last-modified (.lastModified file-three)] (move-ns 'com.example.a.four 'com.example.b.four src-dir [src-dir]) (is (.exists new-file-four) "new file should exist") (is (not (.exists old-file-four)) "old file should not exist") (is (not (.exists (.getParentFile old-file-four))) "old empty directory should not exist") (is (= file-three-last-modified (.lastModified file-three)) "unaffected file should not have been modified") (is (not-any? #(.contains (slurp %) "com.example.a.four") [file-one file-two file-three new-file-four])) (is (every? #(.contains (slurp %) "com.example.b.four") [file-one file-two new-file-four]))))) tools.namespace-tools.namespace-0.2.11/src/test/clojure/clojure/tools/namespace/parse_test.clj000066400000000000000000000126501254106746100326040ustar00rootroot00000000000000(ns clojure.tools.namespace.parse-test (:use [clojure.test :only (deftest is)] [clojure.tools.namespace.parse :only (deps-from-ns-decl read-ns-decl)])) (def ns-decl-prefix-list '(ns com.example.one (:require (com.example two [three :as three] [four :refer (a b)]) (com.example.sub [five :as five] six)) (:use (com.example seven [eight :as eight] (nine :only (c d)) [ten])))) ;; Some people like to write prefix lists as vectors, not lists. The ;; use/require functions accept this form. (def ns-decl-prefix-list-as-vector '(ns com.example.one (:require [com.example two [three :as three] [four :refer (a b)]] [com.example.sub [five :as five] six]) (:use [com.example seven [eight :as eight] (nine :only (c d)) [ten]]))) (def ns-decl-prefix-list-clauses-as-vectors "Sometimes people even write the clauses inside ns as vectors, which clojure.core/ns allows. See TNS-21." '(ns com.example.one [:require [com.example two [three :as three] [four :refer (a b)]] [com.example.sub [five :as five] six]] [:use [com.example seven [eight :as eight] (nine :only (c d)) [ten]]])) (def deps-from-prefix-list '#{com.example.two com.example.three com.example.four com.example.sub.five com.example.sub.six com.example.seven com.example.eight com.example.nine com.example.ten}) (deftest t-prefix-list (is (= deps-from-prefix-list (deps-from-ns-decl ns-decl-prefix-list)))) (deftest t-prefix-list-as-vector (is (= deps-from-prefix-list (deps-from-ns-decl ns-decl-prefix-list-as-vector)))) (deftest t-prefix-list-clauses-as-vectors (is (= deps-from-prefix-list (deps-from-ns-decl ns-decl-prefix-list-clauses-as-vectors)))) (deftest t-no-deps-returns-empty-set (is (= #{} (deps-from-ns-decl '(ns com.example.one))))) (def multiple-ns-decls '((ns one) (ns two (:require one)) (ns three (:require [one :as o] [two :as t])))) (def multiple-ns-decls-string " (println \"Code before first ns\") (ns one) (println \"Some code\") (defn hello-world [] \"Hello, World!\") (ns two (:require one)) (println \"Some more code\") (ns three (:require [one :as o] [two :as t]))") (deftest t-read-multiple-ns-decls (with-open [rdr (java.io.PushbackReader. (java.io.StringReader. multiple-ns-decls-string))] (is (= multiple-ns-decls (take-while identity (repeatedly #(read-ns-decl rdr))))))) (def ns-docstring-example "The example ns declaration used in the docstring of clojure.core/ns" '(ns foo.bar (:refer-clojure :exclude [ancestors printf]) (:require (clojure.contrib sql combinatorics)) (:use (my.lib this that)) (:import (java.util Date Timer Random) (java.sql Connection Statement)))) (def deps-from-ns-docstring-example '#{clojure.contrib.sql clojure.contrib.combinatorics my.lib.this my.lib.that}) (deftest t-ns-docstring-example (is (= deps-from-ns-docstring-example (deps-from-ns-decl ns-docstring-example)))) (def require-docstring-example "The example ns declaration used in the docstring of clojure.core/require" '(ns (:require (clojure zip [set :as s])))) (def deps-from-require-docstring-example '#{clojure.zip clojure.set}) (deftest t-require-docstring-example (is (= deps-from-require-docstring-example (deps-from-ns-decl require-docstring-example)))) (def multiple-clauses "Example showing more than one :require or :use clause in one ns declaration, which clojure.core/ns allows." '(ns foo.bar (:require com.example.one) (:import java.io.File) (:require (com.example two three)) (:use (com.example [four :only [x]])) (:use (com.example (five :only [x]))))) (def deps-from-multiple-clauses '#{com.example.one com.example.two com.example.three com.example.four com.example.five}) (deftest t-deps-from-multiple-clauses (is (= deps-from-multiple-clauses (deps-from-ns-decl multiple-clauses)))) (def clauses-without-keywords "Example of require/use clauses with symbols instead of keywords, which clojure.core/ns allows." '(ns foo.bar (require com.example.one) (import java.io.File) (use (com.example (prefixes (two :only [x]) three))))) (def deps-from-clauses-without-keywords '#{com.example.one com.example.prefixes.two com.example.prefixes.three}) (deftest t-clauses-without-keywords (is (= deps-from-clauses-without-keywords (deps-from-ns-decl clauses-without-keywords)))) (def reader-conditionals-string "(ns com.examples.one (:require #?(:clj clojure.string :cljs goog.string)))") (deftest t-reader-conditionals (when (resolve 'clojure.core/reader-conditional?) (let [actual (-> reader-conditionals-string java.io.StringReader. java.io.PushbackReader. read-ns-decl deps-from-ns-decl)] (is (= #{'clojure.string} actual)))))