Problem overwritting java method from clojure - clojure

I am desperetly trying to create a class that with a "toString" method from clojure
According to clojure docs the following should work:
(ns override-test.simpleClass
(:gen-class
:name simpleClass
:methods [[^{Override {}} toString [] String]]
:state state
:init init
:constructors {[String] []}))
(defn -init
[name_]
[[] (atom name_)])
(defn -toString [this]
(deref (.state this)))
However evaluating
(simpleClass. "test")
Throws
CompilerException java.lang.ClassFormatError: Duplicate method name "toString" with signature "()Ljava.lang.String;" in class file simpleClass, compiling:(override_test/simpleClass.clj:19:3)
Any incites of what i might be doing wrong ?

As Biped Phill mentioned the problem was that toString seems to be already implemented by virtue of the automatic subclassing mechanism of gen-class. Probably Ljava.lang. String is treated as a (?) superclass and toString is added automatically, so i just had to remove it from :methods which is for not inherited methods and it worked like a charm.

Related

Problems calling a clojure function that takes a map as parameter from java

I'm using maven with several modules, one in java, another in clojure. I'm calling a clojure function from java and want to pass in a HashMap as a parameter and return a HashMap.
(I ran lein uberjar and lein pom on the clojure project to make it work with maven. I can get things to work for clojure function with simple types e.g. String, so the maven setup does work.)
I am getting the following error when I run some java unit tests calling the java code:
java.lang.ClassCastException: class clojure.lang.LazySeq cannot be cast to class java.util.Map (clojure.lang.LazySeq is in unnamed module o
f loader 'app'; java.util.Map is in module java.base of loader 'bootstrap')
How can I get this to work? Is this the proper way to call clojure methods from java?
What about if the HashMap had a POJO object as a value rather than a String?
My java code:
import interop.Core;
public class BillingCalc {
static Map<String, String> nonEmptyItems(Map<String, String> items) {
return Core.non_empty_seats(new HashMap<String, String>());
}
}
My clojure code:
(ns interop.core
(:gen-class
:name interop.Core
:methods [^{:static true} [apply_vat_to_netto [java.math.BigDecimal java.math.BigDecimal] java.math.BigDecimal]
^{:static true} [non_empty_seats [java.util.Map] java.util.Map]]) )
(defn -filter-empty-seats
"filter out those with empty seats"
[seats]
(filter (fn [[_ v]] (pos? (:booked-items v))) seats))
(defn -non_empty_seats
[java-seats]
(-filter-empty-seats (into {} java-seats)))
I guess that your error is caused by this definition in :gen-class:
[non_empty_seats [java.util.Map] java.util.Map]]
From docs for :gen-class:
:methods [ [name [param-types] return-type], ...]
The expected type of returned value is java.util.Map, but filter in -filter-empty-seats returns instance of clojure.lang.LazySeq. You should rewrite -non_empty_seats like this:
(defn -non_empty_seats
[java-seats]
(into {} (-filter-empty-seats java-seats)))

Resolving a keyword into a Malli schema from the default registry in Clojure

How do I resolve a keyword to a schema from the default Malli registry? I seem unable to look up a value in the registry in order to walk it.
(def registry
(atom {}))
(defn register! [type ?schema]
(swap! registry assoc type ?schema))
;; Combine the default registry with our own mutable registry.
(mreg/set-default-registry!
(mreg/composite-registry
(mreg/fast-registry (malli/default-schemas))
(mreg/mutable-registry registry)))
(register! :db/kasse
[:map
[:id [:int {:primary-key true :db-generated true}]]
[:odlingsplats [:string {:foreign-key "odlingsplatser"}]]
[:diameter_m :int]
[:djup_m :int]
[:volym_m2 [:int {:db-generated true}]]])
(malli/walk
:db/kasse
(malli/schema-walker identity))
;; => :db/kasse
I've tried wrapping :db/kasse in different functions from malli but none seem to do the lookup and malli/-lookup is private. Just running (:db/kasse malli/default-registry) does not work either. Using malli/schema seems like the obvious choice but it seemingly has no effect.
(malli/walk
(malli/schema :db/kasse)
(malli/schema-walker identity))
;; => :db/kasse
Calling malli/deref was the answer:
(malli/walk
(malli/deref :db/kasse)
(malli/schema-walker identity))
;; => [:map [:id [:int {:primary-key true, :db-generated true}]] [:odlingsplats [:postgres/string {:foreign-key "odli\
ngsplatser"}]] [:diameter_m :int] [:djup_m :int] [:volym_m2 [:int {:db-generated true}]] [:namn {:optional true} [:po\
stgres/string {:db-generated true}]]]
Thank you to ikitommi at the Clojurians slack for providing the answer. He also provided an explanation as to why the library works this way:
The :db/kasse returned is a Malli Schema instance, it’s print output is just the form, so looks like keyword. It’s type is :malli.core/schema, which is the internal eager reference, like a Var in Clojure. If you want to get the schema behind it, you can m/deref it. But, calling m/validate on :db/kasse works too. the :malli.core/schema forwards the calls to the actual instance, like Var.

with-redefs not replacing valid Compojure route function call in clojure.test

I want to test my Compojure endpoints and see that the required method is called. However, with-redefs-fn is not replacing the method.
Test method-
(ns project-name.handler-test
(:require [clojure.test :refer :all]
[project-name.handler :as handler :refer [retrieve-records test-handler]]
[ring.mock.request :as mock]))
(deftest fn-handler-routes
(with-redefs-fn {#'handler/retrieve-records (fn [arg] :test)}
(let [response {:status 200 :body :test}]
(let [mock-handler (test-handler)]
(is (= response (mock-handler (mock/request :get "/records/name"))))))))
I am guessing routes are static, and thus the method fires and returns actual data instead of calling the redefined function. test-handler is a failing attempt to try to circumvent -- it fires okay, but does not use with-redefs and returns repo data.
Pertinent Source Code-
(defn retrieve-records [arg] (repo/get-records arg))
(defroutes routes
(GET "/" [] (resource-response "index.html" {:root "public"}))
(context "/records" []
(GET "/name" [] (retrieve-records :lastname)))
(resources "/"))
(defn test-handler []
(-> #'routes wrap-reload))
Related Issues
I suspect a similar underlying issue as this SO question about Midje but I am using clojure.test and not midje.
This question is different from a very similar SO question in that the Compojure route is legitimate and I want to return something in the :body, not just test the status (already tried that answer).
This question is different from an integration test question in that it is a handler unit test, though probably the same underlying issue.
Any help would be greatly appreciated.

how to get the Class generated by gen-class in clojure

I am using some java lib which require the access the class generated by gen-class
(ns cljfx.test
(:import some.java.Lib))
(gen-class :name Main)
(defn -main [& arg]
(Lib/method-require-class-arg (classOf Main)))
the prototype of method-require-class-arg is
public static void method-require-class-arg(Class someClass) {
// ...
}
How to write the (classOf Main) part in the first snippet?
Your gen-class example is incorrect. If you use
(gen-class :name cljfx.test.Main)
then just cljfx.test.Main will return the Class object.
You can use Class/forName method.
Ex: (Class/forName "java.lang.String")
In your case it should be (Class/forName "Main") and you need to make sure you enable aot in your project.clj

Declare array as return type in gen-class method declaration in Clojure

How to declare an array in method declaration in gen-class?
(ns foo.bar
(:gen-class
:methods [[parseString [String Object] Object]]))
That works fine. But the return type is really an array. How I can declare that so Java can understand it?
Try
(ns foo.bar
(:gen-class
:methods [[parseString [String Object] "[Ljava.lang.Object;"]]))
I needed a
static Number[][] method(int, Number[][]);
signature, in a similar way:
(:gen-class
:methods [#^{:static true} [method [int "[[Ljava.lang.Number;"] "[[Ljava.lang.Number;"]])
seemed to work.