How to Run Jetty Example with Ring in Clojure - clojure

I am following along with this example on creating a simple web service in Clojure using ring and jetty.
I have this in my project.clj:
(defproject ws-example "0.0.1"
:description "REST datastore interface."
:dependencies
[[org.clojure/clojure "1.5.1"]
[ring/ring-jetty-adapter "0.2.5"]
[ring-json-params "0.1.0"]
[compojure "0.4.0"]
[clj-json "0.5.3"]]
:dev-dependencies
[[lein-run "1.0.0-SNAPSHOT"]])
This in script/run.clj
(use 'ring.adapter.jetty)
(require '[ws-example.web :as web])
(run-jetty #'web/app {:port 8080})
And this in src/ws_example/web.clj
(ns ws-example.web
(:use compojure.core)
(:use ring.middleware.json-params)
(:require [clj-json.core :as json]))
(defn json-response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/json"}
:body (json/generate-string data)})
(defroutes handler
(GET "/" []
(json-response {"hello" "world"}))
(PUT "/" [name]
(json-response {"hello" name})))
(def app
(-> handler
wrap-json-params))
However, when I execute:
lein run script/run.clj
I get this error:
No :main namespace specified in project.clj.
Why am I getting this and how do I fix it?

You're getting this error because the purpose of lein run (according to lein help run) is to "Run the project's -main function." You don't have a -main function in your ws-example.web namespace, nor do you have a :main specified in your project.clj file, which is what lein run is complaining about.
To fix this, you have a few options. You could move the run-jetty code to a new -main function of the ws-example.web function and then say lein run -m ws-example.web. Or you could do that and also add a line :main ws-example.web to project.clj and then just say lein run. Or you could try using the lein exec plugin to execute a file, rather than a namespace.
For more info, check out the Leiningen Tutorial.

You have to put that (run-jetty) stuff into a -main somewhere and then add it to the project.clj like
:main ws-example.core)

From lein help run:
USAGE: lein run -m NAMESPACE[/MAIN_FUNCTION] [ARGS...]
Calls the main function in the specified namespace.
So, you would need to put your script.clj somewhere on the project source path and then call it as:
lein run -m script

Related

How do I compile and use a class defined with gen-class in the repl and/or tests?

Clojure noob. I'm unable to get a basic example with :gen-class working.
$ lein new app pizza-parlor
; project.clj
(defproject pizza-parlor "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.1"]]
:main ^:skip-aot pizza-parlor.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
; src/pizza_parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
(:gen-class))
(defn -deliver [pizza]
(println "pipin' hot"))
$ lein repl
pizza-parlor.core=> (require 'pizza-parlor.Deliverator)
nil
pizza-parlor.core=> (def d (pizza-parlor.Deliverator.))
Syntax error (ClassNotFoundException) compiling new at (/tmp/form-init10318668819087633543.clj:1:1).
pizza-man.Deliverator
pizza-man.core=> (import pizza-parlor.Deliverator)
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:435).
pizza-parlor.Deliverator
I've tried the :aot option in project.clj and run lein compile to generate classes in target/default, but I get the same error.
What is the correct way to define a Java class via gen-class and then use it in the repl alongside the rest of my project code, or a test that I can run with lein test?
You have to pay attention to your names/folders with a dash - in it.
A clojure namespace of "some-clojure-namespace" will correspond to "some_clojure_namespace" in java world (and in folder structure)
So in your particular case :
; src/pizza-parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
should be in "pizza_parlor" folder so :
; src/pizza_parlor/Deliverator.clj
(ns pizza-parlor.Deliverator
And then
(ns pizza-parlor.Deliverator)
would correspond to a (java) import :
(import pizza_parlor.Deliverator)
(note the underscore here)
But as stated by #Alan Thompson you do not need to create classes and import them in clojure. You can require the namespace and call it like he described. You want to generate Java Class and import them mostly if you have to interop with a java lib or so.
You can also use gen-class directly to generate any Java class that you would need like, see many example here
You are misunderstanding how to execute a function in the REPL.
You do not need to "create a class" in Clojure. Just start up a repl and invoke a function. There are several choices:
(ns demo.core
; (:gen-class) ; uncomment to make runnable JAR file
)
(defn -main []
(println "main - enter")
)
As the comment says, you only need the (:gen-class) expression in the ns form if you want to create an executable JAR file.
Method 1: Switch to the desired namespace and run the function:
~/expr/demo > lein repl
demo.core=> (in-ns 'demo.core) ; single-quote is required
demo.core=> (-main)
main - enter
Method 2: Just invoke the function with a fully-qualified symbol:
~/expr/demo > lein repl
demo.core=> (demo.core/-main) ; no quote!
main - enter
This will only work with the "main" ns, identified by the expression
:main demo.core
in project.clj.
Method 3: Require the namespace, then invoke the function with a fully-qualified symbol:
> lein repl
demo.core=> (require 'demo.core) ; single-quote is required
demo.core=> (demo.core/-main)
main - enter
This will work for any namespace, even demo.core if the REPL places you in the user namespace.
Method #4: Build an executable uberjar
You must ensure that (:gen-class) is present in the (ns ...) form AND that you have :main demo.core in your project.clj. Then
~/expr/demo > lein uberjar
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT.jar
Created /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
~/expr/demo > java -jar /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
main - enter
If you see an error message like this:
~/expr/demo > java -jar /home/alan/expr/demo/target/uberjar/demo-art-0.1.0-SNAPSHOT-standalone.jar
Error: Could not find or load main class demo.core
Caused by: java.lang.ClassNotFoundException: demo.core
then you forgot to include the (:gen-class) in the ns form.
Look here for more details on gen-class.
Update
If you really want to create a Java class from within Clojure code, you need the function gen-class , not the ns expression. See:
examples
reference guide
API docs
Update #2
Do you really need to generate a Java class from Clojure code? It might be easier to just write a Java class in a *.java source code file. Leiningen is perfectly able to compile mixed Clojure & Java source code into a single executable. This might be the easiest way to go.

LightTable not connecting/evalling a Clojure project

Just starting a new Clojure project and running into some issues getting LightTable to play nicely. The app/REPL spin up fine with lein ring server / lein repl etc, but trying to connect to any file in LightTable just keeps the blue spinner…spinning…even after it says it's connected.
These are my deps (with LightTable 1.5.4 - just updated from 1.4.something which was doing the same thing)
(defproject clollo "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.5"]
[trello "0.1.1-SNAPSHOT"]]
:plugins [[lein-ring "0.8.5"]]
:ring {:handler clollo.handler/app}
:profiles
{:dev {:dependencies [[ring-mock "0.1.5"]]}})
Files in question are
(ns clollo.handler
(:use compojure.core)
(:require [compojure.handler :as handler]
[compojure.route :as route]
[trello.core]))
(defroutes app-routes
(GET "/" [] "Hello Wo1rld")
(route/resources "/")
(route/not-found "Not Found"))
(def app
(handler/site app-routes))
(+ 1 1) ;; test LightTable eval!
I then tried getting rid of all the requires etc for debugging, so I have a new file
(ns clollo.ihopethisworks)
(+ 1 1) ;; this doesn't work either
Any ideas? As I say, LightTable's InstaRepl works fine on its own, and LightTable is connecting to this project's project.clj in the connect window. Just spinning when it tries to eval anything.
Thanks!
From testing your sample code, it looks like the [trello.core] require item, in the handler.clj file, is causing the issue. The library seems to have an issue with initializing clj-http in Light Table.
Also, there is an issue, with LT v0.5.x, where the working indicator, in some cases, keeps spinning after the operation is complete. You can use the Statusbar: Reset working indicator command to stop it.

How to define project.clj for both lein run and lein repl to work?

I'm new to Clojure, and I don't quite understand how to write my project.clj so it works for both lein repl and lein run. Here it is (whole path: ~/my-project/project.clj):
(defproject my-project "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]]
:main my-project.core/hello
)
Then I have my ~/my-project/src/my_project/core.clj file
(ns my-project.core)
(defn hello []
(println "Hello world!")
)
lein run works just fine but I get a FileNotFoundException when running lein repl:
~/my-project$ lein run
Hello world!
~/my-project$ lein repl
REPL started; server listening on localhost port 42144
FileNotFoundException Could not locate hello__init.class or hello.clj on classpath: clojure.lang.RT.load (RT.java:430)
clojure.core=>
How should I edit the project.clj to solve this? Or do I have to call lein repl in a different way?
Thanks in advance.
EDIT: tried with lein deps and lein compile, but still the same error
~/my-project$ lein version
Leiningen 1.7.1 on Java 1.6.0_27 OpenJDK Client VM
~/my-project$ lein deps
Copying 1 file to /home/yasin/Programming/Clojure/my-project/lib
~/my-project$ lein compile
No namespaces to :aot compile listed in project.clj.
~/my-project$ lein repl
REPL started; server listening on localhost port 41945
FileNotFoundException Could not locate hello__init.class or hello.clj on classpath: clojure.lang.RT.load (RT.java:430)
One thing you could do to get it to work would be to change core.clj to:
(ns my-project.core
(:gen-class))
(defn hello []
(println "Hello world!"))
(defn -main []
(hello))
And edit the project.clj to:
(defproject my-project "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]]
:main my-project.core)
The (:gen-class) will tell the compiler to generate a Java class for the namespace, and the :main directive in project.clj will tell lein run to run the main method on the class, which is given by -main. Why lein repl was failing to find my-project.core/hello is unclear to me, but I don't know much about leiningen internals.

command line arguments with leiningen

I have recently started out with clojure, and I am using leiningen to create a small project. I am having troubles in getting leiningen to behave with command line arguments. The following is my src/project/core.clj
(ns project.core
(:gen-class))
(defn -main [& args]
(println (apply str args)))
and my project.clj
(defproject project "1.0.0-SNAPSHOT"
:description "FIXME: write"
:main project.core
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]])
Now when I run lein run arg1 arg2, it gives me this error:
Exception in thread "main" java.lang.Exception: Unable to resolve symbol: arg1 in this context (NO_SOURCE_FILE:1)
at clojure.lang.Compiler.analyze(Compiler.java:5205)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3057)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5371)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.analyze(Compiler.java:5151)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:4670)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4328)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3173)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:5367)
at clojure.lang.Compiler.analyze(Compiler.java:5190)
at clojure.lang.Compiler.eval(Compiler.java:5421)
at clojure.lang.Compiler.eval(Compiler.java:5415)
at clojure.lang.Compiler.eval(Compiler.java:5391)
at clojure.core$eval.invoke(core.clj:2382)
at clojure.main$eval_opt.invoke(main.clj:235)
at clojure.main$initialize.invoke(main.clj:254)
at clojure.main$null_opt.invoke(main.clj:279)
at clojure.main$main.doInvoke(main.clj:354)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.lang.Var.invoke(Var.java:369)
at clojure.lang.AFn.applyToHelper(AFn.java:165)
at clojure.lang.Var.applyTo(Var.java:482)
at clojure.main.main(main.java:37)
Caused by: java.lang.Exception: Unable to resolve symbol: arg1 in this context
at clojure.lang.Compiler.resolveIn(Compiler.java:5677)
at clojure.lang.Compiler.resolve(Compiler.java:5621)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:5584)
at clojure.lang.Compiler.analyze(Compiler.java:5172)
... 23 more
However, if I do a lein uberjar and then do java -jar project-1.0.0-SNAPSHOT-standalone.jar arg1 arg2, I get the correct output.
arg1arg2
It isn't very comfortable to have to create the uberjar to run it every time while development, is there a better way?
This looks like it's caused by a bug that's been fixed in git. The fix will be in 1.4.2, which should be out in a few days. In the mean time, you can use workarounds discussed here: http://groups.google.com/group/clojure/msg/a8160b23a5019a12
From lein-run: "Args will be passed on as *command-line-args*"
So you will have to use those. The example on the site shows how. If you now what arguments you are passing you an also use :run-aliases to specify those in your project.clj. Again, the mentioned site has all the information.
My sample project.clj
(defproject addressbook "1.0.0-SNAPSHOT"
:description "FIXME: write"
:main addressbook.core
:run-aliases {:addressbook [addressbook.core -main "arg1"]}
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]]
:dev-dependencies [[lein-run "1.0.0"]])
And the test code:
(ns addressbook.core
(:gen-class))
(defn -main [& [args]]
(if args (println args)))
Both "lein run addressbook foo" as "lein uberjar" work for me.

Problem using redis-clojure with Leiningen

Hey, I'm new to Clojure and Leiningen and a bit stuck. I've managed to setup a project with Leiningen. I'm able to compile it into an uberjar and run the repl. I've also managed to load a dependency named aleph to run a simple concurrent webserver.
The next step for me is to use redis-clojure to access redis. But here I'm stuck. This is my project.clj:
(defproject alpha "0.0.1-SNAPSHOT"
:description "Just an alpha test script"
:main alpha.core
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]
[aleph, "0.1.2-SNAPSHOT"]
[redis-clojure "1.2.4"]])
And here is my core.clj: Note that I only added the line (:requre redis) according to the example from redis-clojure.
(ns alpha.core
(:require redis)
(:gen-class))
(use `aleph.core 'aleph.http)
(defn alpha [channel request]
(let [] (enqueue-and-close channel
{:status 200
:header {"Content-Type" "text/html"}
:body "Hello Clojure World!"}))
(println (str request)))
(defn -main [& args]
(start-http-server alpha {:port 9292}))
When I try to run lein repl this happens:
java.io.FileNotFoundException: Could not locate redis__init.class or redis.clj on classpath: (core.clj:1)
Yes, I have run lein deps and the redis-clojure jar is available in my lib directory. I'm probably missing something trivial, but I've been at this issue for a few hours now and not getting any closer to a solution. Thanks!
Namespace redis does not exist. I suppose you need
(:require [redis.core :as redis])
A way to check for available namespaces:
(use 'clojure.contrib.find-namespaces)
(filter #(.startsWith (str %) "redis") (find-namespaces-on-classpath))
This works with more current versions of Clojure, in this example it finds the names of all namespaces that contains the sub string "jdbc":
(map str
(filter
#(> (.indexOf (str %) "jdbc") -1)
(all-ns)))
The result is a sequence, in example:
=>
("clojure.java.jdbc")