there is also this https://circleci.com/blog/build-a-clojure-web-app-using-duct/
@jeffmad I saw that, though it is by now unfortunately outdated.
Having deliberated on the matter a bit more I have come to the conclusion that it is probably intended that the developer writes new functions after the initialisation section directly within dev.clj
, debugs things there and when done, moves it to its own namespace, calling it a day. There are a few other duct idiosyncrasies - mostly down to individual modules - that ultimately made me decide that I don't want to be using duct at all. I am still undecided as to whether I want to keep integrant
or move to mount
, but I just find duct
hiding too much and forcing me into a particular workflow that does not really align with how I think/write Clojure.
I see. I did this a couple of times and yes, it is not quite comfortable, although you can use integrant.repl.state
which contains the config and the system.
Or you can expose config / system or specific parts of the system in dev
and then use it where needed.
I can, but I still need to initialise the system from the dev
namespace and I do not feel confident that you can always straightforwardly depend on something from the dev
namespace directly in your component namespace declaration.
And if you do, you are already doing pretty much what mount
is doing - just require the things that you need, instantiate the subsystem from the same place, boom done.
Libraries over frameworks.
I think you cannot depend on anything from the dev
ns in production, only when developing and only when the system is initialized. Initialization is required by Integrant, to turn configuration into the system.
There is a similar discussion about Integrant https://www.reddit.com/r/Clojure/comments/ar87te/integrant_how_to_store_and_access_the_running/eglti58
Interesting discussion. The point is general, though, and one can achieve something similar through judicious use of atoms for stateful thing and forgetting about integrant, component etc. Except people use that so that they can have a reloadable system, and they want that to be able to easily instantiate and tap into parts of the state as and when they need it. Integrant, Component and mount are different takes on the problem, but I cannot help but feel that the latter is a great deal "lispy" and closer to doing Clojure without any of this than the former two.
I am thinking the same. There is a lot to uncover here just to understand what is going on, and then there is a commitment to writing modules, like if you want to use hugsql or pedestal. I am having trouble getting a cljs repl going in emacs, figwheel wants to see :cljsbuild key, but it doesn’t exist, instead there is the compiler.cljs module. I can see there are some really compelling ideas here, but it will be a lot of work just to get to the problem that I really want to work on. Will keep an eye on this though.
I found a pedestal module a couple of days ago, though I’ve never used it. https://github.com/lagenorhynque/duct.module.pedestal
We use mount, and are pretty happy with it, but we haven’t encountered the problem of needing 2 db connection pools or anything that causes problems when you need 2 states for the same namespace.
I don't think we'd have such a situation either. I am the only person on the team with prior Clojure experience and it got to a point where the other folk got frustrated to the point of saying we'd be done by now if it had been done in Scala. To be fair I made a decision to explore duct/integrant at the beginning simply because we didn't have strict deadlines but I ended up reading the sources of not only duct and integrant itself but also of the modules, plus having discussions with James himself as some of the stuff seemed downright counterintuitive. Also, without wishing to badmouth the laudable and very fine efforts of James - after all, we still use other parts of his stack! - Integrant and duct feel too much like writing Spring.
James is amazing - intelligent, kind, generous. I just need time to digest the fruits of his efforts.
Why do you need to write new functions within dev.clj
?
I think it may be helpful to others if you’ll elaborate on stuff that seems counterintuitive.
It mostly boils down to how modules expose their own parameters but do not really allow one to override the enveloped integrant keys in the same fashion. To do that, one has to actually provide an override of that specific integrant key in a profile, which ultimately means you have to open the sources, read the code, and traverse any dependencies the module might have if it gets more complicated.
It also means things like migrations are non-configurable when using the duct sql module - they either always run or they never do, and while it is possible to get hold of ragtime in a duct system to work them manually, it's not straightforward.
Then there is the fact that in duct, the entire system is meant to be started together, and the developer is meant to stay in the dev
namespace, which means that they either need to develop in said namespace or pull things into it, which can be cumbersome (i.e., a DB namespace needing to be required after the system has been initialised).
Finally, duct
precludes the use of config management libraries. Components are meant to be declared with their configuration; one can declare the config section for a component as coming from the environment (and coerce to a non-string type, which is really handy) and provide dev overrides through profiles, but that means moving a sizeable chunk of the application's wiring to the profiles themselves. If some part of it is incorrect upon initialisation, cryptic errors ensue - we ran into a fair share of messages complaining about a record missing a protocol implementation. This is par for course in Clojure where error messages have historically left something to be desired, but is a more pressing issue when it's coming from a third-party dependency that assembles the system from a completely declarative description.
I realise this comes off as one giant rant, and it will most probably be especially aggravating to James as he helped me out when I was setting it up, and patiently listened to some of these remarks in a private conversation. But that is not the intention, and my hats off to James for churning out so much useful stuff for the rest of us to use, on an entirely voluntary and spare-time basis.
If they are not pure and need parts of the system and I want to test them in the REPL, how else would I do it?