Fork me on GitHub
#clojurescript
<
2016-06-10
>
kendall.buchanan02:06:12

Anyone have suggestions for taking a Clojurescript library, compiling it down to a JS file, and then requiring it within JS project? I’ve followed mori’s example (https://github.com/swannodette/mori/blob/master/src/mori/macros.clj) for exporting functions.

kendall.buchanan02:06:30

Problem is this: once I use Mori’s toClj function and pass, say, an object, to an exported function in the Clojurescript library, I get errors like the following:

kendall.buchanan02:06:44

Error: {:example "stuff"} is not ISeqable

kendall.buchanan02:06:07

My library appears to not understand the type of data being submitted to it.

atroche05:06:15

is there a way of getting my project to depend on clojurescript master, or at a particular git SHA? I want access to those new cljs.spec predicates 😃 https://github.com/clojure/clojurescript/commit/73ab8ff8f4610a6f11cf64cc09e7173dcada5dc0

danielcompton07:06:13

@atroche: probably easiest to install from master

danielcompton07:06:33

There's instructions for running local maven install

martinklepsch09:06:48

@kendall.buchanan: Datascript also provides a JS artifact, maybe you can look for inspiration there: https://github.com/tonsky/datascript

Yehonathan Sharvit11:06:20

is there a way with leiningen to transpile from the same codebase two different js files with two different entry points?

kendall.buchanan11:06:12

@martinklepsch: That’s a great idea. I’ll check there. Thanks.

curlyfry12:06:20

Does anyone here have any experience with d3 in clojurescript? I'm looking at the different ways to do it and wondering if anyone has found one they think works well.

curlyfry12:06:25

For example, using the library "directly" http://zachcp.org/blog/2015/reagent-d3/ or using a clojurescript interop library like https://github.com/dribnet/strokes

Yehonathan Sharvit12:06:23

@rohit: my problem is that i cannot separate the builds in separate sets of src-paths. Id like to specify different entry points. Makes sense?

rohit12:06:17

@viebel: maybe try the :modules feature of the compiler in that case https://github.com/clojure/clojurescript/wiki/Compiler-Options#modules

dnolen12:06:39

@viebel: there’s not a good way to do that mostly due to an artifact of history

dnolen12:06:22

we compile everything in a source path - I’ve been thinking we should maybe add a compiler flag to disable the standard behavior

dnolen12:06:50

in the mean time you can put your entry points in different source directories to achieve the desired result

dnolen12:06:18

(i.e. only have the entry point by itself on some source path and include the shared stuff on different one)

dnolen13:06:29

@rauh yes missing things should be added - feel free to update - thought it should be made clear that’s only applicable to cljs.build.api/watch

rauh13:06:25

I keep getting :

Applying optimizations :advanced to 175 sources
java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.javaUpperCase()Lcom/google/common/base/CharMatcher;
Anybody seen this before?

dnolen13:06:32

@rauh seems like you need to check your dependencies - you likely have a conflict

dnolen13:06:43

Closure Compiler depends on quite a few other libs

rauh13:06:54

Ok, thanks. I'll dig.

dnolen13:06:02

lein deps :tree

kendall.buchanan14:06:20

@martinklepsch: Looks like Datascript uses the ^:export tag. I somehow missed that in JS interop. Results are still the same, though:

kendall.buchanan14:06:43

Error: No protocol method IMap.-dissoc defined for type object: [object Object ] upon passing an object into the function.

anmonteiro14:06:14

@kendall.buchanan: dissoc doesn’t work in JS objects, only in CLJS data structures

anmonteiro14:06:28

that seems to be your problem

anmonteiro14:06:53

just adding the ^:export metadata won’t make your functions work with JS objects by itself 🙂

kendall.buchanan14:06:11

@anmonteiro: Right, but I assume mori ought to be able to correct for that, right?

kendall.buchanan14:06:25

Error: No protocol method IMap.-dissoc defined for type object: {:my “map"}

kendall.buchanan14:06:41

Using mori.toClj before passing the data in.

anmonteiro14:06:58

Ah, right. I missed that part

kendall.buchanan14:06:15

Hehe, thanks anyway 🙂

kendall.buchanan14:06:02

Interesting, it appears to work if I leverage js->clj on the way in, and clj->js on the way out within the library directly. Huh.

kendall.buchanan14:06:20

Okay, debrief: I simply created my own toClj and toJs functions within my own library. Works. No idea why the identical code from Mori doesn’t produce objects with the same types.

pauldelany14:06:42

Hi all - if anyone is familiar with re-frame, I've posted a question here: http://stackoverflow.com/questions/37751108/re-frame-subscription-not-working-as-expected - any advice appreciated...

kendall.buchanan14:06:48

@pauldelany: There’s a #C073DKH9P channel in this very Slack team.

kendall.buchanan14:06:33

Yeah, the author’s super active too.

attentive15:06:18

@curlyfry Yeah, I did some D3 stuff a while back, I used https://github.com/dribnet/strokes.

attentive15:06:36

Here's a gist . To be honest it wasn't anything spectacular, just used the JS port of COLA with D3 forcelayout https://gist.github.com/attentive/0054a12c280a063ea3fa3e0db7c86917

exupero15:06:50

@curlyfry: Strokes worked fine for me, but I still ended up with fairly procedural (D3-like) code. I’ve taken generating SVG more directly, sometimes using D3’s auxiliary libs for things like mapping and color operations.

attentive15:06:26

Yep, I'd agree with that. Using strokes my code (as above) was just a port of the usual idioms of D3, and quite verbose. If there's a better way to wrap it, I'd love to know about it.

exupero15:06:25

D3’s utility functions work well in Cljs, but its enter/exit/update logic is less useful. If you’re already generating DOM via a pure function of state, you probably don’t even need it.

ag16:06:53

hey all, has anyone used CLJS without ties to Clojure? Complete nodejs Clojurescript app with back-end (api layer). I am thinking maybe it’s worth trying to build something like that. Aside obvious benefits, what are the drawbacks? I won’t be able to use Java interop (fine), would it be too difficult to get Figwheel running, some other pitfalls?

ag16:06:17

can anyone point to great examples of real stuff/prototypes on Github?

dnolen16:06:39

@ag it's probably possible but I suspect you will run into many hurdles going down that path

dnolen16:06:43

it’s not a popular option

ag16:06:24

@dnolen: I wonder why? Node is extremely popular. And I am totally convinced that as Clojure is better Java than Java - Clojurescript is better Javascript than Javascript, yet it’s still somewhat not very popular on the back-end. Theoretically nothing is stopping us from building nodejs apps using CLJS, am I right?

exupero16:06:26

@ag I’ve used Cljs on Node without any Java other than the Clojure compiler. I was only scripting, not creating a server, so I can’t speak to other benefits. Figwheel wouldn’t be useful unless you have a DOM UI.

manutter5116:06:26

@ag: are you familiar with #C0Y3CSPHQ?

ag16:06:59

@exupero: That’s what I’m talking about - I want to build complete web-app, api on node (maybe something like Express), and front-end with Om/Next

ag16:06:56

@manutter51: I use planck for tinkering, and running scripts, yes. But I haven’t thought about planck as a platform to run server on

mfikes17:06:08

@ag There is work that Ramsey Nasser has done recently to make a pure Node-based ClojureScript setup. It employs bootstrap and part of what it can do is run a REPL. https://github.com/nasser/clojurescript-npm Planck might get Socket / server support soon, if that’s of interest to you (Erik Assum has been working on that). And, you can also use the stuff from http://websocketd.com with Planck. Like you said, these things are fun to tinker with. But big-picture concepts about avoiding the JVM and Clojure are here https://github.com/clojure/clojurescript/wiki/Bootstrapped-ClojureScript-FAQ#does-bootstrapped-clojurescript-mean-that-ill-be-able-to-develop-clojurescript-without-the-jvm

ag17:06:37

@mfikes: Oh cool! Thanks for sharing these, I’ll take a look

dnolen17:06:05

@ag because all the tooling for Clojure(Script) focuses on the JVM

dnolen17:06:12

nothing do with whether Node.js is popular or not

dnolen17:06:58

@ag yes theoretically it can be done - but there’s an escape velocity problem wrt. being a good experience

dnolen17:06:38

to be fair it’s a bit of a chicken and egg problem

gowder19:06:53

Can I ask a really stupid question here? Does anyone know how to actually return something from the response handler in a function that generates a post request in cljs-ajax? I'm doing something wrong here... I've got the following code, where POST comes from cljs-ajax, and what I think should happen is that if the response from the server is "valid", valid-user? should return "yay!" and otherwise "boo :-(". But what actually happens is that I get an incomprehensible javascript error message.

(defn valid-user? [creds]
  (let [{:keys [username password]} creds]
    (POST "/login" {:params creds 
                   :handler #(do 
                              (.log js/console (str "response: " %))
                              (if 
                                (= % "valid")
                                "yay!" "boo :-("))
                   :error-handler #(.log js/console (str "error: " %))})))

(defn authenticate [creds]
    (.log js/console (valid-user? creds)))
and the (truncated in my console) javascript error message is:
g…g.net.XhrIo {disposed_: false, onDisposeCallbacks_: undefined, eventTargetListeners_: g…g.e…s.ListenerMap, actualEventTarget_: g…g.net.XhrIo, parentEventTarget_: null…}

gowder19:06:25

(the first call in do works fine and produces the response my serverside code ought to send)

gowder19:06:54

oh wait. maybe that isn't an error. maybe that's an object and I'm an idiot and the string I want is buried somewhere in there... oy

gowder19:06:16

(can you tell I don't do a lot of web stuff? 🙂 )

gowder19:06:44

ugh. apparently it is an object, but it has a million different nested things, and I don't even know where to look in it to find the string that my handler function is supposed to return

gowder19:06:50

there's gotta be some middleware floating around to parse this thing for me. yeesh

jjfine19:06:49

chrome dev tools network tab will allow you to see what's being returned by your server

jjfine19:06:06

i set this up a while ago FWIW I needed to merge { :response-format :json :format :json :keywords? true} into all of my cljs-ajax requests since I my backend sends / reads json

jjfine19:06:20

with that set up a map is passed into handler/error-handler

gowder19:06:33

thanks. I'll poke around in dev tools, which I have to confess I don't really know (and all the online tutorials about how dev tools I can find are like 5 years old)

jjfine19:06:06

however I'm not sure that valid-user will return anything other than an XhrIo

jjfine19:06:16

since it's 'async'

jjfine19:06:54

just realized that's what you're logging there

jjfine19:06:08

in my app, these handler functions mutate the app's global state (a reagent atom)

noonian19:06:09

You are either going to have to do the logging in a callback or use something like core.async or promises and return a value containing the result from valid-user?

dnolen19:06:26

try this dep, if everything works for you then I will bump ClojureScript to use it

gowder19:06:31

yeah. I just tried a similar problem and am getting async problems now:

(def validated-user? (r/atom false))

(defn valid-user? [creds]
  (let [{:keys [username password]} creds]
    (POST "/login" {:params creds 
                   :handler #(do 
                              (.log js/console (str "response: " %))
                              (reset! validated-user? (= % "valid")))
                   :error-handler #(.log js/console (str "error: " %))})))

(defn authenticate 
  "right now this is just a stub" 
  [creds]
    (do 
      (valid-user? creds)
      (.log js/console 
            (if @validated-user? "happy" "sad"))))
which would be fine, except that authenticate does its job before the server returns

gowder19:06:35

seriously is time to bust out core.async I guess

noonian19:06:56

You could make valid-user? take a callback as the second argument and call that with true in handler (or the creds) and with false or nil in the error-handler. Then authenticate is (valid-user? creds #(.log js/console %))

gowder19:06:36

oooh that makes sense @noonian

noonian19:06:37

This is just what its like programming in a JavaScript environment 😉

gowder19:06:59

I took up clojurescript to AVOID callbacks! 🙂

noonian19:06:42

to do it without callbacks you’d want to use core.async, but if you haven’t learned it yet and want to get something going I’d just use callbacks for now

gowder19:06:40

muchos gracias

wilkerlucio20:06:27

@dnolen: thanks, how do I try that?

dnolen20:06:50

@wilkerlucio: just use the dependency information

dnolen20:06:56

same as any other dependcy

wilkerlucio20:06:12

so, just put on my lein dependencies?

dnolen20:06:20

you will need to do :exclusions for your ClojureScript dep so that your explicit dep takes precdence

dnolen20:06:02

look for :exclusions for the example

gowder20:06:17

UGH @noonian is this really the sort of thing that people have to write in javascript? It's so weird and awkward...

(defn valid-user? [creds callback]
  (let [{:keys [username password]} creds]
    (POST "/login" {:params creds 
                   :handler #(callback %)
                   :error-handler #(.log js/console (str "error: " %))})))

(defn handle-auth [b]
  (.log js/console (if (= b "valid") "happy" "sad")))

(defn authenticate 
  [creds]
  (valid-user? creds handle-auth))
all the actual logic once I write it will have to go into that handle-auth function and now I want to go and wash the side effects off my dirty dirty hands. 🙂

noonian20:06:08

Welcome to callback hell

gowder20:06:44

I'm going to go learn how to use core.async in cljs right this second. otherwise I'm going to have to cry whenever I read my own code...

noonian20:06:53

In javascript its popular to use promises. That way you can still return a value synchronously and then attach your callbacks somewhere down the chain. Core async is very nice but I found it hard to understand at first.

noonian20:06:49

With core.async you’d return a channel from the valid-user? function and then authenticate would be something like (go (.log js/console (<! (valid-user? creds))))

gowder20:06:43

yeah, that's the ticket. so much less sadness...

gowder20:06:31

I know enough core async to be able to more or less read code using it, but never written any myself. now's the time to start...

wilkerlucio20:06:38

@dnolen: working great 🙂

dnolen20:06:17

@wilkerlucio: excellent, you tried a REPL and everything?

wilkerlucio20:06:05

@dnolen: yes, before this I had made a custom implementation for that base64 decoding, I swiped with the google closure and tried again on the REPL

gowder21:06:14

@noonian THIS IS SO MUCH NICER.

(def auth-chan (chan))

(defn valid-user? [creds]
  (let [{:keys [username password]} creds]
    (POST "/login" {:params creds 
                   :handler #(go(>! auth-chan (= % "valid")))
                   :error-handler #(.log js/console (str "error: " %))})))

(defn authenticate [creds]
  (go (valid-user? creds)
      (.log js/console 
            (if (<! auth-chan) "happy" "sad"))))
eat it, callbacks.

noonian21:06:16

Huh, so does cljs-ajax return the value returned by whichever handler is called?

noonian21:06:35

Ah, nvm didn’t see you had auth-chan as a global

gowder21:06:04

yeah, I gave up on getting cljs-ajax to return anything

noonian21:06:21

You could also have valid-user? return a new channel with the value or take a channel as another argument that it puts its result onto.

gowder21:06:42

like wrapped into a do block with the POST call or something?

gowder21:06:20

maybe I'll do that rather than have globals floating around

noonian21:06:58

@gowder: something like this maybe?

(defn valid-user? [creds & [c]]
  (let [{:keys [username password]} creds
        c (or c (chan))]
    (POST "/login" {:params creds 
                    :handler #(go (>! c (= % "valid")))
                    :error-handler #(close! c)})
    ;; Return the channel that will contain the result
    c))

(defn authenticate [creds]
  (go
    (if-let [valid? (<! (valid-user? creds))]
      (.log js/console "happy")
      (.log js/console "sad"))))

wilkerlucio21:06:33

@gowder: I was writing something but noonian was faster, posting it anyway, see what you think:

wilkerlucio21:06:37

(defn valid-user? [{:keys [username password] :as creds}]
  (let [c (async/promise-chan)]
    (POST "/login" {:params        creds
                    :handler       #(async/put! c %)
                    :error-handler #(async/put! c %)})
    c))

(defn authenticate [creds]
  (go
    (.log js/console
          (if (<! (valid-user? creds)) "happy" "sad"))))

noonian21:06:12

Yeah, promise-chan is probably what you want here. I haven’t actually used core.async since it was added hehe.

gowder21:06:21

what's the difference between put! and >! anyway? (he says while going to look up promise-chan)

wilkerlucio21:06:36

put! can be used outside of go blocks

wilkerlucio21:06:57

>! blocks inside a go block when the channel is full

noonian21:06:33

Yeah, you will commonly use put! in your callbacks at the edges of your system hooking up to callback based non core.async code.

gowder21:06:47

much better! thanks so much @wilkerlucio and @noonian !

(defn valid-user? [creds]
  (let [auth-chan (promise-chan)]
    (POST "/login" {:params creds 
                       :handler #(go(>! auth-chan (= % "valid")))
                       :error-handler #(.log js/console (str "error: " %))})
      auth-chan))

(defn authenticate [creds]
  (let [auth-chan (valid-user? creds)] 
    (go (.log js/console 
         (if (<! auth-chan) "happy" "sad")))))

gowder21:06:53

woah, looking back at noonian's code, I didn't even know if-let existed.

noonian21:06:42

I think you should also put a value on the channel (or close it) in your error handler or your authenticate function will park and never un-park in the if statement.

wilkerlucio21:06:01

I would recommend putting the error on the channel

gowder21:06:13

ooh good point

wilkerlucio21:06:14

later you can check if the value is an error, and trigger an exception when it happens

wilkerlucio21:06:09

then you can do stuff like this:

wilkerlucio21:06:15

(go
  (try
    (let [valid? (<? (valid-user? creds))]
      (if valid? (js/console.log "VALID!")
                 (js/console.log "AUTH ERROR")))
    (catch :default e
      (js/console.log "Some error happened" e))))