I'm trying to setup a boot project, which uses a resource directory holding a subdirectory public. The later should contain for instance all the web related content, the compiled clojurescript etc.
This makes sense for the case you want to hold resources which are not meant to be exposed publicly.
It works perfectly when doing so without the extra public directory. When trying to use the additional subdir, I'm getting errors. Here are the relevant configurations:
In the build.boot:
(set-env! :resource-paths #{"resources"} ...)
...
(deftask dev
[]
(comp
(serve
:handler 'myapp.server/handler
:reload true
:port 3000)
(watch)
(reload)
(cljs-repl)
(cljs)
(target :dir #{"resources/public"})))
From inside the resources directory:
bash-4.3$ tree
└── public
├── index.html
└── js
└── main.cljs.edn
Whereas main.cljs.edn looks like:
{:require [{{name}}.core]
:compiler-options {:asset-path "js/main.out"}}
When invoking boot dev the following occurs: ClojureScript keeps on compiling with errors, until the process is killed manually. It looks like a recursive error related to some relative paths.
Compiling ClojureScript...
• public/js/main.js
Writing target dir(s)...
java.util.concurrent.ExecutionException: java.nio.file.NoSuchFileException: resources/public/public/public/js/main.out/goog/deps.js
whereas the longer it keeps running the public/public/public/.. expands.
Anybody suggestions where this could be fixed?
Update
Here is an updated version of the question:
Consider this structure of a resources folder:
bash-4.3$ tree
└── private_file.txt
└── public
├── index.html
└── js
└── main.cljs.edn
And the following parts from build.boot:
(set-env! :resource-paths #{"resources"} ...)
(deftask dev
[]
(comp
(serve
:handler 'myapp.server/handler
:reload true
:port 3000)
(watch)
(reload)
(cljs-repl)
(cljs)
(target)))
'myapp.server/handler knows to just serve files from resources/public (implemented by wrap-resource or the compojure equivalent resources. The later even defaults to "private".
But the problem occurs even earlier:
when running boot dev the whole resources directory gets reproduced in the target directory, including private_file.txt of course. (is this intended behavior?, I mean the file could be quite big and then it would consume double of the disc space)
At this point I'm not sure how the :asset-path from inside main affects this. In the example here I kept it to "js/main.out", which might be incorrect.
I think the issue is that your are putting your generated code into your source directory and it might cause your build tasks to get confused as they will start consuming files they are generating. The culprit is:
(target :dir #{"resources/public"})
I would leave it as the default:
(target :dir #{"target"})
which is equivalent to just
(target)
As you are using serve task with providing your own ring handler ('myapp.server/handler) you also need to make sure that your handler will serve the resources from your classpath by specifying the correct root directory on your classpath ("public"). Probably you are already using ring.middleware.resource/wrap-resource like that:
(wrap-resource handler "public")
The last thing is your main.cljs.edn file. Its :asset-path should be set to js as it should be the relative path to JS files served by your server/handler (files from target/public/js will be served http://localhost:xxxx/js).
With this setup your source files (html, css and other files from resources/public) as well as files generated by tasks like cljs in target/public should be available in your browser.
Related
I have 2 project: my backend on Clojure and frontend on ClojureScript.
I decided to merge them. So i copied files from both projects, runed lein deps and try to start my backend. So i got this error
Couldn't locate web.clj on classpath
In my project source-paths:
["src/clj" "src/cljs"]
And main ^:skip-aot clj.web
My frontend is working properly.
My folder structure:
src
clj
web.clj
cljs
*some cljs files*
So how can i setup my source-paths setting to run my backend?
The namespace you want to start is not clj.web but web so your project.clj file should have:
main ^:skip-aot web
And your web.clj file should being with:
(ns web)
If you use subfolders in the future, the namespaces will be mapped with the following rules:
(ns com.my-company.clojure.examples.my-utils)
The ns form names the lib’s namespace and declares its dependencies. Based on its name, this lib is typically defined in a source file at the classpath-relative path: com/my_company/clojure/examples/my_utils.clj (note the translations from period to slash and hyphen to underscore).
I have 2 external Clojure files in my file system.(a.clj and b.clj)
/Users/e/.somedir/a.clj:
(ns a (:require [b]))
(b/printt)
/Users/e/.somedir/b.clj:
(ns b)
(defn printt [] (println "aaaa"))
The thing is I want to be able to load those files to my REPL(preserving dependency order)
If I load a.clj first I get this exception:
(clojure.main/load-script "/Users/e/.somedir/a.clj"):
CompilerException java.io.FileNotFoundException: Could not locate b__init.class or b.clj on classpath., compiling:(/Users/e/.somedir/a.clj:1:1)
If I load b.clj first there is no problem.
So how can I figure this dependency order out and load b.clj first, imagine that this is a complex dependency graph, are there any code examples?
P.S: I don't want to use leiningen or any other build tool(want to do it programmatically). There could be some kinda library/code snippet that shows me files that needed to get loaded in dependency order. How do build tools like leiningen achieve this thing? They do it in some way.
You don't need to load all the files, just the root file of your dependency tree. The other source files are loaded as needed. So if namespace A requires namespace B and namespace B requires namespace C, then you just:
(load "com/example/A.clj")
and B.clj and C.clj are loaded automatically.
Also, since you're not using a build tool, you'll need to be careful about a few things:
1) You need to make sure your source files folder structure mirrors your namespace declarations so an ns of 'com.example.A' should be in a folder structure like src/com/example/A.clj. If you don't Clojure has no way to know where a dependency is located in the file system.
2) A running Clojure REPL is initiated with a specific class path. If the 'src' folder above was not defined on the classpath, Clojure doesn't know about it. So be careful to add that folder to your classpath: java -jar clojure-1.9.0.jar -cp [need to include your src folder here] clojure.main
One last note, running without a build tool like Leiningen means you'll need to be an expert on java class paths and understanding Clojure's dependency model. That's a lot for someone trying to learn Clojure.
You say do not want to use lein or any other build tool, and that's fine. So you'd be better just to organize your code according to Deps & CLI Guide about the latest CLI tools released with Clojure 1.9.
Briefly, in your folder, create another src folder and put all the .clj files there. The system will find them automatically. For example:
└── your-project
├── deps.edn # your dependencies
└── src
└── a.clj
└── b.clj
Then, in the root of your project, just run clj, and the REPL will appear. There, you may require your modules as well.
Take a look at this article, it would really help.
This is easy if you set up your project using lein. Here is an example project fred. To create a simple project, just type:
> lein new app fred
> cd fred
> cp src/fred/core.clj src/fred/second.clj
It now looks like so:
fred
└── src
├── fred
├── core.clj
└── second.clj
with 2 source files core.clj and second.clj. Add your functions:
> cat src/fred/core.clj
(ns fred.core)
(defn aaa [& args]
(println "aaa - enter")
(println "aaa - exit"))
> cat src/fred/second.clj
(ns fred.second
(:require [fred.core :as fc]))
(defn bbb [& args]
(println "bbb - enter")
(fc/aaa)
(println "bbb - exit"))
Then, you can access both using lein repl:
> lein repl
Clojure 1.9.0
fred.core=> (aaa)
aaa - enter
aaa - exit
nil
fred.core=> (require '[fred.second :as fs])
nil
fred.core=> (fs/bbb)
bbb - enter
aaa - enter
aaa - exit
bbb - exit
nil
fred.core=>
You can also use the following variant to reload a namespace and its dependencies:
(require '[fred.second :as fs] :reload-all)
although this is not perfect and can get into a confused state (see How to reload a clojure file in REPL for more details).
I think I found the solution first of all I created dependency graph then I sorted namespaces in topological order so this is the thing I've been looking for a while. Now I can load Clojure files in that order(increasing dependency order).
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?
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.
Is there a way to programmatically insert the name of my home directory into file paths in Leiningen's project.clj?
I run a Leiningen project on different machines, where I have different home directories. The project uses jar files that are not managed by Maven; I download 'em and put them in a directory that's relative to my home directory, or copy them into the Leiningen project. The second option works but is undesirable.
An easy way to use the first option--keeping the jar files somewhere else--is to add a soft link to the "somewhere else" directory in my Leiningen directory. That works, but the link has to be different on each machine, so I can't include the link file in the git repository, and I'd rather include everything in the git repo.
Isn't there a way to use environmental variables, or otherwise refer to my home directory, in my project.clj file? I've gone through the sample project file and have not found a solution, so far.
I thought I could just construct path strings at run time--what's in project.clj is just Clojure code, after all. Since hard-coding my home directory into project.clj works without any trouble:
:resource-paths [/Users/myhomedir/dist/mason/jar/mason.19.jar")]
I figured I could do this:
:resource-paths [(str (System/getenv "HOME") "/dist/mason/jar/mason.19.jar")]
However, Leiningen doesn't like this at all:
java.lang.IllegalArgumentException: No implementation of method: :as-file of protocol: #'clojure.java.io/Coercions found for class: clojure.lang.PersistentList
Changing [...] to (vector ...) gives the same error but with 'clojure.lang.Symbol` at the end.
In the name of repeatability you'd better have the jar installed in the local maven repository and have it added to the project.clj :dependencies so it's fetched from there. But you said those jars won't be managed by maven, so here we go:
defproject is a macro and it allows to use unquoting to do arbitrary evaluation. It does it by calling the internal fn unquote-project. So you can do the following:
:resource-paths [~(str (System/getenv "HOME") "/dist/mason/jar/mason.19.jar")]
I am using a luminus web project and added a library that I am developing in parallel to it via the checkouts feature of leiningen.
Now, what I want is that the reloading of source files works too for the project that I refer to via the checkouts folder.
Is there way to do that? I have not succeeded so far changing the :reload-paths or wrap-reload options.
Ok, in the end it was pretty easy, however, finding that out, was not.
In core.clj there is this code:
(http-kit/run-server
(if (dev? args) (reload/wrap-reload app) app)
Simply change it to this one:
(http-kit/run-server
(if (dev? args) (reload/wrap-reload app
{:dirs ["src" "checkouts/subproject/src"]}) app)
You can add as many folders you want, all will get watched for source changes.