untangled

NEW CHANNEL: #fulcro
cjmurphy 2017-07-02T00:51:45.755723Z

I've watched that video a few times. Is one of the best for understanding things - very good video. I see that load-data does have config as last argument, which can include params. So I guess can achieve [({server-property (om/get-query SubqueryClass)} params)] by other means - in fact I can see that [state-atom server-property-or-ident SubqueryClass config] will do the job.

cjmurphy 2017-07-02T01:01:12.785351Z

So -action means it is callable from within a client mutation?? That's the all important thing for me right now.

tony.kay 2017-07-02T01:38:20.894740Z

@cjmurphy yes. The -action is intended to mean that. It’s really simple how it works: Calls to load basically run a mutation transaction on the untangled mutation load (don’t remember the ns). The load mutation gets processed, but what it does is stores your desired load info into an app state load queue. The mutation specifies that it wants to be remote. When the layer just above networking sees the load mutation, it instead pulls the stuff from the load queue in app state and sends those queries instead.

tony.kay 2017-07-02T01:41:43.905625Z

The -action versions are the part that put the load on the queue. So, you still need to specify a remote-side, and data-fetch has a function (`remote-load`) for generating the right-looking thing for the remote side that will cause load queue processing to happen.

tony.kay 2017-07-02T01:43:27.910127Z

any number of load-actions can be run (each adding an entry to the load queue), and one remote-load (as the return value of the :remote side.

tony.kay 2017-07-02T04:13:27.333758Z

@cjmurphy You need a remote on it

tony.kay 2017-07-02T04:13:43.334529Z

that’s what I was saying in prior comment

tony.kay 2017-07-02T04:13:59.335397Z

(remote [env] (df/remote-load env))

tony.kay 2017-07-02T04:14:04.335760Z

inside your defmutation

tony.kay 2017-07-02T04:14:39.337535Z

load-action affects state, but Om won’t even try to go remote without a remote indicator, which is generated by the remote-load function.

tony.kay 2017-07-02T04:15:43.340816Z

The doc string for load-action is pretty specific about that. Do you not have docstring help?

tony.kay 2017-07-02T04:16:22.343019Z

otherwise it looks good

tony.kay 2017-07-02T04:17:34.346526Z

(defmutation load-existing-rules [{:keys [sub-query-comp source-bank target-ledger]}]
  (remote [env] (df/remote-load env))
  (action [{:keys [state]}]
          (df/load-action state :my-existing-rules sub-query-comp
                          {:target  help/rules-list-items-whereabouts
                           :refresh [[:banking-form/by-id p/BANKING_FORM]]
                           :params  {:source-bank source-bank :target-ledger target-ledger}})))

tony.kay 2017-07-02T04:20:22.354951Z

Mutations in Om can have multiple parts. The action is always local. It cannot trigger anything to do with networking. The other sections are names of remotes (the default name of a remote is remote). Each one indicates what should be done on that specific remote for this action. The special value true means “the same abstract thing that happened on the client`...but for load actions, you don’t want to run load-existing-rules on the server, you want to send the query. The load-action has stored the query in a queue. The remote-load returns an AST that will trigger the queue processing on that remote.

tony.kay 2017-07-02T04:21:35.358483Z

technically, for loads, triggering remote-load from any remote causes queue processing, and the queue entries themselves use a :remote option to indicate which remote they query

cjmurphy 2017-07-02T06:16:04.661798Z

@tony.kay The docstring even has example, so you are correct it should be pretty obvious. At the moment I just read docstrings by looking at the source (cntl+B on the function), where there is no colour coding or anything. The example is in the old pre-defmutation style and is at the top of some lines of comments. And a colon sometimes looks like a semi-colon to me. So to my eyes :remote (df/remote-load env) was just part of the comments and got missed. If it had been new style, starting with a paren, or the remote had been after the action, so with lots of whitespace around it - in either of those case it would have been hard to miss. Syntax colouring is probably my answer - I'll see about using the proper IntelliJ way of looking at docstrings.

tony.kay 2017-07-02T06:18:14.666911Z

Yeah, updating the docstrings would be good 🙂

cjmurphy 2017-07-02T06:27:45.689224Z

It is cnrl+Q on my Linux machine for looking at docstrings, but it doesn't have a different colour for code. Really I should have understood/researched your comment: "`data-fetch` has a function (`remote-load`) for generating the right-looking thing...". All my eyes needed was to see (remote [env] (df/remote-load env)), which was nowhere to be found. I did try (remote [_] true), but of course that didn't help.

tony.kay 2017-07-02T06:42:36.728341Z

Yeah, this was one of the important realizations in Untangled: how to turn mutations into loads so that we don’t need a parser to be involved in remote queries. It turns out that you need a mutation to modify state so that your parser can trigger a load anyway in Om Next (otherwise you’d be loading at every parse…the state has to change so your parser can be intelligent about loading). So, the trick was to realize that any mutation with a remote AST will cause Om Next to try to send that over the wire, so we hack into that to transform the mutation into a query. The only downside is that composing loads into mutations then looks a little wierd.

tony.kay 2017-07-02T06:44:45.733823Z

I guess there could be some macro similar to defmutation that always indicates you’re wanting to process the load queue and does it for you.

cjmurphy 2017-07-02T06:45:17.735219Z

I'm going to be calling om/tranact! from within a defmutation... that's what about to try

tony.kay 2017-07-02T06:45:31.735846Z

no, that is not advised

tony.kay 2017-07-02T06:45:54.737206Z

mutations are the bit twiddling. Don’t embed a higher-level abstraction in them

tony.kay 2017-07-02T06:46:54.739841Z

modify state, indicate what is remote. That’s all.

cjmurphy 2017-07-02T06:46:57.739955Z

So I'll compose df/load-action into the threading macro on the state.

tony.kay 2017-07-02T06:47:04.740254Z

yep

tony.kay 2017-07-02T06:47:10.740466Z

that is the intention

cjmurphy 2017-07-02T06:47:22.740989Z

So the other one that wasn't remote before will now become remote too.

tony.kay 2017-07-02T06:47:31.741303Z

?

tony.kay 2017-07-02T06:48:07.742688Z

You’re writing one mutation, right?

cjmurphy 2017-07-02T06:48:20.743277Z

One that calls another.

tony.kay 2017-07-02T06:49:03.744951Z

I don’t see one that calls another

cjmurphy 2017-07-02T06:50:02.747622Z

When a drop down is loaded with all its values - thats the first mutation. It works out the value which will be the chosen value of the drop down. Now data needs to be loaded from remote for this chosen value.

tony.kay 2017-07-02T06:50:30.748948Z

ah, so the completion of one load needs to trigger another?

cjmurphy 2017-07-02T06:50:48.749727Z

No - the first is not a load

tony.kay 2017-07-02T06:51:21.751169Z

so, you’re trying to write a mutation that 1. sets the dropdown display, and 2. triggers a load

cjmurphy 2017-07-02T06:51:28.751507Z

(as it happens in my case)

cjmurphy 2017-07-02T06:51:37.751942Z

Yes

tony.kay 2017-07-02T06:52:18.753593Z

ok. (swap! state (fn [s] (-> s (do-dropdown-update) (load-action :kw ...))))

tony.kay 2017-07-02T06:52:22.753791Z

within action

tony.kay 2017-07-02T06:52:27.753966Z

and remote-load in remote

cjmurphy 2017-07-02T06:53:04.755538Z

Yes good. So the first mutation now becomes a remote when it wasn't before.

tony.kay 2017-07-02T06:53:31.756703Z

what first mutation? You mean do-dropdown-update?

cjmurphy 2017-07-02T06:53:58.757870Z

Yes - put the list of values in the actual drop down control.

tony.kay 2017-07-02T06:54:01.758003Z

that isn’t a mutation, it is a function

tony.kay 2017-07-02T06:54:25.759074Z

that is part of the implementation of the action (local only) of a mutation

cjmurphy 2017-07-02T06:54:34.759379Z

It is a mutation because it is changing state.

tony.kay 2017-07-02T06:54:44.759781Z

the remote-load sends an AST that just processes the network queue

tony.kay 2017-07-02T06:54:48.759998Z

it knows nothing of what you did to state

tony.kay 2017-07-02T06:55:29.761897Z

if you say “remote true”, then you’re asking the top-level transaction to go over the wire…still doesn’t know the guts of action

tony.kay 2017-07-02T06:55:55.763009Z

by using remote-load, you’re replacing the top-level transaction with a special AST that processes the network queue

cjmurphy 2017-07-02T06:56:27.764338Z

I can just try it both ways, with and without making the first mutation remote. So it sounds right from what you are saying to make the first one be remote true.

tony.kay 2017-07-02T06:56:37.764828Z

(I’m using the term mutation here to mean the top-level transaction)

tony.kay 2017-07-02T06:56:51.765483Z

I don’t see a first one

cjmurphy 2017-07-02T06:56:59.765830Z

I've got two defmutations.

tony.kay 2017-07-02T06:57:02.765955Z

so you’re just confusing me. I see defns

cjmurphy 2017-07-02T06:57:13.766487Z

Both local.

tony.kay 2017-07-02T06:57:16.766630Z

not in the code you posted

tony.kay 2017-07-02T06:58:28.769637Z

but I assume you just elided that. So, remote true means you want to send the top-level tx, as DATA, across the wire to the server, as is from the UI

tony.kay 2017-07-02T06:58:39.770084Z

if that is what you want, then yes 🙂

cjmurphy 2017-07-02T06:58:49.770467Z

Yeah the code I posted is the second defmutation, that I'm calling from another one.

tony.kay 2017-07-02T06:59:05.771164Z

If you’re doing a load-action, you do not want to send the top level UI tx…you want to process the queries of load-actions.

tony.kay 2017-07-02T06:59:38.772724Z

but you don’t call one defmutation from another

tony.kay 2017-07-02T07:00:56.777770Z

you either: 1. Write a transact that does the two in succession: (om/transact! this '[(a) (b)]) 2. Write a transact that calls one (om/transact! this '[(combo)]) and then in that one mutation you do the local updates along with load-action

cjmurphy 2017-07-02T07:01:11.778651Z

I think I have to. No way round it - if not actually call, do the equivalent of calling.

cjmurphy 2017-07-02T07:02:01.781102Z

Yeah I'll be doing the second, but semantically it is a call of one of the other.

tony.kay 2017-07-02T07:02:05.781372Z

Why not write a query that asks for both facts you need, as a union query. Then the server can tell you everything you need with one load

tony.kay 2017-07-02T07:02:16.781852Z

without loading more than you need

tony.kay 2017-07-02T07:02:26.782369Z

chaining is a bad practice in general

tony.kay 2017-07-02T07:03:37.785970Z

[{:dropdown-options (om/get-query DD)} ({:subdata (om/get-query Union)} {:current-selection :a})]

tony.kay 2017-07-02T07:03:49.786615Z

which you could queue up with two calls (on one event) to load

cjmurphy 2017-07-02T07:03:58.787070Z

There are other ways. Good to think...

tony.kay 2017-07-02T07:04:02.787290Z

and use :params to send the current selection

tony.kay 2017-07-02T07:04:37.789054Z

either the server knows the complete answer, or you know a fact you can tell the server. There is no need for chaining in these cases 🙂

tony.kay 2017-07-02T07:05:14.790915Z

(and two calls to load in the same event handler will be sent together as one request)

tony.kay 2017-07-02T07:05:57.793276Z

(load this :dropdown-options DD) (load this :subdata Union {:params {:current :a}})

cjmurphy 2017-07-02T07:06:11.793946Z

Yeah the first mutation is choosing the one to be selected as current in the drop down, but that is the wrong place for that choice, I think...

tony.kay 2017-07-02T07:07:02.796377Z

It’s tough to un-learn the habits of js 🙂

cjmurphy 2017-07-02T07:07:51.798709Z

Well especially when you never were a js programmer haha!

tony.kay 2017-07-02T07:07:56.798934Z

😄

tony.kay 2017-07-02T07:08:21.800050Z

You’re doing a thing like “choose your make of car”, then “choose a model”, where model gets loaded after they choose a make

tony.kay 2017-07-02T07:08:23.800154Z

right?

cjmurphy 2017-07-02T07:09:18.802767Z

Yes - one dd to another dd. My first is types of account - Expense, Income etc

cjmurphy 2017-07-02T07:09:35.803558Z

And second is then all the expenses say.

tony.kay 2017-07-02T07:09:40.803849Z

sure

tony.kay 2017-07-02T07:10:00.804863Z

you prob don’t even need a union then

cjmurphy 2017-07-02T07:11:31.809186Z

Are you saying I should have all the models for all the cars already loaded?

tony.kay 2017-07-02T07:11:48.809919Z

1. the initial setup (e.g. a load of the first dd content) 2. The user interacts with the dd. This triggers (transact! this '[(choose-first {:val v})]) 3. The choose-first defmutation: 3a. Threads state through a function that updates the dropdown they interacted with 3b. continues the thread through a load-action that can send the proper query and param based on v 3c. Has a remote of (remote-load env)

tony.kay 2017-07-02T07:12:33.811951Z

When the load completes, the second dd is now populated

tony.kay 2017-07-02T07:12:56.812980Z

(assuming you targeted the load-action result to the dd state, or otherwise got it there with a post-mutation)

cjmurphy 2017-07-02T07:13:18.813939Z

Hmm - problem is I need to load the second lot before any interaction.

tony.kay 2017-07-02T07:13:28.814354Z

but you don’t know which to load

tony.kay 2017-07-02T07:13:36.814710Z

until they select the first dd content

cjmurphy 2017-07-02T07:13:47.815259Z

Yes - the default one that the first mutation is choosing.

tony.kay 2017-07-02T07:14:07.816181Z

how is the default chosen?

tony.kay 2017-07-02T07:14:29.817335Z

you must have known it at step (1), so you can do it during that load

tony.kay 2017-07-02T07:14:48.818211Z

so yes, you’d preload the “models” for the known “default” make

cjmurphy 2017-07-02T07:14:53.818396Z

By default "Expenses" say. Kind of arbitarily - perhaps the most popular choice in my mind, so user has to do less actions.

tony.kay 2017-07-02T07:15:16.819554Z

yeah, but you’re loading data they may not want to see at all, which is extra overhead for your user and your server

cjmurphy 2017-07-02T07:15:20.819748Z

I've got no "No choice" in the drop down.

tony.kay 2017-07-02T07:15:21.819814Z

bad for multiuser performance

tony.kay 2017-07-02T07:15:51.821333Z

but whatever…you can do it either way, as I just described…just move the default choice load to step (1)

cjmurphy 2017-07-02T07:16:40.823754Z

But they are always going to go to the next step.

tony.kay 2017-07-02T07:17:05.824695Z

I don’t understand “but”. What’s missing?

tony.kay 2017-07-02T07:17:14.825143Z

the next step is as I described above

tony.kay 2017-07-02T07:17:26.825634Z

event triggers dd update and next load

cjmurphy 2017-07-02T07:17:37.826085Z

Was talking about perf, I didn't get the issue there.

tony.kay 2017-07-02T07:17:39.826189Z

steps 2+

tony.kay 2017-07-02T07:18:03.827232Z

oh

cjmurphy 2017-07-02T07:18:42.829024Z

If I make the first choice explicit at the client level (not defmutation level) then I think my design becomes better.

tony.kay 2017-07-02T07:18:52.829462Z

say you show me expenses. You just loaded data from the server. That just consumed CPU and disk IO. Say you have 10000 users. They all go to that screen. You just did 10000 bits of CPU and IO. Now let’s say 20% of them didn’t want that. You just blew 2000 users worth of interaction time…slowing down the other 8000 as well.

cjmurphy 2017-07-02T07:19:16.830729Z

ie "Expenses" - no reason that should be down at defmutation level - then my problem goes away.

cjmurphy 2017-07-02T07:20:19.833965Z

They will all want it in my use case. They are creating a rule that they must create. So the 20% you mention is really 100%.

tony.kay 2017-07-02T07:20:43.835205Z

oh, then you already know everything you need at step 1

cjmurphy 2017-07-02T07:21:28.837525Z

Yes. But I needed your help to get a better design. So thanks for taking the time. Working solo here...

tony.kay 2017-07-02T07:21:38.837950Z

NP. Glad it helped

👍 1
tony.kay 2017-07-02T07:22:05.839300Z

My experience with Untangled so far has been: If it’s hard, you’re doing it wrong or missing a concept.

tony.kay 2017-07-02T07:22:32.840558Z

So, when I run into something that seems difficult, I try to remind myself of that.

tony.kay 2017-07-02T07:24:06.844954Z

so far, I have yet to find anything that didn’t work out 🙏

tony.kay 2017-07-02T07:24:16.845414Z

:knock-on-wood:

1
tony.kay 2017-07-02T07:26:01.851188Z

The “chaining” temptation is tricky: I loaded A, now A told me something that makes me want B. The thing is, unless you’re loading from more than one server, the server knew about what was in A when it sent it to you, meaning you could have also asked for B in a generic sort of way and had it handled at the same time.

tony.kay 2017-07-02T07:29:29.860814Z

There’s nothing technically impossible about using transact! within a mutation, other than it leads to a design where you could conceivably end up with recursive-looking transaction loops. So, poor design. If you had to do a two-server chained load, I think you’d probably be stuck with it, though. I just strongly encourage not doing it in most cases. Hm. actually, I take that back. There’s no way to get the timing right. You’d probably have to install a merge handler and trigger the second one from there. Or use a post mutation to trigger a transact! against app. Post mutations are not allowed to have remote sides (they’re ignored).

tony.kay 2017-07-02T07:33:53.875322Z

If possible, I’d probably design server A to talk to server B for me, and avoid the whole client scenario 😜

wilkerlucio 2017-07-02T18:45:51.379761Z

hello people, I would like to announce a new little open-source project I built with Untangled, it's a Chrome extension to represent an Youtube Queue based your emails, I made a post about it, I hope you can enjoy it 🙂 https://medium.com/@wilkerlucio/using-gmail-messages-to-track-youtube-channels-ef35308a0ceb

🙂 5