Fork me on GitHub

I gave Duct + CLJS a quick shot, and having one (refresh) command that runs db migrations, refreshes your handler code and syncs those changes to the browser was impressive.


Is it just a matter of running (refresh) at the REPL?


From my spotty memory of events that happened a year ago: yes.

😃 1

I remember having to fiddle a bit to get it to work, though.


Remember what it was you needed to fiddle with?


Some dir locals. Other than that, sorry. Thanks for Calva, by the way. I've done some remote pairing with a colleague new to Clojure lately. We got him started with Calva, and it was a breeze!

❤️ 1

We have an isomorphic, reagent, duct app with a shadow-cljs front end and clj server side rendering, in a tools.deps project. We don’t currently have it wired up to update cljs on (reset), but tbh I don’t think that would be better than what we have anyway. I think this would be pretty trivial to do though; just wrap shadow-cljs with a ig/init-key / halt key — and hook into shadow-cljs’s appropriate functions. What we do have is almost equivalent in practice; and simpler to setup, with more flexibility for alternative workflows etc. Essentially it’s: 1. Run shadow-cljs watch app for a figwheel like front end workflow against the dev / backend app. 2. If you then want a cljs browser repl whilst working on the code; you can connect to one easily enough — in addition to giving you a repl this also enables ide like features in editors like emacs/cider, calva etc. 3. We start a different repl via tools.deps, for the duct backend; and use overmind to manage the processes (there is also another backend clojure service we start too). You don’t strictly need this in a separate process; i.e. it’s totally possible to use the shadow-cljs process to run your app/backend also. We mainly start this extra repl mainly because our app has a few deps that conflict with shadow-cljs machinery. You can then have the standard duct workflow — (reset) or evaling forms as normal for the clj side of the app. For the cljs side of the app shadow-cljs can do its figwheel like magic, and auto-deploy on save; or just compile on save/eval; and wait for a page refresh. It’s up to you.


Either way the workflows can be as integrated or separeted as you like


for example you can develop components with or without your backend; but in the same project… by just having a skeleton html shim with test data; or even using something like devcards or nubank/workspaces.


If you want to help me writing instructions for using Calva to support a workflow you like, I would appreciate that a lot.


I know Calva, you know Duct and of a good workflow. We should be able to pull it off together. 😃


TBH I think the biggest barrier is the lack of a good minimal duct template here — our duct app has probably strayed quite a bit from the standard layout because we avoided the templates to begin with because. 1. I dislike their starting layout (it’s not feature oriented, and is more organised by framework/ring concepts) 2. We wanted to support multiple tenants out of the box; i.e. multiple customer configurations of the same platform… so we have augmented the profiles, and have a more nuanced notion of 3. We wanted to use tools deps not lein 4. We wanted to use shadow-cljs not lein/cljs-build etc 5. Perhaps less relevant to the template (We wanted to build an isomorphic app with server side rendering) 6. We had a bunch of existing test helpers for testing duct apps that we wanted to use/refine; that assume various profile arrangements etc. 7. We use a niche graph database, that almost certainly isn’t supported by duct’s migrator, and we wanted to use our own loader system for managing fixtures of dev/test data. 8. We wanted to use kaocha for our tests 9. I wanted to use with slf4j/log4j2 and not timbre and the duct logger. I’ve wanted to create such a set of templates for a while; but it’s pretty much at the bottom of my priorities. (I also don’t really know what a duct template looks like these days…)

👍 2

Non of it is rocket science at all; but it is a pretty significant task to put all the pieces together and refine it into something nice.


I’d certainly be very happy to help create such a thing; but I don’t have a lot of time for doing it what with family commitments etc


Also I think whilst ataraxy is in many ways quite nice; it’s lacking important features like bi-directional routing. Also it’s clj only. Which has led to me use reitit for front end routing, and I’d much prefer to use reitit on the backend too; as it has more features. A colleague spiked out how to use it in duct; and it’s pretty simple… the main thing I’m unsure of though; is how amenable its data syntax is to meta-merge semantics.

👍 2

we are moving towards this kind of systems as well, we just skipped duct entirely and rolled our own with integrant


Yeah I did consider doing that too however I think duct brings some useful things over integrant. The main one is essentially the wiring up of the basic ring stack via configuration and sensible starting defaults; and I think the arrangement of profiles. Over time though most of that disappears as you override and replace pieces of the stack; with your own more specialised variants. Personally I find ducts modules are a bit too much magic though; I’d rather they were closer to more explicit integrant config… However there is still a need for something like them. Also duct_hierarchy.edn is I think useful.


@rickmoynihan by any chance, have you talked about your experiences on a conference, podcast, or some other place long-form informal? I would love to hear you dig through the details of your experience.


I think the goal of Duct is to provide a framework in which you can just add keys as building blocks. So you want reitit, shadow-cljs, ? Add those keys. Though we're not at the point where it's seamless plug-and-play sadly, I agree on that


Modules might be a but magical, but I do think we need a way to manipulate the base config. We use them to generate routes for example


Profiles are also just modules at the moment


That’s true; and I’ve done exactly that. Duct is really a module/component system that includes a web framework. It’s almost purely a module system; but there are a few small bits and pieces that are baked in, that I think ideally would be extracted.


The Duct core modules are very opinionated yes, if that's what you mean


they’re opinionated too; but there are a few places where it’s not fully decoupled… I can’t recall them all; but iirc things like :duct.core/project-ns make a few too many assumptions about what you’re doing.


But I guess that's the implementation of a module? The :duct.core/project-ns is very useful (which just derived fron :duct/const). We use it to be able to tell which application is sending logs to Sentry for example


There was a graphQL package for Duct, and I think it used the :duct.core/project-ns to shorten the definitions of your handlers in your config.edn


That really tripped me over, and made it more obscured imho


yeah ataraxy does something similar; it makes finding keys impossible; if you don’t switch it off.


To be fair it has been a long time since I looked at this bit of duct; and duct has changed a fair bit since we started using it (since essentially the first integrant based release), and it has become more decoupled. We’ve kept up with most of the changes over the years; but some details might’ve been lost on me.


I'm really enjoy following this discussion 👀 🙏