carry

kauko 2016-06-29T10:49:47.000009Z

@metametadata: is there a way to change the state of a carry app through the REPL (or in general, programmatically)? I'm asking because devcards' history feature works by saving snapshots of the "data atom" to a "history atom", and then resetting the data atom when the user clicks on the rewind button

kauko 2016-06-29T10:50:24.000010Z

but since carry's model atom is actually read-only (EntangledReference), I don't think this can work

kauko 2016-06-29T10:51:26.000011Z

inspecting the card's data atom's state is straight forward to implement, just have to make devcards accept atoms that are read-only if history is not on

metametadata 2016-06-29T11:22:01.000012Z

Hey @kauko! The idea of Carry API was to force a user to only change model through the signals

kauko 2016-06-29T11:22:55.000013Z

is the signal log available from the REPL?

metametadata 2016-06-29T11:23:37.000014Z

so that's how state can be changed from the REPL: ((:dispatch-signal app) some-model-changing-signal)

metametadata 2016-06-29T11:24:44.000015Z

signal log is available only if you explicitly save it somehow, e.g. via carry-debugger middleware - it saves the log of actions and signals into the model under ::debugger key

metametadata 2016-06-29T11:26:02.000016Z

I'm not familiar with all devcards features, but maybe you could add a [:reset state] signal to your app and let Devcards call this signal instead of directly resetting "data atom"?

kauko 2016-06-29T11:27:05.000017Z

I'd rather think of some way to make devcards support carry's model, than assume that users want to add explicit devcards support to their app 🙂

kauko 2016-06-29T11:27:23.000018Z

TBH I don't think the history thing is that important, data inspection is much more important

kauko 2016-06-29T11:27:30.000019Z

but history would be nice obviously

metametadata 2016-06-29T11:29:05.000020Z

I don't quite imagine what you mean but I'd like to see how it works 🙂

metametadata 2016-06-29T11:31:58.000021Z

another idea is to use carry-debugger instead of devcards history feature, but we will need to simplify debugger's UI to fit into the devcards pages, e.g. by showing only back/forward buttons in the small widget..

kauko 2016-06-29T11:33:23.000022Z

The debugger will need to be split to model, reagent-view and rum-view namespaces anyway, so maybe if it had a devcards-view, that could be used from devcards

metametadata 2016-06-29T11:33:35.000023Z

exactly

kauko 2016-06-29T11:34:01.000024Z

Devcards has some Reagent specific macros, and I was thinking of adding some for Carry too

kauko 2016-06-29T11:34:15.000025Z

the macro could maybe set up the debugger

kauko 2016-06-29T11:34:15.000026Z

hmm

kauko 2016-06-29T11:35:16.000027Z

or.. add a :carry-debugger option to devcards. I'll have to talk to bruce about this at some point

metametadata 2016-06-29T11:37:53.000028Z

I doubt he'll be happy about bloating the devcards just to support some new frontend framework )

kauko 2016-06-29T11:38:08.000030Z

Yeah

metametadata 2016-06-29T11:44:30.000032Z

Another idea: augment your "data atom" reference with ISwap using specify! in order to make it send a special [:swap new-state] signal to your app. This way devcards rewind button will call swap! on the "data atom" and it will lead to sending the model swap signal.

kauko 2016-06-29T11:46:14.000034Z

Wouldn't that signal still need to be defined in the app somewhere?

kauko 2016-06-29T11:48:30.000035Z

Actually, how does the debugger implement time travel? Or does it?

kauko 2016-06-29T11:49:33.000036Z

Does it only allow you to go back to the initial model, and then replay all the actions that took you there?

kauko 2016-06-29T11:55:42.000037Z

side note (though probably a pretty important topic): should the dispatch log be available even without the debugger? Having that available in a prod environment seems pretty powerful, you could serialize it and send it over whenever you have an error

metametadata 2016-06-29T11:57:27.000038Z

yes. that signal would need to be additionally defined

metametadata 2016-06-29T11:58:12.000039Z

debugger is a middleware which catches all bypassing signals and actions. than it replays the actions starting from the initial model value to implement the time travel

metametadata 2016-06-29T11:59:00.000040Z

(so signals are not really replayed - only the action dispatched from signals)

metametadata 2016-06-29T12:00:58.000041Z

I agree, saving a log in production and recording it to debug real errors seems like a nice idea

metametadata 2016-06-29T12:01:48.000042Z

but I'd like to not make it a part of Carry core, an optional middleware would be better

metametadata 2016-06-29T12:03:30.000043Z

I even think the extracted carry-debugger-model (name is TBD) middleware will suffice to make it work

kauko 2016-06-29T12:08:12.000044Z

carry-signal-log? 🙂

metametadata 2016-06-29T12:10:11.000045Z

I dunno ) I'd like it to be something about a debugger as it will also add all the debugger's signal handlers

kauko 2016-06-29T12:11:11.000046Z

But if you use that to save the signal log in a prod environment, shouldn't the name reflect that somehow? You're not really debugging in prod 😛

metametadata 2016-06-29T12:11:57.000047Z

I agree, but I'd like to not have 2 separate middleware with a lot of duplicated code

kauko 2016-06-29T12:13:44.000048Z

Hmm I think I'm misunderstanding you. I thought you meant that you'd split the debugger's model into its own middleware, that could be used for the prod thingy, and if you want to use the debugger, you'd add that middleware AND a debugger-ui middleware of your choosing?

metametadata 2016-06-29T12:13:54.000049Z

well, and one can actually imagine it to be a "debugging", just an offline one )

metametadata 2016-06-29T12:15:32.000050Z

I guess you understand me correctly, let me write it down again so that we are on the same page..

metametadata 2016-06-29T12:21:45.000051Z

1) At the moment there's a carry-debugger middleware implemented - it catches signals and actions, is able to time travel, save and load logs etc. It also comes with a Reagent UI (a sidebar with buttons and stuff) 2) We'd like to split it into several middleware: carry-debugger-core (or carry-debugger-model or whatever) and separate UIs for Reagent, Rum etc. in order to make it work in Carry apps with different UI libs. 3) We'd also like to be able to record user sessions in order to send them to server on errors. This can be implemented on top of carry-debugger-core middleware.

metametadata 2016-06-29T12:24:48.000052Z

for a moment I thought you wanted to also implement signal-log middleware in addition to debugger-core one

kauko 2016-06-29T12:25:51.000054Z

When you say "implemented on top of", what do you mean? What would the final solution look like to a dev? Let's say I want to have a debugger UI and send errors etc to the server, would I have something like (-> app (debugger-model/add) (debugger-rum-ui/add) (some-third-middleware??/add))?

metametadata 2016-06-29T12:26:19.000055Z

well. yeah

metametadata 2016-06-29T12:26:40.000056Z

because the way you send logs to server is specific to your app

metametadata 2016-06-29T12:26:59.000058Z

maybe you send them to Sentry or maybe your own server

kauko 2016-06-29T14:10:21.000059Z

@metametadata: haha, actually Carry worked with devcards straight out of the box 🙂

kauko 2016-06-29T14:10:59.000061Z

Just had to do this

kauko 2016-06-29T14:11:59.000062Z

So basically we just need something like carry/app, but it needs to take an atom as the initial model

kauko 2016-06-29T14:12:08.000063Z

so devcards can use the same atom as its data atom

kauko 2016-06-29T14:12:54.000064Z

Oh wow, even the history seems to work!

kauko 2016-06-29T14:21:37.000065Z

@metametadata: I'm thinking, maybe the writable model atom should be returned in the application spec in some form. It's a good idea to not be able to reset it at whim in your code, but for development and debugging it could be handy to be able to access it from the REPL

kauko 2016-06-29T14:21:49.000066Z

if I have some faulty data in my model, I'd like to be able to edit it

metametadata 2016-06-29T15:28:38.000067Z

would it help if app returned :-writable-model or smt. in addition to :model and :dispatch-signal?

metametadata 2016-06-29T15:38:19.000069Z

I'd like to not add magic hidden keys if possible in this case because it's possible to edit the model explicitly by simply defining [:reset new-state] signal in your app

metametadata 2016-06-29T15:40:48.000071Z

or one can even create a utility middleware/function which exposes a "writable" app model for debugging

metametadata 2016-06-29T15:44:42.000072Z

I'll try to play with devcards today and see if we can make it work without changing the core API much

kauko 2016-06-29T15:57:01.000074Z

This would be enough to make carry work with devcards

kauko 2016-06-29T15:57:12.000075Z

Devcards would call -build-app directly

kauko 2016-06-29T15:57:38.000076Z

but honestly, all it needs is a way to provide the initial model as an atom, not a value

kauko 2016-06-29T15:57:40.000077Z

then it works

kauko 2016-06-29T15:58:23.000078Z

The writable atom / REPL thing I mentioned is not really related to devcards, it's another concern 🙂

metametadata 2016-06-29T15:58:34.000079Z

ah I see

kauko 2016-06-29T15:59:16.000080Z

But actually the :reset signal is a good point

metametadata 2016-06-29T16:00:12.000081Z

I have a feeling that we can make it work without an additional -build-app function

kauko 2016-06-29T16:00:55.000082Z

Great! How?

metametadata 2016-06-29T16:02:34.000083Z

we can make devcard's state atom a "proxy" which calls ((:dispatch-signal app) [:reset ...])

metametadata 2016-06-29T16:03:13.000084Z

or maybe not )

metametadata 2016-06-29T16:03:17.000085Z

I'll try to do that

metametadata 2016-06-29T16:04:13.000086Z

so that user will only have to call an utility helper function to bind Carry and Devcards together

kauko 2016-06-29T16:06:07.000087Z

Sounds interesting! Is it possible to add a "generic" [:reset ..] signal? One that is always available in a carry app?

metametadata 2016-06-29T16:06:21.000088Z

it can be easily added via a middleware

kauko 2016-06-29T16:06:52.000089Z

Yeah, I'm just wondering whether it should be added automatically

metametadata 2016-06-29T16:07:16.000090Z

ah, I'd rather force user to do it explicitly

kauko 2016-06-29T16:07:58.000091Z

Yeah that probably makes sense, I just brought this up because I know I would've come across that need very soon

kauko 2016-06-29T16:08:22.000092Z

and if it's a need that 95% of devs have, it could make sense to just have it available

kauko 2016-06-29T16:08:27.000093Z

and look as "familiar" as possible

kauko 2016-06-29T16:09:25.000094Z

but I mean, it's your call. 🙂 Just bouncing ideas

metametadata 2016-06-29T16:10:37.000095Z

And I appreciate your input a lot! I agree about 95% but it's hard to tell at the moment how many devs would need it ))

metametadata 2016-06-29T16:11:12.000096Z

another possible scenario is that most devs would like to dispatch actions instead of editing a model by hand

metametadata 2016-06-29T16:11:45.000097Z

and for some time there was a hidden :-dispatch-action available for this purpose

metametadata 2016-06-29T16:12:34.000098Z

but then I decided to delete it to keep API smaller and bc it's possible to simulate it using an additional signal

kauko 2016-06-29T16:12:57.000099Z

I wonder if there should be something like a carry.repl middleware

kauko 2016-06-29T16:13:02.000101Z

that adds these sorts of things

metametadata 2016-06-29T16:13:33.000102Z

yep, maybe

metametadata 2016-06-29T16:14:17.000103Z

let's see how it goes

metametadata 2016-06-29T16:14:41.000104Z

and add it when it feels like it's really commonly useful

kauko 2016-06-29T16:15:23.000105Z

btw, my reason for wanting to reset! and swap! the model directly is related mostly to filtering out bad data. In my current project I do that all the time. We draw stuff on a map and these things are defined as clojure data structures, so sometimes when there's a bug it's really handy to only draw things that have :type :multiline for example 🙂

kauko 2016-06-29T16:16:36.000106Z

So the easier I can see the data, and change the data through the REPL, the happier I will be 😛

kauko 2016-06-29T16:16:46.000107Z

some solutions hide it too much

metametadata 2016-06-29T16:17:07.000108Z

OK, cool example, it's the kind of scenario which doesn't include dispatching actions

kauko 2016-06-29T16:17:33.000109Z

Yeah, just what data is in the model 🙂

kauko 2016-06-29T16:18:49.000110Z

Another thing, I'm really liking Carry. Enough to want to contribute to it. Is there anything I can help with? I was thinking the devcards thing would be "my thing", but I'm happy to let you do it if you have ideas 🙂 I understand if you feel like you'd rather work on it yourself mostly

kauko 2016-06-29T16:19:37.000111Z

I'll probably start working on a series of blog posts tomorrow, I want to write about server-side rendering with Rum, and about working with Carry

metametadata 2016-06-29T16:20:26.000112Z

of course, it would awesome! but yeah, let's not hurry with devcards issue yet )

metametadata 2016-06-29T16:21:56.000113Z

I have a list of things I'd like to be implemented, e.g. debugger UI enhancements

kauko 2016-06-29T16:22:13.000114Z

would it make sense to have this list in github or something?

metametadata 2016-06-29T16:23:18.000115Z

right, but there are also a lot of stupid/esoteric items )

metametadata 2016-06-29T16:23:39.000116Z

so I'd rather much would like to hear what people need first )

kauko 2016-06-29T16:23:50.000117Z

Also, the reason I keep pushing devcards, is that in my experience Carry is the first.. framework(?) that really works with devcards. And it works out of the box with no hassle! The great thing about this is not that devcards is some best thing ever, but that if a framework does not work with devcards, that's a big big design smell IMHO

kauko 2016-06-29T16:24:07.000118Z

so the fact that Carry just works says to me, that there's been some great design decisions

kauko 2016-06-29T16:24:28.000119Z

it's another thing if they hold up when (if?) people start using it in anger

kauko 2016-06-29T16:25:14.000120Z

I'd like to write a blog post about this topic, because it really hilights the difference of re-frame and carry. I don't like comparisons, but re-frame is the most popular of the frameworks, so you kind of have to compare to it

kauko 2016-06-29T16:25:21.000121Z

but re-frame just does not work with devcards

metametadata 2016-06-29T16:26:28.000122Z

I see, yep, but carry also is not that devcards-friendly as we'd like it be - you had to rewrite the core function to make it work 😉

kauko 2016-06-29T16:28:24.000123Z

Which took like 1 minute

kauko 2016-06-29T16:28:26.000124Z

😄

metametadata 2016-06-29T16:28:54.000125Z

yeah, but it's only because the "framework" is 10 lines of code lol

metametadata 2016-06-29T16:31:41.000126Z

OK, I'll try to play with the Devcards issue and maybe it will also solve REPL "writable" atom issue. Splitting the debugger middleware also seems like a high priority. Please don't hesitate to tell about any other pain points.

metametadata 2016-06-29T16:33:34.000127Z

Oh yeah, server-side rendering is a cool feature to be able to implement

kauko 2016-06-29T16:34:31.000128Z

(defn debugger-added? [app]
  (-> app
      :model
      deref
      ::debugger
      some?))
I added this to my carry-rum-debugger

kauko 2016-06-29T16:34:44.000129Z

should probably be in the reagent one too

metametadata 2016-06-29T16:35:34.000130Z

or it can go into a "core" debugger middleware?

kauko 2016-06-29T16:35:43.000131Z

Yeah 🙂

metametadata 2016-06-29T16:35:46.000132Z

seems like a common function

metametadata 2016-06-29T16:35:49.000133Z

OK

kauko 2016-06-29T16:35:51.000134Z

yup

kauko 2016-06-29T16:36:30.000135Z

(defn start-app [app]
  (let [[app-view-model app-view] (carry-rum/connect app view-model/view-model app/view)
        [_ debugger-view] (when (debugger/debugger-added? app) (debugger/connect app))]
    (mount! app-view debugger-view)
    ((:dispatch-signal app) :on-start)
    (assoc app :view-model app-view-model)))
This is what I needed it for

kauko 2016-06-29T16:37:09.000136Z

the spec, app, and middleware are defined elsewhere

kauko 2016-06-29T16:37:42.000137Z

with a production profile I don't add the debugger, but I need to be able to use the same start-app function

kauko 2016-06-29T16:37:55.000138Z

which is why I have to have the debugger check 🙂

metametadata 2016-06-29T16:38:27.000139Z

I see

metametadata 2016-06-29T16:38:32.000140Z

OK!

metametadata 2016-06-29T16:39:25.000141Z

but what about the final JS file size? wouldn't it be smaller without carry-debugger added?

metametadata 2016-06-29T16:39:56.000142Z

I guess with your current approach all the debugger code will also be included in the prod build

kauko 2016-06-29T16:40:05.000143Z

oh, good point

kauko 2016-06-29T16:40:57.000144Z

maybe I wasn't that smart after all! :lol:

metametadata 2016-06-29T16:41:06.000145Z

so maybe if you reconfigure the bootstrap code you won't need debugger-added? anymore

metametadata 2016-06-29T16:41:06.000146Z

lol

kauko 2016-06-29T16:41:37.000147Z

The thing is, the connect call returns the react component

kauko 2016-06-29T16:42:13.000148Z

Right now the entry function, that is the first thing called despite which env we're using, only gets a spec

kauko 2016-06-29T16:42:37.000149Z

if the debugger is connected in the dev env code, it would have to take the app and some component

kauko 2016-06-29T16:42:40.000150Z

and the component can be nil

kauko 2016-06-29T16:43:07.000151Z

which isn't terrible, but it's kind of.. not pretty 😄

kauko 2016-06-29T16:46:58.000152Z

Anyway, I'm off for the evening. If there's anything I can help you with, please let me know, I'm anxious to help 🙂 Tomorrow I'll probably start working on the blog series (which will hopefully bring more people to Carry/Rum 😄 ), and I could see about publishing the carry-rum package (which is like, 15 lines long lol) during the weekend

metametadata 2016-06-29T16:47:21.000153Z

awesome!

metametadata 2016-06-29T16:47:32.000154Z

thank you 🙂

kauko 2016-06-29T16:47:34.000155Z

I'll also keep working on this demo app of mine, in hopes of finding more warts from Rum/Carry 🙂

metametadata 2016-06-29T16:48:14.000156Z

❤️

kauko 2016-06-29T16:49:10.000157Z

oh actually, I had a question. The way Carry handles async things seems really powerful, (signal comes in, send a :loading action, start AJAX call and :on-complete send another signal), but what if I have a signal where I want to send multiple actions?

kauko 2016-06-29T16:49:19.000158Z

Is that something that is supported, or even should be supported?

metametadata 2016-06-29T16:49:56.000159Z

like multiple parallel actions?

kauko 2016-06-29T16:50:10.000160Z

yeah

metametadata 2016-06-29T16:50:19.000161Z

hm

metametadata 2016-06-29T16:50:42.000162Z

maybe I got the question wrong, action just changes the model so it canNOT be parallel )

metametadata 2016-06-29T16:51:09.000163Z

you can dispatch-action in a sequence easily from the same signal

kauko 2016-06-29T16:52:01.000165Z

Yeah that's what I thought, the only problem I see there is that in theory if you have a signal that triggers 100 actions, stuff will be re-rendered 100 times 😄

kauko 2016-06-29T16:52:08.000166Z

But I guess that's not really Carry's concern lol

metametadata 2016-06-29T16:53:05.000167Z

yep, it's more of a problem of UI layer which can debounce model updates or redraw strictly 60 fps

metametadata 2016-06-29T16:54:07.000168Z

actually latest version of Reagent seems to add some optimizations - reaction updates are batched and UI is redrawn not immediately after a reaction update

kauko 2016-06-29T16:54:37.000169Z

you should see the light and start using Rum too 😉

metametadata 2016-06-29T16:55:25.000170Z

hah yeah maybe

kauko 2016-06-29T17:36:05.000171Z

https://github.com/Day8/re-frame/wiki/Dynamic-Subscriptions This is a problem that Carry doesn't have, right?

kauko 2016-06-29T17:41:51.000173Z

https://github.com/Day8/re-frame/wiki/Effectful-Event-Handlers This one kind of applies, but the pain is not felt with Carry so much because of the split between the controller and the reconciler

metametadata 2016-06-29T17:42:07.000175Z

Carry doesn't have subscriptions, but in carry-reagent you have something like them in the form of a view model

kauko 2016-06-29T17:42:18.000176Z

I'm looking at re-frame's wiki etc, because it's used widely so they've seen how it holds up

metametadata 2016-06-29T17:43:03.000177Z

"dynamic subscriptions" seem to be just reaction macro calls in a view-model

metametadata 2016-06-29T17:44:27.000179Z

yes, it looks to me that effectful handlers solve the similar problem I solved by separating signals and actions

martinklepsch 2016-06-29T20:08:15.000182Z

Hi! Really liking some of the ideas in carry, happy to be able to listen in here occasionally

metametadata 2016-06-29T20:12:49.000183Z

@martinklepsch Hi! Welcome and thanks!

metametadata 2016-06-29T23:04:47.000184Z

@kauko I think both Devcards and REPL issues can be fixed by introducing atom-sync middleware which makes the specified atom bidirectionally sync itself with the app model. I made a Devcards example, seems like history and hot reloads work on changing the spec and UI code: https://github.com/metametadata/carry/blob/d0efd9a11aff0a6967c345ddebb12f05b6a410c2/examples/counter-devcards/src/app/core.cljs#L72 This way we won't have to touch core Carry API at all.