Fork me on GitHub
#clojurescript
<
2015-12-01
>
settinghead00:12:21

so, i’ve been trying to leverage an existing js/react library called react-textarea-autosize in cljs: https://github.com/andreypopp/react-textarea-autosize since this library does not have a direct browser-friendly version (it uses require in its code and i suppose it expects you to use your own js build pipeline like browserify to make it browser-friendly), i will have to convert it to be usable in cljs. ways i can think of: 1. refactor it to be browser friendly 2. fork and add to cljsjs both seem a little complicated. is there another way i’m not aware of? any tips would be appreciated.

dnolen00:12:30

@settinghead: I would just build React yourself with it included using react-tools

settinghead00:12:27

ah, good. thanks

johanatan02:12:57

anyone know why this doesn't work on ClojureScript?: (defmacro nofail [& body] [backtick](try ~@body (catch :default e)))

johanatan02:12:47

[I've tried multiple catch clauses-- also tried using try* (JS try) instead].

dnolen02:12:13

@johanatan: that can’t work, you’re writing a ClojureScript try/catch in a Clojure macro file

dnolen02:12:26

oh I see you have the backtick ...

johanatan02:12:43

@dnolen: actually I was trying to write a CLJS macro in a cljs file.

dnolen02:12:50

yeah doesn’t work

johanatan02:12:00

I'm able to view the JS source for it after entering the form in the REPL simple_smile

dnolen02:12:10

macros have to be in Clojure source files

dnolen02:12:20

or .cljc files

johanatan02:12:21

Ahh, right. So I need to use CLJX?

trancehime02:12:34

I believe cljx has been replace by cljc ?

dnolen02:12:45

none of these things change the fact that macros are defined in Clojure not ClojureScript

johanatan02:12:18

So, I think a simple HOF will do here: (defn nofail [cb] (try (cb) (catch :default e)))

johanatan02:12:32

Does that look correct?

dnolen03:12:30

sure or write your macro in Clojure file per usual

johanatan03:12:05

Okay. Yea, I haven't had a big enough need to have to create a Clojure file for this project yet-- putting it off a bit longer until something bigger comes up.

trancehime03:12:13

Hi, I just want to clarify and understand something

trancehime03:12:17

(defn- base-handler [{:as ev-msg :keys [?data]}] (some code here...)) what the [{:as ev-msg :keys [?data]}] portion mean is the kvs in ?data are binded to ev-msg?

trancehime03:12:42

(I guess it should be {:keys [?data] :as ev-msg} actually...?)

cjmurphy03:12:27

It means base-handler has a hash-map argument that has :?data as one of its keys. This destructuring gives you the value of the key :?data in ?data. ev-msg is the whole hash-map argument.

trancehime03:12:27

So basically the stuff in the [] part is just a way to represent a destructuring of the whole argument which is ev-msg hence :as ev-msg

cjmurphy03:12:44

The outermost [] is normal args. The inner one for the binding/destructuring of keys. Normally there's a few keys to make it worthwhile.

cjmurphy03:12:59

There might actually be lots of keys in the hash-map argument, but :?data is the only one being picked out. I'm now gonna ask in beginners what the ? at the beginning means...

trancehime03:12:11

It's sente stuff

trancehime03:12:43

?data is just a map of data that would be sent to server-side

cjmurphy03:12:46

Its not particular to sente thou.

trancehime03:12:54

Well I mean in that particular example

cjmurphy03:12:02

?data denotes that there's prolly a key :?data in the hash-map, otherwise why would it be being picked out??

trancehime03:12:23

@cjmurphy: curiously, what other instances of ?name have you seen aside from the one I just showed

cjmurphy03:12:14

Can't remember, but I know this is not the first. I did look at Sente recently so maybe it's just from there (watched your video recommendation).

trancehime03:12:58

Ah, probably. 'Cause sente is the only thing I saw such a naming thing myself

cjmurphy03:12:54

Its more general apparently, come to beginners ...

trancehime03:12:38

I am reading

mikethompson04:12:42

For those that missed @richiardiandrea's low-key announcement: http://www.clojurescript.io/

richiardiandrea05:12:25

Ahaha thanks @mikethompson , I guess I need to improve in marketing :)

richiardiandrea05:12:09

That was fun to develop thanks to re-frame and re-com!!

mikethompson05:12:00

@richiardiandrea is there a repo available? I can see replumb but it appears to be a library

richiardiandrea05:12:33

@mikethompson not yet for the website...soon though, because I really would like to have it reviewed. Yes replumb is the library behind it.

mikethompson05:12:01

Would love to see this integrated with http://cljs.info/cheatsheet/

richiardiandrea05:12:02

Well it kind of is :) basically I have a generator that given the http://cljs.info edn file, does some purging and creates a smaller version of it, with some adjustment. For instance for the examples I had to strip the markdown so that they could be sent to the Repl.

richiardiandrea05:12:00

The generated namespace is 700kb, and I wanted to use it for code completion...but...next iteration I guess...

richiardiandrea05:12:45

In any case we will opensource it of course, just a matter of time

richiardiandrea06:12:07

To tell you the truth, there are some hacks in there that I would like to remove (for instance I would like to get rid of jquery)

jaen07:12:08

@cjmurphy: that ?data is just a convention as far as I can tell stemming from being able to use all kinds of weird characters in Clojure names. In case of sente the author uses it to just mean "the map may or may not have a key named data". Other usages of initial question mark I've seen are logic libraries like core.logic or datalog, where they are mean to signify the variables logic engine will try to unify on. But all in all - it's nothing inbuilt into language, just some kind of cultural "orthography" thing. @trancehime: the order doesn't matter in map destructuring, {:as ev-msg :keys [?data]} means the same as {:keys [?data] :as ev-msg}, though the second is a more common "spelling" in practice. Basically :as sth in any destructuring form binds the whole form to a name, can be used as well in vector patterns [first second & rest :as some-list]. {:keys [some-key]} is a shorthand for doing {some-key :some-key}, that is extracting a value under key :some-key and binding it to some-key (if you don't use :keys those can be different). There's also :strs and :syms for strings and symbols respectively.

trancehime07:12:33

@jaen gotcha, thanks for the clarification

trancehime07:12:16

as I understand. If I were to use :keys, :some-key and some-name would actually be exactly the same.

trancehime07:12:13

i mean it would be :some-key and some-key

jaen07:12:34

Yes, I made a mistake there, writing some-name instead of some-key. :keys et consortes are meant for the cases where you want to extract the key under the same name.

jaen07:12:07

Sometimes you wouldn't if you want some more descriptive name for something, like {carousel-id :id}, but most of the time people just use :keys to avoid repeating themselves.

trancehime07:12:20

yeah, I think that's pretty nice

trancehime07:12:44

:keys is good to avoid the duplication but the option of providing a more descriptive (semantically speaking I suppose) name is always welcome.

jaen07:12:08

Yeah, and :keys don't nest. You can do something like this {{[this actually was a & vector :as that-nested-vector] :subkey :as that-nested-map} :key :as top-level-map} for a somewhat contrived example.

jaen07:12:14

It's sometimes hand to do so.

jaen07:12:12

There's also :or modifier for default values, but I think it had some gotcha.

trancehime07:12:22

I think I saw the fact :keys do not nest in a video.

trancehime07:12:40

As for :or directive, it appears that most likely people tend to forget it exists because usually the maps you are passing tend to be complete. Of course I guess there is a case where you are passing in data which is not fully known

jaen07:12:00

Right, I think it was also somewhat awkward in some situations, but can't think of an example now. I tend to not use destructuring in function signature but first merge with a default option map and then destructure in a let if need default values most of the time.

trancehime09:12:23

Haha oh man z.z

jaen09:12:51

Oh my, that reminds me of how on my first years they had us write Pascal on paper xD

jaen09:12:25

I just hope you won't get any bad habits from my sample sente code I've linked to '

trancehime09:12:05

(ignore the fact I forgot to name-space the ID)

agile_geek09:12:09

Reminds me of COBOL coding sheets!

agile_geek09:12:40

Which then sometimes got translated to punch cards! (Yes, I am that old)

trancehime09:12:02

But yeah, I've been writing code on my notebook to help me visualize things better... Somehow. Actually it helps me to understand some concepts better than typing it out and using REPL

agile_geek09:12:39

😄 whatever works for you.

trancehime09:12:22

@jaen well, comprehending how everything works has been a real challenge for me

trancehime09:12:30

Or rather, wiring everything up

martinklepsch09:12:22

@richiardiandrea: I think the second js interop example has a leftover thing from editing: there are two -property "calls"

martinklepsch09:12:40

@richiardiandrea: very nice thing though :)

jaen09:12:59

@trancehime: well, you don't have to namespace the ids as long as they don't collide, it's just that, say, :chat/retrieve-messages is more readable than :chat-retrieve-messages I think. Also yeah, that code is probably more complicated than strictly necessary but you get the nice side effect of events being easily extendable, you can do (defmethod that.async.namespace/async-handler ...) calls in other namespaces to separate thins by topic. You just have to require them and boom, new events will be registered.

trancehime09:12:17

Oh no I didn't mean the event handling

trancehime09:12:35

There was a video I saw that made it pretty clear to me how that worked

trancehime09:12:44

I was more referring to how sente actually runs

trancehime09:12:19

For example, I can choose to call make-channel-socket at will, but do I call start-chsk-router! on web app initialization? etc.

jaen09:12:36

I see. Yeah, it's somewhat confusing to figure out at first and clever names don't help.

trancehime09:12:13

For something so simple (you could literally just copy-paste the code in the docs and already have a working websocket client<->server comms) it sure is hard to actually understand

jaen09:12:38

And for the question - if that was a question - you can call call both at your convenience - you would call make-channel-socket when you want to connect with server-side and you would call start-chsk-router! at some point after that (you need to pass the channel you get from make-channel-socket there, so you can't do it before) to initialise the go-loop that will dispatch the events to the function you supplied for you. You usually do both at once when you connect.

trancehime09:12:56

OK, that's what I thought. You have to do start-chsk-router! after make-channel-socket Now you just said you usually do both at once when you connect... But then I plan to have users only establish connection after hitting a certain route when they are authenticated. If nobody authenticates and hits that specific route where I will call those 2 functions, does this mean the websocket server never actually gets initialized?

trancehime09:12:12

For example, you got like, user A and B, right. And they connect to the server thru websockets after let's say, /route. If neither users A nor B ever hit /route/ what does this actually entail? The server is still listening for connections but nobody actually connected?

jaen09:12:47

Oh wait, we were talking about client-side or server-side there?

trancehime09:12:21

ok, cause client-side you could just call make-channel-socket whenever right. but then what does that mean for the server side?

jaen09:12:13

Yeah, for client you connect whenever you want. For the server, you call make-channel-socket whenever you want to start accepting any connections.

jaen09:12:43

So basically the same question as to when you would want to start accepting any requests for your web page, ie. when you would want to call immutant/web or whatever.

jaen09:12:54

Probably right when the application boots as well.

trancehime09:12:26

Hmm. Okay, so like this, on the server-side, it's OK for me to make-channel-socket on application boot so that the server immediately starts listening for any requests.

jaen09:12:06

And any incoming request will net you an event in your handler, like I have here - https://gitlab.com/jaen/clj-cljs-presentation/blob/master/src/presentation/backend/async/core.clj#L50

trancehime09:12:14

The start-chsk-router! on the server-side would use the socket from make-channel-socket on the server-side in order to begin the go-loop since it now got a receive channel, am I getting this right?

jaen09:12:31

You would just have something like

jaen09:12:10

(defn start-sente! []
  (let [sente-socket    (sente/make-channel-socket ...)
        receive-channel (:ch-recv sente-socket)
        sente-router    (sente/start-chsk-router! receive-channel handle-receive)]
    (reset! sente-state {:sente-router-stop! sente-router})))

(defn stop-sente! []
  (swap! sente-state (fn [{:keys [sente-router-stop]}]
                       (sente-router-stop!)
                       nil)))

jaen09:12:14

To boil it down to a minimum example

jaen09:12:32

And you would call start-sente! when you boot your application

jaen09:12:38

and stop-sente! when you close it

jaen09:12:39

Since you're using Immutant there's http://immutant.org/documentation/current/apidoc/immutant.util.html#var-at-exit to call when the application shuts down, but I'm not sure if this works only inside Wildfly or in a stand alone mode as well.

jaen09:12:43

You'd have to check.

trancehime09:12:05

Noted, and also thank you much for your patience.

jaen09:12:12

Nah, no problem.

trancehime09:12:20

I'm not so good at this just yet

jaen09:12:51

Well, that's just how it is when you get dumped into a new ecosystem, so nothing to be sorry about.

jaen09:12:31

Of course keeping things in an atom is not mandatory, but if you ever want to stop the router it makes sense

jaen09:12:26

There are also other useful things in the return of make-channel-socket you may want to store of course, like :send-fn and :connected-uids.

jaen09:12:37

I just omitted them for clarity here

jaen09:12:05

But you probably want to store them as well, without :send-fn you won't be able to broadcast and without :connected-uids you won't have a list of connected users.

trancehime10:12:30

so maybe I could do something like storing :send-fn into something like send! and :connected-uids into list-of-uids for example in addition to the socket

jaen10:12:29

Yep, exactly. A more complete exampel would be more like so:

(defn start-sente! []
  (let [sente-socket    (sente/make-channel-socket)
        receive-channel (:ch-recv sente-socket)
        send!           (:send-fn sente-socket)
        connected-uids  (:connected-uids sente-socket)
        sente-router    (sente/start-chsk-router! receive-channel handle-receive)]
    (reset! sente-state {:sente-router-stop! sente-router
                         :send!              send!
                         :connected-users    connected-users})))

(defn stop-sente! []
  (swap! sente-state (fn [{:keys [sente-router-stop]}]
                       (sente-router-stop!)
                       nil)))

(defn send! [uid event payload]
  (let [{:keys [send!]} @sente-state]
    (send! uid event payload)))

(defn connected-users []
  (let [{:keys [connected-users]} @sente-state]
    @connected-users))

jaen10:12:36

But that's just an example of course

jaen10:12:43

You might want to do things differently.

jaen10:12:33

For example if you look at my sample code - https://gitlab.com/jaen/clj-cljs-presentation/blob/master/src/presentation/backend/async/core.clj#L38 - I've decided to not make send! a function in the namespace, but rather pass the state when calling the event handler.

jaen10:12:50

But it's entirely up to you how you structure it.

trancehime10:12:54

so you pass the state to the event handler and get the stuff you need from the state and process accordingly?

jaen10:12:25

Yes, I use it as an additional argument in the handler, so the signature is like [sente-state event-payload]

jaen10:12:32

But it might or might not be a good idea.

jaen10:12:57

I'd probably stick with global functions like in the example above for starters, it's the simplest solution

jaen10:12:09

And you'll see if you want something done differently in time

trancehime10:12:38

Hmm, okay, thanks for your advice

dvcrn10:12:08

self advertisement spacemacs fans here? I ported a good amount of spacemacs functionality into atom (with the help of clojurescript!) Looking for people that want to help the project a bit simple_smile https://github.com/dvcrn/proton

dvcrn10:12:38

I have a fear that clojurescript scares possible contributors off

danielgrosse10:12:05

I have an javascript function, which returns an promise, how can I handle this in clojurescript? When I assign it to a core async channel I get an error, that take! is not defined.

sooheon11:12:33

@dvcrn: I’m a spacemacs user and this looks really interesting. You have my interest for sure :)

grav11:12:22

@danielgrosse: Do you need to get it into the core async domain?

grav11:12:07

You can just handle promises directly, like (a-promise.then (fn [v] (prn “Got value “ v)))

dvcrn13:12:52

If we get more cljs people on it, we could build something more solid a good chunk faster. my clojure skills are still pretty limited

donmullen14:12:54

@dvcrn - looks interesting - lots of potential to be a great option for those new to clojurescript/clojure. Did you integrate parinfer?

dvcrn14:12:52

@donmullen: yup! Part of the clojure layer already

monjohn14:12:30

I filled out the survey and had to write in ClojureScript.

dnolen14:12:28

@monjohn: thanks for sharing that, filled it out myself

gabe17:12:17

is there any way to dynamically determine the number of args expected by a function at runtime if all i have is a symbol?

gabe17:12:31

not fully qualified

jaen17:12:37

Not in Clojurescript.

jaen17:12:45

Clojure vars have metadata to that effect.

jaen17:12:57

Clojurescript has no vars and thus no such metadata.

gabe17:12:05

ClojureScript vars aren’t vars

gabe17:12:39

More precisely, ClojureScript var doesn’t return a var

gabe17:12:14

interesting…i’ll try it out

gabe17:12:11

@jaen: works great!

gabe17:12:38

wouldn’t work as well if i were using any multi-arity functions

dnolen18:12:58

@gabe: that’s not true var will return a Var in ClojureScript, but it’s a static not a dynamic feature

dnolen18:12:14

what you want is runtime ns-resolve and no that does not exist outside of the bootstrapped context

gabe18:12:34

@dnolen: so Var objects do exist in ClojureScript?

dnolen18:12:45

only as a static feature but yes

gabe18:12:41

makes sense. i’ve run into the lack of vars a couple of times in the last two weeks.

gabe18:12:58

trying to code too meta i guess

gabe18:12:37

anyway, thanks for the knowledge @dnolen simple_smile

dnolen18:12:44

for some history, they got added because clojure.test uses them and I ported that to cljs.test

jaen19:12:09

Well then, my bad, sorry.

gabe19:12:29

@jaen: you would have been correct a year ago

jaen19:12:09

Well, that's not helpful a year later though P ;

gabe19:12:03

well the Function.length tip more than made up for it

dnolen19:12:01

@gabe: note that alone could be very misleading

dnolen19:12:26

since it doesn’t communicate actual implemented arities nor whether some arity is variadic

gabe19:12:36

as i stated above. fortunately my use case doesn’t require anything more sophisticated

gabe19:12:02

as long as i don’t try to get too clever 😉

hkjels21:12:57

Is it possible to call a function by it’s string-representation?

hkjels21:12:02

that is the function-name as a string

martinklepsch21:12:12

I have a line like (reagent/render [welcome] (dom/getElement "abcxyz”)) causing a react error: Invariant Violation: _registerComponent(...): Target container is not a DOM element. When I do goog.dom.getElement(“abcxyz”) in the console it returns the DOM node. The script tag comes after the target container node. Anyone an idea? simple_smile

grav21:12:50

@hkjels: it is with javascript interop - (aget my-ns.core "my-fn”)

dnolen21:12:07

@martinklepsch: markup order matters

dnolen21:12:23

your script tag needs to come after the dom tag you want to use

dnolen21:12:09

@grav: you cannot do that due to advanced compilation

dnolen21:12:16

@hkjels: the real answer is no

grav21:12:27

@dnolen: ah, that’s right. Only tested it using the repl.

dnolen21:12:45

@hkjels: it’s not generally supported as it defeats advanced optimization

dnolen21:12:39

@martinklepsch: skepticism over here unless you use step debugging to prove this to yourself 😉

hkjels21:12:40

Ok. I’ll avoid it then and find some better way

martinklepsch21:12:08

with ^:export you should be able to access things by their string name, no?

dnolen21:12:28

yeah you could, but ^:export is really about presenting an API for external users

dnolen21:12:47

it’s not for making everything available at runtime

martinklepsch21:12:31

yeah, also thinking it’s probably not what @hkjels wants but who knows what he’s up to simple_smile

martinklepsch21:12:54

@hkjels: some sort of problem statement always helps people give appropriate answers simple_smile

martinklepsch21:12:05

@dnolen: how do I step debug the DOM rendering? Looking at the inspector/source the order is definitely right. println debugging shows though that at the point of rendering the selector returns null. will try some things...

hkjels21:12:30

I’m making a slideshow with function-names postfixed with the slide-number

joelkuiper21:12:46

is there a way of getting file upload progress with iframeio?

dnolen21:12:03

@martinklepsch: the JS doesn’t lie, something is going on such that the DOM element does not exist when the script executes

hkjels21:12:05

so the route /slides/1 should fire off slide-1 fn

dnolen21:12:37

@hkjels: sounds you don’t need all this machinery simple_smile

dnolen21:12:43

you just want to associate some fns w/ a key

dnolen21:12:01

{:foo (fn [] ...)}

hkjels21:12:32

that’s what I have now, it’s just not as DRY as I’d like it

hkjels21:12:35

no problem though

martinklepsch21:12:28

@hkjels: you can also just make a vector of functions if your slides have a well defined order

dnolen21:12:13

the other option is multimethods

dnolen21:12:29

there are functions to get the method for a dispatch key

hkjels21:12:28

@martinklepsch: that’s probably the densest solution without hackery simple_smile

hkjels21:12:25

@dnolen: I wasn’t entirely true earlier, multimethod is what I’m using

dnolen21:12:13

or a vector of dispatch keys

richiardiandrea23:12:42

@bensu how can I debug which test nss are taken into consideration in doo? the phantom and slimer tests are fine, nodejs are not even printing the namespace (only Testing with Node). I went in the compiled js and I see the require though.