Fork me on GitHub
#clojurescript
<
2017-06-22
>
mattgeb02:06:32

Hrm, following this tutorial: https://github.com/Day8/re-frame/blob/master/docs/CodeWalkthrough.md but when I go to http://localhost:3449/example.html the app doesn't load, it just says "Reagent example app – see README.md"

mikethompson03:06:50

@mattgeb the code walkthrough is just this example is in the re-frame repo: https://github.com/Day8/re-frame/tree/master/examples/simple You should be able to clone the repo and run it

mikethompson03:06:59

BTW, there is a #re-frame channel

mattgeb03:06:56

Oh, cool, thanks for the re-frame room.

mattgeb03:06:09

I did copy it, though. Just followed the instructions

mattgeb03:06:48

Oddly, there's an example.html file that was generated, and when I open that, it works 😕

mikethompson03:06:24

Sounds like you are away

qqq05:06:16

how do I override the key press handler for the entire document/window ?

rauh05:06:34

@qqq The lower function of this snippet is independent of rum: https://github.com/tonsky/rum/wiki/Mixins#keyboard-shortcut

mikeb06:06:44

I'm stuck how does one export cljs functions? Here is the equivelent js.

exports.hello = function hello (req, res) {
  res.send('Hello World!');
}; 

Jon06:06:19

mistake.... you want to call this function in ClojureScript?

mikeb06:06:46

I want to export it for a google cloud function, which requires this.

mikeb06:06:07

I tried (set! (.-exports js/module) #js {:hello hello}) and (set! js/hello hello) and (set! js/exports.hello hello). All are rejected.

mikeb06:06:03

I guess it's so javascript can call the cljs function.

Jon06:06:31

what's your compiler, does it do optimizations?

mikeb06:06:40

yes, advanced.

Jon06:06:50

(set! (.-hello js.exports)
  (fn [req res]
    (.send res "Hello World!")))

Jon06:06:56

normally this code will work

Jon06:06:12

with advanced optimization, the method may be renamed

Jon06:06:39

(defn ^:export init []
  ;; can do one time init here
  (js/console.log "app init"))

(defn ^:export start []
  (js/console.log "app start")
  (render (root) (dom/by-id "root")))

Jon06:06:26

Or maybe:

(aset js/exports "hello"
  (fn [req res]
    (.send res "Hello World!")))
https://clojuredocs.org/clojure.core/aset advanced optimization won't rename methods defined in aset

mikeb06:06:53

Thanks @jiyinyiyong ! This worked (aset js/exports "hello" hello) and there was no need to set the ^:export on the function and as far as I can tell the ^:export didn't work all by itself.

Jon06:06:53

i guess ^:export would only work in some cases, like :npm-modules of shadow-cljs, or other case compiling to npm modules

thheller07:06:33

@jiyinyiyong :node-library is what you want, never mess with js/exports

thheller07:06:44

not yet implemented for browser libs though

Jon07:06:15

oh, what is the difference between :node-library and :npm-modules?

thheller07:06:50

:npm-modules is meant to work with JS tools .. like webpack, create-react-app or just node directly

thheller07:06:01

:node-library is meant to create bundles for node

thheller07:06:13

so one file not a whole bunch of files

Jon07:06:20

I thought :node-script does that

thheller07:06:30

:node-script is for things you run yourself, so not something that is included somewhere else

Jon07:06:51

so, it's like :npm-modules but only exposing as a single file?

thheller07:06:59

is has a few differences so not quite

thheller07:06:24

but it creates one file for you can require from JS

thheller07:06:06

any node.js experts around? is there any way to tell why a node.js instance doesn’t exit? I have a process that doesn’t exit but I closed everything and there shouldn’t be anything left running

thheller07:06:11

any easy way to debug this?

thheller07:06:41

hmm, any chance to get at that remotely? If I have anything connected to the process that will be amongst the things keeping it alive 🙂

thheller07:06:08

hmm I can probably add a signal handler and dump that info on request

valyagolev07:06:30

yeah or maybe like a setTimeout

valyagolev07:06:46

it’d keep it too, but it’s happening anyway

ajs08:06:03

This article talks about passing entire app state to components in Om Past https://circleci.com/blog/why-we-use-om-and-why-were-excited-for-om-next/

ajs08:06:31

What I don't understand is, wouldn't this violate the react concept, and force re-rendering of all components even for small changes to data that components don't need?

crankyadmin10:06:57

Hi, Calling PropTypes validators directly is not supported by the prop-types package. Use PropTypes.checkPropTypes() to call them. Anyone dealt with this in Clojurescript?

crankyadmin10:06:32

Actually scrap that... Looks like my problem is actually this error: Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded

pesterhazy10:06:13

I've seen that issue

pesterhazy10:06:31

it's probably correct that you're using multiple versions of React in the same program

pesterhazy10:06:02

wild guess: some cljsjs dependency erroneously includes its own React due to Webpack fail

dnolen10:06:49

@dealy what do you mean push stuff? You cannot just push to one or the other and expect interleaving. You must have two Async loops running. Is that what's happening?

crankyadmin10:06:04

> npm ls react
[email protected] /Users/crankyadmin/code/Personal/testapp
└── [email protected]

pesterhazy10:06:44

ah you're using your own version of react using npm?

crankyadmin10:06:40

> npm ls  | grep react
├─┬ [email protected]
│ ├── [email protected]
├─┬ [email protected]
│ └─┬ [email protected]
├─┬ [email protected]
│   ├─┬ [email protected]
│   │ ├── [email protected]
│   │ ├─┬ [email protected]
│   │ │ └── [email protected]
│   │ ├── [email protected]
│   │ ├── [email protected]
├── [email protected]
├─┬ [email protected]

crankyadmin10:06:53

No I haven't, taking a look now 👍:skin-tone-2:

pesterhazy11:06:43

@crankyadmin I've written a draft blog post on this topic, which may help as well: https://github.com/pesterhazy/presumably/blob/master/posts/double-bundle.md

pesterhazy11:06:21

check your clojurescript dependencies (`lein deps :tree`) to see if any depend on cljsjs/react, cljsjs/react-dom or cljsjs/react-server

pesterhazy11:06:17

plus some cljsjs dependencies accidentally include react (see this issue: https://github.com/gadfly361/baking-soda/issues/3#issuecomment-304524921 )

crankyadmin11:06:48

ah yes, reagent is depending on react.

crankyadmin11:06:39

Which side is it better to exclude from, lein or npm?

pesterhazy11:06:09

I'd argue, lein

crankyadmin11:06:42

Do I need to do some magic with the namespaces then?

metametadata11:06:35

@ajs As far as I understand, there're two slow stages in React: calculation of DOM diff and applying actual DOM changes. If you have ClojureScript react wrapper then also add "interop stage" into the mix (e.g. conversion from Reagent's Hiccup-like datastructure into JavaScript React components). But even with a single app state you can avoid doing most of costly recalculations thanks to ClojureScripts's fast comparisons of immutable data passed into smaller components: if the piece of data passed into the particular component is the same then React wrapper will quickly bypass the whole component "recalculation" (https://facebook.github.io/react/docs/optimizing-performance.html#using-immutable-data-structures). But to take advantage of such optimisations you need to understand what can trigger recalculation of the particular component in the React wrapper of choice. I'm not sure about Om and can only point to a nice explanation for Reagent: https://github.com/Day8/re-frame/wiki/When-do-components-update%3F Maybe there's a more thorough explanation somewhere on the web, because the same question can also be applied to JS frameworks with a single state, such as Redux.

ajs11:06:08

@metametadata thanks, I've got some reading to do

crankyadmin11:06:36

Fair dos, cheers pal 🙂

pesterhazy11:06:02

hope it'll make you a bit less cranky

crankyadmin11:06:14

@pesterhazy It was because I was loading multiple copies of react 😳

crankyadmin11:06:46

Removed the dup and added the namespaces in and everything works as expect. Thanks again for your lead!

tomaas13:06:13

Hi, in a simple compojure get route, I need to call an async function and return the result of that callback as a response

gaverhae13:06:16

compojure itself only does route matching; whether (and how) or not your handler can return an async response rather than a plain Ring response map is up to your HTTP server (jetty, httpkit, immutant, etc.)

gaverhae13:06:01

Depending on what the function does and why it's async, the easiest (though probably not best) thing to do might be to just wait for it to finish

gaverhae13:06:28

though keep in mind that that would hold onto one of your HTTP response threads, which again may or may not matter depending on your HTTP library

dealy13:06:40

@dnolen when I said push stuff I mean that messages arrive from the server and are added to the channel that the publication was created on. And yes there were two loops running, the looper function was called twice in the example I gave yesterday.

dnolen13:06:12

@dealy but then recreating the problem so that a 3rd party can confirm should be trivial no?

dnolen13:06:47

make two loops that feed those channels with (timeout 150) or something like that

dnolen13:06:33

@dealy but based on your followup comment it seems like there might be a bug in the code or a problem with expectations

dnolen13:06:48

(not surprised about the later, core.async can be quite counter-intuitive)

dealy14:06:40

@dnolen sorry, I'm not understanding your suggestion. In my example all messages are pushed on to the same channel. the async loops pull from separate channels

dnolen14:06:13

sorry then feed -> read

dealy14:06:24

yes, that's what I've been trying to get clear all along, I was originally just wanting to talk with someone to see if my understanding of how this was supposed to work was correct

dnolen14:06:47

right it’s impossible to say without a minimal example that shows your expectations

dealy14:06:41

yesterday I just gave a brief outline of the way my code was structured

dealy14:06:52

I'll try to make something small that exhibits the same behavior, of course it probably will just work correctly when I first put it together.

dnolen14:06:51

@dealy I looked backed what you wrote, I just don’t understand “calling of the functions … aren’t really interleaved”

dnolen14:06:03

“slight overlap” for a moment … what does this mean?

dnolen14:06:08

are you seeing

dnolen14:06:17

h1 h1 h1 h2 h2 h2 h1 h2 h1 h1 ...

dnolen14:06:22

something else entirely?

dnolen14:06:41

(where h signifies handler)

dealy14:06:42

yes that is basically what I'm seeing

dnolen14:06:52

and how quickly are you writing to the pub?

dnolen14:06:58

sub millisecond? 5ms?

dealy14:06:42

just that there are gaps also for instance If my servers sends m1 m2 a1 a2 m3 a3 m4 a4 a5 my handlers are only getting m1 a1 m3 a4

dealy14:06:58

yes, things can get pushed very fast

dnolen14:06:17

(that sounds like a separate issue which should talk about separate, I just suspect a bug in your code)

dealy14:06:38

that is what I was curious about, am I working outside of the expected limits of messaging by pushing too much too fast

dnolen14:06:44

if it’s submillisecond then your expecations about interleaving are too much

dealy14:06:03

oh sorry its not 5ms

dnolen14:06:09

JS is singlethreaded and the fast async dispatch mechanism is no finer than 0.025ms

dealy14:06:33

I see it pushing something on the order of maybe 10 messages per second

dnolen14:06:17

ok so then make a complete gist that simulates your system 🙂

dnolen14:06:27

then it’s easier to for someone to run & confirm

dnolen14:06:42

finish your example so that you write 10 messages a second and print in your handlers

dnolen14:06:04

sorry a complete code snippet

dnolen14:06:31

then I can just drop what you’ve written into a REPL & verify

gaverhae14:06:01

"gist" is a github feature that allows you to share a single file without having to create a full github repo; you can also use something like http://pygments.org or codebin or, well, really anything that lets others see the full example

dealy14:06:47

ok, I'll take a look at that once I have something to show

gaverhae14:06:09

dealy: http://pygments.org/demo/6625943/ inspired by your code above seems to have the expected behaviour to me

gaverhae14:06:38

calling (run) gives me -+-++--++-+-++--+-+-+-+-+-++--+-+-++--+-+-+-+--++-+-+-+-+-+-+-+-+-+-+-+-+--++-+-+-+-+--++-+-+-+-+-+-+--+++--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++--+-+-+-+-+ which seems properly interleaved

gaverhae14:06:55

I'm running it on the JVM though, maybe it wouldn't work as well in a browser

dnolen14:06:34

@dealy I took @gaverhae’s snippet and added a 100 ms delay between writing to the pub chan and I could not replicate under Node.js - my interleaving looks the same as on the JVM

dnolen14:06:58

@dealy and I just confirmed in browser - cannot replicate

dealy15:06:54

yea, so far I haven't been able to make a small standalone example which demonstrates the problem, I thought something like this might happen

dealy15:06:55

The layout that @gaverhae used looks basically correct, except that I have been using >! and <!

gaverhae15:06:32

dealy: put! is non-blocking whereas >! is blocking, which may explain the behaviour you observe: if the producers get blocked that may be the reason you don't see the interleave.

peeja15:06:30

@ajs You understand precisely. That's why we love Om Next so much. 🙂

peeja15:06:48

(Well, it's one reason.)

ajs15:06:38

@peeja i'm curious, if the point of react is to avoid rendering components unnecessarily, what is the point of using the old om at all if you're forcing all components to always re-render even if they don't need it?

dealy15:06:46

@dnolen one thing that is also different is that in my handler functiions I'm updating an atom which triggers a reagent component refresh

ajs15:06:03

I'm confused why reacts page on dom elements doesn't actually list all the supported elements, only supported attributes within the elements. Does such a list exist anywhere? https://facebook.github.io/react/docs/dom-elements.html

dnolen16:06:25

@dealy that doesn’t seem significant to me, but I would definitely say the issue does not seem to be core.async but something higher level in your application

peeja16:06:24

@ajs Well, it worked well for a while, and then we found we had to pass the entire app state around most of the time to do anything interesting. We didn't anticipate that being an issue going in.

peeja16:06:31

It's also not as bad as it sounds: any change to the app state requires all of those components to re-render their virtual DOM (and run lifecycle methods), but if the rendered virtual DOM stays the same, React still saves us the cost of making any changes to the real DOM.

ajs23:06:35

@peeja I've noticed that the term "render" can mean one of two things: the "calculation" for a component's content, and the actual rendering in the Dom. Re-render often doesn't actually mean a Dom render, despite how it sounds, as you point out.

peeja15:07:02

@ajs (Replying to this rather late 😛) While you're right, FWIW, in my experience "rendering" in React can pretty much always be taken to mean the process that calls the render methods, not the process of updating the DOM (which is usually described as "reconciliation", although I think properly speaking the reconciliation process includes calling the render methods too. People generally just don't talk about the DOM updating much in React because it's so well abstracted. 🙂

ajs15:07:38

yet, the actual updating of the DOM is the real costly piece, and where the actual onscreen rendering happens, so it can be a bit confusing as to what "render" means but thanks for pointing out the most common use of the word

dealy16:06:20

@dnolen yea, I didn't really expect that there was a bug in core.async. But that there was some issue relating to it or cljs that I'm not understanding.

dealy17:06:59

@dnolen maybe something about the singlethreaded nature of javascript? All of the puts to the bus happen as Sente receives events over a websocket from the server. I assume that sente is putting multiple events on the bus while the looper can be off in the event handler and then when the looper finishes and calls <! those events should be waiting in the channel. But it seems like often those events are just lost and looper doesnt' respond until the next event arrives

noisesmith17:06:36

the most plausible explanation there is that there’s something reading the channel that you forgot or didn’t intend

dnolen17:06:43

losing stuff doesn’t make any sense unless you’ve specified those semantics on the chan you constructed

dealy17:06:22

@dnolen do you mean by specifying a dropping buffer?

dealy17:06:18

@dnolen i'm only just using (chan) in every instance

dnolen17:06:17

so I would start looking for takers that you somehow missed

dnolen17:06:52

also I don’t know anything about Sente & it’s semantics wrt. core.async usage

dealy17:06:43

Sente doesn't call put on the bus itself, it calls a handler I wrote which in turn puts on the client side bus

noisesmith17:06:30

one thing that might help with the debugging is to change (chan) to (chan 1 (map (fn [x] (println "processing" (pr-str x)) x)) which should confirm the messages that actually go through the channel without altering behavior *fixed

jiri.knesl17:06:46

where would you store app state when application goes offline? atom? datascript? - I need to have couple of records downloaded on page start - user does changes offline, this changes need to be synchronized - user almost never uses 2 devices which can be offline, conflicts are unlikely - all dataset are basically some 3-4 database tables, couple of megabytes common, tens of kilobytes per user

noisesmith17:06:06

@jiri.knesl would the localstorage api work for you? https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage - it’s easy to use from cljs

jiri.knesl17:06:06

as a framework I prefer re-frame, but I guess it isn't important

noisesmith17:06:32

unlike session storage, local storage persists across tabs

jiri.knesl17:06:37

@noisesmith I know about localstorage, just if you would save atom, or datascript synchronised in local storage

noisesmith17:06:11

well, atoms aren’t data, they are stateful objects, but I use transit to serialize data that gets copied into my state atom

noisesmith17:06:18

not the whole state, just the parts that need persisting

jiri.knesl17:06:32

I understand

jiri.knesl17:06:43

so atom + hashmap + transit for sync

noisesmith17:06:52

that’s what has worked for me, yes

jiri.knesl17:06:58

+ some delta database I will use to know what to sync

jiri.knesl17:06:23

I think event-sourcing in clj+cljs would make all this izomorphic

noisesmith17:06:00

oh, so you could even store the event series as localstorage indexes, and then you know you have more to load when more exist

noisesmith17:06:27

(plus bundling up state occasionally for efficiency that’s a typical event sourcing thing iirc)

dealy17:06:51

@noisesmith yea, I had done something like that yesterday. but I was looking for exceptions not sure if I printed anything out

dealy17:06:57

@dnolen @noisesmith here is some of the log output that shows the behavior I'm experiencing. Notice that there are many >! values that don't have corresponding <! messages: https://gist.github.com/dealy663/f95caef8806da76bec724a59e520f3c9 If you feel like taking a look

dealy17:06:03

Am I supposed to import the Gist link as slackbot is suggesting?

noisesmith17:06:21

only if you think it needs an inline preview

noisesmith17:06:50

@dealy they are getting put on the chan weirdly out of order, which makes me think it’s happening in a callback that is waiting on some other callback, that might take an indeterminate amount of time, and perhaps simply not get called?

noisesmith17:06:56

that’s kind of speculative though

noisesmith17:06:34

I wouldn’t expect things to get that out of order without some other async factor in the middle that potentially goes wrong…

dealy17:06:31

no, the puts are in order notice that there are received and cooked puts which correspond to ev1 and ev2 in the previous examples

dealy18:06:52

the received puts go in order from 68 to 74 while the cooked puts go 67 to 74 in order

dealy18:06:27

@noisesmith I added your xform to the sub channels and they are only showing up in direct correlation to the updating received/cooked blob messages. This is so weird, I can't find anything else taking off of these channels

dealy18:06:36

I think I'm gonna have to abandon pub/sub on the client side

timgilbert18:06:38

@jiri.knesl: if you're thinking about persisting a datascript db to localstorage you might want to check out https://github.com/tonsky/datascript-transit which makes it pretty easy. There's also https://funcool.github.io/hodgepodge/ which is a nice clojureish interface to the API, and another few projects which are linked from that page

jiri.knesl18:06:07

@timgilbert thanks! I will look into it

souenzzo19:06:51

@timgilbert there is some trigger "on-change" on this hodgepodge?

timgilbert20:06:42

I've never tried it, I've just used localStorage as read/write storage

timgilbert20:06:39

There's also https://github.com/alandipert/storage-atom which lets you add a watch to localStorage

shader20:06:03

I'm trying to write a macro in cljs that collects a set of defns into a hash-map with keywords matching the function names as keys. For some reason the macro works when I run it directly from the repl, but not when it is used in the source files; is there a difference between the return value of defn in the repl vs in compiled code?

souenzzo20:06:33

I use storage atom. It's awesome. And has this callback. But I have no idea how to operate in a functional way without a "on-change" trigger

metametadata20:06:56

I may be tired but it looks like reify doesn't compile inside core.async's go:

(defprotocol Foo)
(go
  (reify Foo))
Here's the minimal example with the error message I get in readme: https://github.com/metametadata/cljs-go-reify-bug

metametadata21:06:22

OK, it seems to be officialy not supported: https://dev.clojure.org/jira/browse/ASYNC-57

metametadata20:06:20

@shader I think I did something similar in a macro, using cljs.analyzer.api/ns-publics to find the specific functions in current ns: https://github.com/metametadata/clj-fakes/blob/ede68f97dacbef11a881e701977e75d560c16bac/tasks/core.cljc#L46

shader20:06:56

but simply loading it in the browser returns {nil <last fn in group} instead; I presume this means that all of the name metadata was stripped during compilation

metametadata20:06:39

and what is the example usage? I mean, how does the invocation of the macro look like?

shader20:06:18

before, I had simply been defining the events object with (def events {:load-messages (fn ..., but using defn first seems like it would be easier to test, and the fact that it returns vars should make it more reloadable as well

shader20:06:35

I could have collect walk the defns and just grab the 2nd element for the name I guess, if it really is the metadata that's the problem

metametadata20:06:36

I see, that's now much more clear. Although I can't comment much about if the end-goal, I'm trying to think why the provided macro misbehaves.

metametadata20:06:53

Isn't f# just a form though during the macro expansion? This would explain why there's no :meta in it.

metametadata20:06:50

i.e. I guess the first f# will contain the data structure of (defn load-messages [db [_ {:keys [messages]}]](assoc db :messages-list messages))

shader20:06:18

hmm; it works when I run it in the repl though...

shader20:06:04

simpler too

metametadata20:06:13

I misinterpreted the macro

metametadata20:06:48

but it would fail if name is not the second element I suppose

shader20:06:54

don't know why I didn't think of that the first time... reaching for post-compilation metadata is a much more convoluted approach

shader20:06:58

does that ever happen in a defn?

metametadata20:06:25

not often, e.g. if you decide to add ^:no-doc meta

shader20:06:35

any other suggestions then?

shader20:06:54

maybe searching for the first non-`defn` symbol?

shader20:06:57

sounds like a pain

shader20:06:53

I guess I'll just leave it the way it is until I prove an alternative is necessary

shader20:06:25

@metametadata thanks for your help!

metametadata21:06:13

@shader ah, right, ClojureScript would not even create a Var, it compiles defn into load_messages = (function ... (..) {..});

shader21:06:30

yeah, I was just confirming that

shader21:06:36

so it doesn't do exactly what I want...

shader21:06:56

at least, I think if it used the var it would update the reference when the function was redefined

shader21:06:59

though that may not be true

shader21:06:08

it worked in the clojure repl...

shader21:06:27

I have another solution though; I'll just have the reload function in my core js file update all of the hash-maps; probably better than relying on implicit reloading anyway

shader21:06:22

what's the right way to programatically look up a variable in multiple namespaces?

shader21:06:35

suppose I have a vector of namespaces [a b c], and I want to get the values [a/events b/events c/events]?

metametadata21:06:09

it's shouldn't be possible, as there're no vars during runtime

shader21:06:50

how about using cljs.analyzer.api/ns-publics?

shader21:06:03

or something similar?

metametadata21:06:35

that would give you compilation time information - var names, etc.

shader21:06:24

(aget <ns> "events") returns a js object that appears to match the one I generated

shader21:06:48

so I'll try that

metametadata21:06:30

I suspect that may not work after the advanced compilation

metametadata21:06:52

because of name munging, you should test it though

potetm21:06:17

I'm pretty certain it won't.

noisesmith21:06:29

you can ensure it survives with ^:export though

potetm21:06:04

Would have to put it on all of your vars.

noisesmith21:06:41

oh, pardon I didn’t read the scrollback

noisesmith21:06:56

I have a suspicion that namespaces are being used where hash-maps would be a better fit

noisesmith21:06:05

ns are not good data structures

noisesmith21:06:27

not even in clj, but it’s worse in cljs

ajs23:06:35

@peeja I've noticed that the term "render" can mean one of two things: the "calculation" for a component's content, and the actual rendering in the Dom. Re-render often doesn't actually mean a Dom render, despite how it sounds, as you point out.