Library and sample application in the same repository - clojure

I'm working on a Clojure library, and I'd like to include a sample application in the same repository for demonstration and testing purposes. Ideally the sample application would be in a subdirectory (with the main library in the root) and would use the version of the library that's in the repository (i.e., if I modify the library I don't have to push to Maven before testing it with the sample application).
Things I've considered:
lein-sub seems to require the application to be in the repository's root with the library in a subdirectory, which is the opposite of what I need.
Checkouts require a symlink and are not meant to be committed to source control.
Is there a way to say (the equivalent of) :deps ["../../project.clj"]?

The re-frame library has something similar to this, but also depends on the clojars stuff:
:cljsbuild {:builds {:client {:source-paths ["src" "../../src"]
:compiler {:output-dir "resources/public/js"
:output-to "resources/public/js/client.js"}}}})
Having said that, I don't see anything wrong with a repo that contains the library source in one subdir and an example application in another subdir. Both of them would be separate lein projects, and the example app could indeed have a ./checkouts subdir with a symlink that points to the root of the lib project.

Related

Can Leiningen recursively download the dependencies of its checkout dependencies?

Checkout dependencies can be used to add another work-in-progress project to your Leiningen project during development (for example: you're developing an app and underlying library in parallel).
However, when a checkout dependency itself has a "traditional" dependency (from Clojars), running lein run in the parent project will throw a java.io.FileNotFoundException since it apparently does not retrieve the "traditional" dependencies of its checkout dependencies.
Is there a way to let a Leiningen project recursively download the dependencies of its checkout dependencies?
My opinion of the "proper" way to do this is to have your project depend on the library in your checkouts directory as a traditional dependency in addition to having it in your checkouts directory.
Then every time you change dependencies, run lein install in your library project. This will cause lein to generate the appropriate jar file and install it into your local maven repo. It does not matter if this library project is finished, because you are not actually running it in this state, just using it to fetch dependencies.
Then when it does work you don't have to do anything to "switch to production" other than remove your checkouts directory. The dependency is already in place in the dependent project.
There is a side effect of using checkouts to work on libraries in that the code is loaded twice. Once from the "depended on" version, and then again from the "checkouts version". This is very occationally a problem for me when I'm using protocols and have to remember to re-load the protocol definition.

How can I add a local library into my Leinigen project?

I'd like to add a local library of utilities that I wrote to my project in Leinigen without having to make jars of the library, or without copying the code.
Is that possible?
You can use the checkouts feature of leiningen to add a symbolic link to the project directory containing the library.
cd project-dir # where the project.clj file is
mkdir checkouts
ln -s ~/library/project/dir/ checkouts/library-name
Then add a dependency to the project.clj file
EDIT: If your included code is not it's own project then perhaps including the source directly with git submodules is an option, though some would recommend making it a project that can have a version. It's also worth considering running lein install to build jars and put them in your local maven repo since it only takes two words.
ps: i'm assuming your library is a clojure project.

Updating a local dependency in Clojure

My current project is split up in multiple sub-projects using lein-sub. The core sub-project depends on other sub-projects. Right now, I'm typically working through the repl and simply re-compiling the current namespace to get an updated result; However, whenever I update a sub-project, and try to re-compile that namespace, I don't get the updated results for those projects. I've tried to delete everything in target/ and re-installing the dependencies, but nothing is working.
How would I be able to reload sub-projects in the quickest way possible?
lein-sub doesn't put your subprojects on the classpath; if they're available at all, I expect that's due to a lein sub install issued at some point?
For the type of simultaneous interactive development you're asking about you can use Leiningen's built-in checkouts feature. Just create a directory called checkouts in the root of your top-level project and in there create symbolic links to the root directories of the dependencies. You still need to add them as :dependencies to project.clj, but the fresh code from the checkouts will be used. You can then run your REPL in the top-level project while simultaneously working on all of them, reloading the individual namespaces from the dependencies just like you would with those from the top-level project.
See the tutorial (link to the version on master) for a detailed description.

How to install a dependency in a Clojure project

This is a noob question, so I'm sorry if I offend somebody.
But how do I install seesaw on a *nix computer?
Yes, I've read the README.MD file, but how does the project.clj know where to find the library jars (for seesaw for example)?
Edit project.clj and add the dependency (the vector of project-identifying info and version) to the :dependencies vector in project.clj.
The dependency declaration looks like this: [seesaw "1.4.2"] Which you find by searching for seesaw on http://clojars.org.
Your project file should at minimum look something like:
(defproject my-awesome-gui-application "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.4.0"]
[seesaw "1.4.2"]])
If you are using a newer version of leiningen you can type lein deps :tree to see your dependency tree. In other words you can see what libraries are actually being used, ie. the ones you declared and their transitive dependencies.
$ lein deps :tree
[org.clojure/clojure "1.4.0"]
[seesaw "1.4.2"]
[com.jgoodies/forms "1.2.1"]
[com.miglayout/miglayout "3.7.4"]
[j18n "1.0.1"]
[org.fife.ui/rsyntaxtextarea "2.0.3"]
[org.swinglabs.swingx/swingx-core "1.6.3"]
[org.swinglabs.swingx/swingx-action "1.6.3"]
[org.swinglabs.swingx/swingx-autocomplete "1.6.3"]
[org.swinglabs.swingx/swingx-common "1.6.3"]
[org.swinglabs.swingx/swingx-painters "1.6.3"]
[org.swinglabs.swingx/swingx-plaf "1.6.3"]
If you are using an older version of leiningen, type lein deps and look in ./libs to see what jars got fetched (newer versions of lein are smarter and use the jars in ~/.m2 directly instead of copying them into your project. The directory ~/.m2 is the location of your local Maven repository. Leiningen deals with Maven and downloads all the dependencies you've specified so that you don't have to worry about Maven directly.)
I mentioned Maven and your local maven repository in ~/.m2. With any luck you may never have to think about Maven at all (except perhaps browsing through maven central to look up Java libs to stick in your project.clj), but there are times when you might suspect that a jar was corrupted or something to that effect, and it is good to know that you can just blow away that state by deleting your .m2 repository.
project.clj files specify the project configuration for leiningen.
Leiningen downloads and installs the dependencies specifies in the project.clj file and starts the project / runs the repl process / compiles the project to a java jar / whatever. See the link above. In short, leiningen is the most popular glue between your basic OS and the basic java-based clojure runtime/compiler.
Normally, you should not have to install any clojure libs (or even clojure). Except when you need additional libs to develop/debug the current project (and often you don't), you just install leiningen, and leiningen will install the dependencies for the project you want to run.
To be more specific: leiningen gets its download locations/install instructions by delegating to maven, which is a very interesting project. But possibly not worth looking into too closely if your time is precious.

Determining Clojure Jar Path

The point of this question is to clear up confusion about Clojure project.clj dependencies and how to specify a local dependency.
I have a bunch of Clojure lein projects in a tree
./projects/clojure/bene-csv # A csv parsing library
./projects/clojure/bene-cmp # A main program that depends on bene-csv
I'm editing bene-cmp's project.clj file. I want to make a dependency to
./projects/clojure/bene-csv/bene-csv-1.0.0-SN.jar .
Do I use simple directory notation to specify the path or something else
Thank you.
Edit:
I can include bene-csv in my project by entering lein install in the bene-csv project directory, and using these project.clj entries in bene-cmp's project directory's project.clj file:
(defproject bene-cmp "1.0.0-SN"
:description "This is the main benetrak/GIC comparison program."
:dependencies [[org.clojure/clojure "1.3.0"]
[clojure-csv/clojure-csv "1.3.2"]
[bene-csv "1.0.0-SN"]])
However, I am still trying to figure out what the path is, and would appreciate any pointers or help along those lines. Thank You.
Leinigen uses maven dependency management under the covers, so all dependencies get installed in
${HOME}/.m2/repository/${groupId-as-path}/${artifactId}/$[version}/${artifactId}-${version}.jar
where for [org.clojure/clojure "1.3.0"] groupId is org.clojure, artifactId is clojure and version is 1.3.0. groupIds are converted to paths, so a groupId of org.clojure has a path of org/clojure.
In a maven dependency, specified in pom.xml, this would look like:
<project>
...
<dependencies>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
...
</project>
Note - If no groupId is specified then leiningen uses same value for both the groupId and artifactId.
The benefit of using maven dependency management is that it handles transitive dependencies for you, ie. if you specify a dependency on something, you get all the things it depends on and all the thing that those things depend on etc etc.
So to depend on a local project, the correct thing is to install the local project in your local repository.
To save you changing your versions endlessly while in a development phase, maven supports SNAPSHOT dependencies, whereby some extra information is appended to the version (the datetime basically) and maven knows, that for say 1.3.1-SNAPSHOT it should look for the latest version of that snapshot. This is triggered by the naming convention of {version}-SNAPSHOT.
You can, in maven, specify system dependencies with a hard coded path but generally that's bad practice - it's usually used for things that are platform dependent, i.e. may have a native library component.
By default maven central repository is searched, and leinigen adds in the clojars repository, which serves as a central repo for clojure jars.
leinigen uses this stuff under the covers and builds a classpath referring to the jars in your local maven repository.
Note that you can generate a pom.xml from a leinigen project with lein pom. You could then drive maven from that. A useful feature is
mvn dependency:tree
which gives an ascii art represenation of all the dependencies.