Clojurians
#om
<
2016-10-22
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

tobiash15:10:29

i finally figured out what my problem with process-roots was: it only seems to work on join queries, not even on a flat [:prop] query. is there a reason for that or is it just missing functionality?

jfntn16:10:45

I'm integrating compassus in our app, my approach was to use an App wrapper component, with a query for global state, and read methods for our routes that'd just recurse with the parser like:

(defmethod client-read :route/home
  [{:keys [parser query] :as env} _ _]
  {:value (parser env query)})
I was hoping this would let us have compassus' routing sit on top of our current component/parsing structure, but the above doesn't work. While the queries on App work as expected, our Home component -- which has a link query like [{[:current-user '_] [:db/id :foo]}] -- is now parsed into these props {[:current-user '_] {:db/id 123 :foo "bar"}}, when it used to be {:current-user {:db/id 123 :foo "bar"}}

anmonteiro16:10:03

@jfntn you’ll only get [:current-user _] in props if you don’t use db->tree

anmonteiro16:10:13

links are supposed to be used with normalization

jfntn16:10:53

I’m using it, :current-user falls back to our :default reader which is:

(defmethod client-read :default
  [{:keys [state query] :as env} k _]
  (let [st @state]
    {:value (om/db->tree query (get st k) st)}))

anmonteiro16:10:58

@jfntn can you put a minimal repro together then? could be a compassus bug

anmonteiro16:10:10

should be easy to create one

arohner17:10:25

I’d really appreciate more docs on remotes and :send. What is a remote mutate allowed to return? Just {:value {:keys []}} ? I assume it ignores :action?

arohner17:10:49

Is there a way to send back “error” data with a remote mutate, e..g “you aren’t allowed to do that”?

arohner17:10:27

I could list an extra key to be read, but then I’d need to store it on the server until the next time the user reads, which seems annoying and anti-scalable

anmonteiro17:10:55

@arohner IIRC there’s a :result key that’s returned in the mutation response

anmonteiro17:10:52

anyway, a remote mutation’s response is a map, you can add whatever you’d like to it

anmonteiro17:10:39

it’ll get thrown away when merging back to the local app state, but you can change that behavior

arohner17:10:07

the docs hint at :result, but don’t actually specify it

arohner17:10:14

@anmonteiro by replacing :merge in the reconciler?

anmonteiro17:10:24

@arohner also note that you don’t necessarilly need to reimplement merge when overriding it, what I commonly do is call om.next/default-merge and do my custom stuff

anmonteiro17:10:36

the default-* functions are public vars for a reason

jfntn19:10:59

@anmonteiro wrote this gist to illustrate the problem: https://gist.github.com/julienfantin/dc3d1bf89edfbacc1447331bf851ce97

arohner19:10:48

@anmonteiro not sure if this is a good idea, but I made my merge recognize transacts in :keys, and just call om/transact on everything returned

arohner20:10:16

so now on the server, I just return {:value {:keys [(foo/error :text “an error”)]}} from a mutation

anmonteiro20:10:06

@arohner right. one thing I forgot to mention is that whatever you return from the :action thunk will be assoced to the returning response under :result

anmonteiro20:10:30

maybe I should have started there, apologies :slightly_smiling_face:

arohner20:10:32

but :result is ignored?

arohner20:10:46

and this is in remote sends, so I don’t think I could use :action anyways, right?

anmonteiro20:10:16

wait, I mean the mutation that’s executed on the server

anmonteiro20:10:37

I suppose that’s what we’re talking about

arohner20:10:57

I still don’t see what :result is connected to

arohner20:10:43

my original goal was, given the client does a transaction that goes to the server, change client state in a different key, to signal errors, etc

anmonteiro20:10:48

^ here’s a server mutation example for a talk I gave at a meetup

anmonteiro20:10:48

so if the server knows it’s an error, return that from the :action thunk, say {:error :nuke/wrong-code}

anmonteiro20:10:12

and the server will return that to the client

anmonteiro20:10:46

so your merge will receive something like {‘launch/missiles {:result {:error ::nuke/wrong-code}}}

arohner20:10:18

right, and then does vanilla om do anything with that :result?

anmonteiro20:10:25

nop it just throws it away

anmonteiro20:10:30

which is why you need to override :merge

arohner20:10:40

right, ok we’re on the same page so far

arohner20:10:09

so then I modified my merge to walk {:value {:keys []}, and om/transact on the client anything that looks like a transaction

anmonteiro20:10:03

right, but remember that those transactions could in theory send to the server again an enter a loop

anmonteiro20:10:07

it’s up to you to prevent that ofc

arohner20:10:09

yep. don’t do that :slightly_smiling_face:

anmonteiro20:10:47

@arohner so what you could do is just swap! to the state directly, or only support a predefined set of mutations that are guaranteed to run locally

anmonteiro20:10:50

@jfntn thanks, I’ll have a look soon!

arohner20:10:54

I’m not too worried about loops

jfntn20:10:16

@anmonteiro thank you, added a bit of context in the ns docstring!

arohner21:10:27

@anmonteiro one weirdness I just noticed: it seems like the return value of remote mutations is getting merged into the client state, even when using default-merge

arohner21:10:30

that seems…wrong

arohner21:10:14

note that in default-merge, :keys calls remove symbol?, but the next state, which is reset! to the value of :next, merges everything in

anmonteiro21:10:51

@arohner the return value of mutations will be merged into the app state, yes

anmonteiro21:10:31

that’s normal, but note that tempid migrations will clear that “garbage"

arohner21:10:36

maybe I’m doing something wrong then. I have a mutation that returns {:value {:keys}}. So then the return value is {‘foo {:value {:keys []}}

arohner21:10:17

so when I manually inspect my app-state atom, I see {:foo “”, :bar “”, ‘foo '{:value {:keys []}}}

arohner21:10:17

and I’m surprised to see ‘foo, because I never asked for it to go in there

anmonteiro21:10:39

right, but that’s supposed to happen

anmonteiro21:10:51

the default-merge merges every remote result into the app state

anmonteiro21:10:09

even mutations

arohner21:10:18

is that used for anything? seems like a memory leak waiting to happen

arohner21:10:54

components are responsible for updating IQuery/query to contain all of the items their child components need?

arohner21:10:57

what do you do if you need one query prop to determine the rest?

arohner21:10:08

I guess set-query! ?

anmonteiro21:10:49

@jfntn so this actually looks more like a Om bug

anmonteiro21:10:39

here’s a truly minimal repro:

(require '[om.next :as om])

(def p (om/parser {:read (fn [_ _ _]
                           {:value 42})}))

(p {} [{:foo [:bar]}]) ;; => {:foo 42}  <- expected

(p {} [{[:foo/by-id 0] [:bar]}]) ;; => {[:foo/by-id 0] 42}   <- yup

(p {} [{'[:foo/by-id _] [:bar]}]) ;; => {[:foo/by-id _] 42}  <-- hrm?

anmonteiro22:10:28

I’ll get to that tomorrow

jfntn22:10:15

great, thank you!