Fork me on GitHub
#clojurescript
<
2017-09-29
>
dviramontes01:09:43

Hi all, has anyone here tried to use re-captcha in a CLJS project ?

Geoffrey Gaillard07:09:38

Hi ! I did and it works quite well. First I included the re-captcha script tag in my index.html. I'm using reagent, so I made a function called re-captcha that I call in the :component-did-mount callback

Geoffrey Gaillard07:09:52

(s/fdef re-captcha
        :args (s/cat :dom-id string?
                     :callback ifn?))
(defn re-captcha
  "Display a google re-captcha securer"
  [dom-id callback]
  (.render js/grecaptcha dom-id
           #js {:sitekey  (:recaptcha_client_key (get-env))        ;"6Lec_hEUAAAAALbHlhuwhunfF3RAu6kabEz5qaV2"
                :callback callback}))

(defn reset-captcha!
  ([]
    (reset-captcha! nil))
  ([captcha-id]
    (if captcha-id
      (.reset js/grecaptcha captcha-id)
      (.reset js/grecaptcha))))

Geoffrey Gaillard07:09:46

You might notice I have a function called get-env, it returns a map of environnement variables. This way I have a different re-captcha siteKey in dev, test and prod.

dviramontes01:10:09

hi @U2DART3HA that worked / thanks very much!

pesterhazy08:09:16

@lilactown @yury.solovyov I wouldn't jump to using core.async without compelling reasons. Promises are pleasant to use from cljs, and they're easily available in node

qqq09:09:26

in addition to @pesterhazy , I've found that I like reqctive-atoms much much more than core.async: reactive-atoms = think about relationships, core.asyc = think about processes, and reactive-atoms gives better stack traces too

pesterhazy09:09:47

@qqq, are you talking about reagent's ratoms? OP's context was using Node.js apis I think

qqq09:09:22

@pesterhazy: yes, reagent ratoms, I'm pretty sure they can be used without gui; though perhaps I misunderstood OP's question

pesterhazy09:09:44

you probably can but I doubt it's a good idea...

qqq09:09:39

why not? I really like the idea of functional reactive programming / specifying a graph of "when this updates, update these other things too" -- it's like spreadsheets -- I find it far more declarative / easier to reason about than core-async

pesterhazy09:09:38

ratoms are intended to used in react render functions. Maybe a similar but more general-purpose abstraction could be built but AFAICS ratoms are not that

qqq09:09:28

actually, I thought r-atoms just did some black amgic to keep track what the given atom derefs, then addas watcher to the derefed atoms to auto update

qqq09:09:41

I'm not sure where the react render part comes in.

psdp12:09:55

Not sure if it is the appropriate place to ask the question here, but I just couldn't figure out how to make it work with rum... https://github.com/clauderic/react-sortable-hoc/blob/master/examples/basic.js

metametadata12:09:20

I guess there're alternatives to ratoms for doing reactive computations, e.g. freactive.core. I compiled a list of similar libs a while ago here: https://metametadata.github.io/carry/faq/#is-there-a-way-to-code-a-view-model-if-i-dont-use-reagent Although using ratoms or the like as alternative to core.async and promises sounds interesting, I wonder how to deal with error handling then? Promises have the built-in notion of errors and for core.async smt. like <? macro can be used. cc @qqq @pesterhazy @lilactown

pesterhazy14:09:34

I'm increasingly convinced that Promises are officially a Good Idea[TM] and work really well with ClojureScript

dpsutton14:09:22

agree. had to interact with firebase which is all promise based. quite nice actually

metametadata14:09:25

It was OK working with promises in JS, but I bit the bullet and went directly to using core.async with <? macro in CLJS. I like that I can use built-in try-catch-finally to handle errors instead of the DSL and code reads almost as usual synchronous one (syntactically it looks quite similar to JS code with async/`await`).

mccraigmccraig14:09:10

i have promise-based cljs code reading almost like synchronous code @metametadata πŸ™‚

pesterhazy15:09:39

To me core.async in CLJS has drawbacks. Extra dependency, doesn't work great in bootstrapped cljs (lumo), channel abstraction not ideal to just chain async computations (main use case), macros are not composable

pesterhazy15:09:16

The fact that it covers both cljs and clj has its cost - it core.async may be better for clojure, but for cljs it's often (not always!) overkill

metametadata15:09:16

@mccraigmccraig do you use some kind of library? promesa I guess?

pesterhazy15:09:36

Promises by contrast are just functions πŸ™‚

mccraigmccraig15:09:47

here - using one of the promise-based js fs apis - with promesa+cats @metametadata - https://gist.github.com/mccraigmccraig/a8fe17eb500b231e8cc949bb74adb49f

metametadata15:09:56

@pesterhazy In Lumo I use deasync and make all my code literally synchronous πŸ˜„

metametadata15:09:08

I guess it's written in C

metametadata15:09:57

works nicely for my shell scripting needs as I don't really want to coordinate anything asynchronous and just execute commands in sequence

mccraigmccraig15:09:31

ever since i realised that my utility scripts often end up being bundled up into api-side functions i started to write everything async and focused on ways of making async as easy as possible to write/grok

pesterhazy15:09:08

@mccraigmccraig same for me - you start off goofing around synchronously-ish but eventually it'll end up in your code

lilactown15:09:38

personally I find that channels are a much better abstraction as soon as I want more than one value over time

lilactown15:09:28

however, I've realized after writing it in channels that I actually don't need that functionality πŸ˜… wishing I had gone with promises now

lilactown15:09:20

especially given the overhead of core.async and compiled cljs vs. using a lumo script

lilactown15:09:37

I rolled with just using lein's figwheel-node template for a simple command line app and the bundle is 1.5mb. can't use advanced optimizations because I used core.async too πŸ˜†

anmonteiro15:09:36

@lilactown FWIW you can run your Lumo script with -K and subsequent runs will be faster (core async compilation will be cached)

lilactown15:09:54

i'm working on converting it from standard cljs to use lumo now

lilactown15:09:05

i might just rewrite it to use promises, too

metametadata15:09:49

for comparison, here's how my core.async/<? code looks like: https://gist.github.com/metametadata/bb31af03c9949b3518ed2f94f3c84ce3

metametadata15:09:19

@lilactown for CLI apps I would really recommend deasync-ing async functions if in nature your code is sequential

lilactown15:09:14

it's incredibly async

lilactown15:09:41

it basically makes 10-20 HTTP requests and saves the json responses to files πŸ˜›

metametadata15:09:48

aah, ok then πŸ™‚

lilactown15:09:24

that deasync library is pretty cool

lilactown15:09:43

that's some serious black magic

lilactown15:09:42

@metametadata i'm gonna use that gist as a reference for error handling in my app πŸ˜„

lilactown15:09:45

thanks for posting

metametadata15:09:55

sure, you're welcome. you'll need to find some implementation of <? though. we hand-rolled our own but it's not open-sourced yet

lilactown15:09:47

you said you're using cats for maybe?

metametadata15:09:15

no, cats is what promesa uses as far as I understood

mccraigmccraig15:09:31

promesa doesn't use cats, but cats has a promise-monad implementation for promesa

pesterhazy16:09:06

just sayin' - promises give you error handling as well

kennytilton16:09:12

@qqq β€œit’s like spreadsheets” Great, isn’t it? I ported my reactive Cells library from CL to CLJ/CLJS (re-christened Matrix) and started on a web framework but am just starting on the doc. I have a TodoFRP treatment here: https://github.com/kennytilton/todoFRP/blob/matrixjs/todo/MatrixCLJS/README.md A lot of that is still a copy/paste from the sister JS version ../MatrixJS. Ping me if you have Qs at kentilton at gmail.

yury.solovyov16:09:18

is it possible to wait for promise by de-refing it in cljs?

yury.solovyov16:09:55

(let [value @promise])

darwin16:09:54

@yury.solovyov no, because Javascript is single-threaded

yury.solovyov16:09:50

I meant is it possible to make js promises feel the same way?

darwin16:09:49

yes, for example with core.async, the go macro will rewrite your linear-looking code to be callbacks under the hood

darwin16:09:25

it is pretty easy to convert js promises to go channels and back

yury.solovyov16:09:35

I know it is possible with core.async (I really need to write questions better), but what I'm trying to get into, is I want to be able to use js promises as in clojure or in go blocks, but with all that stuff hidden from me

yury.solovyov16:09:21

maybe it is possible to implement new kind of promises that wrap native js ones and "wait" on deref?

yury.solovyov16:09:47

with whatever is needed under the hood

lilactown16:09:54

the semantics of a ratom and a promise are quite different though

darwin16:09:14

@yury.solovyov I'm not aware of any experimental work being done in that area

lilactown16:09:24

a ratom is never in an "incomplete" state

thheller16:09:29

@yury.solovyov no, not possible. due to the async nature. once you go async everything has to be async.

thheller16:09:55

core.async go is a good boundary for that

thheller16:09:29

just like the async keyword in JS, if you use it once you have to use it everywhere basically πŸ˜‰

yury.solovyov16:09:34

but in cljs go is basically free, right?

darwin16:09:35

indeed, you would still need something like go macro to wrap your code in

darwin16:09:42

or I can imagine someone trying to emit async/await modern javascript

darwin16:09:04

which is AFAIK very similar thing, it can be transpiled into callback-style code

thheller16:09:23

@darwin async/await is basically the go macro of JS

darwin16:09:30

but must be explicitly bounded

thheller16:09:46

does pretty much the same thing just as a language feature

yury.solovyov16:09:16

well, that's fair, thanks

darwin16:09:24

btw. I tend to view core.async as generalization of promises, they can be called multiple times and yield a stream of values instead of one

darwin16:09:55

so the conversion promises -> channels is quite clear, the other way must introduce some strong assumptions

darwin16:09:36

also error handling / exceptions problem is quite blurry in my mind - depends on situation

darwin16:09:04

so it is not that easy to cross that boundary in more general cases

yury.solovyov16:09:07

I kinda wish I had to deal with less channel passing all over the place

darwin16:09:18

it is just different manifestation of the callback-hell, less error-prone I would say

darwin16:09:53

try to design core of your app to be synchronous data-in -> data-out, and then wrap it on the edges with async stuff

darwin16:09:03

similar approach as with purity

yury.solovyov16:09:03

that's what I did, but the async part still need some love w.r.t. generality

darwin16:09:05

I think Tim covered some of the ideas how to "hide" async parts of your app here in this talk:

yury.solovyov16:09:40

it takes a while to "see" these places tho

yury.solovyov16:09:06

and then to come up with an elegant replacement

yury.solovyov16:09:52

I'm not complaining, it just takes time to develop these senses

darwin16:09:55

yes, instead of working with channels directly, you should be building transducers (still sync code) and glue all together at the edge of your app (where you be async)

yury.solovyov17:09:33

yup, I even have a TODO with "transducer here?" πŸ™‚

darwin17:09:37

sounds pretty abstract, but he showed some concrete examples

darwin17:09:07

think about transducers as functions which do not work with input and output data structures directly, they are abstracted from it

darwin17:09:44

so instead of threading a data through a set of functions to transform it, you first compose transducers of those transformations and get a composed transducer back, and then use some machinery to put transducer, input data structure and output data structure together to get the same result

yury.solovyov17:09:08

but they have to be sync, right?

yury.solovyov17:09:28

so then you need to pipe some channels or something

darwin17:09:48

if there is some asynchrony associated with input or output, you are not affected by it when working on transducers

darwin17:09:30

and that's the trick how to keep core of your logic synchronous

yury.solovyov17:09:39

well, what if to know the next value, I need to call an async thing?

darwin17:09:29

pretend that you have it πŸ™‚ and some machinery which will be composing your code at the edge of your app will wire it with async stuff πŸ™‚

darwin17:09:51

but this is getting too abstract, it is not always easy, transducers or function composition is just a tool

yury.solovyov17:09:06

ok, now I don't get it, how can I pretend? say I'm reducing a channel with sum of values, to get the next value I need to call something async. how do I pretend?

yury.solovyov17:09:08

ok, maybe it is too abstract

darwin17:09:48

In your example I was concerned about the function +, that is syncronous and does not need to be involved in async ops, reduction over a channel is that machinery happening at the edge of your app

darwin17:09:25

the question is how can you restructure your code which is a chain of async ops to be a chain of synchronous operations which get assembled by something at "outer" level and that something knows how to do async/await

yury.solovyov17:09:23

I get it when people say that I need to move async stuff to "the edge of your app", but it is too hard to act on, like I still need to build that part, even on the edge of my app, and for some reason nobody talks about it πŸ™‚

darwin17:09:58

watch the talk

yury.solovyov17:09:41

ok, I'll re-watch it cause I've seen it, but probably missed something the first time

yury.solovyov17:09:49

Still thanks for a chat

uwo19:09:52

what’s a good way to load code from cljs/user.cljs only during dev?

uwo19:09:50

(I’m assuming that there would be an analog to the clojure user.clj file)

uwo19:09:12

nvm. preloads