Fork me on GitHub
Mr. Savy04:01:30

when deploying my fulcro app it crashes saying postgres refused to connect. does anyone know if there's more information available anywhere for configuring the prod.edn file in the template? my guess is it may be something in there?


No exception? No stack trace? Have you debug Iogging on? (see default.edn)

Mr. Savy18:01:59

the specific error is Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. there's a number of thing that can be causing this but my understanding is that it's ssl rleated.


if you suspect that there is some java ssl system property you can set to get detailed logs (not related to Fulcro in any way)

👍 3
Timofey Sitnikov12:01:33

@holyjak, first of all awesome writeup and I do have a question, looking at the first diagram of, makes me wonder where does the application code fit in? So there are resolvers, the front end (the defsc macro), parsers and so on. I wonder if it is possible to make a diagram showing where each of these elements fit in.


Thank you! Do you think it is worth it? Defsc clearly belongs to the UI, parser and resolvers to Pathom, defmutation to both. It is simple, no?

Timofey Sitnikov13:01:53

It becomes simple after a while, but there are a lot of components that fit together and it is not easy to grasp it right away. I tried to put something together quickly, but it needs more work. The idea is to gain understanding of how everything fits together.

Timofey Sitnikov13:01:10

@holyjak, let me know what you think.


Cool! Sadly neither Slack nor my phone can display it. Will try later on PC

Timofey Sitnikov13:01:22

@holyjak OK, here you go.

❤️ 10

Nice! Yes, I guess something like this could be useful. Do you want to polish it when you have time?

Timofey Sitnikov13:01:56

Where would it fit into the doc? How about adding it into the doc and polishing it as we go?

Timofey Sitnikov14:01:10

One more version

❤️ 6
Timofey Sitnikov14:01:26

OK, so I created a pull request for you and when you have time, maybe you can place it into the right section and I will keep working on it.

Timofey Sitnikov14:01:04

I am going to make one more post, on the main thread, maybe I can get some good feedback.


So remember that mutations all go through mutations


Sorry, I do not really get this "mutations all go through mutations". You mean that remote Fulcro mutations are sent as EQL mutations?


that was word salad…not sure what I meant to say…..let me reconstruct it in my head 😜


Yeah, I was commenting on Timofey’s diagram. Remote behavior always involves a mutation….even loads (there’s an internal-load mutation)

👍 6

There’s no alternative way to talk to a remote. You issue a mutation through transact (read df/load!…that’s what it does too), the mutation says it has local and remote operations. Everything that changes in Fulcro or talks on the network is a mutation, even internally. (load puts in and clears load markers, locally, then indicates desired remote operation, and uses a different result-action than normal mutations…go read the mutation in the data-fetch ns 🙂 )

👍 3

Re-reading my own comment….“There’s no alternative way to talk to a remote” means “through Fulcro’s mechanims”…you can of course use http client or any such thing to grab data, and one you have data you could use swap! against Fulcro’s state atom, but you’d get zero support from Fulcro that way. By at least using merge-component! you’d get normalization and refresh. By using the official mutation/remote system you get even more. Just clarifying.

👍 3

I'm having trouble getting fulcro-rad to start up. I'm using a copy of fulcro-rad-demo as is - I have not made any changes yet. When I execute shadow-cljs watch main, I get this: Execution error (IllegalArgumentException) at$loading (js_support.clj:1). No matching field found: getRegisteredGroups for class It looks like a version conflict in the Google closure compiler code being called by shadow-cljs? The nearest issue I have found is I followed Thomas', and checked dependencies. My project was using a slightly older version of closure-compiler-unshaded, as listed for the corresponding version of shadow-cljs. I forced the correct version of closure-compiler-unshaded in my deps.edn, but it made no difference. I also checked the source code of closure-compiler-unshaded and it does contain the getRegisteredGroups method. I have also confirmed that the correct version of the library is being used with clj -Spath. Has anyone experienced this before? I find it strange that the exact copy of the repo as is, runs into this problem?


@andre.richards that is definitily a version conflict. should be

org.clojure/clojurescript 1.10.773 v20200830


Thanks @thheller. I changed it to those versions, but still get the same error...


did you restart the shadow-cljs process after? did you change it in the correct place?


Hi, yes I did restart. If I do clj -Spath to print the classpath, I get this (I split and filtered classpath using clojure just to make it easier to find correct info): ("/Users/andre/.m2/repository/org/clojure/google-closure-library/0.0-20191016-6ae1f72f/google-closure-library-0.0-20191016-6ae1f72f.jar" "/Users/andre/.m2/repository/org/clojure/google-closure-library-third-party/0.0-20191016-6ae1f72f/google-closure-library-third-party-0.0-20191016-6ae1f72f.jar" "/Users/andre/.m2/repository/com/google/javascript/closure-compiler-externs/v20200830/closure-compiler-externs-v20200830.jar" "/Users/andre/.m2/repository/com/google/javascript/closure-compiler-unshaded/v20200830/closure-compiler-unshaded-v20200830.jar")


and which clojurescript version? and which shadow-cljs? they all need to align otherwise you get this error


ClojureScript: ("/Users/andre/.m2/repository/org/clojure/clojurescript/1.10.773/clojurescript-1.10.773.jar") shadow-cljs: ("/Users/andre/.m2/repository/thheller/shadow-cljsjs/0.0.21/shadow-cljsjs-0.0.21.jar" "/Users/andre/.m2/repository/thheller/shadow-cljs/2.8.110/shadow-cljs-2.8.110.jar" "/Users/andre/.m2/repository/thheller/shadow-util/0.7.0/shadow-util-0.7.0.jar" "/Users/andre/.m2/repository/thheller/shadow-client/1.3.2/shadow-client-1.3.2.jar") This is the version of shadow-cljs specified in the fulcro-rad-demo. Should I try the latest shadow-cljs? I have included my config files


well as I said .. they need to align. and that shadow-cljs version is too old for the above


yes, you should try 2.11.15. or get the matching versions for the older ones


Thanks, 2.11.15 is working. I promise, I did earlier match my versions of clojurescript and Google closure compiler to :man-shrugging: Thanks again for your help. I'll sponsor you a drink on GitHub if you are listed there for sponsorship. 👍


ideally you just have shadow-cljs as a dependency then you don't need to worry about it for the most part


but that also runs into trouble if other libs declared dependencies on other clojurescript versions that they shouldnt


so yeah its a bit messy unfortunately


Understood, thanks 🙂 And thanks for a wonderful library! 👏👏

Timofey Sitnikov14:01:48

Good morning Clojurians, I am trying to create a diagram of how parts of fulcro work together. Here is what I have, I am looking for some feedback, how can I make it more accurate/better?

❤️ 3

You have defmutation displayed on the frontend side, but not on the backend. Perhaps add that in to the Pathom section to show that mutations exist there as well?


Here is my attempt to do simple typeset for your diagram, with my suggestion using the example of People and Car in the Fulcro 3 tutorials. It looks not very pleasant, but it has the advantage that it's created with text, that we can effectively edit the textual specification to improve it together. Here is the source code:

#+BEGIN_SRC plantuml :exports none :file ./normalization-illustrated.png
skinparam linetype polyline
title App State
rectangle "UI" as UI {
circle "root :sample" as root_data
rectangle ":sample"  as sample_table {
        circle ":person/id 3" as person_id_3
        rectangle ":car" as car_table {
                circle ":car/id 22" as car_id_22
                circle ":car/id 42" as car_id_42

database "Normalized Data Graph" as DB {
'rectangle "root :sample" as root_data
        rectangle ":person/id" as person_table_n {
                rectangle ":person/id 3" as person_id_3_n
        rectangle ":car/id" as car_table_n {
                rectangle ":car/id 22" as car_id_22_n
                rectangle ":car/id 42" as car_id_42_n

UI .right.> DB : UI tree to normalized DB
root_data -down->  person_id_3
'person_table -down-> person_id_3: :person/id 3
'car_table -down-> car_id_22 : :car/id 22
'car_table -down-> car_id_42 : :car/id 42

person_id_3 -down-> car_id_22
person_id_3 -down-> car_id_42

person_id_3_n -down-> car_id_22_n : person/cars
person_id_3_n -down-> car_id_42_n : person/cars

With your correction/feedback, I can further improve it. (I'm still not quite comfortable with modeling the data in terms of graph yet. My understanding may not be accurate.) I use to create it with my emacs setup.


Thanks for giving that a try. The main concept I was trying to get across is just that the operations of Fulcro are always centered around some portion of a tree. Sometimes it’s the whole tree, sometimes it is a subtree. But the overall point is that a very small number of core concepts are involved in pretty much everything you do.


I’m not sure what to say about your diagrams. I sorta see what you’re getting at, but they don’t convey what I was trying to get across.


The “dashed lines” for something that doesn’t exist at some point, and stressing manipulation of/with subgraphs was most of what I was trying to talk about


mixing in keywords might be helpful somehow…this is really more of a whiteboard discussion/lecture I think, or a presentation with animations 😄


I'll try to express your idea of partial tree of UI elements being absorbed into the normalized tables.


That’s basically what’s in my head on those kinds of diagrams. Use dashed lines for elements that you “know the shape of” from the query, but do not yet have in the database. Initial state is just the portion of the UI that goes into client db, and then is turned into a tree for sending to root. Root is only able to render the data it gets. Loads/merge component are just “tacking” new things into the db, and then creating edges (targeting).

🙌 3

Thanks a lot! I will think about it.


Yeah, I’d love better formal diagrams, so making some variant of these pretty would be very welcome

👍 3

The operation of Fulcro is really mostly those three frames. Start, render, put in data, render, put in data, render …


and render is nothing more than “get data from db w/query and pass to root”


Mutations are the missing part from this set of diagrams, because there’s also the “fiddle with db and issue remote updates that can return data”…But a load is just a special internal mutation that has one particular structure


There’s a mutation diagram that might be useful @timofey.sitnikov


Technically a mutation can talk to any number of remotes, or a transaction could run some number of mutations each which could talk to any remote.


Fulcro enforces (by default) that writes are prioritized over reads, and that operation is in the order of submission (per remote)


But the diagram of a mutation result is JUST exactly the first diagram for normal operation. A mutations’ returning and with-target indicators (which you say in a remote) allows the remote to return a tree of data that is normalized via the query, with edges created by targeting. It’s all the same thing over again. The big exception is that the action sections of mutations can also do arbitrary manipulation of the normalized db.


So the “return value” story of mutations looks exactly like the “load” and “merge component” cases from diagram 1.


And action sections can use merge-component to do structured data insertion via the exact same mechanism (normalize a tree of data and create join edges).


If you’re using websockets, then a push can send a tree of data, and include a “topic” that names which kind of thing it is. Then merge component can be used to do the same thing: normalize it into the db and create edges….or a push could trigger a mutation. As another side-note: UISM is nothing more than a way to “join together” a bunch of scattered mutations into a more cohesive unit that abstracts the specific UI components away from the logic. An actor in UISM is one of the UI elements as an abstraction. Thus, UISM gives you both a way to unify related logic to a task (i.e. session management) while simultaneously not having to know any specifics of the UI components themselves.

👍 6