4

I have a small project with ~/src/proj/{foo,bar}.clj that I'm hacking on with emacs, cider and nREPL. In bar.clj I do (def base-13-joke 42) and I want the code in foo.clj to refer to bar.clj's base-13-joke. How do I do this?


My current classpath (with ~/src/proj = /jonas/src/mine/code/move-the-box)

/home/jonas/src/mine/code/move-the-box/target/classes
/home/jonas/.m2/repository/cider/cider-nrepl/0.11.0/cider-nrepl-0.11.0.jar
/home/jonas/.m2/repository/org/tcrawley/dynapath/0.2.3/dynapath-0.2.3.jar
/home/jonas/.m2/repository/org/clojure/tools.nrepl/0.2.12/tools.nrepl-0.2.12.jar
/home/jonas/.m2/repository/clojure-complete/clojure-complete/0.2.4/clojure-complete-0.2.4.jar
/home/jonas/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar
/usr/lib/jvm/java-7-openjdk-amd64/lib/tools.jar

I've tried (add-classpath "file:///jonas/src/mine/code/move-the-box") which appeared to have no effect, as well as (cider.nrepl.middleware.util.java/add-classpath! "file:///home/jonas/src/mine/code/move-the-box") which throws IllegalArgumentException array element type mismatch java.lang.reflect.Array.set (Array.java:-2). What's going on there?

Jonas Kölker
  • 7,680
  • 3
  • 44
  • 51

1 Answers1

4

If you're working with multiple Clojure source files as a single project, you'll get a lot of mileage out of using a build tool like Leiningen or Boot. For example, here's how you could create a simple Boot project called myproject with multiple source files.

First, create a myproject directory (it doesn't matter where) with these contents:

myproject
├── build.boot
└── src
    └── myproject
        ├── bar.clj
        └── foo.clj

Boot will run your build.boot file before it does anything else (e.g. running some of your code, creating a JAR, or starting a REPL). For now, all you need to tell Boot is that your source files will be in the src directory, so just add this line to build.boot:

(set-env! :source-paths #{"src"})

In bar.clj, put the definition that you want to access from your other code:

(ns myproject.bar)

(def base-13-joke 42)

Then, in foo.clj, you can reference bar.clj using a :require clause in your ns declaration:

(ns myproject.foo
  (:require [myproject.bar :as bar]))

(defn make-joke []
  (println (Long/toString bar/base-13-joke 13)))

And that's all there is to it! Of course, you probably want to actually do stuff with the code you've just written. Boot handles "doing things" through tasks. Let's write one that simply runs one of the functions in your project.

When you want to define a task to run with Boot, you can do so by adding the code to define that task to your build.boot file. First, though, since we're going to be calling some of our main code (in this case, the make-joke function in the myproject.foo namespace), we'll need to require that code. Add this line to build.boot:

(require '[myproject.foo :as foo])

Now we can define a simple run task (again, in build.boot) that will run our function:

(deftask run []
  (with-pass-thru _
    (foo/make-joke)))

The with-pass-thru business just has to do with some of the specifics of tasks in Boot, which you can read about in detail here.

Executing tasks is quite easy. Run this in the command line from your project root:

$ boot run
33

Boot also comes with some tasks built-in. For instance, if you run boot repl, you'll be presented with a familiar REPL prompt in the boot.user namespace:

boot.user=>

This namespace is the same one in which Boot executes the build.boot script, so since we have a require for myproject.foo in build.boot, we can use that namespace in the REPL:

boot.user=> (foo/make-joke)
33
nil

Of course, you can require other namespaces too:

boot.user=> (require '[myproject.bar :as bar])
nil
boot.user=> bar/base-13-joke
42

There's a whole lot more that you can do with Boot, like code reloading and interacting with CIDER, which you can read all about in the Boot Wiki.

Sam Estep
  • 12,974
  • 2
  • 37
  • 75