Fork me on GitHub
#om
<
2016-03-04
>
seanirby00:03:14

looking at the om next todovc example... i've set up figwheel and a ring server+datomic so far and both are working fine but how do you use them together?

tawus00:03:49

(om/db->tree [:y] {:x {1 20} :y [[:x 1]]} {:x {1 20} :y [[:x 1]]}) is get resolved to {:y [[:x 1]]}. I was expecting {:y 20}. Surely my understanding is flawed. Can something throw some light on it simple_smile

seanirby01:03:02

nvm my last question. figured it out

reedho01:03:48

@tawus if my understands is correct, the query must use join explicitly to get ident/link resolved, e.g. [{:y [:x]}]

george.w.singer01:03:33

I'm trying to figure out what I need conceptually on the backend to make an Om parser for a remote. Swanodette has a tutorial here which showcases a bunch of read/mutate functions that take an env with a conn key and a query key. For example:

(defmethod readf :todos/by-id
  [{:keys [conn query]} _ {:keys [id]}]
  {:value (d/pull @(d/sync conn) (or query '[*]) id)})
But what does that conn key represent? How do you know that the env passed to you from the front-end (which I assume is an ast map) is going to have that key on it?

seanirby01:03:38

george.w.singer: conn is the database connection

george.w.singer01:03:49

How does it get into a the env argument passed into the parser?

seanirby01:03:48

george.w.singer: lemme look. i'm currently making sense of this project too...

seanirby01:03:10

george.w.singer: i do know that the database connection is stored in the system component, servlet-system i believe

reedho01:03:39

@george.w.singer its in server.clj, in api fn where the parser is invoked

seanirby01:03:57

reedho: i see where you're referring to but why is it in the request?

george.w.singer01:03:34

I'm looking at server.clj and I still don't see where/how the back-end parser knows anything about conn (specifically, how conn is passed in as a key of the env variable w.r.t. the read/mutate functions)

seanirby01:03:54

oh ok, i see its assoced onto the request

tawus01:03:48

@reedho: Thanks, that is indeed the case!

george.w.singer01:03:45

So before the req is handed to the parser, it is assoc'ed with a :datomic-connection as well as whatever conn ends up being elsewhere in the code key/value

george.w.singer02:03:03

I'm assuming conn will eventually :conn

george.w.singer02:03:22

And that's how it ends up in the env variable passed into the parser

george.w.singer02:03:25

is that about right?

george.w.singer02:03:19

In case curious, here is directly where :conn is added to env:

(defn api [req]
  (let [data ((om/parser {:read parser/readf :mutate parser/mutatef})
                {:conn (:datomic-connection req)} (:transit-params req))
        data' (walk/postwalk (fn [x]
                               (if (and (sequential? x) (= :result (first x)))
                                 [(first x) (dissoc (second x) :db-before :db-after :tx-data)]
                                 x))
                data)]
    (generate-response data')))

george.w.singer02:03:04

The call to om/parser returns a parser which takes env as its first argument (which, in this context, is a map with the :conn key).

george.w.singer09:03:47

Is it possible to define a multimethod as a (let...) binding? The purpose for doing so is that I need to invoke the <! macro within the definition of a multimethod's method. So I'm trying to put the definition of this multimethod within a (go ...) block. And I know that I can put a go block within a let statement. So if I could define a multimethod as a let binding (wrapped inside a go block), I would safely be able to use <! within the multimethod.

iwankaramazow11:03:14

@george.w.singer: this is material for #C03S1L9DN

george.w.singer12:03:03

@iwankaramazow: Yes, sorry for the off-topic post. I got it resolved elsewhere.

george.w.singer12:03:20

Question RE: sending the result of a parser function from the back-end to the front-end:

george.w.singer12:03:44

When I send a map that contains a go block as one of its values from the back-end to teh front-end

george.w.singer12:03:47

my back-end crashes

george.w.singer12:03:59

I THINK it's because I'm sending the map via cljs-transit

george.w.singer12:03:21

Does it now allow the sending of channels over the wire?

iwankaramazow12:03:08

You'll need to extend Transit to handle channels

george.w.singer12:03:24

You mean use something besides Transit?

george.w.singer12:03:40

When I actually think about it, it's insane to think that transit could send a channel over the wire

george.w.singer12:03:48

What if it's not populated yet?

george.w.singer12:03:56

The client would never get the value

iwankaramazow12:03:14

You'll probably don't want to send channels over the wire though

seanirby12:03:44

would that even work?

iwankaramazow12:03:39

Honestly I don't know

iwankaramazow12:03:08

it doesn't seem to be worth the effort

iwankaramazow12:03:32

there's also no sane reason I can think of why someone would to that

seanirby12:03:37

yo dawg, we put a channel in yo channel

george.w.singer12:03:13

Went with another solution

jlongster15:03:12

@dnolen: query is passed into path-meta but not used right now, is it passed in because what we should be doing is only marking data that is tied to a piece of the query? https://github.com/omcljs/om/blob/master/src/main/om/next/impl/parser.cljc#L162

jlongster15:03:27

I'm going to try to implement that optimization because I'm running into it again

anmonteiro15:03:58

@jlongster: that's correct, the query is not used because if I remember correctly David just put it there in a "prep" commit

anmonteiro15:03:15

git blame will tell you if I'm right

jlongster15:03:26

ok cool. This may test my understanding of queries but I'm gonna try to implement it

anmonteiro15:03:47

that's probably something to spend a little time on

jlongster15:03:47

because this is the last slow piece of my app, otherwise it'd be super fast!

anmonteiro15:03:05

It didn't look too hard

anmonteiro15:03:11

but it's not a very simple problem either simple_smile

jlongster15:03:48

alright. I'll start it and maybe I'll get it right after a few passes of feedback

anmonteiro15:03:03

feel free to ping me if you need any help

jlongster15:03:09

@anmonteiro: yeah, already have some basic questions... where is the path updated? I see that it adds the :path key initially as [] but I don't see where it appends to it

jlongster15:03:17

@anmonteiro: oh, duh path-meta creates the path... obviously I don't really know what this is used for yet. I guess something else can put a different initial path in env if it wants to, but nothing else does that right now

anmonteiro15:03:39

@jlongster: this path-meta is what is attached to components

anmonteiro15:03:45

in omcljs$path

anmonteiro15:03:52

represents the data path of the component

jlongster15:03:35

yeah, I thought this path was constructed somewhere else and path-meta recursively tagged it, but it does both

anmonteiro15:03:59

i.e.

(defui Child
  static IQuery
  (query [this]
    [:foo :bar]))

(defui Root
  static IQuery
  (query [this]
    [:child (get-query Child)]))

anmonteiro15:03:08

in this case, Root has path []

anmonteiro15:03:16

Child has path [:child]

jlongster15:03:38

are things like recursive and union queries the things that make this complicated? If I just make it so it at least doesn't recurse in more basic queries, it should be simple right?

jlongster15:03:43

yeah, thanks

anmonteiro15:03:09

unions are probably not complicated

anmonteiro15:03:18

since every union item has the same data-path

anmonteiro15:03:25

both the union component and the leaves

anmonteiro15:03:39

recursion has repeated keys, yes

jlongster16:03:55

@anmonteiro: this is going to make path-meta aware of query semantics, right? it's going to "recurse" down into the query along with recursing down into the data. do we already enforce returning data in the right way in parsers, i.e. [:foo {:bar [:x :y]}] -> {:foo "foo" :bar [{:x 1 :y 2}]} parsers right now can return whatever they want

jlongster16:03:05

I guess a better phrasing is: parsers should not arbitrarily execute sub-portions of the query in ways that don't match those rules

anmonteiro16:03:11

@jlongster: I think it's not enforced

anmonteiro16:03:38

and I also think it wouldn't really matter

anmonteiro16:03:05

if you write a query for a component, you really are going to return the right keys to match the query

anmonteiro16:03:45

if you at least use the default db format you're going to get the right keys if you do everything right

jlongster16:03:36

k, I'm not really familiar with other uses like datascript

jlongster16:03:44

but I'll do an initial implementation and see what happens

anmonteiro16:03:08

Datascript users will also want to return data that matches the query

anmonteiro16:03:44

especially because they're using the Om query directly against the database, I think

jlongster16:03:46

Om queries != datascript queries, how can they run it directly?

anmonteiro16:03:54

(d/q '[:find [(pull om-query) ...]])

jlongster16:03:14

oh, yeah for query "fragments" but not for whole queries

anmonteiro16:03:25

the pull syntax works for most cases, I believe

jlongster16:03:59

joins are different, aren't they?

anmonteiro16:03:35

I don't think they are, but I'll let anyone that has used Datascript more than I did answer

jlongster16:03:28

ok, not going to worry much about that right now anyway

jlongster16:03:43

I ran script/figwheel.clj, where do I point my browser again to get figwheel connected?

adamfrey16:03:32

I’m running into a problem with transactions and reconciliation and would like to run it by someone with knowledge of Om.next internals. Problem: After I transact! the reconcile! function is called not once, but twice. On the second time in reconcile the :queue of components to update is empty so I trigger this condition and rerender from the root with my unparsed app-state https://github.com/omcljs/om/blob/20f790d686eeb716275148dc1fa3349b3cfe800d/src/main/om/next.cljs#L1590

adamfrey17:03:21

Explanation: The reason I hit reconcile! twice is because my transaction causes some components to leave the screen, triggering their componentWillUnmount callback here, which updates that reconciler state: https://github.com/omcljs/om/blob/88c62914e8948b329d2829c740e732eb4a95b7ac/src/main/om/next.clj#L153

anmonteiro17:03:45

@adamfrey: and what's the problem that it causes you?

adamfrey17:03:15

My app is fine for a fraction of a second after the transaction, but then it rerenders from the root without parsing the app state

anmonteiro17:03:29

FWIW, there's a pending PR that'll fix unnecessary calls to swap! in componentWillUnmount (https://github.com/omcljs/om/pull/638)

adamfrey17:03:29

so, my idents aren’t expanded into anything

anmonteiro17:03:01

that seems like a bug, can you make a minimal case?

adamfrey17:03:39

maybe, my extenuating circumstance is that this is a react-native app

anmonteiro17:03:48

((:render st)) is just React/render which should not re-render unnecessarily

adamfrey17:03:51

I should definitely try this out in a web app and see if it is react-native specific. I never ran into this problem on the web.

anmonteiro17:03:47

@adamfrey: I'm happy to look at a minimal case if it turns out to happen in web too

adamfrey17:03:08

well applying @jlongster’s PR fixed the problem, actually

jlongster17:03:16

I feel like that triggering a rerender when something unmounts isn't the best idea in general

anmonteiro17:03:51

@adamfrey: right, but if the component has a custom query your problem will come back again

adamfrey17:03:19

that’s true. If I get time this weekend I’ll try a minimal case and for now I’ll just keep that in mind

jlongster17:03:17

@anmonteiro: a very crude version that seems to work for basic queries https://gist.github.com/jlongster/1c661626a0860022ef26 will need to add support for union and recursive queries. look like the right track though?

anmonteiro17:03:01

@jlongster: give me 30 min or so and I'll get back to you

jlongster17:03:18

ok no worries, need to probably get back to work anyway

anmonteiro17:03:40

I know the feeling

iwankaramazow17:03:49

and I hate that feeling 😛

iwankaramazow17:03:19

is there any way to access the history of the reconciler?

iwankaramazow17:03:36

for basic undo/redo?

anmonteiro17:03:01

@iwankaramazow: not publicly, no, there's an issue for that though: https://github.com/omcljs/om/issues/407

anmonteiro17:03:52

I mean, there's from-history

iwankaramazow17:03:56

I was looking at a simple undo/redo in an excel spreadsheet like application.

anmonteiro17:03:08

You might also want to look at the raw Cache type

iwankaramazow17:03:26

somehow getting those UUID's from transactions would come in handy

jlongster17:03:54

@anmonteiro: in the first case where x is a map, the current query would actually be a map instead of a vector in the case of unions, and I'm not sure what part of that union query to pick off when recursing down. just leaving that here, no need to respond until you take a look.

dnolen17:03:32

@tony.kay doing company related stuff, will have wait for weekend or Monday

jlongster18:03:47

@anmonteiro: there are many things wrong with the initial attempt I posted, no need to look at it. I'll make another attempt soon-ish

anmonteiro18:03:58

@jlongster: oh OK thx for the heads up

anmonteiro18:03:03

was going to look now

jlongster18:03:45

yeah, I forgot things like joins that return a single item don't return a vector, they return just the item

jlongster18:03:55

I'm understanding the problem more though, should be able to get this to work

jlongster18:03:02

gotta go back to real work though

seantempesta18:03:10

I’ve got an interop question (if anyone has a minute). How do you pass props between om.next components and normal react components? It appears the om is passing a PersistentArrayMap as their props, but normal react components can’t read that.

seantempesta18:03:20

well, wouldn’t that convert the CLJ data structure to a javascript data structure?

seantempesta18:03:16

Meaning if I have a tree of components (om.next —> js-react —> om.next) I would end up with a javascript data structure at the final om.next component.

anmonteiro18:03:15

are you passing om/props ?

anmonteiro18:03:34

so for react components just pass (.-props this)

seantempesta18:03:40

but I also need to pass normal js parameters so the js-react component can read them

iwankaramazow18:03:20

there's no way around it (I think)

anmonteiro18:03:25

in React.js -> Om Next you'll probably need to get the "omcljs$value" from the props

iwankaramazow18:03:03

ah so in the grandchild you can have access to cljs data structures?

iwankaramazow18:03:09

without converting them back

seantempesta18:03:16

well, they don’t really convert back

seantempesta18:03:23

clojure is so much more rich simple_smile

anmonteiro18:03:02

@seantempesta: are you still having problems with what I've said?

seantempesta18:03:18

well, I guess I don’t really understand

seantempesta18:03:10

If I go from cljs -> js then there isn’t a omcljs$value

anmonteiro18:03:39

so: 1. don't pass (om/props this)

anmonteiro18:03:45

to the JS component

anmonteiro18:03:20

but whatever's in (.-props this), which is a JS object

seantempesta18:03:30

let me try that

seantempesta18:03:07

so I just need to merge the js props with (.-props this) and pass that to the js-react component?

anmonteiro18:03:45

@seantempesta: merge won't work with JS objects

anmonteiro18:03:06

you probably need to set! the fields you want in the JS object's props

seantempesta18:03:24

ah, gotcha. I’ll work on that.

iwankaramazow18:03:36

@seantempesta: do you need access to transact! or set-query! in your vanilla React component?

seantempesta18:03:09

no, but I do in the grandchild om.next component so it has to be passed by the js-react component

seantempesta18:03:49

basically, the middle js-react component is a router. So it needs to be able to pass everything from the om.next root component

iwankaramazow18:03:59

ah, React-Router?

seantempesta19:03:10

react-native-router-flux

seantempesta19:03:38

yeah, it’s kind of a pain, but being able to change screens on my app is kinda important. simple_smile

iwankaramazow19:03:20

No idea how routing works in React Native, but I feel your pain 😄

seantempesta19:03:11

okay, so this is what’s surviving in the grandchild om.next component when I call (om/props this):

dispatch: undefined
hideNavBar: true
name: "_login"
omcljs$depth: 0
omcljs$instrument: null
omcljs$parent: null
omcljs$path: cljs.core.PersistentVector
omcljs$reconciler: om.next.Reconciler
omcljs$shared: null
omcljs$value: om.next.OmProps
plugin: ExRouter(props)
route: Route
sceneConfig: Object
title: "Kidlink"
__proto__: Object

seantempesta19:03:57

So, it successfully transferred the om props. How do I convert them back to what I’m expecting (ie a cljs data structure)?

iwankaramazow19:03:28

You could try js->clj, that isn't the most efficient

iwankaramazow19:03:34

function out there

seantempesta19:03:14

hmm, that doesn’t seem to work: No protocol method IEmptyableCollection.-empty defined for type : [object Object]

iwankaramazow19:03:26

Sorry, no idea how to solve that

anmonteiro19:03:16

@seantempesta: what do you need in your grandchild?

seantempesta19:03:45

both the router props from the js-component and the om-next props so I can use transact and query

anmonteiro19:03:45

can't help you with the router's props, where are they?

seantempesta19:03:18

in the above list it’s everything that isn’t “omcljs$"

anmonteiro19:03:04

@seantempesta: so those you can get

seantempesta19:03:34

but I guess I don’t understand why when I called (om/props this) I didn’t get back a clj data structure

anmonteiro19:03:43

@seantempesta: this might seem strange, but try doing (om/props (om/props this))

seantempesta19:03:27

Assert failed: (component? component)

seantempesta19:03:16

So I need to trick om into thinking it is an om.next component?

seantempesta19:03:22

(defn ^boolean component?
  "Returns true if the argument is an Om component."
  [x]
  (if-not (nil? x)
    (true? (. x -om$isComponent))
    false))

seantempesta19:03:42

Do I just need to append om$isComponent true?

anmonteiro19:03:45

no, one second

anmonteiro19:03:55

well, that would probably work as well

seantempesta19:03:05

okay, I’ll try that

anmonteiro19:03:44

(om/unwrap (om/get-props (om/props this)))

anmonteiro19:03:47

this should work

seantempesta19:03:32

holy shit! it worked!

seantempesta19:03:41

you’re a golden god!

seantempesta19:03:08

@anmonteiro: So, calling (om/transact! this …) should work too? Or do I need to do any conversions?

iwankaramazow19:03:39

Is there a way to replace the :app-state of your reconciler ?

iwankaramazow19:03:20

the wiki mentions replacing the initial app-state, not the current app-state

seantempesta19:03:34

@anmonteiro: Nope. When I call transact I’m getting an error. In (defn transact* [r c ref tx] r is null. That’s the reconciler, right?

seantempesta19:03:30

The reconciler is read from the props, right?

iwankaramazow20:03:24

found it in Om's source => merge!

seantempesta20:03:00

Okay, so I fixed the problem of the reconciler being null, by overwriting the props:

(let [om-props (om/props this)]
      (set! (.-props this) om-props)

seantempesta20:03:50

but now I’m getting this error: No queries exist for component path from (defn full-query

seantempesta20:03:20

which I’m guessing (from it’s description) returns the full query path back to the root. Which isn’t going to work, right? Since I’m going through a non-om router?

iwankaramazow20:03:01

If your router switches out its children components, you have to make sure you set the query at the root om component above the router

iwankaramazow20:03:39

A No queries exist for component path in 99% means you 'steal' the query of a child

seantempesta20:03:16

So no queries can be defined below the root because I’m using the router.

seantempesta20:03:25

I guess that’s fine.

iwankaramazow20:03:39

I think you can

iwankaramazow20:03:49

that's what set-query is there for

seantempesta20:03:21

Well, I didn’t even call set-query!. I called a transact! (which oddly enough succeeded)

iwankaramazow20:03:58

In my root component I have something like (into [] (concat root-query [{:route-root child-query}]))

iwankaramazow20:03:34

the :route-root join makes the query valid

seantempesta20:03:36

hmm, I’ll take a look at that. It’s tough debugging a problem when you don’t understand the underlying idea. Maybe I should re-read the om-tutorials again.

iwankaramazow20:03:58

(defui Child
  static om/IQuery
  (query [this]
         [:prop]))

;;wrong, you steal the child's query, => no path exists for component...
(defui Root
  static om/IQuery
  (query [this]
         [(om/get-query Child)]))

;;valid query
(defui Root
  static om/IQuery
  (query [this]
         [{:root (om/get-query Child)}]))

seantempesta20:03:46

Oh, I think I see what you’re saying

iwankaramazow20:03:18

a router component could prefix every Child query with :root here, so it's a valid query in the end

iwankaramazow20:03:52

In theory if you put your native js router in between Root & Child, it shouldn't make a difference

seantempesta20:03:07

Okay, I checked and the omcljs$path is empty on my child component. I think that’s what’s causing the problem. Anyone know how this is supposed to be populated?

seantempesta21:03:33

Ugh. This is all feeling too hacky. I’m going to take a break and then I’ll just try implementing my own router as an om.next component. Thanks again guys for helping me work through those bugs!