Recently started playing with Clojurescript in Spacemacs(emacs 26.1). I am using figwheel, I usually make a change in the *.cljs file and then test the results in the function in the REPL. I recently watched a video where someone evaluated the function in the file without having to go to the REPL.
I tried to do the same using cider in spacemacs, The problem is it evaluates but does not display the results.
Below snippet is from config in my project.clj
:profiles {:dev {:source-paths ["src" "env/dev/clj"]
:dependencies [[binaryage/devtools "0.9.10"]
[figwheel-sidecar "0.5.16"]
[nrepl "0.4.4"]
[cider/piggieback "0.3.9"]]}})
Snippet from cljs file.
(defn tes []
(inc 1234))
(prn (tes))
(tes)
I tried evaluating (tes) and (prn (tes)), it evaluates but does not print anything in the echo area.
I have cider setup in spacemacs and cider REPL running during the execution. Executing the function in cider REPL by navigating to the namespace works.
Is there anything I am missing here in order to get it working in terms of setup.
You probaly need cider-nrepl.Put the following in your ~/.lein/profiles.clj file.
{:repl {:plugins [[cider/cider-nrepl "0.18.0"]]}}
Related
I am trying to use AWS Amplify Authentication lib in a re-frame app.
The lib provides a higher order component withAuthenticator which is supposed to wrap the main view of your app. I am trying to use reactify-component and adapt-react-class but unfortunately I get the following error:
Uncaught TypeError: Failed to construct ‘HTMLElement’: Please use the ‘new’ operator, this DOM object constructor cannot be called as a function.
(defn main-panel []
[:div
[:h1 "Hello" ]])
(def root-view
(reagent/adapt-react-class
(withAuthenticator
(reagent/reactify-component main-panel))))
(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(aws-config/configure)
(re-frame/dispatch-sync [::events/initialize-db])
(reagent/render [root-view]
(.getElementById js/document "app")))
Any help is appreciated
I had this issue with reagent + amplify.
Solved it with 2 changes, but I'm unsure if both are needed
#1 Change output of the google closure compiler to es6 (or higher). Amplify seems to use es6 features that Cannot be polyfilled.
This is for shadow-cljs (shadow-cljs.edn), but this should be possible for other build systems as well.
{:builds {:app {:compiler-options {:output-feature-set :es6}}}}
Disclaimer: I switched to shadow-cljs from lein-cljsbuild since I could not get lein-cljsbuild to respect the configuration for es6 output.
#2 Use functional components.
In reagent 1.0.0-alpha1 and up you can change the compiler to produce functional components by default.
(ns core
(:require
[reagent.core :as r]))
(def functional-compiler (r/create-compiler {:function-components true}))
(r/set-default-compiler! functional-compiler)
There are other ways to make functional components. Check the documentation if you don't like, or can't use, this approach.
I have a library of functions which I want to let users play with in the browser.
So I want to set up a situation like this :
I'm developing with figwheel and devcards.
In the main core.cljs I require various functions from my library, so they're all in scope.
Now I want to let the user enter some code which calls that library.
I see how I can run that code with eval, but I can't see how to make my library functions visible to the code being evaled.
And I'm confused by most of the documentation I'm seeing about this (eg. How can I make functions available to ClojureScript's eval?)
Is it possible? And if so, does anyone have a simple example of it being done?
cheers
Phil
Yes, it is possible to provide access to an ambient / pre-compiled library used by evaluated code.
First, you must ensure that the functions in your library are available in the JavaScript runtime. In other words, avoid :advanced optimization, as this will eliminate functions not called at compile time (DCE). Self-hosted ClojureScript is compatible with :simple.
Second, you need to make the analysis metadata available to the self-hosted compiler that will be running in the browser (either making use of cljs.js/load-analysis-cache! or an optional argument to cljs.js/empty-state).
A minimal project illustrating how to do this is below (and also at https://github.com/mfikes/ambient):
Project Code
src/main/core.cljs:
(ns main.core
(:require-macros [main.core :refer [analyzer-state]])
(:require [cljs.js]
[library.core]))
(def state (cljs.js/empty-state))
(defn evaluate [source cb]
(cljs.js/eval-str state source nil {:eval cljs.js/js-eval :context :expr} cb))
(defn load-library-analysis-cache! []
(cljs.js/load-analysis-cache! state 'library.core (analyzer-state 'library.core))
nil)
src/main.core.clj:
(ns main.core
(:require [cljs.env :as env]))
(defmacro analyzer-state [[_ ns-sym]]
`'~(get-in #env/*compiler* [:cljs.analyzer/namespaces ns-sym]))
src/library/core.cljs:
(ns library.core)
(defn my-inc [x]
(inc x))
Usage
We have a main.core namespace which provides an evaluate function, and this example will show how to call functions in an ambient / pre-compiled library.core namespace.
First, start up a browser REPL via
clj -m cljs.main
At the REPL, load our main namespace by evaluating
(require 'main.core)
Test that we can evaluate some code:
(main.core/evaluate "(+ 2 3)" prn)
This should print
{:ns cljs.user, :value 5}
Now, since main.core required library.core, we can call functions in that namespace. Evaluating this at the REPL will yield 11:
(library.core/my-inc 10)
Now, let's try to use this "ambient" function from self-hosted ClojureScript:
(main.core/evaluate "(library.core/my-inc 10)" prn)
You will see the following
WARNING: No such namespace: library.core, could not locate library/core.cljs, library/core.cljc, or JavaScript source providing "library.core" at line 1
WARNING: Use of undeclared Var library.core/my-inc at line 1
{:ns cljs.user, :value 11}
In short, what is going on is that even though library.core.my_inc is available in the JavaScript environment, and can indeed be called, producing the correct answer, you get warnings from the self-hosted compiler that it knows nothing about this namespace.
This is because the compiler analysis metadata is not in the main.core/state atom. (The self-hosted compiler has its own analysis state, held in that atom in the JavaScript environment, which is separate from the JVM compiler analysis state, held via Clojure in the Java environment.)
Note: If we instead had the source for library.core compiled by the self-hosted compiler (by perhaps by using main.core/evaluate to eval "(require 'library.core)", along with properly defining a cljs.js/*load-fn* that could retrieve this source, things would be good, and the compiler analysis metadata would be in main.core/state. But this example is about calling ambient / pre-compiled functions in library.core.
We can fix this by making use of cljs.js/load-analysis-cache! to load the analysis cache associated with the library.core namespace.
This example code embeds this analysis cache directly in the code by employing a macro that snatches the analysis cache from the JVM-based compiler. You can transport this analysis cache to the browser by any mechanism you desire; this just illustrates one way of simply embedding it directly in the shipping code (it's just data).
Go ahead and evaluate the following, just to see what the analysis cache for that namespace looks like:
(main.core/analyzer-state 'library.core)
If you call
(main.core/load-library-analysis-cache!)
this analysis cache will be loaded for use by the self-hosted compiler.
Now if you evaluate
(main.core/evaluate "(library.core/my-inc 10)" prn)
you won't see any warnings and this will be printed:
{:ns cljs.user, :value 11}
Furthermore, since the self-hosted compiler now has the analysis metadata for libraray.core, it can properly warn on arity errors, for example
(main.core/evaluate "(library.core/my-inc 10 12)" prn)
will cause this to be printed:
WARNING: Wrong number of args (2) passed to library.core/my-inc at line 1
The above illustrates what happens when you don't have the analyzer cache present for a namespace and how to fix it using cljs.js/load-analysis-cache!. If you know that you will always want to load the cache upon startup, you can simply things, making use of an optional argument to cljs.js/empty-state to load this cache at initialization time:
(defn init-state [state]
(assoc-in state [:cljs.analyzer/namespaces 'library.core]
(analyzer-state 'library.core)))
(def state (cljs.js/empty-state init-state))
Other Projects
A few (more elaborate) projects that make library functions available to self-hosted ClojureScript in the browser:
Klangmeister
power-turtle
life-demo
Perhaps this is bug in Clojure... my question is...
Why when I create a new JLabel in the code below do I get a ClassNotFoundException while creating the JavaFX Stage but when I don't create the JLabel then the JavaFX Stage is created (and the class is found) just fine?
To reproduce this create a new leiningen project with lein new app class-path-fail and replace core.clj with this code:
(ns class-path-fail.core
(:gen-class)
(:import (javafx.stage Stage)
(javafx.application Platform)
(javax.swing JLabel SwingUtilities)
(javafx.embed.swing JFXPanel)))
(JFXPanel.)
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(defn simple-fn-will-fail []
(let [form `(fn [] (new Stage))]
(eval form)))
(Platform/runLater #(simple-fn-will-fail))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
You can verify this doesn't work by running it... but then... comment out the creation of the JLabel and observe it works!!
I'm using Clojure 1.8.0.
Here's my project.clj:
(defproject class-path-fail "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.8.0"]]
:main ^:skip-aot class-path-fail.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
I discovered that if the first two lines of code are reversed then Stage is found on the classpath and loaded. The new first two lines would look like:
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(JFXPanel.)
I have run into similar issues (albeit in a different context), and I suspect it is due to the class loader configuration of the JavaFX application thread, not necessarily anything specific to Clojure. See for example this similar question: API works with Java application but not JavaFX and: https://community.oracle.com/thread/2564617?start=0&tstart=0
That said, why are you using so many levels of indirection, and so many different mechanisms for scheduling things to happen in the future? The eval function is rarely needed in normal Clojure code.
The line (Platform/runLater #(simple-fn-will-fail)) can be simplified: there is no need for the anonymous function, because simple-fn-will-fail is already a function taking no arguments. So this line is completely equivalent (and fails in exactly the same way): (Platform/runLater simple-fn-will-fail)
Also, simple-fn-will-fail does not actually try to create a new Stage, it just returns a function that will do that (and that function is discarded, never invoked), so if you were not invoking the compiler by calling eval, this problem would not occur.
If we can step back and get a better understanding of what you are actually trying to accomplish, there is probably a way to work around the issues with the class loader on the JavaFX application thread.
Anyway, those are some ideas. Perhaps we will hear more from someone with a deeper understanding of JavaFX and Clojure compilation.
I tinkered a bit more, after making my comment below, and found that even if I pulled the side-effecting forms inside -main, the same exception would be thrown trying to eval the form inside simple-fn-will-fail. So the issue was pointed all the more strongly at the JavaFX class loader.
I was able to get everything to run fine, however, by making sure that compilation occurs on the normal Clojure thread, and then running the resulting function (which, I think, is probably what you wanted to do anyway) using Platform/runLater. Here is my tweaked version:
(ns class-path-fail.core
(:gen-class)
(:import (javafx.stage Stage)
(javafx.application Platform)
(javax.swing JLabel SwingUtilities)
(javafx.embed.swing JFXPanel)))
(defn simple-fn-will-fail []
(let [form `(fn [] (new Stage))]
(eval form)))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(JFXPanel.)
(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(let [stage-creator (simple-fn-will-fail)]
(Platform/runLater stage-creator))
(println "Hello, World!"))
With this version, I can invoke (-main) with no problem.
I am trying to use figwheel in my ClojureScript build.
It works with lein cljsbuild auto already, but I have to put :optimisations :whitespace.
Otherwise I get a message in the browser :
Uncaught ReferenceError: goog is not defined
However figwheel require :optimisations :none to run. Here is the part of my leiningen file :
:cljsbuild {
:builds
[{:id "dev"
:source-paths ["src/cljs"]
:figwheel { :websocket-host "localhost"
;;:on-jsload "example.core/fig-reload"
:autoload true
:heads-up-display true
:load-warninged-code true
;;:url-rewriter "example.core/fig-url-rewrite"
}
:compiler {;; :main
:output-to "resources/public/js/gdb/gdb.js"
:output-dir "resources/public/js/gdb/cljsbuild-dev"
;;:asset-path "js/out"
:optimizations :none
:source-map "resources/public/js/gdb/gdb.js.map"
:pretty-print true}}]}
What is missing for me to get the missing dependencies ?
It turns out this is a classic case of RTFM.
The answer was in the ClojureScript quickstart guide.
Specifically, I had to add a :main field, as specified in the Less Boilerplate section :
:main "example.core"
Nothing jumps out as being obviously wrong or missing. However, lein is pretty powerful in the degree it lets you set things to fit your personal taste/workflow, so it is hard to spot things if the approach is signficantly different.
When I run into these types of problems, I find using the standard templates provided by many libraries or projects really useful. My recommendation would be to run
lein new figwheel ft -- --reagent
This will setup a basic project called ft (in this case also with reagent - there is another option for om or you could leave all of this out for a bare bones default. See the figwheel repo on github for more details. This will provide a good working lein figwheel setup which hyou can use as a guide.
I'm experimenting with Clojure and Leiningen.
I was successful in executing to following line in the REPL:
(print (:body (client/get "https://coinbase.com/api/v1/prices/spot_rate?currency=CAD" {:as :json}))
I created a project with lein new http. When I run the following lines witn lein run then the coercion to JSON doesn't work. It simply prints a correct JSON string.
(ns http.core
(:require [clj-http.client :as client])
(:use clojure.pprint))
(defn -main
[]
(print
(:body
(client/get "https://coinbase.com/api/v1/prices/spot_rate?currency=CAD" {:as :json}))
the output of the script is
{"amount":"306.89","currency":"CAD"}
Any idea what's wrong?
As it turned out there was a breaking change with clj-http version 2.0.0.
Now one has to list explicitly the optional dependencies in project.clj.
After I added
[cheshire "5.5.0"]
to my list of dependencies the program worked as expected.
Please see the documentation for the change here.
I don't know exactly what changed, but [clj-http "1.1.2"] has the behavior you want.