Fork me on GitHub

Can't help thinking that it would help readability if instead of prim/get-query, component queries used prim/get-one or prim/get-many for joins. Both these new methods would really be get-query under the hood. The concept of joins being one or many is an up front part the application design. Perhaps in the rare case where you want to change this 'join-arity' on the fly prim/get-query could be used to indicate the intention.


@cjmurphy usually I can get it by attribute name, but you can just create alias functions and use if you think make it cleaner


@wilkerlucio But maybe you think its such a good convention you would like to create alias functions too? Then what's the point of us both creating them?


IMO this is not necessary, the attribute name is usually enough for me to know if it's a one or many (plural vs singular attr name)


The plural verses singular thing mostly works but I doubt anyone would want to make is a 'standard', because quite often it doesn't work, whereas function names can be a standard. When you have a standard code is quicker to read.


maybe we are doing something different, it's often that you get confused if the relation is one or many? and how this is affecting your dev? just trying to understand


Not so much confused but get drawn into thinking about it. It is just a small thing really but I don't see a downside to having this as a kind of standard. Whether a relation is one or many seems important to me. But you are right, maybe it is just what I'm used to, and 1:M or 1:1 is not actually that important. And there may be other reasons that having self-documenting code helps. For instance what if you wanted to auto-create spec that can recognise a table in state from defsc? (This thought actually comes from Tony, in a chat I had with him prior to releasing 'Default DB Format').


@cjmurphy you mean using spec to derive data from the query?


'Default DB Format' uses conventions it is told about (such as ending in "by-id") to (for example) tell the difference between a root level join and a table. The theory is that it may not need these conventions. To quote Tony: "I’m trying to think if there’s a way to generate the spec in a custom registry from defsc. For a given entity, we know the Ident details. We know the query which indicates where there are joins. We can check the joins to see if they’re normalized and we know all of the property names. I think it is technically possible to derive the intended schema from that, other than to-one vs. to-many relations". 'Default DB Format' is checking things like if the value of a join (field or root level) is an Ident, or a vector of Idents, or some scalar value. Any of these three things and all is good. But if it is not one of these then there's is a problem, and the HUD displays. At the moment it does this job by itself, but if there was a spec registry that could answer things like: is it a vector of Idents (many?), is it an Ident (one?), then it could just use that registry. Personally I like the idea of having conventions because it makes code more readable, and don't really see the need for such a spec registry. But it is a good point that 1:M or 1:1 is missing, and perhaps shouldn't be, hence my suggestion to have prim/get-one / prim/get-many.


Hey, I think I found a bug (I am using version 2.1.7). Im doing two loads for the same ident in one transaction. The first one merges the result in the app state, the second merge replaces the already loaded data by the result of the second load instead of merging them. The second merge doesnt seem to see the correct 'before-state' when merging the load result.

Drew Verlee17:02:23

are there any overview videos on fulcro? Something i could watch on a long walk while 🙂

Drew Verlee17:02:33

Thanks. Im working through them now. i was thinking their might be a presentation style overview somewhere


but nothing else as far as i'm aware 🙂

Drew Verlee05:02:05

Awesome thanks!


@cjmurphy So, having a to-one and to-many API would also imply to the reader that they matter. (e.g. you must use one over the other), which we’d then be tempted to enforce with error checking at runtime. This in turn does two things: It limits your flexibility on the schema (which can be good or bad), and it creates a refactoring task when the join changes. If we had more schema, we could create tools that could give us warnings during development when we screw up, which is what we were talking about when I said what you quoted: if we had auto-derived specs for your database, then we can validate your database during development and point out problems (like spurious stuff hanging out we didn’t expect). I’ll give it a bit more thought. It isn’t something I’m ready to do right now, but I see some potential for tooling. I tend to agree with @wilkerlucio that naming conventions are good enough for the reader in the vast majority of cases (person vs people).


@cjmurphy one thing you can use is the spec itself, you can check the spec for that attribute, and if it's a collection, you can infer it's a to-many, this idea requires the user to make the proper specs, but I think it's a minor cost, and that doesn't require any other changes


@wilkerlucio My understanding is that there isn't a problem that needs to be solved: 'Default DB Format' checks that your client state is normalized and does not need spec to do it. However later on spec could possibly be used for recognizing Idents, tables etc, once (if ever) such specs are generated by Fulcro itself. I think developers would normally just be concerned with specs for his/her own 'business data'.


but attributes used on queries are business data, aren't they?


my point is just there are other ways to have a deterministic way to know how some attribute should behave (I'm suggesting spec, but you can do anything based to the property name, that's the beauty of namespaced keywords) without having to change how we deal with queries in the current way, I'm afraid making it more strict (by having specific methods) might compromise the flexibility we have now


Sure but the tool in question doesn't need to go to that kind of depth to see that there are normalization problems. Oh we are still talking about get-query - sorry I kind of missed the connection. I was just suggesting (and I know it is a bit of a bold suggestion) adding two method names. Kind of impossible to do any real harm as they would both just be like (def get-one get-query).


as tony said, is more about the confusions it can cause to people that expect it to work in some way, but I have a suggestion that you can try in the meantime


you could provide some wrappers for get-query on Default DB Format, you can sell as "get better error telling by using then"


so the get-one / get-many could be implemented as this:


(defn get-one [klass]
  (vary-meta (prim/get-query klass) assoc ::relation-type ::one)))

(defn get-many [klass]
  (vary-meta (prim/get-query klass) assoc ::relation-type ::many)))


this way you would have deterministic information to run validations, so we can try this approach without changing fulcro directly


does that helps on what you are trying to accomplish?


But D-DB-Format doesn't need this stuff at all. How did D-DB-Format even get into this discussion?? My fault I know...


you pointed that out on the first sentence 😛, sorry if I misunderstood


The errors from D-DB-Format work fine right now.


it's just it's a big change to the interface without unforeseen impact, transforming something that is a single option (always use get-query) to 3 options, IMO this is the kind of thing we need a very good reason to adopt, with clear gains


I just don't see those gains at this point


TBH I probably agree with you, but over time we may change our minds. I know I'll be using my-prim/get-one and my-prim/get-many from now on.


(if indeed that works).


yeah, the good part is that nothing prevents you from doing it, so you can get the readability you want 🙂


Good thing it will work then. Thanks simple_smile


@timovanderkamp I think i’ve run into this before. The two end up trying to clobber each other, right? A workaround is to set the component to nil, and then just manually do your prim/tree->db stuff in your done callback.


@levitanong Right, thanks for this solution. Though i think you shouldnt be worrying about firing multiple loads in a transact


@timovanderkamp Happy to help. Please let me know how it goes. When you say one shouldn’t worry about firing multiple loads, do you mean one should be able to do it without worrying about workarounds, or that one shouldn’t even do it altogether?


I mean that you shouldnt have to do workarounds to make it work


ah, yes, i would agree with you on that. 🙂 If you come up with a minimal repro, you can post an issue in the fulcro github.


But let’s see if @tony.kay has the time to do a quick once-over on what’s been stated, and see if he can easily identify the problem. For his convenience, I will quote the problem here: from @timovanderkamp > Hey, I think I found a bug (I am using version 2.1.7). Im doing two loads for the same ident in one transaction. The first one merges the result in the app state, the second merge replaces the already loaded data by the result of the second load instead of merging them. The second merge doesnt seem to see the correct ‘before-state’ when merging the load result.


beginner q regarding routing: how do I handle the case where 1) for a flat routing structure with no nested routers 2) I want to for example. I have a Main screen, a Projects screen, and a Project screen. Main and Projects are not parameterized, but Project should render a screen for specific project given an id. my router declaration looks something like:

  (ident [this props] [(:page props) :top)])
  :main Main
  :projects Projects
  :project Project)
and my routing tree looks like:
                        (fr/make-route :main [(fr/router-instruction :root-router [:main :top])])
                        (fr/make-route :projects [(fr/router-instruction :root-router [:projects :top])])
                        (fr/make-route :project [(fr/router-instruction :root-router [:project :param/route-id])]))
obviously, the ident in my router is incorrect. I just don’t quite know how to structure it. fwiw, my Project component looks something like:
(defsc Project [this props]
  {:initial-state (fn [params]
                    {:page :project
                     :route-id nil})
   :query [:page :route-id]}
the only example in the book seems to suggest nesting a router to achieve this effect of parameterized routing on some routes but not on others. is that the case?