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?
I'm asking because: https://clojureverse.org/t/luminus-calva-smooth-getting-started-w-clojure-development/5623/6?u=pez
From my spotty memory of events that happened a year ago: yes.
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!
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 clojure.tools.logging 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ā¦)
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.
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āve not no
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, clojure.tools.logging ? 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 š š