@niwinz decided to give potok a shot, now wondering I have something that returns a promise, do you have an example how I would use that with WatchEvent
?
yes, just coerce it to reactive stream (rx/from-promise promise)
In uxbox I have get rid of promises
http and everything async related is now usig rx
don’t like promises anymore? 😄
I like them but they are not silver bullet, rx observables are designed from the ground up to separate composition from execution
they enables better resource management...
as you know, http requests are cancellable, and managing http request cancellation with promises is awful.
I have the pending task of release the httpurr library with promises replaced by observables 😛
I see, interesting perspective. You really are into observables haha
I searched the beicon docs for “promise” and similar but the from-promise
thing didn’t show up
http://funcool.github.io/beicon/latest/api/beicon.core.html#var-from-promise
not all api is documented with examples
ah ok, I thought the API docs were embedded in that page somewhere
sorry 🙂
So I now created a stream from the promise, now what’s the right way to do something when it succeeds/fails? For failures I use rx/catch
I guess? For success I use rx/map
? 😄
And another question: let’s say I want to log something for every event — How would I do that?
The beicon merge
example shows concat
: http://funcool.github.io/beicon/latest/#merge
Something that I think is really cool about re-frame is the ability to swap effect handlers for testing easily — have you developed any patterns to do similar things with potok?
I didn't need it or at least I'm not clearly understand the need of it.
If I don't want to some event to execute, I just redef it's constructor "Var"
the watch event, should return a stream of events, so you need is rx/map
over value
So for stubbing out an event you just redef it?
and return a instance of event that transforms the state
I now ended up with something like this
(rx/merge (rx/just (->UiStart (hash this)))
(->> (rx/from-promise
(-> (js/firebase.database)
(.ref (str (name type)))
(.push (clj->js (get-in state (into [::ui] form-path))))))
(rx/delay 4000)
(rx/map (fn [x] (->UiDone (hash this))))))))
which does pretty much what I needThat looks good if you need that
merge is not sequential, if you need to emit events in strictly order, rx/concat
can be used
later, the functions implements the update event, so you can emit them as is, without creating additional types
> later, the functions implements the update event, so you can emit them as is, without creating additional types Can you expand, I don’t think I understand
a plain cljs functions implements the UpdateEvent
, so you can emit them as is
@niwinz order is not super important, just that an event is put on the stream as it becomes available
(defn increment [state] (update state :counter inc))
(ptk/emit! store increment)
Interestingly when I replace merge
with concat
in the above snippet the UiDone
event is processed before UiStart
hmm strange
have you put the trace logs in the impl or in constructors ?
ptk/UpdateEvent
(update [_ state]
(js/console.log :UiStart (update-in state [::ui ::progress] (fnil conj #{}) op-id))
(update-in state [::ui ::progress] (fnil conj #{}) op-id)))
😞 maybe is a bug in concat
I’m sorry to be the conveyor of bad news 😄
hehe
on the contrary, you are helping!
Do you have any suggestion regarding the “log on every event” hook thing?
at this moment, potok does not comes with builtin solution.
However,
On uxbox, I define a own emit!
function that removes the need constantly pass the store on all the code
and I have put some traces in this function for log every emitted event.
a own emit!
function is just like a partial application function but with traces 😄
yeah, that seems to be a sensible approach
thanks
The question about testing/swapping “handlers” is still bugging me a bit but I’ll think about it some more
As I understand, in reframe the event and the implementation is splitted
I think it’s a tradeoff of attaching the implementation to the message instead of having a kind-of registry that defines implementation
yeah, exactly
do you know specify!
macro?
heh, that’s what I was just thinking about
on the other hand, the system is pretty flexible, I usually define events that does async stuff and other that does state transformation, each one with own constructors
if I want to mock some event that is emited indirectly
I guess I could implement a registry that does
{:some-event (fn [ev-id ev-args]
(reify
WatchEvent
(watch ....)))}
I just, redef the var of the constructor for it.
not specify! infact but kind of similar
yeah, you can implement a own registry like stuff
I think that’s an acceptable solution to this thought experiment for now 🙂
I have experimented in the past with splitted aproach (event and impl) and I found it too much mental overhead
How is it too much overhead? (Not used to the direct approach...)
in the last project when I have worked, the event and the impl was splited, events are defined as static constants and implementation are using some kind of register
the implementation are in one file
and events and event constructors are in other
wen you are working with complex flows,
this becomes very painful, constantly navigating through different files to understand the all the flow
having the event and impl in the same location, reduces considerably this pain, just reducing the number of files/namespaces to inspect.
in any case, potok is more than simple join of event and impl. the fact of using rx, helps on synchronize complex flows in a more "digestible" code
is my opinion
I think I understand what you mean but just technically splitting them doesn’t mean they need to be in different places. I can use a registry that refers to implementations that are located right where they’re used
Hmm, yeah, I agree
I need to think about it...
@martinklepsch thanks for the feedback and your thoughts!
@niwinz you’re most welcome haha
Enjoying how much simpler Potok makes async stuff
So, decided to swap my own wonky xhrio thing for httpurr, now I’m struggling to understand how to properly put it into the rx machinery
(->> (rx/from-promise
(http/send! xhr/client
{:url "/get-friends"
:method :post
:body (->> {:url "<https://api.twitter.com/1.1/friends/list.json>"
:method :GET
:user-params {}}
(merge (get-in @app-db [:current-user :credentials :twitter]))
(clj->js)
(gjson/serialize))
:headers {"Content-Type" "application/json;charset=UTF-8"}}))
(rx/delay 2000)
(rx/map (fn [data]
(js/console.log data)
(->UiDone (hash this))))))))
This is what I have… I would have expected the error to be raised somewhere if theres a 4** but instead it’s just logged in the rx/map
step. Is that expected?@niwinz ^ any suggestions? 🙂
hey @martinklepsch, in httpurr
the responses that are 4xx and 5xx are not treated as errors (the server responded); the connection errors or timeouts are
Oh! that makes perfect sense and sounds like a very reasonable decision 👍
somehow missed that reading the docs/assumed it would be like clj-http et al
can be a bit surprising but i think makes sense
you can discern reponse types and convert to a rejected promise in the then
handler
Agree, I very often use {:throw-exceptions? false}
when using clj-http
@dialelo in the example above I would use rx/map
to check the status code and then rx/throw
if I wanted an error to occur?
i'm not entirely sure about the rx
API but I think that will work
alternatively you can do it with promise's then
, something like (prom/then request (fn [response] (if (status/error? response) (prom/rejected "An error happened!") (parse-body response)))
alternatively, you can use this thin http client https://github.com/uxbox/uxbox/blob/master/frontend/src/uxbox/util/http.cljs
it is based on httpurr but uses rx streams
on future I have plans to export it as a library with httpurr similar api
@niwinz that’s a good pointer, thanks
@dialelo using rx/throw worked like a charm
@niwinz one benefit of using a registry (and not vars) is that you don’t to require a namespace with the var you need and instead can handle that in one spot
@niwinz that makes it a lot easier to move event definitions around as well
yeah, I know that it has benefits.
I need to think in a way of have the best of both
have the rx stuf from potok and have the ability to split impl and event construction
oh and one thing: the tip you gave earlier… using (partial emit! …)
with some logging on top — it doesn’t support logging of events that are the result of WatchEvents
instrumentation and logging support should be added...
partial emit!... is just a workaround
> "one benefit of using a registry (and not vars) is that you don’t to require a namespace with the var you need and instead can handle that in one spot" hmm, but in any case you will need to import ns where events are defined? I'm missing something?
or you are thinking in events defined as plain data?
> hmm, but in any case you will need to import ns where events are defined? I'm missing something?
Yes you still need to import them but through the registry indirection you could have a mapping of {keyword event}
and only provide to the keyword whenever you want to refer to the event
@niwinz if you end up thinking about logging I think it might also be nice to consider interceptors or a similar approach. I haven’t needed them a lot when using re-frame but they certainly seem very extendable and can easily solve issues like logging
ok thanks
i need to re-read the reframe doc
I hope what I’m saying makes sense
I’ll try to experiment a bit as well
😄
that is almost correct
just return (rx/of (->Firebase...) (->UiDone (has...))
or (rx/from-coll vector-of-events-here)
@niwinz not sure I understand, I need to do something with the result of the FirebasePush
event, rx/of has the same issue like rx/just, which is that data-snapshot
in the above snippet is just the FirebasePush record
I’m almost definitely approaching things from the wrong angle haha
hmm, seems that I have explained me badly 😛
the return value of mapcat should be a stream of events
not a vector of events
in your example code snippet you are returning a vector of events on mapcat
and in order to fix it, just use rx/of passing each event as positional argument or call rx/from-coll with the same vector that you are returning
(->> (rx/just (->FirebasePush type (get-in state (into [::forms] form-path))))
(rx/delay 1000)
(rx/mapcat (fn [data-snapshot]
(js/console.log :data-snapshot data-snapshot)
(rx/of (->FirebaseDomainObject type (.-key data-snapshot) (to-clj (.val data-snapshot)))
(->UiDone (hash this))]))))))
I’ve been using mapcat like that the whole time 😄
let me try
So in your snippet data-snapshot
will still be the FirebasePush record and not the result of that event
ofcourse
If you want to syncronize events, you should do something different
I think my understanding is… lacking a bit 😄
just wait 5 min, I'll try to explain how it can be done with an example
@niwinz that’s very kind, thanks in advance!
this is a pretty complex example synchronizing asynchronous flow
many function calls such as http/
and router/
are just examples
Additionally, one thing that is pretty cool in potok, is that you can emit anything you want, it is not mandatory to implement any event protocols
a good example are keywords
they serves pretty well for synchronize async flows in some ocasions
there are a example:
The flow is, EventA --> EventB --> EventC
EventC will be emited after EventB have finished
and EventB finishes emiting a keyword to the stream
i hope you find this useful @martinklepsch
Thanks for the detailed example @niwinz
after reading the potok docs earlier I remembered that this stream
argument to watch events was good for something
😄