is there an equivalent of with-redefs for whole namespaces?
No. Just in case - if you don't need to redef something at run time, you can do it by having the same namespace at a different location. Then a classpath change would "redefine" the whole namespace. But it's something you set up before running a process, not during.
ohh - i ended up using (ns mocked-ns) ... (ns test-ns (:require src-ns)) within a single file and that worked well
Not sure what you mean. How would that make mocked-ns override src-ns? Assuming that's what the code means.
my problem was slightly more complicated - it looked something like this:
;; src/src-ns.clj
(ns src-ns (:require mocked-ns))
;; test/test-ns.clj
(ns mocked-ns)
(def x ...)
(ns test-ns
(:require src-ns))
i am writing an SCI wrapper and mocked-ns only exists within the guest interpreter, so if i want to unit test it i have to emulate it in the hostIn that code block, the namespace name does not correspond to the file name. It might lead to some interesting problems down the line.
E.g. that :require won't work at all, unless you somehow make sure taht test/test-ns.clj is loaded before src/src-ns.clj.
my understanding of clojure's compilation model is that you have "entrypoints" which form the root of a recursive DAG. normally, the entrypoint will have a -main function in the namespace, but when running tests, the entrypoints are the test namespaces, and so they get loaded before any source namespacxes
That should work indeed. As long as no other test namespace requires src-ns before that one.
Brittle. You have a different classpath for tests anyway, so why not just put the whole new test/mocked_ns.clj in there and make sure that the test dir is on the classpath before the src dir? That way there are no issues at all.
oh hm i do see that this is causing problems on reloads though
> make sure that the test dir is on the classpath before the src dir? how do i control classpath ordering in this way?
oh :classpath-overrides is promising
is there a way to debug which namespaces are loaded other than adding println in a bunch of places?
ok i ended up doing :replace-paths ["test" "src"] in deps.clj and then using (ns src_ns) (load-file "src/src_ns.clj") in test/src_ns.clj, that worked well
Wait, so you need the original namespace in the mocked namespace? Do you just do some kind of wrapping of every function instead of replacing them?
kinda; so the actual src-ns is called flower.fs and i am doing Hacks ™️ https://codeberg.org/jyn514/flower/src/branch/dev/flower/eval.clj#L154-L157
Rrrright... Actually, I think I don't wanna know. :D Way too close to my bed time.
i promise i have good reasons!
for some definition of good ....
Oh, and the reason I asked is that maybe you can simply alter-var-root instead. Programmatically find all relevant vars and update their values.
i think that won't work in my case because in order to get a listing with dir it has to load the namespace correctly in the first place, i want to append vars to the namespace rather than overwriting them
i do have to say, clojure has given me much much more control here than i expected to have
the closest thing to this in rust is #[path] and include!() and that kinda works but there's no equivalent of :replace-paths
Ah, appending is even easier - intern.
Or even require the target ns in some other ns, then in-ns to the target ns and define away. Just don't forget to in-ns back to that other ns is there's any other code after that.
oh fascinating, ty!
i kinda like my current setup though because it works automatically for all unit tests
I think you can also dynamically create a whole namespace. Didn't really follow what you want to do, but just wanted to mention it
here is a thing I did; can someone tell me why it is dumb?
;;; ~/.clojure/deps.edn
:repl/jim
{:extra-deps
{nrepl/nrepl {:mvn/version "1.4.0"}
cider/cider-nrepl {:mvn/version "0.57.0"}
org.clojars.abhinav/snitch {:mvn/version "0.1.16"}}
:main-opts
["--eval" ;; should be able to use *let anywhere now during dev
"(require 'snitch.core) (intern 'clojure.core (with-meta '*let (meta #'snitch.core/*let)) @#'snitch.core/*let)"
"--main" "nrepl.cmdline"
"--middleware"
"[cider.nrepl/cider-middleware,suitable.middleware/wrap-complete,portal.nrepl/wrap-portal]"]}Does it work? If not, does it fail in a particular way? Not sure what you're asking about it being dumb...
I want to double check with fresh eyes but yes, seems to work
one thing I meant by being dumb is why haven’t I seen it out there before while researching how to like, have an affordance like *let available everywhere during repl dev in a non-project-local way
someone might come by and say, and here’s why you don’t want to do that. I don’t see how it risks namespace collision since the set of names in clojure.coreis a known quantity
(@seancorfield I’ve read your dot-clojure and repl.clj, also inspiring/informative)
As an alternative, I’ve got a #s reader tag that turns a let form into let*, defn into defn*, etc. avoids injecting into clojure.core while still being ‘global’
hmm interesting . I haven’t played much with custom reader tags.
I don’t have access right now but it was a few lines of code, nothing fancy.
Interning stuff into core is not something I would do but I know there are people who do that. I probably wouldn't use -e for this -- I'd put it in my REPL-startup code (e.g., the user/up-time fn I define in there). Part of that is because I tend to rely on aliases to add dependencies and then I want conditional logic to do stuff based on what's on the classpath...
REPL-startup code meaning user.clj? What I didn’t like about using user.clj was you’d have to repeat yourself across projects.
My dot-clojure repo's REPL-startup code -- not user.clj no.
https://github.com/seancorfield/dot-clojure/blob/develop/src/org/corfield/dev/repl.clj (when you said you'd read the repo, I thought you'd get my reference to this)
yes sorry I see what you did there
What I meant was that I'd conditionally add an intern if snitch was on the classpath, and I'd use a separate alias to add that rather than being part of a single :repl/sean alias (in my case :dev/repl).
Because I start a REPL with all these aliases by default: https://github.com/seancorfield/dot-clojure/blob/develop/bin/repl#L14
now for argument’s sake though you could put the stuff that’s conditional on snitch or lib-x there in an --eval with the alias that loads it?
not necessarily the stuff you have going on in nrepl.clj that deals with standing up the nrepl server, but things like snitch
:main-opts do not combine -- last one wins -- so, no, you can't have multiple aliases with main options...
of course, of couirse, --eval is a m,aimn-opt. sorry late here and I’m letting my newborn dictate my posts
I think you're on the same TZ as me? Eastern?
late for us 🙂 thanks as always for the discussion it was valuable to me. Likely I’ll end organizing things as you do in dot-clojure.
See also https://clojure-goes-fast.com/blog/system-wide-user-clj/ for making user.clj global