Fork me on GitHub
#om
<
2016-03-28
>
threepointone06:03:27

hello! was thinking about how to do 'ttl' caching on data, and the current approach I've thought is - - store data normally in my app atom - always return :remote true from read - in my send fn, instead of directly calling a service, also check an in memory cache with ttl innards, and merge those results in - an alternate approach, (which iirc is how falcor does it), is to store the ttl timestamp directly in my state atom ,and trigger :remote only when the data "expires". what are the pros and cons of either approach? any other alternatives? please and thank you!

tomjack07:03:37

interesting, I think I just asked a related question?

tomjack07:03:13

do you know how you would 'trigger :remote' when data expires?

threepointone07:03:19

when saving, I'd store an expiry time with each entity, and in read, I'd check that value against current time to decide. it makes read impure though (unless you include your own 'clock' on env, etc... ugh)

threepointone07:03:29

former seems simpler

threepointone07:03:34

but yeah, similar to your issue! you'd have to maintain an object (for lack of a better word) that maintains your subscriptions, and updates its configuration based on incoming reads

tomjack07:03:40

I see. I want sends when ttl expires even if no reading is happening; different problem?

threepointone07:03:38

you could hold on to the merge callback (which you mentioned) and call it when your ttl (for a given subscription) expires and you make a fresh fetch

tomjack07:03:10

if alexanderkiel is right about what touch! should do ("...also read from remotes"), then I think maybe solving your problem plus periodic touch! may solve my problem

threepointone07:03:33

oh sweet! cmd-r for components! yes!

tomjack07:03:46

I'm tempted to put my own clock in the state as you say

threepointone07:03:13

scary. surely there's a simpler way

tomjack07:03:40

are you thinking of setting the ttl in query parameters?

threepointone07:03:25

no, just a configuration on the cache

threepointone07:03:44

in general, I interact with any 'outside world' things in send - window.history, ajax calls, external caches. even if they're synchronous, I don't access them in mutate actions. I believe this is the right approach?

tomjack08:03:21

so I think reading big query results out of a cache in your send may incur some cost

tomjack08:03:53

you will always do a send, and always call the cb, on every read, which means a merge-novelty! for every read?

threepointone08:03:54

...yes? if the data hasn't changed, this should be cheap/no-op? and maybe you could it batch it/optimize at the send layer. I dunno 馃槓

tomjack08:03:26

if you have a somewhat deep and wide tree of component data, I don't see how it could be O(1). you need to merge the result of the query into the entire current app state

tomjack08:03:28

I could imagine that maybe things are already O(n), so another O(n) is only a constant factor difference

tomjack08:03:36

not sure though

tomjack08:03:55

but also conceptually it seems strange to merge novelty for every read, regardless of perf concerns

threepointone08:03:43

literally what browsers did pre-ajax, full page/iframe reload? or does that not count

dnolen16:03:48

1.0.0-alpha32 released

frank17:03:15

I'm experiencing strange behaviour in 1.0.0-alpha31 where setting an onChange handler that uses om.next/transact! on an input element makes me unable to edit the contents of that input

frank17:03:28

note that I'm not setting the value of the input

frank17:03:13

if the onChange handler doesn't call om.next/transact!, (e.g. #(do %)), the issue goes away

frank17:03:43

also if I use onKeyUp or onKeyDown instead of onChange, this issue goes away

frank17:03:06

and if I set the value of the input to the piece of state that I'm manipulating to match the input value, the issue also goes away

ethangracer18:03:00

is there a way to view the commits between releases on github?

anmonteiro18:03:55

syntax is "https://github.com/&lt;org&gt;/&lt;repo&gt;/compare/&lt;commit, branch or tag>...<commit, branch or tag>"

cjmurphy19:03:20

If any of the keys you put at the end of the transact! mutation query is one of the join keys in your root component's query, then all the props for the whole application go right through. Is that correct?

hueyp19:03:55

depends what you mean by right through

hueyp19:03:21

I think it will still just parse that one key for sends, but it will schedule root for reconciling (which will read the whole thing)

hueyp19:03:29

but I鈥檇 have to double check !

cjmurphy19:03:01

Yeah - the render doesn't happen for all - but that is because React is smart enough??

cjmurphy19:03:02

I don't feel I understand what's going on with re-rendering.

cjmurphy19:03:48

I mean all the keys are at the root anyway - so all transacts will specify a key that is at the root.

matthavener19:03:15

cjmurphy: I think om.next does tree diffing just like om, so shouldComponentUpdate will return false on some child even if the root component gets new porps

matthavener19:03:09

(if that鈥檚 what you鈥檙e asking and I鈥檓 not misunderstanding :))

cjmurphy19:03:15

Yeah I'm looking to debug to understand - maybe I'll put some debugging on those other React methods.

cjmurphy19:03:40

Yes and read the source (not done that yet).

seanirby20:03:23

my follow on mutate read is causing the root component to rerender rather than the subcomponent I want to rerender. In addition to this, the rerender is happening twice. What could be causing this?

cjmurphy20:03:10

Are you using a keyword for the follow on mutate read? And is that keyword one of those used as a join in the root query?

seanirby20:03:40

1. Yes (the only other possibility is an ident right?)

cjmurphy20:03:39

I've never got idents to work for follow on mutate re-renders so don't know about them.

seanirby20:03:41

2. No, it's not explicitly in the root query, but I suppose its still part of the root query since it all composes to root

cjmurphy20:03:37

All my (non-leaf) keys are in the root query because needed to get default db format. So I don't understand how can have a follow on key that is not part of the root query.

seanirby20:03:14

cjmurphy: well yes it's part of the root query

cjmurphy20:03:21

Then if the root is being re-rendered everything is.

seanirby20:03:51

but its not a top level key

seanirby20:03:08

cjmurphy: do you have a non-trivial app I could look at? The one thing I'm still concerned about is how to go from app data -> UI tree data. I've got a method that works currently but I'm thinking it might be the wrong approach.

seanirby20:03:36

or I could just show you my code

cjmurphy20:03:26

In my 'big app' everthing is rendered too much. So I'm trying to take stock and understand myself. I just did a small app kind of the same and the checkboxes don't all re-render like they do with the big app.

cjmurphy20:03:55

We both need a bigger app to look at where we can study these re-rendering from follow ons (or not - where current component is used). I'm not sure there is such a thing.

cjmurphy20:03:57

I was going to go back and look at Kanban, see if that could help me.

seanirby20:03:36

cjmurphy: ok sounds like we're in the same boat

cjmurphy20:03:09

Maybe everyone is.

seanirby21:03:25

my approach has been to query for all the necessary data at the root level such that normalization can occur. the only other root query I have is a "widgets" query that constructs the UI tree https://github.com/seanirby/komokio/blob/master/src/cljs/komokio/parser.cljs#L19-L31 . I think the root rerenders when I try to rerender these UI widgets individually because they don't have a corresponding read function. Not sure though.

cjmurphy21:03:56

That's a serious read defmethod. My approach has been to keep all of them really simple - if state is in default db format then db->tree can be used for everything.

cjmurphy21:03:19

That's the approach Untangled takes I believe - I read that reads are done away with.

seanirby21:03:56

cjmurphy: yeah i'm probably doing it wrong simple_smile , but I thought thats how you were supposed to use db->tree, to construct the UI tree.

cjmurphy21:03:18

We should probably know when it is appropriate to go the simple way or not. As I remember in one tutorial parameterized reads, user being able to sort, that sort of thing - required going off the simple pathway.

cjmurphy21:03:12

Not sure how Untangled manages those situations then!

seanirby21:03:50

cjmurphy: yeah some more examples would be nice.

cjmurphy21:03:51

Just looked at the Kanban demo and every transact! is done in the Root component, and none of them have follow-on keys.

seanirby21:03:51

interesting

cjmurphy21:03:51

Yeah - he goes to a lot of effort to kind of have a 'controller' in the Root component - called App. So functions are passed down as computed to be able to do this. I chose not to copy that part of the app as seemed like a lot of work. But good for maintenance.

seanirby21:03:11

I didn't realize you could define object methods like that

seanirby21:03:17

makes sense though

cjmurphy21:03:31

I really like the way he went for simplicity. Not that its all walk in the park - as I remember the sorting / dragging part of it quite involved.

cjmurphy21:03:08

That app came early on. Nothing bigger to best it unfortunately 馃槥

seanirby21:03:21

its not clear to me why having the App handle all the transactions is a benefit

cjmurphy21:03:18

Well one place to go to look for any action - MVC type thinking.

matthavener21:03:46

yeah, I don鈥檛 see the kanban demo as very idiomatic for a large app

cjmurphy21:03:48

Taking the controller out of the view.

matthavener21:03:18

single large controller is the thing we鈥檙e trying to avoid by having the configurable parser IMO

cjmurphy21:03:09

Yeah - the controller is really in the mutates, not the calls to the mutates.

seanirby21:03:15

cjmurphy: thats true. but that causes the root to rerender on every change right?

cjmurphy21:03:04

Yes. But I only know how to write an app where every change makes rendering happen right from the top.

matthavener21:03:06

The top level render is going to be called for every single mutate, right? Unless the transact! didnt affect the state atom

cjmurphy21:03:26

Yes - every mutate.

matthavener21:03:53

The optimization is not calling render for children with queries focused on a part of the tree that was unchanged

cjmurphy21:03:59

That's optimization you talked about before where Om does some diffing. We can't really affect that and that's good I think.

seanirby22:03:02

matthavener: oooh ok. I'm not sure where I got the idea that narrowed renders could happen, maybe i made it up

matthavener22:03:41

I could be wrong simple_smile :X

cjmurphy22:03:09

So what's the point of the keys for follow on read in mutates? You could use any random key, would always work the same.

matthavener22:03:13

narrow render looks like it happens on set-state!

cjmurphy22:03:49

That makes sense, but local state not really part of Om Next mechanics too much, and good to avoid as a general rule.

kovasb22:03:36

Q about re-reading remote state after mutation..

kovasb22:03:56

After a mutation, om will re-read from local state

kovasb22:03:04

if I sent remote :true on everything

kovasb22:03:13

it will also send the mutation

kovasb22:03:22

but it will not re-read the remote state

kovasb22:03:43

(Note im dealing with idents here)

kovasb22:03:00

one can specify re-reads in the tail of the mutation

kovasb22:03:28

but om wont automatically splice in the full query associated with the component

kovasb22:03:21

Is this correct?

kovasb22:03:36

I'm about to try pathopt :true to see if it will..

kovasb22:03:07

Actually my problem is simpler:

kovasb22:03:57

How do I trigger a remote read from a local mutation: Does it require specifying it in the mutation, or should Om be able to figure out that something needs to be re-read remotely?

seanirby22:03:48

kovasb: I don't know how to fully answer your question but I know the ":value" map you specify in your mutations is whats used to communicate to the what should be reread

kovasb22:03:52

Basically I'd like to see an example of

kovasb22:03:43

Though I'd rather be able to have things happen more automatically for the ident case

isak22:03:43

@seanirby are you sure the :value returned from mutations actually does anything? i was under the impression it was for doc purposes only, and the only one that had an effect were the ones you passed to the transact! call

kovasb22:03:37

@isak you are correct

anmonteiro22:03:57

@kovasb: your transact! call must be the same as the example you linked

anmonteiro22:03:13

your parser must know that :thing is to be re-read on the server

anmonteiro22:03:21

so return :remote true for that

anmonteiro22:03:39

maybe I'm not understanding the full question

kovasb22:03:42

I'm trying that, but it isn't being sent

kovasb22:03:05

my setup: I have remote :true for all read and mutation cases

kovasb22:03:11

and my send function just prints to the console

kovasb22:03:34

plus im printing the individual reads and mutatations

kovasb22:03:47

on app load, it sends a read to the remote

kovasb22:03:52

the mutations are sent to the remote

kovasb22:03:57

but no other reads are sent

anmonteiro22:03:07

let's assume that the reads following the transaction is :thing as in the example you linked

anmonteiro22:03:23

are you seeing the parser for :thing being called after the mutation?

seanirby23:03:08

i_s: thanks, the wording regarding :value in the quick start confused me

anmonteiro23:03:14

and the send function is never called?

kovasb23:03:23

the send function is called with the overall mutation

kovasb23:03:36

(including 'thing in the tail)

anmonteiro23:03:50

well then that's what you want

anmonteiro23:03:03

the server will perform the mutation and do the remote read

anmonteiro23:03:09

but it gets everything at once

kovasb23:03:42

yes, that is OK

kovasb23:03:53

however, the read that I want is more complex

kovasb23:03:27

in particular it depends on the queries of the components

kovasb23:03:37

here is what is confusing:

kovasb23:03:51

when I do the mutation, it rereads from the local state with the right query

kovasb23:03:57

(the full query)

kovasb23:03:19

now, what if I want to reread with the full query from the remote?

kovasb23:03:28

you'd think setting remote :true would work, but it doesn't

kovasb23:03:08

so: I have to manually construct the same query as is assembled for me automatically in the base case?

kovasb23:03:27

I'm really more concerned with an ident situation here for my use case

anmonteiro23:03:38

so if you want the full-query

kovasb23:03:43

instead of being like [(my-mutation) [:some-ident 1])

anmonteiro23:03:05

have you tried: (transact! [(some/mutation!) (om/full-query this)])

kovasb23:03:12

i want the equivalent of [(my-mutation) {[some-ident 1] the-subquery}]

kovasb23:03:24

but that sounds exciting

anmonteiro23:03:21

I'm assuming the full-query contains the ident

kovasb23:03:00

(incidendly, I still dont understand the relationship between these comments and the comments on tony kay's link)

anmonteiro23:03:37

this comment? You can force them to be remote reads by quoting them

anmonteiro23:03:34

look into force

kovasb23:03:59

but where is that quote consumed in the code?

anmonteiro23:03:14

it'll basically call the parser with target set to the remote

kovasb23:03:20

ah there we go

anmonteiro23:03:22

you still have to return :remote true

kovasb23:03:54

i'm going to take another crack at this

anmonteiro23:03:59

@kovasb: I'm curious whether my suggestion worked out, I haven't tried it before

anmonteiro23:03:24

i.e. following the mutation with the call to full-query