Fork me on GitHub
#fulcro
<
2022-08-29
>
sheluchin17:08:43

Maybe it's a weird question or something that already should be obvious? The pco/? "hack" I'm using works good enough until I come to understand a better way. Moving on to another question... One of my entity attributes holds large strings. I'd like to break those strings up at a given set of ranges (`[[5 8] [12 16] ...]`) and surround those parts with dom/mark tags. My thinking is that I should do the string processing server-side and have the resolver for that attribute return DOM. Does that sound right? If so, I can't figure out how to render DOM created with the dom-server functions inside my cljs frontend. I don't have to switch everything over to cljc to support this limited case, do I?

Jakub Holý (HolyJak)18:08:14

Why return dom and not just vec of the parts to (map dom/mark parts)?

sheluchin18:08:29

@holyjak The strings can be very long. I guess I'm pre-optimizing (which I shouldn't do), but it seems like with many sub-ranges it could be quite a bit of work for the client, and would be better to do on the server.

Jakub Holý (HolyJak)19:08:05

I'm in agreement with that,no? I wrote the resolver would return those sub-ranges, the client just maps mark over them.

tony.kay19:08:12

I’d define an additional resolver and a mock attribute :thing/long-string -> :thing.long-string/parts , which can define the first as its input, and the latter as its output.

tony.kay19:08:33

I don’t recommend overloading keywords, since they often have specs/other expectations. But making a defattr with an odd invented type like :array-of-strings is fine. Just don’t give is a schema and no db plugin with touch it, and if you have RAD render it, make sure you install a renderer for that type.

tony.kay19:08:05

(defattr long-string-parts :thing.long-string/parts :array-of-strings 
 ...)

sheluchin16:08:33

The RAD renderer could define the array to automatically get wrapped in the proper DOM elements. That sounds convenient. Thanks for the advice. A little off-topic, but I always feel a little tension about :a.b/c style keywords. I like to use auto-resolved kws from my RAD model namespaces, and using these dot extensions doesn't work with that strategy. Although, @U0CKQ19AQ, I notice you don't use auto-resolved keywords for any of the RAD demo code. Am I wasting my time trying to use fully-qualified kw namespaces that map to files everywhere?

tony.kay16:08:30

So, couple of comments: 1. Since a namespaced keyword should have a single meaning (I sometimes break this rule, but try not to), then intention of the narrowing (for me) so to indicate a related meaning. So, :invoice/customer and :invoice.customer/subset seems pretty good. The alternative is :invoice/customer-subset, which has distinct advantages (it destructures into something nice and still works with ns aliasing). The disadvantage is that it is not clear how to go both ways with the transform. It is trivial to reverse the former to get the base attribute keyword, but the latter doesn’t work (e.g. :invoice/customer-status -> :invoice.customer-status/someething-with-hyphens). 2. I don’t use fully-namespaced keywords in my data models. This is true in my production code as well. Part of that was that Clojure didn’t have good ns aliasing (without having to have a namespace) before 1.11, and it was just a royal pain to do it, for pretty much no distinct advantage (other than an imagined “someday I might want to integrate with some unknown other company in a way where we share databases”), but if that happens then I likely sold the company and cashed out, and it is now somebody else’s problem, and they are probably going to hire people that don’t want to work in clojure and port the whole thing to something else anyway…so, why buy the trouble 😛

tony.kay16:08:32

I’m first a pragmatist, and then a pessimist 😄

😄 1
sheluchin16:08:41

Okay, sounds like there's quite a bit of personal preference to this choice and trade-offs in both directions. I did notice in the new server-pagination namespace that the pagination resolvers should use such "narrowed" keywords, so I suppose that's one place where the choice does matter.

tony.kay17:08:43

Right, I’m using them in the library. For search parameters as well. It simplifies RAD code considerably to just have a standard that is also easy to be sure goes both ways. So, I think they’re a win there for sure.

fulcro 1
sheluchin16:09:43

In the end I found mark.js which supports all my use cases through markRanges():man-facepalming: 🙂 I did search before starting on all the manual string processing, but for some reason my searches didn't produce that library.

genekim18:08:51

Maybe a quick question: I spent a couple of hours dealing with an issue that I’ve struggled with before. I can generate the exact output, but it goes something like this. I wrote a resolver that returned in Fulcro Inspect EQL query the following, which didn’t work in my df/load! :

{:people ({:person/id 1 :person/first-name "Gene"}
          {:person/id 2 :person/first-name "Gene"})}
Note that it was a list of persons, not a vector. When I added a vec to the resolver, everything worked. This took hours for me to figure out. Has this bitten anyone before? Is there a reason why Pathom doesn’t turn everything into a vector? Are there situations where you actually want a non-vector? Is there a way to catch this error in Transactions or Network? (@holyjak: may I propose that this scenario be added to your fantastic Fulcro Troubleshooting Guide? I honestly don’t even know how to diagnose this, aside from manual inspection of the EQL tab. The load showed up as expected in Transaction and Network, but didn’t show up in the DB. Thank you!!!)

👍 1
sheluchin18:08:24

https://pathom3.wsscode.com/docs/resolvers/#unsupported-batch > The process works fine with maps and vectors but not with sequences, lists, or sets. That's because the later data structures don't support indexed updates for the elements in them.

sheluchin18:08:52

I've tripped up on that a few times. My understanding is that Pathom generally just works best with Associative collections so I just stick to vectors and maps.

genekim18:08:32

Thank you. I'm pretty sure the lists are created from lazy sequences that I forget to turn into vectors. 😭😱

Jakub Holý (HolyJak)18:08:43

I don't think this is batching. I think it is rather that Fulcro simply expects vectors. Off PC so can't check code.

sheluchin18:08:39

@holyjak Yeah, you might be right there. But returning non-vectors has tripped me up more than once when using Pathom on its own, so I stick to vectors. Is there ever a case where there would be some benefit to returning a list instead?

Jakub Holý (HolyJak)19:08:05

A good #pathom question :)

tony.kay19:08:52

The benefit is purely one of convenience: lazy seqs are returned by many many things. The sequence is unambiguous, but it is tough to know what you’re going to get on the client from a lazy seq on the server. NOTE: You can easily install a parser plugin to fix this yourself. Just use wrap-parser, and then post-process the result before returning it and convert lists to vectors.

tony.kay19:08:15

Or make a defresolver macro wrapper that checks your output and fixes it, etc.

tony.kay19:08:43

It is probably also possible to fix tree->db to watch for those during normalization, but the expectation is definitely that a to-many result should be in a vector.

genekim19:08:27

Great discussion, and glad to know I’m not the only person who’s been bitten by this. (Reflecting on this, I think what happens is that I map over a sequence returned from a database or whatever, and I forget it’s a LazySeq… …maybe I should get into the habit of using mapv in resolvers, or explicitly using vec at end of threading macro…) But knowing what I know (which I admit, isn’t a lot), I wish there were some defensive mechanisms somewhere in Fulcro (or Fulcro Inspect) to detect when this happens.

genekim19:08:34

Thanks, all!

genekim19:08:35

Tony, on the idea to wrap defresolver — based on advice on this channel, I typically use defattr instead of defresovler. Is there an easy way to wrap the resolver fn when using defattr? (And did I interpret the advice of using defatt over defresolver correctly?)

Jakub Holý (HolyJak)19:08:59

Hm, I got the opposite impression 😅 (prefer defresolver). But I might have missed this particular discussion...

tony.kay19:08:46

You didn’t say if you were using defresolver, defattr with pco/resolve, or something else. I personally have security and improved code reloading baked into a custom macro for defresolver.

tony.kay19:08:24

So in my project, I’d probably code it there…my second choice would probably be a pathom plugin

genekim21:08:24

(Yes, I use defattr with pco/resolve — it indeed sounds like this is preferred, because of improvements you made over defresolver.)

genekim21:08:04

Cc'ing @U066U8JQJ, because of the richness of discussion, instead of starting new thread in #pathom Thank you!

tony.kay22:08:31

After a lot of experience using RAD I’ve found that I don’t use pco/resolve hardly at all, because it tends to lead to circular refs

tony.kay22:08:58

I only use it when it’s trivial stuff that doesn’t need access to any kind of other project nses.

genekim22:08:54

Oh! I think I misunderstood your previous reply — you use a defresolver, and specifically, a hardened version that is not included in Fulcro?

tony.kay22:08:21

security is kinda project-specific

tony.kay22:08:38

but I want my macro to refuse to compile unless the security of a given resolver is defined

tony.kay22:08:01

My add-ons: • Make resolvers hot reloadable (you can reeval them to update them without having to restart) • Add security enforcement • Auto-register themselves in a registry (so all I have to do is require the ns)

😻 1
tony.kay22:08:55

This also makes it so that if you add a new resolver you just need to load that ns, and reload the pathom ns (assuming you’re using mount which will restart the parser defstate)…again, instead of restarting completely.

tony.kay22:08:06

It also adds tufte performance instrumentation around each one, which makes it much easier to find performance bottlenecks (if you do profiling).

tony.kay22:08:54

The registry is just an atom holding a vector, with a register function that adds the resolver to it. Then the resolvers in the parser construction includes that deref’d atom

genekim22:08:46

(Oh, the ability to not have to restart Fulcro! I almost want to weep, that sounds so awesome. In fact, I just fired up some code I wrote in kaocha , which reloads resolvers, so I can get faster feedback on changes to solve this problem…) (On our next call, I’d love to go thru this with you, and publish a sample project using it, because I’ve found it so useful: I started putting all the EQL queries as tests, to detect when I break things, with file system watching. And I’ve found it to be fantastic, sublime way to build out resolvers. So much easier than save file, reload all files, restart fulcro, manually send parse EQL to pathom.

wilkerlucio23:08:23

hello, just joining the party 🙂 about collection types and Pathom, vector is for sure the favorite and should be used as much as possible. that said, Pathom 2 has some issues handling lists, Pathom 3 does a better job there, and will work with vectors, seqs and sets, except the batch thing (mentioned earlier here). one other thing that might confuse you when using lazy seqs is the pathom viz timeline, lazy seqs get wrong times because Pathom marks the end of a resolver after the resolver call, but if you get a lazy list, that means some heavy process might occur after the resolver is done running

wilkerlucio23:08:28

@U0CKQ19AQ curious about the circular reference thing with defresolver, can you tell me more about it?

tony.kay01:08:57

you misunderstand @U066U8JQJ. I have an option in RAD for defining a resolver on a defattr, but those are at the root of your data model and are needed everywhere, so defining a resolver that is likely to need other things there leads to circ refs. Nothing really to do with defresolver or Pathom in particular

🙏 1