I have a project with three subprojects like so (using lein-sub):
(defproject trident "0.1.0"
...
:sub
["admin"
"api"
"site"])
Each has a ring handler inside, something like this:
(defproject trident-api "0.1.0-SNAPSHOT"
...
:ring {:handler trident.api.core/handler
:servlet-name "trident-api"
:init trident.api.core/init
:port 33333})
Right now I go into each and type:
> lein ring server
This obviously gets tiresome. I also tried
> lein sub ring server
Which seems like it should work, but it only starts the first one and the process joins it! Possibly there is a way around that? It seems like this would be the simplest approach.
In lieu of that, I am now writing a leiningen task to do this for me, but am running into some issues. I can get the project file for each easily enough using leiningen.core.project/read, but from here, how do I start each ring server in a way that honors all of the dependencies and other settings from their respective project.clj files?
Any ideas?
This is just a suggestion, as I am not able to verify if this will work right now.
There is a var in leiningen.core.project that identifies default values. Maybe you could write a plugin (or fork lein-sub?) and have it override these values for the sub project? Then you can create a plugin that iterates over each sub project while applying a given task to each one.
For example, the defaults declares the source path like so:
:source-paths ["src"]
You could then override it with the following for each sub project:
:source-paths ["sub-project/src"]
Do that with all the pertinent defaults, and it might just work.
There might be a way to do this with Leiningen 2's profiles, but I'm not sure. I imagine if you create a profile for each sub project in the parent project, you could easily merge the profile when invoking the task on the respective sub project.
I ended up building a metaserver to start all three jetty instances at once. Code is here:
https://github.com/antler/lein-caribou/blob/master/src/leiningen/caribou/server.clj
Related
I want to create an uberjar of a leiningen app. My config is:
:uberjar {:omit-source true
:aot :all
:uberjar-name "myapp.jar"
:source-paths ["env/prod/clj" ]
:resource-paths ["env/prod/resources"]}
But upon doing lein uberjar, I find the the files in the project are being compiled, but the compilation is stuck on the file that contains most of the code, for ten minutes and counting. This file doesn't contain more than 140 lines.
TL;DR: never def side-effects
As stated in the comments:
... I just figured that this line: (defonce server (http/start-server server-handler {:port 8982})) is causing the hang.
Don't put stuff like that at top-level.
defonce only means it will not be re-def-ed once it's there (so in this case
it would prevent some "port already in use" error on reloading.
Ways out of that dilemma
Write a function, that starts this server. Then call that from your main. For
development you can run that function from the REPL or you can sprinkle some
reload/restart logic in your user-ns.
Another option could be using delay: it will only execute the code once it gets derefed.
The more "binding of resources" you have to deal with, the more some systematic
approach will give your application a better structure. E.g. take a look at:
weavejester/integrant
stuartsierra/component
tolitius/mount
So why is putting blocking things or side-effects in a def problematic?
The way the Clojure compiler works, is by actually "running" the code. So compile basically is:
enable generation byte code and write it out as .class files
load the namespace and "run" it
This means, that at compile time, the top level side-effects are executed.
Therefor blocking operations in a def, will block compilation (which is quite
obvious), or your CI server will fail to compile, because it can not connect to
the database etc.
A great explanation of how the code generation in Clojure works:
What Are All These Class Files Even About? And Other Stories - Gary Fredericks
I'm using tools.namespace to provide smart reloading of namespaces on the REPL. However, when calling refresh or refresh-all, it throws an error.
user=> (require '[clojure.tools.namespace.repl :as tn])
user=> (tn/refresh)
:reloading (ep31.common ep31.routes ep31.config ep31.application user ep31.common-test ep31.example-test)
:error-while-loading user
java.lang.Exception: No namespace: ep31.config, compiling:(user.clj:1:1)
And it seems to end up in this weird state where (require ep31.config) works without an error, but afterwards the namespace isn't actually defined.
I kind of figured this out, this seems to be a combination of circumstances
there were AOT compiled classes left in target/classes from doing lein uberjar previously
tools.namespace doesn't function correctly when loaded namespaces are AOT compiled
target/classes is by default on the classpath
So long story short, if you did a jar/uberjar build before, then remove target/ and things should start working again.
The question I haven't been able to solve yet is why target/classes is on the classpath to begin with. I'm suspecting it's being added by Leiningen, but haven't found yet where or why it's happening.
I learned this the hard way, documentation for :target-path says (https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L309-L313):
;; All generated files will be placed in :target-path. In order to avoid
;; cross-profile contamination (for instance, uberjar classes interfering
;; with development), it's recommended to include %s in in your custom
;; :target-path, which will splice in names of the currently active profiles.
:target-path "target/%s/"
I guess there has to be legacy reasons that :target-path "target/%s/" isn't the default.
I'm currently trying to reimplement the todo example app to understand how it works and I'm getting an error when I load the page. I'm not certain how to go from here. What concerns me is the error appears to be in cljs.core.
todo-app.simulated.services.receive_messages = (function receive_messages(app){
return io.pedestal.app.protocols.put_message.call(null,(new cljs.core.Keyword("\uFDD0:input")).call(null,app),cljs.core.PersistentArrayMap.fromArray([io.pedestal.app.messages.type,"\uFDD0:create-todo",io.pedestal.app.messages.topic,cljs.core.PersistentVector.fromArray(["\uFDD0:todo"], true)], true));
});
The exception message is:
Uncaught TypeError: Object function (meta,cnt,arr,__hash){
this.meta = meta;
this.cnt = cnt;
this.arr = arr;
this.__hash = __hash;
this.cljs$lang$protocol_mask$partition1$ = 4;
this.cljs$lang$protocol_mask$partition0$ = 16123663;
} has no method 'fromArray'
And my dependencies are:
[[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-1820"]
[domina "1.0.1"]
[ch.qos.logback/logback-classic "1.0.7" :exclusions [org.slf4j/slf4j-api]]
[io.pedestal/pedestal.app "0.1.9"]
[io.pedestal/pedestal.app-tools "0.1.9"]]
Any help or insight would be appreciated!
I was seeing this error too, and it seemed like it came out of nowhere. Clearing the out/ dir (:target-path in your project.clj) fixed it for me. Based off that, I think there was some disconnect in the cljs compilation process and/or pedestal.
This issue looks similar and the fix was similar, so I assume it's a cljs build problem.
I don't have much to offer regarding pedestal debugging in general, but if I see an error that appears to be in a core library, I start from the assumption that something is wrong on my end. :)
EDIT
A little more info, it's recommended to delete the out\ dir every time you upgrade ClojureScript or Pedestal.
As bostonou suggested, the best approach is deleting the out directory. My current approach is to use lein-cljsbuild, I personally do this by adding it to my user profile.
You can do so by calling nano ~/.lein/profiles.clj
Mine currently looks like:
{:user {:plugins [[lein-difftest "2.0.0"]
[lein-marginalia "0.7.1"]
[lein-pprint "1.1.1"]
[lein-swank "1.4.4"]
[lein-catnip "0.5.1"]
[environ/environ.lein "0.3.0"]
[lein-cljsbuild "0.3.2"]]
:hooks [environ.leiningen.hooks]}}
You can now automatically build cljs files by calling lein-cljsbuild once inside the project folder. Calling lein-cljsbuild auto ensures that when the source files are edited then they are automatically compiled.
I also currently add :hooks [leiningen.cljsbuild] to my project.clj so that calling lein clean will also remove files built by lein-cljsbuild.
I'm trying to create a text based Clojure game (inspired by Land of Lisp).
(def *nodes* {:living-room "you are in the living-room. a wizard is snoring loudly on the couch."
:garden "you are in a beautiful garden. there is a well in front of you."
:attic "you are in the attic. there is a giant welding torch in the corner."})
(defn describe-location [location nodes]
(nodes location))
The code is running in the REPL but if I saved the code to a file and trying to run:
(describe-location :attic *nodes*)
I got:
Exception in thread "main" java.lang.IllegalArgumentException: Wrong
number of args (1) passed to: user$describe-location (wizard-game.clj:
0)
What I'm doing wrong?
Here is the file: http://dl.dropbox.com/u/3630641/wizard-game.clj
You have too many parentheses. Instead of (describe-location(:garden *nodes*)), you want (describe-location :garden *nodes*).
Remember that the name of the function goes after the open paren, not before: you were calling (:garden *nodes*) and then calling describe-location on the result, which failed because describe-location wants two arguments, not one.
one potential problem is that the version of the function that is loaded into the repl in the 'user' name space may not be the one you expect, so you may want to (load "wizard-game.clj") into a fresh REPL. though many people are using leiningen for this these days, except for the good number of people using maven directly.
first give your're game a namespace
(ns com.me.myGame
....)
then you can load it into the repl by running
(use 'com.me.myGame)
and call the functions by either their name-space-qualified names
(com.me.myGame/describe-location :attic)
or from the repl switch into that namespace:
(in-ns 'com.me.myGame)
(describe-location :attic)
or you can use leiningen to create your project and name-space automatically.
leiningen is worth it in this case because it just took me longer to write this sentence than to make a project with lein. There are a lot of good tutorials for leiningen.
lein new wizard-game
and then edit src/wizard-game/core.clj. this will let you add dependencies later with out fuss if when the project grows to world-famous-success
I have a problem with the Enclojure REPL and using clojure modules from it. The Load/Change REPL to file/ns works fine with an isolated clojure file, but breaks with a file which has references to another clojure file which I try to use from my project.
Here are the exact steps:
Create a new project.
Create a clojure module foobar.clj (namespace com.acme.foobar)
Define a function which returns a value in foobar.clj:
(ns com.acme.foobar
(:use com.acme.othermodule))
(defn myfunc1 []
"a")
Open a Netbeans IDE REPL
From foobar.clj's context menu select:
Change REPL to file/ns
Load
From REPL call the (myfunc1) function. This works just fine:
com.acme.foobar=> (myfunc1)
"a"
The problems start when when I try to refer to other files from foobar. Here's what I do:
Create a new clojure module othermodule.clj
(ns com.acme.othermodule)
(defn fromothermodule []
"b")
Now try to call this from foobar.clj:
(defn myfunc2 []
(fromothermodule))
From othermodule.clj's context menu I select:
Change REPL to file/ns
Load
To make the REPL realize that there is new module it should be able to run.
I do same things things to foobar.clj which now refers to othermodule.clj, but I get:
CompilerException java.io.FileNotFoundException: Could not locate com/acme/othermodule__init.class or com/acme/othermodule.clj on classpath: (NO_SOURCE_FILE:50)
com.acme.foobar=>
This error message comes from both "Change REPL to file/ns" and "Load"
What am I missing? Should I do some other tricks to make this happen? Even the desperate measure of Run->Clean and Build the main project doesn't help (that would of course make the REPL business pretty painful anyway).
I am using NetBeans 6.7.1 and enclojure-plugin-2009-11-3.nbm.
Got the right solution from Eric Thorsen in the google group:
There are three ways to create the REPL from window-menu. Don't use any of those, instead right
click on the Project and "Start Project REPL". Now the paths are set up accordingly.
My first recommendation is to move from NetBeans/Enclojure to IDEA/La Clojure. JetBrains recently created an open source version of their IDE, and the Clojure plugin works fine in it. Since discovering this, I ditched NetBeans and Enclojure. I find La Clojure a pleasure to work in, but of course your mileage may vary.
Back from when I did this in NetBeans, I seem to remember Enclojure source code resides in a subdirectory called "lib". I think I solved a similar problem by fiddling with directory prefixes on the name of the file to load. Probably something like "../lib/YourName". I managed this by trial and error, so I can't relate the exact rules and syntax.
Two things that might help:
You can run something like (println (System/getProperty "java.user.dir")) to find out where Clojure thinks it's executing from.
You could start, as I did, with using an absolute path until you find your way to the correct directory name. Something like "/home/carl/NetbeansProjects/MyProject/lib/myfile.clj" .