How can I remove leiningen :injections in a profile? - clojure

I use humane-test-output defined in my ~/.lein/profiles.clj like so:
{:user {:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]}}
However some old Clojure projects don't function well with humane-test-output active (and also sometimes I just want standard clojure.test output).
I thought I could occasionally disable humane-test-output by adding a new profile like:
{:user {:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]}
:nohumane {:injections ^:replace []}}
So I that I can easily invoke this whenever I need to like:
lein with-profile nohumane test
However when I do this I see:
Caused by: java.lang.ClassNotFoundException: leiningen.core.injected
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:69)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at clojure.lang.DynamicClassLoader.loadClass(DynamicClassLoader.java:77)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
Which I think is caused the by fact that I have removed this essential injection that is part of lein's core and needs to be active in lein's test profile.
So my question is, how can I remove the humane-test-output injection in a profile but leave this core injection intact? Note, leiningen.core.project/hooke-injection is also private, and I have so far been unable to refer to it, even using the #' trick.

Related

How to properly setup shadow-cljs for hot reload? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I've been trying to get shadow-cljs hot reload to work but I haven't been able to, I've tried multiple settings in my project.clj file but none have worked. This is what my project.clj looks like:
:shadow-cljs {:nrepl {:port 8777}
:builds {:app {:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "/js/compiled"
:modules {:app {:init-fn my-app.core/init
:preloads [devtools.preload]}}
:devtools {:http-root "resources/public"
:http-port 8080
:http-handler my-app.handler/dev-handler
}}}}
:aliases {"watch" ["with-profile" "dev" "do"
["shadow" "watch" "app"]]}
:profiles
{:dev
{:dependencies [[binaryage/devtools "1.0.2"]]
:source-paths ["dev"]} ;; <- this is the default from the template pointing to "dev", I've tried chaning it multiple times.
}
When I run lein watch I understand that it's just running shadow-cljs watch app which should run my "app" build. This is what my dev-handler looks like (referenced in :http-handler):
(def dev-handler (-> api-routes
wrap-params
wrap-json-response
wrap-keyword-params
wrap-json-params
wrap-multipart-params
wrap-reload
(wrap-resource "public")
(utils/cors-wrapper utils/cors-policy)
(utils/wrap-content-type-security)
(wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))
I've tried changing the source-paths in my :dev profile to ["src"] and even tried including all inner folders as in ["src/clj" "src/cljs" "src/cljc"] with no success. I've even tried creating a new app from scratch with lein new re-frame making no changes whatsoever and running lein watch, code compiles and everything seems fine but whenever I change something in a cljs file (views.cljs for example) nothing is re-rendered/reloaded, I've gone to localhost:9630 which gives you a shadow-cljs dashboard listing your builds and clicked on the "force compile" button and see a clojurescript animation in my app's page but no re-rendering/reloading again. I notice that when shadow-cljs finishes compiling, there are 0 files compiled, is it getting the source-paths config from somewhere else? This is what it looks like when I'm trying to test it out:
EDIT: Maybe it's worth saying that I'm running ubuntu with xfce terminal and emacs from WSL2 within MobaXterm, could it have something to do with the code/app not hot reloading?
Everything seems to be running smoothly and as intended. It might be that the only thing you are missing is a lifecycle callback to trigger the actual re-render of your page.
See https://shadow-cljs.github.io/docs/UsersGuide.html#_hot_code_reload
In most re-frame/reagent apps that would be the function that calls reagent.dom/render (or reagent.core in case you are on old version).
I also wrote more about how this all works here.
... is it getting the source-paths config from somewhere else?
shadow-cljs looks at the JVM classpath however that is constructed on startup. You appear to be using lein so only project.clj will matter here and the classpath is constructed by adding all :source-paths, :resource-paths and so on. So if you have :source-paths ["src/clj" "src/cljs" "src/cljc"] that will be enough for it to be picked up, assuming of course the source files you are working with are actually in those dirs.
Can't comment on your setup otherwise. I used it fine with WSL2 but there are known issues if running inside containers (eg. docker) so I wouldn't rule out something being funky here that prevents shadow-cljs from actually "watching" the files.

Using my own format function with `log4-clj-format`

I'm looking at log4-clj-layout to bring a little order to the mess that is logging in Java. I'd like play around a little with a format function of my own, but I'm failing at getting it to work.
Following the instructions on the page above I started with adding the following to my log4j.properties file:
log4j.appender.console.layout=log4_clj_layout.layout.Layout
log4j.appender.console.layout.FormatFn=json-format
That worked beautifully. Then I added a function, log-test.core/my-format and tried to use that for logging:
log4j.appender.console.layout=log4_clj_layout.layout.Layout
log4j.appender.console.layout.FormatFn=log-test.core/my-format
But now all I get is an error when starting:
(log/warn "foo")
Setting format-fn: log-est.core/my-format
log4j:WARN Failed to set property [formatFn] to value "log-test.core/my-format".
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
...
Clearly I'm missing something, but what?
I defined my format function like this (l is an alias for log4-clj-layout.layout):
(defn my-format [event]
(pr event)
(l/json-format event))
Any and all help would be much appreciated.

clojure.tools/namespace refresh fails with "No namespace: foo"

I'm using tools.namespace to provide smart reloading of namespaces on the REPL. However, when calling refresh or refresh-all, it throws an error.
user=> (require '[clojure.tools.namespace.repl :as tn])
user=> (tn/refresh)
:reloading (ep31.common ep31.routes ep31.config ep31.application user ep31.common-test ep31.example-test)
:error-while-loading user
java.lang.Exception: No namespace: ep31.config, compiling:(user.clj:1:1)
And it seems to end up in this weird state where (require ep31.config) works without an error, but afterwards the namespace isn't actually defined.
I kind of figured this out, this seems to be a combination of circumstances
there were AOT compiled classes left in target/classes from doing lein uberjar previously
tools.namespace doesn't function correctly when loaded namespaces are AOT compiled
target/classes is by default on the classpath
So long story short, if you did a jar/uberjar build before, then remove target/ and things should start working again.
The question I haven't been able to solve yet is why target/classes is on the classpath to begin with. I'm suspecting it's being added by Leiningen, but haven't found yet where or why it's happening.
I learned this the hard way, documentation for :target-path says (https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L309-L313):
;; All generated files will be placed in :target-path. In order to avoid
;; cross-profile contamination (for instance, uberjar classes interfering
;; with development), it's recommended to include %s in in your custom
;; :target-path, which will splice in names of the currently active profiles.
:target-path "target/%s/"
I guess there has to be legacy reasons that :target-path "target/%s/" isn't the default.

How to debug static initialiser errors in jars generated by Leiningen?

in my Clojure library testlib, i've got a namespace with a :gen-class directive that looks like this:
(ns com.some_long_path.NewClass
(:import (java.util List ArrayList))
(:gen-class
:name com.some_long_path.NewClass
:methods [^{:static true} [getValues [String] java.util.List]]
)
(:require
[testlib.core :refer [var1 var2]]))
(defn getValues [^String]
(java.util.ArrayList. [3 5]))
If i try to import this class inside another namespace in the testlib project (after invoking compile), i can invoke getValues method without errors.
However, if i lein install, include testlib in another project jartest, and then use it in a test namespace below
(ns jartest.core
(:import [com.some_long_path NewClass]))
(NewClass.)
(NewClass/getValues "some string")
invoking NewClass constructor gives an exception
CompilerException java.lang.NoClassDefFoundError: Could not initialize class com.some_long_path.NewClass
and getValues as a consequence gives
CompilerException java.lang.IllegalStateException: Attempting to call unbound fn: #'com.some_long_path.NewClass/getValues
However, if i remove the requires from NewClass namespace definition above, the code works even in another library. So the problem is caused by some missing dependencies, although i've made sure that all dependencies of testlib are also included in jartest, and that testlib.core namespace is AOT-compiled.
Also, i've tried decompiling generated com.some_long_path.NewClass class file, and there is a static initializer block that looks like this:
static
{
Util.loadWithClass("/com/some_long_path/NewClass", NewClass.class);
}
Most probably the above-mentioned error is thrown from within loadWithClass. But how do i find out what exactly is wrong?
Thanks!
UPDATE: I was able to figure out what was wrong in the namespace i was requiring through a binary search for errors (commenting out code until things worked again). It turned out that some files were slurped from resources folder in testlib, but they were not present in jartest project. Changing the code to use clojure.java.io/resource fixed the problem. However, the question still stands - how to find out exactly what was the problem, without resorting to brute force methods?
Here is the obligatory answer- there isnt a better way, without understanding the dependency tree more deeply, which often can only be done by commenting stuff out and seeing what now works. That has been my experience with java classloading from clojure and using gen-class.
Heres hoping this isnt the highest voted answer.

What's the correct approach to debugging a pedestal.io app?

I'm currently trying to reimplement the todo example app to understand how it works and I'm getting an error when I load the page. I'm not certain how to go from here. What concerns me is the error appears to be in cljs.core.
todo-app.simulated.services.receive_messages = (function receive_messages(app){
return io.pedestal.app.protocols.put_message.call(null,(new cljs.core.Keyword("\uFDD0:input")).call(null,app),cljs.core.PersistentArrayMap.fromArray([io.pedestal.app.messages.type,"\uFDD0:create-todo",io.pedestal.app.messages.topic,cljs.core.PersistentVector.fromArray(["\uFDD0:todo"], true)], true));
});
The exception message is:
Uncaught TypeError: Object function (meta,cnt,arr,__hash){
this.meta = meta;
this.cnt = cnt;
this.arr = arr;
this.__hash = __hash;
this.cljs$lang$protocol_mask$partition1$ = 4;
this.cljs$lang$protocol_mask$partition0$ = 16123663;
} has no method 'fromArray'
And my dependencies are:
[[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-1820"]
[domina "1.0.1"]
[ch.qos.logback/logback-classic "1.0.7" :exclusions [org.slf4j/slf4j-api]]
[io.pedestal/pedestal.app "0.1.9"]
[io.pedestal/pedestal.app-tools "0.1.9"]]
Any help or insight would be appreciated!
I was seeing this error too, and it seemed like it came out of nowhere. Clearing the out/ dir (:target-path in your project.clj) fixed it for me. Based off that, I think there was some disconnect in the cljs compilation process and/or pedestal.
This issue looks similar and the fix was similar, so I assume it's a cljs build problem.
I don't have much to offer regarding pedestal debugging in general, but if I see an error that appears to be in a core library, I start from the assumption that something is wrong on my end. :)
EDIT
A little more info, it's recommended to delete the out\ dir every time you upgrade ClojureScript or Pedestal.
As bostonou suggested, the best approach is deleting the out directory. My current approach is to use lein-cljsbuild, I personally do this by adding it to my user profile.
You can do so by calling nano ~/.lein/profiles.clj
Mine currently looks like:
{:user {:plugins [[lein-difftest "2.0.0"]
[lein-marginalia "0.7.1"]
[lein-pprint "1.1.1"]
[lein-swank "1.4.4"]
[lein-catnip "0.5.1"]
[environ/environ.lein "0.3.0"]
[lein-cljsbuild "0.3.2"]]
:hooks [environ.leiningen.hooks]}}
You can now automatically build cljs files by calling lein-cljsbuild once inside the project folder. Calling lein-cljsbuild auto ensures that when the source files are edited then they are automatically compiled.
I also currently add :hooks [leiningen.cljsbuild] to my project.clj so that calling lein clean will also remove files built by lein-cljsbuild.