Clojurians
#om
<
2015-12-30
>

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

tony.kay00:12:42

@dnolen: When you get a minute, I'd like to know which internal thing is correct, and I'll try to fix the Om bug I reported. Should components that use a top-link like [:a _] be indexed (when you build the index from root) by the ident, or just by the :a keyword?

dnolen00:12:05

just :a keyword

tony.kay00:12:15

k, I think that is the problem. I'm seeing the ident link in the indexer

tony.kay00:12:02

thanks, I'll take a look

olivergeorge04:12:05

Wow, you can make a pretty powerful db remote using korma...

olivergeorge04:12:59

That covers picking fields, limit, offset, ordering and basic where clauses!

olivergeorge05:12:15

Hmm, I'm fallen quite flat trying to hook up a reader for my paramatised query. db->tree doesn't seem to like params. It's possible I've been misusing it.

dnolen09:12:14

@olivergeorge: db->tree does not understand parameterized queries - there’s no intention to change that. query parameters should no longer be present by the time you invoke db->tree. They will either not be applied at all or should be applied on the result.

dnolen09:12:54

people that want fancier recursive querying will need to experiment on their own - this is what the Query AST is for

olivergeorge11:12:47

@dnolen thanks

janherich11:12:18

Hi everybody ! I would like to ask one question about merging new state deltas into app-state. The format of the state delta which default-merge (https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L1348, argument named res) expects seems to be little bit odd: {some/mutation {:tempids {[:key-path tempid] [:key-path real-id]}}} is the correct form for the default-merge to resolve tempids. But it doesn't pick state delta when it's nested in {some/mutation {:result some-state-delta}} - so it's not possible to directly feed external (server) mutation parse result to merge! function. It's possible only when we are parsing read expression or mutation which results in tempids... It's not particularly difficult to handle, but it seems little bit strange to me, am i missing something ?

olivergeorge11:12:58

For what it's worth. Not being applied would work fine for me (the params are applied server side, the client just needs to access the data). At the moment I think the presence of params means it returns nothing.

dnolen11:12:55

@olivergeorge: that's worth pondering. I thought about that too.

juhoteperi11:12:08

@dnolen: Btw, CONTRIBUTING.md (https://github.com/blog/1184-contributing-guidelines) might help bringing the contributing rules to everyone's attention.

olivergeorge12:12:19

@dnolen I gave that a go. Stripping out was easy. Seems helpful to me.

olivergeorge12:12:52

The next thing which caught me was handling a vector of maps. I expected my query [{:friends [:name]}] to handle data in this format {:friends [{:name "anna"}{:name "bob"}]} but there's no checking to differentiate between the value of :friends being a map and a vector.

olivergeorge12:12:33

That's not much harder to add.

olivergeorge12:12:37

(slightly fiddly actually since we're accumulating idents-seen along the way)

olivergeorge12:12:55

I'll log tickets in case I'm on track and it proves a helpful reminder later.

dnolen12:12:16

@olivergeorge: the later is just a bug if true but I'm skeptical that's a showstopper bug. Do not report an issue without a minimal case.

dnolen12:12:14

@juhoteperi: yeah I'll probably copy the wiki there

olivergeorge12:12:05

@dnolen righto, thanks.

olivergeorge12:12:03

This is my minimal case:

(let [query [{:friends [:name]}]
      data {:friends [{:name "anna"}{:name "bob"}]}
      actual (om.next/db->tree query data data)]
  (if (= data actual)
    :good
    [:bad {:expected data
           :actual actual}]))

olivergeorge12:12:37

I get [:bad {:expected {:friends [{:name "anna"} {:name "bob"}]}, :actual {:friends [{} {}]}}]

dnolen12:12:40

@olivergeorge: that's not how db->tree works

dnolen12:12:49

Your db isn't normalized

olivergeorge12:12:23

Also I think that code has had changes since alpha28

olivergeorge12:12:34

I'll look again

anmonteiro13:12:52

(def state {:friends [[:friend/by-name "anna"]  [:friend/by-name "bob"]]
            :friend/by-name {"anna" {:name "anna"}
                             "bob" {:name "bob"}}})

(om/db->tree [{:friends [:name]}] state state)
@olivergeorge: FWIW this works for me ^^

anmonteiro13:12:11

you need to use normalized data in db->tree

olivergeorge13:12:34

Thanks @anmonteiro. I just got there myself.

olivergeorge13:12:41

So requiring normalisation means I will need to define a client side schema using defui in order to use db->tree

anmonteiro13:12:54

@olivergeorge: exactly

anmonteiro13:12:15

in your example it would be something like:

(defui Friend
  static om/Ident
  (ident [this {:keys [name]}]
    [:friend/by-name name])
  static om/IQuery
  (query [this]
    [:name]))

(defui Friends
  static om/IQuery
  (query [this]
    [{:friends (om/get-query Friend)}]))

olivergeorge13:12:40

Yeah, that's pretty much in my editor just now!

anmonteiro13:12:38

:simple_smile:

olivergeorge13:12:47

I was hoping not to have to do that. Seems like a lot of code. I wonder if denormalised could be supported or if it would get in the way.

anmonteiro13:12:31

@olivergeorge: you can use denormalized data if you like, nothing prevents you from doing that

anmonteiro13:12:43

just don't call db->tree then

anmonteiro13:12:56

deref state and access it directly

olivergeorge13:12:58

It's 95% of what I want but easy to copy/past and modify.

olivergeorge13:12:24

(really the recursive select-keys is what I want I guess... I'll go away and play)

olivergeorge13:12:30

Thanks for confirming for me.

akiel15:12:18

I have a component which should render one particular entity. Should I use a JoinExpr with IdentExpr for the query like: '[{[:foos/by-id ?id] [:name]}]?

adammiller16:12:45

Hard to say without knowing more but seems like you’d always want a component that just has the query for the attributes for which it should display. Then a parent component would do the job of selecting and providing a specific one. Is that not possible in your case?

akiel16:12:05

In my case I like to show only one particular entity on a page. The URI would be /foos/123.

akiel16:12:28

In contrast to the page which shows a list of all foos where the URI would be /foos.

akiel16:12:53

Now I have routing which selects one component to render.

akiel16:12:24

In the list case, I have a list component which than uses a list item component as usual.

akiel16:12:40

But in the single entity case - what should I do?

anmonteiro16:12:43

@akiel: from your description, the query looks okay

anmonteiro16:12:56

@akiel: hard to say more without looking at some actual code

jonsmock16:12:55

@akiel I’ve been playing with this too, and that’s the query I came up with (still in progress, though). The only alternative I could see is to use params

jonsmock16:12:25

Note the result of the query will then be something like {[:foo/by-id 1] {:name “Jon”}}

jonsmock16:12:47

(I chose 1 for ?id)

akiel16:12:22

@jonsmock Yes I will also try params because I get the data back keys under [:foos/by-id 123] which is hard to unwrap.

akiel16:12:39

oh yes :simple_smile:

jonsmock16:12:35

A slight tweak I can think of is to have the query param as the full [:foo/by-id 1] ref

jonsmock16:12:58

So ‘[{?foo-ref [:name]}]` instead of '[{[:foos/by-id ?id] [:name]}]`

jonsmock16:12:25

Which might make it slightly more straightforward to unwrap

jonsmock16:12:36

(let [foo-ref (:foo-ref (om.next/get-params this))] ….)

jonsmock16:12:43

Is sort of what I’m thinking

akiel16:12:05

I have now ’[({:foo [:name]} {:id ?id})] and

(defmethod read :foo
  [{:keys [state query]} _ {:keys [id]}]
  (let [st @state]
    {:value (select-keys (get-in st [:foo/by-id id]) query)}))

jonsmock16:12:14

I’m pretty new still, but both seem reasonable to me. I don’t know what the trade-off is exactly.

akiel16:12:20

I’m also new. I saw the IdentExpr in JoinExpr and thought that it would be useful in my situation.

jonsmock16:12:37

Personally I’m trying to make the ref way work, but I think the params way is good for search results lists and things like that

akiel16:12:30

I think going by the :foo key for a particular foo and using :foos for the list of all foos sounds reasonable.

edpaget16:12:56

I ended up using another different way to accomplish a similar thing. I created a function and returned and anonymous class and passed the id I wanted to render

edpaget16:12:11

(defn page*
  [{:keys [id]}]
  (ui
   static om/IQuery
   (query [this]
              [{[:items id] query}])
   Object
   (render [this] (something))))


(def page (memoize page*))

edpaget16:12:41

since I couldn't figure out a non ast manipulating way to pass the id down from my router

edpaget16:12:44

but i'm also new to om.next and don't know if this is a bad way to handle it

jonsmock16:12:13

Thoughts: 1. Seems like your using the ref query way, which is what @akiel was asking about 2. You’re creating a page for each item - how many items do you have?

jonsmock16:12:39

Not saying that’s bad - I didn’t even think of that way

jonsmock16:12:48

Just seems like a space leak

edpaget16:12:26

I'm not sure i haven't run into problems with it so, far other than discovering i had to memoize the result so om could figure out the query-path to the component

akiel16:12:06

@edpaget: I read the id directly from the location:

static om/IQueryParams
  (params [_]
    (:route-params (r/match-route (u/location-path))))

edpaget16:12:13

and this is simplified version of what i'm actually doing, there's just one specific thing in my om app that has pages like that

edpaget16:12:49

@akiel: i hadn't thought about doing it that way at all, nice. ! I was just trying to keep all the routing logic in my top-level component

a.espolov16:12:23

@dnolen: David I have to you a couple of query and parameters https://gist.github.com/dark4eg/d7dc2364606fae3fb435 1. In the example described in gists, use get-query. In this case, I cannot set-query! with the new settings, so in reality have to simply duplicate the IQuery in components A and B 2. I have the IQuery component A to specify all the keys for remote synchronization and it turns out that when your application runs, stretch all the keys that say to me on the first page of the application does not need a list of employees. Is there a point above or I still have to live with that?)

dnolen16:12:55

@a.espolov ask questions to the channel please, thanks

a.espolov16:12:29

@dnolen: this is channel)

anmonteiro16:12:24

@a.espolov: David meant don't direct the question towards him but to the channel; see the channel topic

a.espolov16:12:36

I already asked but got no answer

anmonteiro16:12:01

I'm happy to help but I don't understand what you are asking

anmonteiro16:12:14

could you please rephrase?

anmonteiro17:12:15

@a.espolov: also, is Insps in component A meant to be B ?

a.espolov17:12:41

sorry, now I'll fix

dnolen17:12:37

@a.espolov: if no one can answer you will just have to be patient - such is life :simple_smile:

a.espolov17:12:38

gist updated

dnolen17:12:00

but really do not ping me unless it’s about bugs

dnolen17:12:08

I’ll be more available post-Beta

a.espolov17:12:42

@dnolen: thanks, ok)

smeister19:12:53

A question concerning tempids...

(defmethod mutate 'company/add-user
  [{:keys [state ast]} _ params]
  (let [tempid (om/tempid)
        user   (assoc params :db/id tempid)
        local  (assoc user :user/confirmed true)]
    {:value  {:keys [:users]}
     :remote (assoc ast :params user)
     :action (fn []
               (swap! state update-in [:by-id] assoc tempid local)
               (swap! state update-in [:users] conj [:by-id tempid]))}))
I need to know the tempid outside of the action to send it to the server with the same mutation, yet the above will not work, as it will generate multiple tempids. Any ideas on how to do this cleanly?

dnolen19:12:00

@smeister: :result of :action will be put into :value

dnolen19:12:05

:result can be whatever you want

dnolen19:12:16

mutations are top level so post processing is trivial

smeister19:12:26

Hm ok so i need a custom parse to achieve this? I mean generally i do not use the :result of client mutations.

dnolen19:12:49

you do not

dnolen19:12:52

re-read what I said :simple_smile:

dnolen19:12:08

(I am assuming this is server-side)

smeister19:12:14

Its client side

dnolen19:12:18

if it isn’t you haven’t clarified

smeister19:12:19

Sorry for being unclear

dnolen19:12:57

I do not know why you need to know the tempid outside

smeister19:12:13

I generate the tempid on the client, then send it off for resolving while storing it in the state

smeister19:12:19

until the real id is known

dnolen19:12:41

I’m going to repeat my last statement until you say something that clarifies what you are doing :simple_smile:

dnolen19:12:02

there should be no need to track tempids at all

dnolen19:12:17

you just send data w/ tempids across the wire

smeister19:12:17

Ok. But i also want to store the newly created object locally in some places and use it until it is resolved.

dnolen19:12:42

everything you’ve said is exactly how tempids work

dnolen19:12:46

w/o doing anything at all

smeister19:12:21

Yes, my question is only how to get the same tempid sent off to the server that is stored in the state.

smeister19:12:33

Still don't really get that.

dnolen19:12:52

I have no idea what you are asking

dnolen19:12:56

you need rephrase

dnolen19:12:06

what you’ve said so far doesn’t make any sense to me at least

dnolen19:12:03

again everything you’ve said is just how it works

dnolen19:12:07

you don’t need to do anything at all

dnolen19:12:15

given that I’m telling you this is case

dnolen19:12:30

you need to explain in this context what your failing expectation is

smeister19:12:36

Ok. I'll explain what i am doing and how it fails. I generate a tempid for a new object, say a user in a local mutation. I want to insert this new user into some collections as well as into my by-id collection (i use normalization). The mutation is remote as well to generate the object on the server. Datomic gives me back the real id and i send a map mapping the tempids coming from the client to the real ids and want to resolve them. But the tempids in the state are not the ones returned by the server for resolving, as they are generated outside of the action method. So different targets get different tempids.

dnolen19:12:21

the last 2 sentences make no sense

dnolen19:12:29

they sound like a bug in your server code

smeister19:12:07

Hm no already checked everything. The server resolves the same id that is requested to be resolved correctly. Why doesn't it make sense? Isn't the mutate called multiple times by the parser?

smeister19:12:51

If this is so i have no idea how i could get what i am seeing - two different tempids, one sent, one written.

dnolen19:12:01

what I’m saying is I cannot understand the behavior you are describing at all

dnolen19:12:04

it just sounds like a bug

dnolen19:12:15

in your code

dnolen19:12:29

you create a tempid on the client, if the server returns a mapping from that to the new one it will work

smeister19:12:54

Ok. But it seems like i am creating two different tempids on the client.

dnolen19:12:32

right because your mutation handler has side effects

dnolen19:12:39

which the docs tell you not do

smeister19:12:47

I understand that.

smeister19:12:02

And i didn't expect that to work.

smeister19:12:18

I just wanted to know if there is a way to do this in one mutation cleanly.

smeister19:12:49

If i had a separate save mutation it would work of course

dnolen19:12:54

OK but showing examples that obviously aren’t going to work don’t help me understand the problem

dnolen20:12:09

I cannot relate what you are saying, with your intentions, with what you are trying

smeister20:12:30

Sorry, i thought the description for my snippet made it clear. Will think about it again.

dnolen20:12:00

@smeister: something I do not understand is why you aren’t passing the tempid as a parameter to the mutation

smeister20:12:10

Ok this would solve my problem. It just didn't seem that clear, as it should be an implementation detail which is better kept inside of the mutation. I'll do it that way now, thanks a lot.

dnolen20:12:45

I don’t see the value of hiding tempid constrution

dnolen20:12:01

you wouldn’t really be hiding it when dealing with Datomic either

dnolen20:12:23

@smeister: reading back over your example I see that I misread

smeister20:12:27

Hm yeah just didn't like it inside of the ui code

smeister20:12:37

But i'll do it anyway as it's a simple fix.

dnolen20:12:47

it’s just how it’s done

dnolen20:12:00

you cannot put tempid creation anywhere else

dnolen20:12:17

because mutations will run multiple times

dnolen20:12:25

well I can think of a couple alternatives - but they aren’t any simpler than just generating this when you invoke the transaction

dnolen20:12:33

transaction stuff isn’t generally portable anyway

dnolen20:12:43

and tempid are unique so I don’t really see any problems

adammiller21:12:24

@smeister: if it helps at all, here is minimal example of the tempid functionality (mocking the resolution of it but should work the same way from server) https://github.com/akmiller78/tut-omnext-tempids