I'm building a Node.js app in ClojureScript and testing out macros.
Directory structure:
├── project.clj
└── src
└── lists
├── core.cljs
└── lib.clj
project.clj:
(defproject lists "0.1.0-SNAPSHOT"
:source-paths ["src/"]
:dependencies [[org.clojure/clojure "1.7.0"]]
:plugins [[lein-cljsbuild "1.1.2"]]
:cljsbuild {:builds
[{:source-paths ["src"]
:compiler {:output-to "target/lists.js"
:optimizations :simple
:target :nodejs}}]})
src/lists/core.cljs:
(ns lists.core
(:require [lists.lib :as lib :include-macros true]))
(enable-console-print!)
(lib/defmain [& args]
(console.log "hello world"))
src/lists/lib.clj:
(ns lists.lib)
(defmacro defmain [& body]
`(set! *main-cli-fn* (fn ~#body)))
When I run lein cljsbuild once, I get a huge error traceback containing:
Caused by: clojure.lang.ExceptionInfo: No such namespace: lists.lib, could not locate lists/lib.cljs or lists/lib.cljc at line 1 src/lists/core.cljs {:file "src/lists/core.cljs", :line 1, :column 1, :tag :cljs/analysis-error}
The folder structure is right, and :source-paths is present in both the outer
defproject call and inner :cljsbuild :builds object. What's even weirder is
that sometimes it exits without printing anything. Anyone have any ideas?
cljsbuild is throwing an exception because it doesn't understand .clj files, it's looking for .cljs or .cljc files in the "src" directory. You have to rename lib.clj to lib.cljc check using cljc on the clojurescript wiki.
Related
I have a Clojure library that has two gen-class directives. When I run lein run, there are no issues. However, when I run lein uberjar, I get errors:
$ lein uberjar
Compiling 6 source files to /Users/frank/src/user/target/uberjar/classes
Compiling user.common
Compiling user.core
java.lang.ClassNotFoundException: user.server.UserAuthenticationServer, compiling:(user/core.clj:15:30)
Exception in thread "main" java.lang.ClassNotFoundException: user.server.UserAuthenticationServer, compiling:(user/core.clj:15:30)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6926)
.....
at clojure.lang.Compiler.analyze(Compiler.java:6701)
Caused by: java.lang.ClassNotFoundException: user.server.UserAuthenticationServer
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
...
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6919)
... 86 more
In addition to the generated java files, there is the project.clj, server.clj, and core.clj.
project.clj
(defproject user "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.9.0-alpha14"]
[io.grpc/grpc-core "1.7.0"]
[io.grpc/grpc-netty "1.7.0"
:exclusions [io.grpc/grpc-core]]
[io.grpc/grpc-protobuf "1.7.0"]
[io.grpc/grpc-stub "1.7.0"]]
:main ^:skip-aot user.core
:aot [user.server]
:target-path "target/%s"
:source-paths ["src/clj"]
:java-source-paths ["src/generated/proto"
"src/generated/grpc"]
:profiles {:uberjar {:aot :all}})
core.clj
(ns user.core
(:import [io.grpc Server ServerBuilder])
(:gen-class))
(defonce start-server-atom (atom nil))
(def port 8080)
(defn start-server []
(when-not #start-server-atom
(reset! start-server-atom
(-> (ServerBuilder/forPort port)
(.addService (new user.server.UserAuthenticationServer))
.build
.start
.awaitTermination))))
(defn -main
[& args]
(start-server))
server.clj
(ns user.server
(:gen-class
:main false
:name user.server.UserAuthenticationServer
:extends xyz.skroo.user.UserAuthenticationGrpc$UserAuthenticationImplBase))
(defn -startUserAuthentication [this req res]
(.onNext res req)
(.onCompleted res))
It's weird because this was working and I think the compile-time order changed and now I cannot generate a standalone jar.
:profiles {:uberjar {:aot :all}} means that when you run uberjar it will try to compile all Namespaces. When you do lein run it only compiles the namespace in the :aot key.
Try to update the uberjar profile to only aot the server namespace and see if that works.
I'm new to clojure and i'm having a hard time figuring out how to reload/ refresh the browser when changes have been made to either html/ js/ css etc.
this is my current setup project.clj
(defproject app2 "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:url "http://exampl.com/FIXME"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.89"]
[ring/ring-core "1.5.0"]
[ring/ring-jetty-adapter "1.5.0"]
[enfocus "2.0.0-SNAPSHOT"]]
:plugins [[lein-cljsbuild "1.1.3"]
[lein-ring "0.9.7"]]
:cljsbuild {:builds [{:source-paths ["src/cljs"],
:compiler {
:main "scripts.client"
:output-to "resources/public/js/main.js"
:output-dir "resources/public/js/out"
:asset-path "js/out"
;;:pretty-print true
;;:optimizations :none
}}]}
:main app2.server/app
:ring {:handler app2.server/app :auto-reload? true :auto-refresh? true :reload-paths ["src" "resources"]}
:profiles {
:dev {
:ring {
:nrepl {
:start? true
:port 9000
}
}
}
}
)
This is my server.clj
(ns app2.server
(:use [ring.middleware.resource :only [wrap-resource]]
[ring.middleware.file-info :only [wrap-file-info]]
[ring.middleware.reload :refer [wrap-reload]])
;;(:require app2.repl)
)
(defn handler
[request]
{:status 200}
)
;handling routing "/" -> "/index.html"
(defn wrap-index [handler]
(fn [req]
(println (pr-str req))
(if (= (:uri req) "/")
(handler (assoc req :uri "/index.html"))
(handler req))))
;setting up a simple resource handler for ring
(def app (-> handler
(wrap-resource "public")
(wrap-file-info)
(wrap-index)
(wrap-reload app {:dirs ["src" "resources"]})
))
How can this be accomplished?
I'm used to developing in node and you have tools like browser sync, weinre and supervisor. What are the equivalents in clojure?
I suggest you have a look at figwheel, which lets you do hot reloading of your ClojureScript and CSS in the browser.
There is of course not one good way of setting up your build, but my way to go for languages like SASS etc. is to watch and compile them as a separate process, and have Figwheel watch the generated CSS.
For example, on one of my ClojureScript projects, I had a script file for LESS compilation which used the LESS compiler and the wr utility directly:
#!/usr/bin/env bash
lessc src/styles/main.main.less resources/public/css/main.css --source-map && cp src/styles/*.less resources/public/css
wr "lessc src/styles/main.main.less resources/public/css/main.css --source-map && cp src/styles/*.less resources/public/css" src/**/*.less
Of course you can also use things like Gulp, Webpack - or any tool you're used to.
The alternative is to use Leiningen plugins, see the list here.
I am setting up a web application building a route and handler with Ring and Compojure. Every time I try lein ring server I get a 404 Not Found. But I should see
Edit
After starting the server I am asked by IE to open or save the file. But Windows is not able to read the JSON file.
My project.clj looks like
(defproject web-viz
:dependencies [[org.clojure/clojure "1.4.0"]
[ring/ring-core "1.1.7"]
[ring/ring-jetty-adapter "1.1.7"]
[compojure "1.1.3"]
[hiccup "1.0.2"]]
:plugins [[lein-ring "0.8.3"]]
:ring {:handler web-viz.web/app})
and inside the src I have a file web.clj
(ns web-viz.web
(:require [compojure.route :as route]
[compojure.handler :as handler]
[clojure.string :as str])
(:use compojure.core
ring.adapter.jetty
[ring.middleware.content-type :only
(wrap-content-type)]
[ring.middleware.file :only (wrap-file)]
[ring.middleware.file-info :only
(wrap-file-info)]
[ring.middleware.stacktrace :only
(wrap-stacktrace)]
[ring.util.response :only (redirect)]))
(defroutes site-routes
(GET "/" [] (redirect "/data/census-race.json"))
(route/resources "/")
(route/not-found "Page not found"))
(def app (-> (handler/site site-routes)
(wrap-file "resources")
(wrap-file-info)
(wrap-content-type)))
There should be a file with the content above located at
web-viz/resources/public/data/census-race.json
You project is working for me. Here is how I structured the project
.
├── project.clj
├── resources
│ └── data
│ └── census-race.json
└── src
└── web_viz
└── web.clj
I don't see anything obviously wrong but the following looks unusual:
(def app (-> (handler/site site-routes)
(wrap-file "resources")
(wrap-file-info)
(wrap-content-type)))
From https://stackoverflow.com/a/22788463/894091:
You don't need any of the extra middleware like wrap-file,
wrap-file-info, or wrap-content-type, since compojure.route/resources
already does everything you need.
See if the following does the trick:
(def app
(handler/site app-routes))
I am trying to :gen-class a Servlet
This is my code:
(ns test.test
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
it can't be compiled by Lein
it said Exception in thread "main" java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet (Test.clj:1)
I already modified the :library-path in Lein as ":library-path "/home/long/workspaces/spring/LongHDi/war/WEB-INF/lib" but it didn't work.
Do you have any idea why?
I am trying to work with Google App Engine. The servlet class I want to extend is already in the lib folder I specified.
Which version of lein are you using ?
I downloaded jetty from here, and lein version1 worked for me with project.clj
(defproject st2 "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:library-path "/Users/Niko/Downloads/jetty-hightide-8.1.7.v20120910/lib"
:aot [st2.core]
:dependencies [[org.clojure/clojure "1.3.0"]])
with st2.core the same as your code:
(ns st2.core
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
If you are using lein2, :library-path is not supported so I suspect you would have to add the dependencies "a-la-maven" and add them to your project dependencies.
I'd like to have two main classes (or more) with leiningen, and then be able to choose which one at the java command line. For example I have:
(ns abc (:gen-class))
(defn -main [] (println "abc"))
(ns def (:gen-class))
(defn -main [] (println "def"))
With a project.clj having:
(defproject my-jar "0.0.1"
:description "test"
:dependencies [
]
:main abc)
Then I build with lein uberjar, and run:
java -cp my-jar-0.0.1-standalone.jar abc
java -cp my-jar-0.0.1-standalone.jar def
I get it that when I specified :main abc in the project.clj it was calling that out as the main-class in the manifest, but I couldn't get it to run without putting something. But either way when I try to run the 'def' main, I get a class not found:
Exception in thread "main" java.lang.NoClassDefFoundError: def
This works at least with leiningen 2.0+
(defproject my-jar "0.0.1"
:description "test"
:dependencies [
]
:profiles {:main-a {:main abc}
{:main-b {:main def}}
:aliases {"main-a" ["with-profile" "main-a" "run"]
"main-b" ["with-profile" "main-b" "run"]})
Then you can run each main like so:
lein main-a
lein main-b
Which expands to this:
lein with-profile main-a run
lein with-profile main-b run
I'm using this in one of my projects and it works perfectly.
I added :aot [abc def] to the project.clj to generate compiled code and it worked.
What worked for me in both lein 2.7.0's run task as well as from the resulting uberjar is as follows...
project.clj:
(defproject many-mains "0.1.0-SNAPSHOT"
:description "Project containing multiple main methods"
:dependencies [[org.clojure/clojure "1.8.0"]]
:main nil
:target-path "target/%s"
:profiles {:main-abc {:main many-mains.abc}
:main-def {:main many-mains.def}
:main-ghi {:main org.rekdev.mm.ghi}
:core {:main many-mains.core}
:uberjar {:aot :all}})
For source like...
$ cat src/many_mains/abc.clj
(ns many-mains.abc
(:gen-class))
(defn -main
""
[& args]
(println "Hello, from many-mains.abc!"))
This lets lein run work like...
$ lein with-profile main-abc run
Hello, from many-mains.abc!
From the command line the '-' in many-mains needs to become a '_' which makes it a legal Java classname.
$ java -cp target/uberjar/many-mains-0.1.0-SNAPSHOT-standalone.jar many_mains.abc
Hello, from many-mains.abc!
There seems to have been some behavior changes between Lein 2.7.0 and prior around the effect of :main nil on the MANIFEST.MF. What I've got here works like a champ in Lein 2.7.0. The full source is at https://github.com/robertkuhar/many-mains