I'm using Leiningen 2.5.2 (Java 1.8.0_45-internal Open JDK 64-bit), and the reagent-template (i.e. lein new reagent foo).
This runs okay with lein figwheel as expected.
Next, the first thing I do is break out the "Views" functions into separate files and add them to the app namespace:
core.cljs snippet:
;; -------------------------
;; Views
(:require home-page)
home-page.cljs (whole file):
(ns foo.core)
(defn home-page []
[:div [:h2 "Welcome to foo"]
[:div [:a {:href "#/about"} "go to about page"]]])
When I go to view the app in the browser (chromium or firefox), it gets stuck at "ClojureScript has not been compiled!" despite seemingly compiling successfully in the terminal. If I enter commands in the figwheel REPL, I see the green Clojure logo when it is working in the browser, so I know it's connected.
I had this working in a reagent app some months ago--what happened? How should I separate my view code? (A single file is unmanageable; that's a lot of Hiccup.)
If you really have only the line (:require home-page) in core.cljs, this should be the culprit. The colon notation :require is only valid inside a namespace declaration with ns. Also, you declare the core namespace in the wrong file (home-page.cljs, not core.cljs). Take a look at this article on namespaces in Clojure for a thorough explanation.
You will want the following in core.cljs:
(ns foo.core
(:require [foo.home-page :as hp :refer [home-page]]))
.... more core.cljs code ...
and then simply in home-page.cljs:
(ns foo.home-page
(:require ....reagent namespaces as needed ....
(defn home-page [] ....
Related
I have a Java app that has a class at this address, inside a standard Maven layout:
src/main/java/com/ollio/nlp/Transformer.java
The class and method that I want looks like this:
package com.ollio.nlp;
public class Transformer {
public String transform(String JSONInput) {
I store the jar artifact locally in my Clojure app at this address:
maven_repository/local/nlp/1.0-SNAPSHOT/nlp-1.0-SNAPSHOT.jar
I have tried a dozen variations to import it into my Clojure app, such as:
(:import
[com.ollio.nlp.Transformer])
But I keep getting the error "No such namespace".
What is the correct way to import this?
EDITED:
Here is how I currently try to do the import statement:
(ns slick.query
(:import
[nlp.*])
I also tried:
(ns slick.query
(:import
[com.ollio.nlp.*])
I tried a few other variations.
The project.clj file looks like this:
(defproject slick "0.1"
:description "slick is an API for other ollio services, such as our mobile app."
:dependencies [[org.clojure/clojure "1.6.0"]
[com.taoensso/timbre "3.2.1"]
[dire "0.5.1"]
[slingshot "0.10.3"]
[ring "1.4.0-RC1"]
[clj-time "0.6.0"]
[org.clojure/data.json "0.2.5"]
[compojure "1.3.4"]
[com.novemberain/monger "2.0.1"]
[org.clojure/tools.namespace "0.2.4"]
[manifold "0.1.0"]
[me.raynes/fs "1.4.4"]
[org.clojure/core.incubator "0.1.3"]
[clj-stacktrace "0.2.7"]
[overtone/at-at "1.2.0"]
[ring/ring-json "0.3.1"]
[clj-http "1.1.2"]
[org.clojure/core.cache "0.6.4"]
[cheshire "5.5.0"]
[org.clojure/core.match "0.3.0-alpha4"]
[local/nlp "1.0-SNAPSHOT"]]
:repositories {"local" ~(str (.toURI (java.io.File. "maven_repository")))}
:disable-implicit-clean true
:source-paths ["src/clojure"]
:java-source-paths ["src/java"]
:main slick.core
:aot :all
:jvm-opts ["-Xms100m" "-Xmx1000m" "-XX:-UseCompressedOops"])
If you are mixing java and clojure source code in one project, you should first review the lein docs: https://github.com/technomancy/leiningen/blob/master/doc/MIXED_PROJECTS.md
Also, if you posted your project.clj and the layout of your java/clojure sources, it would be easier to spot what is missing.
You probably want to change the last period in your :import statement into a space:
(ns mynamespace
(:import [com.ollio.nlp Transformer]))
(EDIT: You can't use wildcards here. Every class that in com.ollio.nlp must be listed explicitly, separated by spaces.) That will allow you to use Transformer unqualified:
(.transform (Transformer. <add constructor args here>) my-json-input)
As #noisesmith said, the :import statement should be part of an ns declaration.
There's also a good chance that there are problems with the way that the project is set up. You've given no indication that that is likely, but it often happens when one is starting to use Java interop, I believe. (It happened to me, in any event.) So #AlanThompson's advice may be relevant.
You an also simply remove the :import statement, and use the Java class name in fully qualified form, e.g.:
(.transform (com.ollio.nlp.Transformer. <add constructor args here>) my-json-input)
If you get an error when you do that, then there's probably a problem with the setup (unless you're using the class incorrectly).
(I'm not sure how helpful any of this. Alan Thompson's answer is probably the appropriate one.)
I am getting the following error when I try to run my program: Exception in thread "main" java.io.FileNotFoundException: Could not locate apply
/clojure/core/vector__init.class or apply/clojure/core/vector.clj on classpath:
, compiling:(erbium/compile.clj:1:1). It seems to be pointing to file below and suggests that I need to put clojure.core/vector in my dependencies. Is it not included by default?
(ns erbium.compile
(require `[clojure.string :as string])
)
(defn produce-out "Convert 'command %1 %2 %3' with stack [5 6 7] to 'command 5 6 7'" [word stack definitions]
(let [
code (definitions word) ; dictionary/hash lookup. eg. "println" -> "echo $1"
replacement (fn [match] (-> match second Long/parseLong dec stack str))
]
; evaluate arguments. eg. "echo %1"
; stack=["blah"]
; -> "echo blah"
(string/replace code #"%(\d)" replacement)
)
)
(defn parse-word "Verifies that word is in defintitions and then returns (produce-out word stack)" [word stack definitions]
(if (some #{word} (keys definitions))
(produce-out word stack)
)
)
(defn compile "Main compile function" [code]
(let [
split-code (string/split code #"\s")
definitions {
"println" "echo %1"
"+" "%1 + %2"
"-" "%1 - %2"
}
stack []
]
(for [word [split-code]]
(if (integer? (read-string word))
(do
(println "Found integer" word)
(def stack (conj stack (read-string word)))
(println "Adding to argument stack:" stack)
)
; else
(do
(parse-word word stack definitions)
(def stack [])
)
)
)
)
)
This file is loaded by the core file via (load "compile") if that makes a difference.
the first error I see is this:
(require `[clojure.string :as string])
It should be like this:
(:require [clojure.string :as string])
in a regular clojure source file. This fixed it for me.
That said, here comes some general advices:
There are a lot of formatting "mistakes". Of course you can format your code as you would like to, however, it's easier for others to follow if you stick to basic formatting principles. Here is a nice collection of them: https://github.com/bbatsov/clojure-style-guide Most editors implement some formatting tool.
split-code (str/split code #"\s") this won't work as you required [clojure.string :as string] So change it to: split-code (string/split code #"\s")
I am not sure about (load ...) and in which context one uses that generally. However, for starting to learn clojure I recommend Lighttable as it has instant feedback built in which is very valuable when learning something new.
To expand on the answer, the "require" appears in the namespace declaration "ns". ns is actually a macro that expands to a series of statements to create the namespace, and do some setup.
This macro treats statements like (:require ...) as calls to a function with the name require, and automatically quotes any following arguments. Since you had specified a quote yourself:
(ns erbium.compile
(require '[clojure.string :as string]))
Then the result ends up being double-quoted, and the call to require ends up being:
... (require (quote (quote [clojure.string :as string])))
So it ends up trying to load a namespace called "quote" followed by a vector that was syntactically in the wrong place. :)
The ns macro is a standard and convenient way to set up namespaces, but it took me a long time to learn it properly. I found the best way was to copy other people's setup code until I learnt how to do it right.
Incidentally, the use of require instead of :require does not matter, though the standard is to use :require so it does not look like a direct call to that function.
With reference to my previous question,
Executing a lisp function from Java
I was able to call lisp code from Java using ABCL.
But the problem is, the already existing lisp code uses CL-PPCRE package.
I can not compile the code as it says 'CL-PPCRE not found'.
I have tried different approaches to add that package,
including
1) how does one compile a clisp program which uses cl-ppcre?
2)https://groups.google.com/forum/#!topic/cl-ppcre/juSfOhEDa1k
Doesnot work!
Other thing is, that executing (compile-file aima.asd) works perfectly fine although it does also require cl-pprce
(defpackage #:aima-asd
(:use :cl :asdf))
(in-package :aima-asd)
(defsystem aima
:name "aima"
:version "0.1"
:components ((:file "defpackage")
(:file "main" :depends-on ("defpackage")))
:depends-on (:cl-ppcre))
The final java code is
interpreter.eval("(load \"aima/asdf.lisp\")");
interpreter.eval("(compile-file \"aima/aima.asd\")");
interpreter.eval("(compile-file \"aima/defpackage.lisp\")");
interpreter.eval("(in-package :aima)");
interpreter.eval("(load \"aima/aima.lisp\")");
interpreter.eval("(aima-load 'all)");
The error message is
Error loading C:/Users/Administrator.NUIG-1Z7HN12/workspace/aima/probability/domains/edit-nets.lisp at line 376 (offset 16389)
#<THREAD "main" {3A188AF2}>: Debugger invoked on condition of type READER-ERROR
The package "CL-PPCRE" can't be found.
[1] AIMA(1):
Can anyone help me?
You need to load cl-ppcre before you can use it. You can do that by using (asdf:load-system :aima), provided that you put both aima and cl-ppcre into locations that your ASDF searches.
I used QuickLisp to add cl-ppcre (because nothing else worked for me).
Here is what I did
(load \"~/QuickLisp.lisp\")")
(quicklisp-quickstart:install)
(load "~/quicklisp/setup.lisp")
(ql:quickload :cl-ppcre)
First 2 lines are only a one time things. Once quickLisp is installed you can start from line 3.
I'd like to use Clojure code within Java. The Clojure code itself should implement a Java-interface (TestGenClassInterface).
My project.clj is:
(defproject com.stackoverflow.clojure/tests "0.1.0-SNAPSHOT"
:description "Tests of Clojure test-framework."
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]
[instaparse "1.3.4"]]
:source-paths ["src/main/clojure"]
:java-source-paths ["src/main/java"]
:test-paths ["src/test/clojure"]
:java-test-paths ["src/test/java"]
;:aot :all
)
The Java interface looks like this:
package com.stackoverflow.clojure;
public interface TestGenClassInterface {
public String addToString(String text, String appendText);
}
The Clojure code is:
(ns com.stackoverflow.clojure.testGenClass
(:gen-class
:name com.stackoverflow.clojure.TestGenClass
:implements com.stackoverflow.clojure.TestGenClassInterface
:prefix "java-"))
(def ^:private pre "START: ")
(defn java-addToString [this text post]
(str pre text post))
(java-addToString "TexT" " :END")
I expected, that after running lein compile or "Run as Clojure-Application" in eclipse+CounterClockwise a .class file (named TestGenClass.class) is generated an saved within *compile-path* (here: target/classes/com/stackoverflow/clojure/). Unfortunately it's not.
When adding :aot :all to my project.clj, I get the following stacktrace:
Compiling com.stackoverflow.clojure.testGenClass
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(com/stackoverflow/clojure/testGenClass.clj:1:1)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6651)
at clojure.lang.Compiler.analyze(Compiler.java:6445)
at clojure.lang.Compiler.analyze(Compiler.java:6406)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5782)
at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:2191)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6644)
at clojure.lang.Compiler.analyze(Compiler.java:6445)
at clojure.lang.Compiler.analyze(Compiler.java:6406)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5782)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5217)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3846)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6642)
at clojure.lang.Compiler.analyze(Compiler.java:6445)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6632)
at clojure.lang.Compiler.analyze(Compiler.java:6445)
at clojure.lang.Compiler.analyze(Compiler.java:6406)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3665)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6646)
at clojure.lang.Compiler.analyze(Compiler.java:6445)
at clojure.lang.Compiler.analyze(Compiler.java:6406)
at clojure.lang.Compiler.compile1(Compiler.java:7221)
at clojure.lang.Compiler.compile1(Compiler.java:7216)
at clojure.lang.Compiler.compile(Compiler.java:7292)
at clojure.lang.RT.compile(RT.java:398)
at clojure.lang.RT.load(RT.java:438)
at clojure.lang.RT.load(RT.java:411)
at clojure.core$load$fn__5066.invoke(core.clj:5641)
at clojure.core$load.doInvoke(core.clj:5640)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5446)
at clojure.core$compile$fn__5071.invoke(core.clj:5652)
at clojure.core$compile.invoke(core.clj:5651)
at user$eval9.invoke(form-init4595004281107083893.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6703)
at clojure.lang.Compiler.eval(Compiler.java:6693)
at clojure.lang.Compiler.load(Compiler.java:7130)
at clojure.lang.Compiler.loadFile(Compiler.java:7086)
at clojure.main$load_script.invoke(main.clj:274)
at clojure.main$init_opt.invoke(main.clj:279)
at clojure.main$initialize.invoke(main.clj:307)
at clojure.main$null_opt.invoke(main.clj:342)
at clojure.main$main.doInvoke(main.clj:420)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
at clojure.lang.RT.seqFrom(RT.java:505)
at clojure.lang.RT.seq(RT.java:486)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$map$fn__4245.invoke(core.clj:2551)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:484)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$map$fn__4245.invoke(core.clj:2551)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.boundedLength(RT.java:1654)
at clojure.lang.RestFn.applyTo(RestFn.java:130)
at clojure.core$apply.invoke(core.clj:624)
at clojure.core$mapcat.doInvoke(core.clj:2586)
at clojure.lang.RestFn.invoke(RestFn.java:423)
at clojure.core$generate_class.invoke(genclass.clj:164)
at clojure.core$gen_class.doInvoke(genclass.clj:638)
at clojure.lang.RestFn.invoke(RestFn.java:1557)
at clojure.lang.Var.invoke(Var.java:519)
at clojure.lang.AFn.applyToHelper(AFn.java:270)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6552)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6630)
... 46 more
Compilation failed: Subprocess failed
The proximate issue you have is a missing :aot :all in your project.clj file. Without that the ahead-of-time compilation will not be attempted.
Once you put that in, you'll have the following further issues:
you must prefix the names of the functions designated to become Java methods (default prefix is -);
you must include this as an explicit first argument of each such method (this is the recommended convention, but any name is acceptable);
you must correct your :implements clause: it takes a vector as the value, even if it has a single member.
According to http://clojure.org/compilation
... the implementation functions for instance methods will always take an
additional first arg corresponding to the object the method is called
upon, called by convention 'this' here.
Try adding 'this' to each function definition.
(defn java-addToString [this text post]
...
http://clojure.org/compilation mentions the compilation process makes use of
... *compile-path*, which must be in the classpath
The default location is a classes folder.
The second answer to Stack Overflow question Compiling Clojure? mentions that this path is relative to the jvm startup folder. If you did lein repl from the main project folder (the folder where project.clj lives) then creating a classes folder there should make it work.
somewhere in here I'm using java.rmi.server.UID which is upsetting GAE.
After :only'ing my dependencies to the bone I'm at an impasse.
(ns helloworld.core
(:use ;[hiccup.core]
[hiccup.page-helpers :only (html5 include-css)]
[clojure.contrib.string :only (split)]
[compojure.core :only (defroutes GET)]
[hiccup.middleware :only (wrap-base-url)])
(:require [appengine-magic.core :as ae]
[compojure.route :as route
:only (resources not-found) ]
[compojure.handler :as handler :only (site)])
(:gen-class :extends javax.servlet.http.HttpServlet))
(defn index-page
([name]
(html5
[:head
[:title (str "Hello " name)]
(include-css "/css/style.css")]
[:body
[:h1 (str "Hello " name)]]))
([] (index-page "World")))
(def match-opperator
{ "add" +
"subtract" -
"multiply" *
"divide" /})
(defroutes hello-routes
(GET "/:f/*" [f & x]
(index-page (apply (match-opperator f)
(map #(Integer/parseInt %)
(split #" " (:* x))))))
(GET "/" [] (index-page))
(route/resources "/")
(route/not-found "Page not found"))
(def app
(-> (handler/site hello-routes)
(wrap-base-url)))
(ae/def-appengine-app helloworld-app #'app)
I can load it up in jetty and it works fine,
after loading it into the dev-appserver i get this:
HTTP ERROR 500
Problem accessing /multiply/1%202%204%208. Reason:
java.rmi.server.UID is a restricted class. Please see the Google App Engine developer's guide for more details.
Caused by:
java.lang.NoClassDefFoundError: java.rmi.server.UID is a restricted class. Please see the Google App Engine developer's guide for more details.
at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
at org.apache.commons.fileupload.disk.DiskFileItem.(DiskFileItem.java:103)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at ring.middleware.multipart_params$loading__4414__auto__.invoke(multipart_params.clj:1)
at ring.middleware.multipart_params__init.load(Unknown Source)
at ring.middleware.multipart_params__init.(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at clojure.lang.RT.loadClassForName(RT.java:1578)
at clojure.lang.RT.load(RT.java:399)
at clojure.lang.RT.load(RT.java:381)
at clojure.core$load$fn__4519.invoke(core.clj:4915)
ps: here is my project.clj incase this helps:
(defproject helloworld "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.2.1"]
[org.clojure/clojure-contrib "1.2.0"]
[compojure "0.6.2"]
[hiccup "0.3.4"]]
:dev-dependencies [[appengine-magic "0.4.1"]
[swank-clojure "1.2.1"]])
FWIW I don't think :only will make a bit of difference to GAE. It's probably watching what classes you load, and refusing to refer to a function doesn't stop its code from being loaded.
With no domain-specific experience other than looking at the stacktrace, I think the handler that's causing the issue is probably compojure.handler/site, which includes wrap-multipart-params. I doubt you need that feature for your application, so see if you can make do with compojure.handler/api. Then if there are particular wrappers from site that you do need, wrap them in manually.
Then again, given my earlier point about loading classes, I guess the code for multipart-params is getting loaded as soon as you require the compojure.handler namespace, so what you do after that might not matter. I suppose you might even have to just do all the wrapping from api and site manually; it's not very complicated. Then you should be able to avoid ever requireing the multipart-params namespace.