Fork me on GitHub

Morning, you missed two nice talks yesterday 😉

🙌 4

Meowning 🐈


@yannvahalewyn Forgot to mention it yesterday, but yada deserves a mention. It's both data driven (data > functions > macros) and fully support the async story.


Definitely! I was a bit short yesterday with making the slides and didn’t include some important ones.. Other ones would be: • Git! Because it has a plumbing and porcelain (composition vs arrangement) • How Datomic is build: it just has a simple protocol and can compose multiple persistence backends like S3, Postgres, H2, DynamoDB..


On that note: crux


but maybe that emphasises composition and not so much arrangement yet


not emphasizing arrangement in a library is perfectly valid imo, let the users arrange it right. Tailwind does this, but also datomic in the sense that you as a consumer can arrange your own datomic out of one or more of those backends


I think a good library offers at least the composition, and may offer one way to arrange them as a “get started quickly” idea but allows to be “remix”’ed 🙂


developers are smart people and I think we are afraid sometimes to open up some internals to the public


maybe because of the ideas of encapsulation coming from years of OO, but I don’t really know why exactly


@stex I also enjoyed your talk btw. The thing about reactions in subscriptions: last time I asked about this in the #re-frame channel (because the docs hinted at this being not recommended anymore) the answer was: events should always be triggered / dispatched as a result of user actions, not by subscriptions


e.g.: user opens table -> event -> http request


but we've used all kinds of different approaches and if they work, they work 🤷


I think the idea is that when a new subscription is consumed, it probably means a new view just got rendered or some input data changed which usually is because of a user action, so @stex’s ideas still align with that no?


I still like it more if say a table gets shown after a click, to let the table declare what he needs instead of guessing what the table will need when the click happens


think in terms of how fulcro co-locates views and queries


Related to your talk I kept wondering if Convention and Composability are mutually exclusive


You could have a convention be a composable standard.. or even the other way around.. somehow make composable conventions


yeah the order is still a bit unclear indeed


I'd stick to having a convention on composability for a certain standard since, composable standards would increase the overhead by requiring participants to remember which conventions were composed.


well, if I think about it a bit I would say composition over convention stands. You want the base to be totally composable with less “magic”, and then on a higher level, the arrangement level, that’s where you can use conventions more


there seems to be a conflation between convention and "magic" I don't necessarily agree has to exist.


But I have to work today.. so I have to focus for a while 😅


sure, I don’t mean to say they always come together indeed


But when you execute conventions tightly sometimes this can introduce magical elements


which is fine, if it’s on the high level arrangement


Seems reasonable


I look forward to you proving your ideas on RAD


It will be Tony that proves his ideas haha, but thanks!


thanks for coming yesterday and listening to what we have to say! There were some really interesting conversations that came out of it and I sure learned a lot! partywombat


From the javascript world they speak highly about using the Apollo React client with a GraphQL backend, to handle state in a nice way. Should also be able to build something similar, not unlike your solution, in reframe. So you could just say witch data you need in the view, and on the background it does the http request if needed (using re-graph). Not sure how fulcro works, but I guess it's something similar?


I learned that if you tell people to tell the person next to them 1 fact about them it will start massive amounts of conversations


Works in bars too

🍹 4

@borkdude Thanks! I’ve read the addition in the docs does surprises me that they don’t encourage this approach. It’s basically the approach I’ve shown in the first part of my talk 😄. If you trigger http-requests only by user-interaction, you still need to be aware of all the dependencies: user opens table -> event -> what http requests are required for the table? It’s also hard to reuse components this way. Let’s say your page have 50 pages and on 20 of those you include a small widget which requires remote state. Do you really want to couple the user entering a certain page with dispatching things for the specific widget? What if a page stops using that widget? What if you add an extra page? What if you have a large combination of widgets and pages? I do understand this introduces more side-effects to subscriptions, which is less pure. But I’m fine with that 😄.


@borkdude Would it be a good idea to have the ability to set class paths in an environment variable? (instead of --classpath argument. e.g. export BABASHKA_CLASSPATH=... ) ?


@stex We have an event for each page open, which will trigger the init events for the widgets, which will then trigger fetching their data


Which would eval before BABASKA_PRELOADS


@kevin.van.rooijen I think that makes sense, planck also has PLANCK_CLASSPATH


@kevin.van.rooijen Note that nothing from the classpath gets loaded unless you use require


True, but then you could require in your PRELOADS, correct? I think that would actually be really helpful


yeah, you can do that


I'll make sure that works


@kevin.van.rooijen Are you interested in testing a version with BABASHKA_CLASSPATH?


if so which OS?


Sure, but I’m not sure if I have time tonight, probably tomorrow. MacOS Catalina


All right, I’ll let you know if I get the chance tonight


I tried out your binary, seems to work fine!


Thanks! I wrote a unit test for it meanwhile and released 0.0.41


More info in #babashka


Great, I’ll update to it now 👍 Thanks


@borkdude That works for simple pages, yet you are having the same problems right? You need to know which widgets are used on the specific page.


@stex we know that already, since we put them there ourselves 🙂


anyway, not to criticise any approach, just thought I'd mention the comment from the re-frame folks


ask in #re-frame if you want more info I guess


@borkdude Haha, but you need to keep the widgets on the page in sync with the dispatch 😄 .


they discouraged triggering events from component didMount etc


Yep, I’ll discuss in #re-frame if I have some more time. Just wanted to check if you’re having the same issues. Sounds like you’re fine with the simple approach, do think this will get hard when you do traditional REST on the backend and need to join in the frontend.


@stex We have quite a complex page with lots of widgets, sometimes depending on each other. And we have a REST backend.


We do have a client-side API abstraction with a cache (key) like you described


Like I said, if it works, it works. I should maybe look into fulcro one day, but that couples the backend and the frontend, which was not an option for us


That's the main reason I didn't really look into fulcro yet. I like to keep options open to another back and/or frontend. Like I can test different GraphQL server implementations and to end using the same re-frame code. Because it's composable.


@gklijs So to answer your question about Apollo: I’m not familiar with apollo, but based on how you describe it: yes. this sounds quite like Fulcro. But fulcro also does something extra that maybe Apollo doesn’t. It composes a query with an ident. These two together are a very powerful combination. It’s the how and the what. If you only have a query, all you can do is fetch the data. If you have a query and an ident, you can do loads more things: you can merge queries, nest queries, normalize and denormalize a client db, make mutations, .. example: Say you have a widget that shows a user’s name and another widget with the same user’s email. They both will have the same ident. So if both views render at the same time, the queries will be merged and executed together. The result will go in a normalized client db:

{id {:user/id id :user/name name :user/email email}}
If both widgets render at different times, two fetches will occur but the results will be merged, resulting in the same shape. You can’t do this if queries don’t have an ident. Other examples would be forms. You query the data you need for the form, but you can mutate that same data in the same spot in the app-db. If you have nested forms you can normalize both querie-results in the app-db, making mutations trivial, but have a denormalized tree to make the rendering of the form easy. I think Fulcro doesn’t deserve the adoption it deserves. It’s extremely powerful, but based on a very simple idea. I don’t think it’s easy to understand though, and people miss these kinds of brilliant aspects of Fulcro and put it away.


@borkdude This is an interesting conversation. It seems that Stefan, me and the fulcro people think colocated queries and views are a great idea, and you + re-frame people don’t. Maybe we could talk about this a bit more


@yannvahalewyn Om(.next) has this too. I don't think it's a bad idea nor do I have a strong opinion on it. I just pointed out that the docs linked to in the presentation yesterday are effectively deprecated and the re-frame folks suggest a different approach.


(different approach than dispatching events in subscriptions I should say)


I don't know what Apollo does either, but sounds similar. You can also add things to the GraphQL schema for client site only date, like 'seen'. I think it also does aggregation maybe even across components. It can replace redux entirely. One of it's problems is because the cashing, and because the db is based on the GraphQL schema there could be inconsistencies. For example if you have a UserPublicView and an UserFriendView, having some fields in common they could diverge.


That last part is solved by fulcro's idents. As for caching, yes. GraphQL and pathom can't really use http caching. You have to bring your own solution to the table


@borkdude fulcro was originally forked from I believe. Interesting to realize we're actually using reframe with om/fulcro ideas


maybe you can blog about it how it exactly works (or an open source lib), that's maybe a better starting point for a discussion


or publish the talk


Both are on the table, it will depend on @stex :)


it's pretty interesting indeed if you combine the good parts of re-frame and fulcro, without having to change your backend. arranging ftw!


Or "how to make fulcro ideas work with rest" could be a nice blog post idea


Re Fulcro was first based on Om.Next, but now it isn't anymore.


We use it here at Avisi, and I am still trying to get my head around it.


I'm hoping we can get a nice fulcro rad clojure rails killer

👌 4

with all due respect to rails of course


Tony Kay is working on something like that.


Hey if it's better then it should kill it!


(keep in mind this is an absolute better (as in better at everything all the time) and so is impossible, Healthy competition and options are always good.)


I'm considering building something on top of re-graph that does some of the things, but I'm considering a lot, and might not be a good idea to try to build something like that when I won't be using it in production.


@thomas I’m actually getting involved with that project, I think it will be great


aah yes you mentioned that... it would be great indeed.


@yannvahalewyn be sure not to include a dependency on nokogiri


looking forward to it very much


@borkdude Crap, running git revert head right now


as for db options, will it consider triplestore-like dbs like datomic / crux / datahike as well as rdbmses?


not out of the box, but attributes will just be open maps so you could implement your own backends, or the community could make plugins for them


right now the plan is to make it work for sql and datomic, two very different backends. We avoid the ActiveRecord style where all follow the same protocol, and the idea is to offer different solutions for both, but not make them transparently swappable. I think that’s a dangerous road to take.


so the answer is yes


when I open the README I see npm install gem install foreman Makefile.. kinda scary


I added foreman because the dev setup is a bit convoluted, you can ignore it if you start the transactor and repl by yourself


@yannvahalewyn imo requiring two "foreign" package managers for a Clojure RAD framework is not attractive


haha yeah sorry


Should’ve gitignored them, thought maybe other devs already have foreman and this way they could start it all up with one command. Do you have other tools / techniques that make this easy?


@yannvahalewyn > Setup For any contributors wanting to run this Run what? why?


is this only for running Datomic? Why do I need Datomic?


docs aren't really clear, sorry 🙂


is it for running multiple build steps at the same time? if you're already using npm for shadow-cljs you could also use it for that? or just bash?


I would say: just type in make foo


make is probably fine


question for the clojurescripters, I'm having some troubles with advanced compilation which go beyond my understanding. I've a function (ns my.namespace) (defn ^:export init [] ...) and I give the browser some HTML with a <script>my.namespace.init()</script>


with :optimizations :none it works


with :optimizations :advanced it fails with my is not defined


any clue where to look? I'm a bit lost at the moment


@skuro what have you used as the argument to -c in the compiler options?


mhhh I do not invoke the compiler manually


@skuro if you start typing my in the console do you see anything appearing? I think in advanced the namespaces might be collapsed into single names


:cljsbuild  {:source-paths ["src/cljs" "src/cljc" "env/prod/cljs"]
             {:output-to     "target/cljsbuild/public/js/app.js"
              :output-dir    "target/cljsbuild/public/js"
              :source-map    "target/cljsbuild/public/js/"
              :optimizations :advanced
              :infer-externs true
              :pretty-print  false}}


that's the build config


(I mean the browser console)


nothing shows up


nothing at all


just hits from my previous console history


maybe a question for #clojurescript


@borkdude Make is not fine when there are multiple async processes to be started. Anyway, maybe it’s way too early to finetune contributor workflows


@yannvahalewyn I mean Make as a "foreign" build tool, it's generally accepted and installed


although I don't know if you'll exclude Windows users 😛


docker compose could also work maybe.


@skuro think you need to add :main my.namespace to the cljs build options


-m, --main ns Call the -main function from a namespace with args


Specifies an entry point namespace. When combined with optimization level :none, :main will cause the compiler to emit a single JavaScript file that will import goog/base.js, the JavaScript file for the namespace, and emit the required goog.require statement. This permits leaving HTML markup identical between dev and production.


not sure if it has an effect on advanced optimizations


-c, --compile [ns] Run a compile. If optional namespace specified, use as the main entry point. If --repl follows, will launch a REPL after the compile completes. If --server follows, will start a web server that serves the current directory after the compile completes.


I think you need that one


but that's a cljs.main argument


yeah, indeed


but again, not using a plugin


alright, thanks for now


I'll dig deeper and resort to #clojurescript if need be


alright, just for the curious: I found where my cljs issue was, there was an actual exception in some init code that crashed the main script before the inline call to init() ran

🎉 4
🐛 4

fixing that exception made it work once again


so just ^:exporting the names suffices


nice, congrats