fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
2020-12-07T03:54:00.150900Z

hmm, that's still happening, so I guess I have a different issue, it seems after each event in my uism's or rerender there is a clojure.data/diff call happening by fulcro.inspect that can take up to a couple seconds (I have around a 1000 entities in my state).

moocar 2020-12-07T04:19:47.155300Z

Does anyone know of a React library that comes close to Fulcro's form state handling? Or more broadly, a React state management library that has the same normalized db + component query functionality? I'm reviewing another team's React codebase and it's infuriating seeing ad hoc "dirty" checking in each of their forms.

xceno 2020-12-07T09:14:30.156800Z

You can take a look at mobx-state-tree (https://github.com/mobxjs/mobx-state-tree), but its documentation barely exists and it was a pain to work with tbh. But they also have the concept of normalizing state, or at least, provide you the means to do so. You have to write a ton of boilerplate code. As you see, I'm not exactly a fan. I tried mobx-state-tree after finishing another project with plain MobX, which was nice enough. How you use it is entirely up to you. Before that, I've always written my own state management based on RxJS which kept things reasonably simple. Anway, long story short: You can give mobx or mobx-state-tree a shot, but in reality fulcro is lightyears ahead to every other solution I've tried over the years

Jakub Holý 2020-12-07T10:05:11.162700Z

Also Relay has a normalized cache for its GQL queries just like Apollo

moocar 2020-12-07T19:55:31.168900Z

Great, thanks folks!

tony.kay 2020-12-07T05:45:32.155500Z

Hm maybe I missed another one. Caller from flame chart???

tony.kay 2020-12-07T05:47:38.155700Z

I don’t see anything in UISM…

tony.kay 2020-12-07T05:50:27.155900Z

yeah, I’m not seeing any other spots I missed. You sure you upgraded? Also, inspect will still ask for a diff, which will be calculated on the UI thread (though that is debounced in Inspect until things settle for a moment)…so it is intented to give you the perception of speed, when in fact it still has to at least build a diff at some point.

tony.kay 2020-12-07T05:55:16.156100Z

I have not looked extensively at current evolutions. Apollo was trying. Not quite the same, but they are at least trying to adopt the normalization mechanisms.

genekim 2020-12-07T06:27:08.156300Z

@holyjak Thanks so much for the kind words! And I’ve very much enjoyed your blog posts over the years — I remember studying your dev REPL posts, and the one on incanter/gnuplot (long before I ventured to use oz!) Okay, I love the encouragement to stay in RAD — I’ll do some poking around tonight inside of it… Can you confirm that I’m thinking about this the right way, and have the arguments clear in my head?

(com.example.components.parser/parser {env} eql-query)
I think {env} will be com.example.components.config/config and eql-query will look something like [{[:vimeo.user/id 118038002] [{:vimeo.album-list/data [:vimeo.album/uri]}]}] Wow, if I could get this part running, I’ll feel AMAZING! 🙂

genekim 2020-12-07T07:39:28.156500Z

OMG. It worked. 🤯🤯🙏🙏 > (parser com.example.components.config/config > {:address/id 1}) > => {[:address/id 1] #:address{:id 1}}

xceno 2020-12-07T09:14:30.156800Z

You can take a look at mobx-state-tree (https://github.com/mobxjs/mobx-state-tree), but its documentation barely exists and it was a pain to work with tbh. But they also have the concept of normalizing state, or at least, provide you the means to do so. You have to write a ton of boilerplate code. As you see, I'm not exactly a fan. I tried mobx-state-tree after finishing another project with plain MobX, which was nice enough. How you use it is entirely up to you. Before that, I've always written my own state management based on RxJS which kept things reasonably simple. Anway, long story short: You can give mobx or mobx-state-tree a shot, but in reality fulcro is lightyears ahead to every other solution I've tried over the years

Thomas Moerman 2020-12-07T09:27:58.160100Z

@tony.kay a small question about the future/commercial guardrails library you mentioned on a podcast: will it depend on clojure.spec or will the "spec'ing" library be pluggable perhaps? I was wondering about this, given the known flaw in the current spec version regarding "selection" context of specs (as discussed in the Maybe Not talk by Rich). What's your current perspective?

genekim 2020-12-09T15:45:26.244200Z

If you want to have a “paid alpha” program or something like that, count me in! 🙂 :thumbsup:

tony.kay 2020-12-09T17:25:26.252100Z

I’m hoping to have an early access program in the next month or two. There are a lot of little kinks to work out, and the CLJS support will probably be a few more months out from there (assuming ppl buy it, and I can afford to keep working on it).

genekim 2020-12-07T09:59:20.161900Z

Thx to all for the help running queries inside of Fulcro RAD, @holyjak @randumbo. Okay, now I’m having a problem with the session/get-all-sessions resolver not being able to connect to the correct database (a Datomic Cloud instance, in addition to the in-memory db in the example.) In my defaults.edn file, here’s the databases I have configured.

:com.fulcrologic.rad.database-adapters.datomic/databases
                                    {:main {:datomic/schema :production
                                            :datomic/database "example"
                                            :datomic/client {:server-type :dev-local
                                                             :system "fulcro-rad-demo"}
                                            :datomic/prevent-changes? true}

                                     :video {:datomic/schema :production2
                                             :datomic/client  {:server-type   :ion
                                                               :region        "us-west-2"                       
                                                               :system        "datomic-2"
                                                               :creds-profile "default"
                                                               :endpoint      "<http://zzz>"
                                                               :proxy-port    8182}
                                             :datomic/database "videolibrary"
                                             :datomic/prevent-changes? true}}
And here is my database query in com.example.components.database-queries
(defn get-all-sessions
  [env query-params]
  (if-let [db (some-&gt; (get-in env [do/databases :production2]) deref)]
    (let [ids (d/q '[:find ?s
                     :where
                     [?s :session/title _]] db)]
      (-&gt;&gt; ids
           flatten
           (mapv (fn [id] {:session/session id}))))
    (log/error "No database atom for production schema!")))
I’m pretty sure I must have something wrong either in the schema name, or name of the datomic entry? Hoping someone can show me what I”m doing wrong. Thx!!!

Jakub Holý 2020-12-08T10:38:41.193400Z

thanks a lot for the explanation, Tony!

Jakub Holý 2020-12-07T10:01:05.162200Z

awesome! And thank you for your kind words!!! (Funny to see we are both roaming the land of Oz :))

Jakub Holý 2020-12-07T10:03:54.162500Z

I invoke the parser like this

(parser
    (development/get-jdbc-datasource-in-env)
    [{[:bill-run/id 123]
      [{:bill-run/invoices-too-long [:invoice/id]}]}])
where the development calls returns
{:com.fulcrologic.rad.database-adapters.sql/connection-pools
   pools/connection-pools}
this is obviously for a SQL DB, Datomic would be different but in the same spirit.

Jakub Holý 2020-12-07T10:05:11.162700Z

Also Relay has a normalized cache for its GQL queries just like Apollo

xceno 2020-12-07T10:05:24.162900Z

Your config looks correct on first glance. Maybe you just forgot to open the ssh tunnel to your cloud system?

genekim 2020-12-07T10:07:48.163100Z

Alas, manual queries work, so tunnel is working. Is it okay for both :main and :video dbs to share :production schema? How does resolver know to look in video db? (Manually examining the get-all-sessions env, it looks like it’s looking in :main db, not :video db… Thx!

Jakub Holý 2020-12-07T10:09:39.163300Z

I guess ""No database atom for production schema!"" is the result you are getting? Are you sure do/databases should not be ::do/databases , assuming (require '[com.fulcrologic.rad.database-adapters.datomic :as do]) ?

genekim 2020-12-07T10:12:34.163900Z

Sorry, @holyjak, I forgot to mention!! I’m getting as a return value: [] That is when I have db entry set to production (not production2), and code in db query set to the same: (if-let [db (some-&gt; (get-in env [do/databases :production]) deref)]

Jakub Holý 2020-12-07T10:13:09.164100Z

OK, so your query runs but returns no data so you suspect it uses the wrong db.

genekim 2020-12-07T10:13:44.164300Z

Yes. (and all the other queries that work use do/databases… I just copied/pasted. 🙂

1👍
Jakub Holý 2020-12-07T10:14:00.164500Z

I don't really know as I use SQL. There I need to explicitely tell it which DB to use:

data-source    (get-in env [::sql/connection-pools :video])

genekim 2020-12-07T10:15:40.164800Z

Ooh…. looking…

Jakub Holý 2020-12-07T10:15:56.165Z

Have you modified https://github.com/fulcrologic/fulcro-rad-demo/blob/master/src/datomic/com/example/components/parser.clj#L29 to add the second db?

genekim 2020-12-07T10:17:08.165400Z

Wow! Trying that now, and assuming I can have one for :main and one for :video. (crossing fingers!)

Jakub Holý 2020-12-07T10:17:26.165600Z

It seems you have not https://github.com/realgenekim/fulcro-rad-demo/blob/gene-experiments/src/datomic/com/example/components/parser.clj#L29 so I would expect (some-&gt; (get-in env [do/databases :production2]) to return nil What does it return?

genekim 2020-12-07T10:19:14.165900Z

(Can I have both :main and :video dbs share :production schema? I’m hoping so, otherwise, I have to handle the attributes differently in com.example.components.auto-resolvers.)

genekim 2020-12-07T10:21:54.166100Z

(Now it’s returning #:session{:all-sessions []}. I think I’m getting closer? Still not sure if I need two schemas or not…. hmm…

genekim 2020-12-07T10:25:52.166300Z

🎉🎉

#:session{:all-sessions [#:session{:session 43518670227374203}
                         #:session{:session 54109166226112675}
                        

genekim 2020-12-07T10:27:12.166500Z

Okay, so it works when I replace out the datomic/pathom-plugin entry in parser. Is there a way to have two datomic databases, and if so, how? Thx again, @holyjak!

(defstate parser
:
:
     (form/pathom-plugin save/middleware delete/middleware)
     ;(datomic/pathom-plugin (fn [env] {:production (:main datomic-connections)}))
     (datomic/pathom-plugin (fn [env] {:production (:video datomic-connections)}))
     (blob/pathom-plugin bs/temporary-blob-store {:files         bs/file-blob-store
                                                  :avatar-images bs/image-blob-

Helins 2020-12-07T11:08:13.166800Z

Indeed, this little thing was more of an intellectual challenge. However, I typically have a quite a lot of grandchildren somewhere which have well-defined behavior and whose mutation also act on some "main" parent. They are specific enough to have the ident of that parent in their query or passed as props and act on their own. I consider that most of the time, a child should not know the exact type of its parent but it sure can expect it to conform to some contract. A child knows in what context it is supposed to be used.

Jakub Holý 2020-12-07T11:30:09.167Z

Perhaps this:

(datomic/pathom-plugin (fn [env] {
:main (:main datomic-connections)
:video (:video datomic-connections)}))
and in the code change to (if-let [db (some-&gt; (get-in env [do/databases :main #_:video]) deref)]

Jakub Holý 2020-12-07T11:31:11.167200Z

the key in the datomic/databases map and in the env are completely arbitrary and do not need to relate to the DB schema, I believe

tony.kay 2020-12-07T16:47:14.167700Z

The version we’re working on “comes with” spec support at the moment, but we’ve put all of that behind a protocol. Hoping to support Spec 1, Spec 2, Mali, and whatever comes next in that arena. I’m aware of the problems, and that no single thing perfectly covers the bases at the moment.

1
tony.kay 2020-12-07T16:51:42.167900Z

A schema should have a 1-to-1 correspondence with a data store. You supply a function that figures out, for a request, which connection(s) should be associated with which schema. This allows you to shard easily, and even spread attributes among database types (Datomic could supply data for schema A, kv store for B, etc.)

xceno 2020-12-07T16:53:47.168100Z

I just happened to have this page open from the pathom docs, maybe that helps too: https://blog.wsscode.com/pathom/v2/pathom/2.2.0/plugins.html#_example_shard_switch

tony.kay 2020-12-07T16:53:57.168300Z

E.g. large strings are bad for Datomic, but you could set up a different db/connection/schema for those attributes and RAD can then save/load them to different stores. There’s a bit more work for you to do around that to make such a thing “atomic” to whatever degree you find satisfactory….RAD has no strong opinion on implementing two-phase commit for you.

Jakub Holý 2020-12-07T17:25:38.168500Z

Is the RAD concept of Schema identical to the underlying relational / Datomic DB schéma or is it a separate thing? Regarding > You supply a function that figures out, for a request, which connection(s) should be associated with which schema. Where, how? Is that what the datomic parser plugin does? The way I understand it, the datomic plugin inserts 1+ DBS into the env, each with a unique key, and I get-in env [.. the-key] to get the one my queey/resolver wants. Do I misunderstand? (I was thinking in terms of manual resolvers just as the one Gene was showing. I guess for the auto-generated ones it is indeed crucial that the Schema and DB key under env are the same so that they know what DB to use.)

moocar 2020-12-07T19:55:31.168900Z

Great, thanks folks!