How to get CKEditor react component usable in ClojureScript - clojurescript

I cannot get CKEditor react component to work in ClojureScript project.
When I try to use the component
(ns simple.core
(:require [reagent.core :as reagent]
[re-frame.core :as rf]
[clojure.string :as str]
[simple.routes :as routes]
["ckeditor4-react" :as ck :default CKEditor]
))
...
[:> CKEditor]
I get
template.cljs:312 Uncaught Error: Assert failed: Expected React component in: [:> nil]
(in simple.core.ui)
(or (string? comp) (fn? comp))
at Object.reagent$impl$template$vec_to_elem [as vec_to_elem] (template.cljs:312)
at Object.reagent$impl$template$as_element [as as_element] (template.cljs:329)
at template.cljs:403
at core.cljs:5598
at core.cljs:5598
at Object.cljs$core$IKVReduce$_kv_reduce$arity$3 (core.cljs:5602)
at Object.cljs$core$_kv_reduce [as _kv_reduce] (core.cljs:700)
at Object.cljs$core$reduce_kv [as reduce_kv] (core.cljs:2543)
at reagent$impl$template$make_element (template.cljs:401)
at Object.reagent$impl$template$native_element [as native_element] (template.cljs:285)
I have created a simple github project to illustrate.
https://github.com/madhat2r/shadow-re-frame-simple-example
If you clone it and follow instructions in the readme you can reproduce.
Thanks for your help!

Turns out it was an issue with the :require using :default you have to use
["ckeditor4-react" :as CKEditor]
...
[:> CKEditor {:onBeforeLoad (fn [ck] (set! (.-disableAutoInline ck) true))}]
Shadow-cljs: https://shadow-cljs.github.io/docs/UsersGuide.html#_about_default_exports
Note: the onBeforeLoad function above fixes a bug with current version. Left it for anyone who might come across it. https://github.com/ckeditor/ckeditor4-react/issues/57

Related

Upload a json object to S3 using Clojure?

I am trying to figure out how I would upload a Json object to S3 using clojure through lambda. I am not doing any json type checking and I am just trying to write a handler such that as soon as receives a json object, it uploads that object to the bucket. Here is my code:
(ns clojurehandler.s3_clojure_handler
(:gen-class
:name "clojurehandler.S3ClojureHandler"
:implements [com.amazonaws.services.lambda.runtime.RequestStreamHandler])
(:require [clojure.data.json :as json]
[clojure.string :as s]
[clojure.java.io :as io]
[aws.sdk.s3 :as s3]))
(def cred {:access-key "..", :secret-key ".."})
(defn handle-event [event]
(s3/put-object cred
"bucket-name" "name" event
:content-type "application/json"))
(defn -handleRequest [this is os context]
(handle-event (io/reader is)))
I am using this library: https://github.com/weavejester/clj-aws-s3
which I know has been deprecated but it seems like it should do what I am aiming to do.
I have already set the role of my lambda function to enable putting objects in S3 buckets, but I get a task timed out after 3 seconds error when I try to test it. What am I doing wrong?
Thanks

DOM Control with ClojureScript and Enfocus - problem with lein cjsbuild auto

Hi I'm not an experienced developer and am working my way through this great book Living Clojure: An introduction and training plan for developers by Carin Meier
I'd appreciate your help with an issue I'm stuck on in Chapter 7 (Creating Web Applications with Clojure).
It's walked me through the following sections okay:
Creating a Web Server with Compojure
Using JSON with the Cheshire Library and Ring
Using Clojure in Your Browser with ClojureScript
Browser-Connected REPL
Making HTTP Calls with ClojureScript and cljs-http
But in the section...
DOM Control with ClojureScript and Enfocus...
...I've got to the middle of page 130, ("...Save your edits, and your cljsbuild will recompile your ClojureScript...") but the lein cjsbuild auto, which detects the changes, fails on the attempted compile.
These are the various files I've set up
project.clj
(defproject cheshire-cat "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.10.0"]
[compojure "1.6.1"]
[ring/ring-defaults "0.3.2"]
[ring/ring-json "0.5.0"]
[org.clojure/clojurescript "1.10.597"]
[cljs-http "0.1.46"]
[org.clojure/core.async "0.5.527"]
[enfocus "2.1.1"]]
:plugins [[lein-ring "0.12.5"]
[lein-cljsbuild "1.1.7"]]
:ring {:handler cheshire-cat.handler/app}
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.4.0"]]}}
:cljsbuild {
:builds [{
:source-paths ["src-cljs"]
:compiler {
:output-to "resources/public/main.js"
:optimizations :whitespace
:pretty-print true}}]})
src / cheshire_cat / handler.clj
(ns cheshire-cat.handler
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
[ring.middleware.json :as ring-json]
[ring.util.response :as rr]))
(defroutes app-routes
(GET "/" [] "Hello World")
(GET "/cheshire-cat" []
(rr/response {:name "Cheshire Cat" :status :grinning}))
(route/not-found "Not Found"))
(def app
(-> app-routes
(ring-json/wrap-json-response)
(wrap-defaults site-defaults)))
src-cljs / core.cljs
(ns cheshire-cat.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [clojure.browser.repl :as repl]
[cljs-http.client :as http]
[cljs.core.async :refer [<!]]
[enfocus.core :as ef]))
(defn ^:export init []
(repl/connect "http://localhost:9000/repl")
(go
(let [response (<! (http/get "/cheshire-cat"))
body (:body response)]
(ef/at "#cat-name" (ef/content (:name body)))
(ef/at "#status" (ef/content (:status body))))))
resources / public / cat.html
<!DOCTYPE html>
<html>
<head>
<title>Cheshire Cat</title>
</head>
<body>
<div id="cat-name">Name</div>
<div id="status">Status</div>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript">cheshire_cat.core.init()</script>
</body>
</html>
Terminal
On one tab, I've started a server on port 3000 using lein ring server
Another tab is watching for changes before compiling ClojureScript using lein cljsbuild auto
It's here that I'm running into trouble. I'll paste the verbose response below. I tried adding a dependency of domina into the project.clj file, but that didn't help so I removed it again.
Many thanks in advance.
Garys-MacBook-Pro:cheshire-cat garyhudson$ lein cljsbuild auto
Watching for changes before compiling ClojureScript...
Compiling ["resources/public/main.js"] from ["src-cljs"]...
WARNING: domina is a single segment namespace at line 1 file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina.cljs
WARNING: Protocol DomContent is overwriting function nodes in file file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina.cljs
WARNING: Protocol DomContent is overwriting function single-node in file file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina.cljs
WARNING: *debug* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic *debug* or change the name at line 111 file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina.cljs
WARNING: Use of undeclared Var goog.dom/query at line 15 file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina/css.cljs
WARNING: Use of undeclared Var goog.dom/query at line 19 file:/Users/garyhudson/.m2/repository/domina/domina/1.0.3/domina-1.0.3.jar!/domina/css.cljs
Compiling ["resources/public/main.js"] failed.
clojure.lang.ExceptionInfo: failed compiling file:target/cljsbuild-compiler-0/enfocus/core.cljs
compiler.cljc:1717 cljs.compiler$compile_file$fn__3955.invoke
compiler.cljc:1677 cljs.compiler$compile_file.invokeStatic
compiler.cljc:1653 cljs.compiler$compile_file.invoke
closure.clj:653 cljs.closure/compile-file
closure.clj:631 cljs.closure/compile-file
closure.clj:727 cljs.closure/fn
closure.clj:721 cljs.closure/fn
closure.clj:549 cljs.closure/fn[fn]
closure.clj:700 cljs.closure/compile-from-jar
closure.clj:690 cljs.closure/compile-from-jar
closure.clj:737 cljs.closure/fn
closure.clj:721 cljs.closure/fn
closure.clj:549 cljs.closure/fn[fn]
closure.clj:1088 cljs.closure/compile-sources[fn]
LazySeq.java:42 clojure.lang.LazySeq.sval
LazySeq.java:51 clojure.lang.LazySeq.seq
Cons.java:39 clojure.lang.Cons.next
RT.java:709 clojure.lang.RT.next
core.clj:64 clojure.core/next
core.clj:3142 clojure.core/dorun
core.clj:3148 clojure.core/doall
core.clj:3148 clojure.core/doall
closure.clj:1084 cljs.closure/compile-sources
closure.clj:1073 cljs.closure/compile-sources
closure.clj:3012 cljs.closure/build
closure.clj:2920 cljs.closure/build
api.clj:208 cljs.build.api/build
api.clj:189 cljs.build.api/build
api.clj:195 cljs.build.api/build
api.clj:189 cljs.build.api/build
compiler.clj:61 cljsbuild.compiler/compile-cljs[fn]
compiler.clj:60 cljsbuild.compiler/compile-cljs
compiler.clj:48 cljsbuild.compiler/compile-cljs
compiler.clj:168 cljsbuild.compiler/run-compiler
compiler.clj:129 cljsbuild.compiler/run-compiler
form-init2776313306215562422.clj:1 user/eval838[fn]
form-init2776313306215562422.clj:1 user/eval838[fn]
LazySeq.java:42 clojure.lang.LazySeq.sval
LazySeq.java:51 clojure.lang.LazySeq.seq
RT.java:531 clojure.lang.RT.seq
core.clj:137 clojure.core/seq
core.clj:3133 clojure.core/dorun
core.clj:3148 clojure.core/doall
core.clj:3148 clojure.core/doall
form-init2776313306215562422.clj:1 user/eval838
form-init2776313306215562422.clj:1 user/eval838
Compiler.java:7176 clojure.lang.Compiler.eval
Compiler.java:7166 clojure.lang.Compiler.eval
Compiler.java:7635 clojure.lang.Compiler.load
Compiler.java:7573 clojure.lang.Compiler.loadFile
main.clj:452 clojure.main/load-script
main.clj:454 clojure.main/init-opt
main.clj:454 clojure.main/init-opt
main.clj:485 clojure.main/initialize
main.clj:519 clojure.main/null-opt
main.clj:516 clojure.main/null-opt
main.clj:598 clojure.main/main
main.clj:561 clojure.main/main
RestFn.java:137 clojure.lang.RestFn.applyTo
Var.java:705 clojure.lang.Var.applyTo
main.java:37 clojure.main.main
Caused by: clojure.lang.Compiler$CompilerException: Syntax error macroexpanding clojure.core/ns at (enfocus/macros.clj:1:1).
Compiler.java:6971 clojure.lang.Compiler.checkSpecs
Compiler.java:6987 clojure.lang.Compiler.macroexpand1
Compiler.java:7074 clojure.lang.Compiler.macroexpand
Compiler.java:7160 clojure.lang.Compiler.eval
Compiler.java:7635 clojure.lang.Compiler.load
RT.java:381 clojure.lang.RT.loadResourceScript
RT.java:372 clojure.lang.RT.loadResourceScript
RT.java:463 clojure.lang.RT.load
RT.java:428 clojure.lang.RT.load
core.clj:6126 clojure.core/load[fn]
core.clj:6125 clojure.core/load
core.clj:6109 clojure.core/load
RestFn.java:408 clojure.lang.RestFn.invoke
core.clj:5908 clojure.core/load-one
core.clj:5903 clojure.core/load-one
core.clj:5948 clojure.core/load-lib[fn]
core.clj:5947 clojure.core/load-lib
core.clj:5928 clojure.core/load-lib
RestFn.java:142 clojure.lang.RestFn.applyTo
core.clj:667 clojure.core/apply
core.clj:5985 clojure.core/load-libs
core.clj:5969 clojure.core/load-libs
RestFn.java:137 clojure.lang.RestFn.applyTo
core.clj:667 clojure.core/apply
core.clj:6007 clojure.core/require
core.clj:6007 clojure.core/require
RestFn.java:408 clojure.lang.RestFn.invoke
analyzer.cljc:4106 cljs.analyzer$ns_side_effects$fn__2653.invoke
analyzer.cljc:4105 cljs.analyzer$ns_side_effects.invokeStatic
analyzer.cljc:4077 cljs.analyzer$ns_side_effects.invoke
analyzer.cljc:4201 cljs.analyzer$analyze_STAR_$fn__2706.invoke
PersistentVector.java:343 clojure.lang.PersistentVector.reduce
core.clj:6827 clojure.core/reduce
core.clj:6810 clojure.core/reduce
analyzer.cljc:4201 cljs.analyzer$analyze_STAR_.invokeStatic
analyzer.cljc:4191 cljs.analyzer$analyze_STAR_.invoke
analyzer.cljc:4220 cljs.analyzer$analyze.invokeStatic
analyzer.cljc:4203 cljs.analyzer$analyze.invoke
compiler.cljc:1535 cljs.compiler$emit_source.invokeStatic
compiler.cljc:1508 cljs.compiler$emit_source.invoke
compiler.cljc:1620 cljs.compiler$compile_file_STAR_$fn__3924.invoke
compiler.cljc:1428 cljs.compiler$with_core_cljs.invokeStatic
compiler.cljc:1417 cljs.compiler$with_core_cljs.invoke
compiler.cljc:1604 cljs.compiler$compile_file_STAR_.invokeStatic
compiler.cljc:1597 cljs.compiler$compile_file_STAR_.invoke
compiler.cljc:1702 cljs.compiler$compile_file$fn__3955.invoke
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec.
alpha.clj:705 clojure.spec.alpha/macroexpand-check
alpha.clj:697 clojure.spec.alpha/macroexpand-check
AFn.java:156 clojure.lang.AFn.applyToHelper
AFn.java:144 clojure.lang.AFn.applyTo
Var.java:705 clojure.lang.Var.applyTo
Compiler.java:6969 clojure.lang.Compiler.checkSpecs
Here is the clue you need:
Caused by: clojure.lang.Compiler$CompilerException: Syntax error
macroexpanding clojure.core/ns at (enfocus/macros.clj:1:1).
....
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/ns did
not conform to spec.
So it is a problem in the enfocus library, not your code.
IMHO the enfocus library is a bit old these days. I think you would be better served using the figwheel.main lib for the CLJS code: https://figwheel.org/
Note that this is basically Figwheel 2.0, but under a new name. It is much better than the original Figwheel.
Go through the tutorial at figwheel.org, and build your CLJS code in a separate project directory from your back-end code.
Also, be sure to use the Clojure CLI/Deps to build the CLJS project (i.e. not Leiningen). CLI/Deps is much easier to use for ClojureScript projects than Leiningen.
https://clojure.org/guides/deps_and_cli
https://clojure.org/reference/deps_and_cli

ClojureScript - Ajax - Retrieving json

I have my dependencies
(ns test.core
(:require [reagent.core :as reagent :refer [atom]]
[ajax.core :refer [GET]]))
I then have my handler that handles the responses to my ajax call
(defn handle-response [response]
(println (type response))
(println film))
THe data from my call is JSON, in the browser it looks like this:
{"id":3,"name":"Sicario","star":"Emily Blunt","rating":5}
When I run the above Clojure, I see this:
cljs.core/PersistentArrayMap core.cljs:192:23
{id 3, name Sicario, star Emily Blunt, rating 5}
Is their a way for me to go from the PersistArrayMap, and destructure it into id, name, star and rating?
I tried
(let [film (js->clj (.parse js/JSON response) :keywordize-keys true)]
...)
Hoping I would get a map named film, but no avail!
I think maybe I could use (get-in), but can't get the syntax right for this either.
Thanks,
Got it,
I was missing :response-format :json and :keywords? true from my GET call
Now, it looks like this:
(GET (str "https://localhost:5001/api/films/" film-name)
{:response-format :json
:keywords? true
And it all works.

NPM Package fails to compile in Clojurescript

I am trying to use uber/react-map-gl with Clojurescript. I have added it the project.clj-
:npm-deps {"#cljs-oss/module-deps" "^1.1.1"
:react-map-gl "^3.3.0-alpha.5"
:react "^16.4.1"}
and npm installed it separately. When I require it from my ns -
(:require [react-map-gl :as M
:refer [BaseControl TRANSITION_EVENTS
FlyToInterpolator]])
(js/console.log M)
(js/console.log BaseControl)
(js/console.log M/BaseControl) ;;-> also fails
M is correctly logged to the console, but drilling into the attributes (such as BaseControl) throws an Exception -
Exception: ReferenceError:
...$node_modules$react_map_gl$dist$esm$components$base_control is not defined at Object.get BaseControl [as BaseControl] (http://localhost:3449/js/compiled/out/node_modules/react-map-gl/dist/esm/index.js:5:19) at Object.remoteFunction (<anonymous>:2:14)]
A handful of the attributes are accessible (e.g. no exceptions), such as TRANSITION_EVENTS and FlyToInterpolator.
I see this bug (which may or may not be relevant), but I am not sure how to proceed or even troubleshoot. Here's a minimal failing example to repro. Any help would be appreciated.
This is not an answer (really just a comment).
I was able to ostensibly get farther by first manually requiring all of the components and overlays (which points to some deps mismanagement of some sort).
$ clj -m cljs.main -co compile-opts.edn -r
ClojureScript 1.10.339
cljs.user=> (require '"react-map-gl/dist/esm/components/popup"
'"react-map-gl/dist/esm/components/navigation-control"
'"react-map-gl/dist/esm/components/interactive-map"
'"react-map-gl/dist/esm/components/base-control"
'"react-map-gl/dist/esm/components/static-map"
'"react-map-gl/dist/esm/components/marker"
'"react-map-gl/dist/esm/overlays/svg-overlay"
'"react-map-gl/dist/esm/overlays/html-overlay"
'"react-map-gl/dist/esm/overlays/canvas-overlay"
'[react-map-gl :as M
:refer [BaseControl TRANSITION_EVENTS
FlyToInterpolator]])
cljs.user=> M
#js {:default nil, :InteractiveMap nil, :StaticMap nil, :BaseControl nil, :Marker nil, :Popup nil, :NavigationControl nil, :CanvasOverlay nil, :HTMLOverlay nil, :SVGOverlay nil, :TRANSITION_EVENTS #js {:BREAK 1, :SNAP_TO_END 2, :IGNORE 3}, :TransitionInterpolator #object[TransitionInterpolator], :LinearInterpolator #object[LinearInterpolator], :FlyToInterpolator #object[ViewportFlyToInterpolator], :experimental #js {:MapControls #object[MapControls]}}
You can see that, while this avoids the ...$node_modules$react_map_gl$dist$esm$components$base_control is not defined error, it is not really a solution as things like :BaseControl end up being nil.
You can get a sense of the dependencies at play by making a revision to the compiler to log the results of calls to cljs.closure/index-node-modules.
My deps.edn:
{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}}}
and compile-opts.edn:
{:npm-deps {:react-map-gl "^3.3.0-alpha.5"
:react "^16.4.1"}
:install-deps true
:output-dir "out"}

troubleshooting clojure web-app: connecting html and css for heroku deployment

I have two files, one html and one css. I have tried to turn them into a heroku app and even used the lein command to create a heroku friendly skeleton and plug these two files in, but cannot get it to work for the life of me. There is something very basic that I don't yet understand about how to coordinate a view with the back-end control. And the hello world tutorials aren't helping me because they do not show me how to do different things or explain what needs to change in my defroutes function, for example, for that to be accomplished. In short, my question is this: How can I coordinate these two files into a Clojure project to make the html render as the front page of a webapp and then deploy it on heroku?
html:
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<img id="sun" src="http://goo.gl/dEEssP">
<div id='earth-orbit'>
<img id="earth" src="http://goo.gl/o3YWu9">
</div>
</body>
</html>
web.clj file in "lein new heroku ..." project:
(ns solar_system.web
(:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
[compojure.handler :refer [site]]
[compojure.route :as route]
[clojure.java.io :as io]
[ring.middleware.stacktrace :as trace]
[ring.middleware.session :as session]
[ring.middleware.session.cookie :as cookie]
[ring.adapter.jetty :as jetty]
[ring.middleware.basic-authentication :as basic]
[cemerick.drawbridge :as drawbridge]
[environ.core :refer [env]]))
(defn- authenticated? [user pass]
;; TODO: heroku config:add REPL_USER=[...] REPL_PASSWORD=[...]
(= [user pass] [(env :repl-user false) (env :repl-password false)]))
(def ^:private drawbridge
(-> (drawbridge/ring-handler)
(session/wrap-session)
(basic/wrap-basic-authentication authenticated?)))
(defroutes app
(ANY "/repl" {:as req}
(drawbridge req))
(GET "/" []
{:status 200
:headers {"Content-Type" "text/plain"}
:body (pr-str ["Hello" :from 'Heroku])}) ; <= Should I change this part here?
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
(defn wrap-error-page [handler]
(fn [req]
(try (handler req)
(catch Exception e
{:status 500
:headers {"Content-Type" "text/html"}
:body (slurp (io/resource "500.html"))}))))
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))
;; TODO: heroku config:add SESSION_SECRET=$RANDOM_16_CHARS
store (cookie/cookie-store {:key (env :session-secret)})]
(jetty/run-jetty (-> #'app
((if (env :production)
wrap-error-page
trace/wrap-stacktrace))
(site {:session {:store store}}))
{:port port :join? false})))
;; For interactive development:
;; (.stop server)
;; (def server (-main))
project.clj file
(defproject solar_system "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://solar_system.herokuapp.com"
:license {:name "FIXME: choose"
:url "http://example.com/FIXME"}
:dependencies [[org.clojure/clojure "1.4.0"]
[compojure "1.1.1"]
[ring/ring-jetty-adapter "1.1.0"]
[ring/ring-devel "1.1.0"]
[ring-basic-authentication "1.0.1"]
[environ "0.2.1"]
[com.cemerick/drawbridge "0.0.6"]]
:min-lein-version "2.0.0"
:plugins [[environ/environ.lein "0.2.1"]]
:hooks [environ.leiningen.hooks]
:profiles {:production {:env {:production true}}})
example of typical handler code that renders text:
(ns hello-world.core
(:use ring.adapter.jetty))
(defn app [req]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello, world"}) ; <= Could I just change this part to slurp in
; the html file and stick it in a file in my
; root directory to get a successful 'git push heroku master'?
Modifying your code:
(defroutes app
(ANY "/repl" {:as req}
(drawbridge req))
(GET "/" []
{:status 200
:headers {"Content-Type" "text/html"} ; change content type
:body (slurp "resources/public/my-file.html")}) ; wherever your file is
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
How I'd write it:
(defroutes app
(ANY "/repl" {:as req} (drawbridge req))
(GET "/" [] (slurp "resources/public/my-file.html")) ; wherever your file is
(route/resources "/") ; special route for serving static files like css
; default root directory is resources/public/
(route/not-found (slurp (io/resource "404.html")))) ; IDK what io/resource does
; you might not need it