(:gen-class) and fully qualified symbol - clojure

I'm wondering why I need to include (:gen-class) to access imported functions from other ns without fully qualify them.
Example:
(ns project.core
(:gen-class))
(defn foo [] "foo")
(ns project.core-test
(:gen-class)
(:require [project.core :refer :all]))
(foo) ;=> "foo"
(ns project.core)
(defn foo [] "foo")
(ns project.core-test
(:require [project.core :refer :all]))
(foo) ;=> ComplilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context
(project.core/foo) ;=> "foo"
I couldn't get my head around this concept.
EDIT:
I had a typo in the require form. Idk why (:gen-class) solved this problem but now the project works again.

The (:gen-class) clause in the ns form is for Ahead-of-Time compilation (AOT). See https://clojure.org/reference/compilation
Something else must be going wrong in your setup. My example:
> d **/core*
-rw-rw-r-- 1 alan alan 98 Jan 13 16:15 src/fred/core.clj
-rw-rw-r-- 1 alan alan 47 Jan 13 16:15 src/tst/fred/core.clj
src/fred/core.clj
------------------------
(ns fred.core)
(defn foo [] (println "foo you"))
(defn -main []
(println "main - enter")
)
src/tst/fred/core.clj
----------------------
(ns tst.fred.core
(:use fred.core ))
(foo)
> lein test
foo you
I get the same results with the require version:
(ns tst.fred.core
(:require [fred.core :refer :all]))
(foo)
It also works if I paste your code into the repl:
~/tmp/fred > lein repl
Clojure 1.9.0
fred.core=> (ns project.core)
nil
project.core=> (defn foo [] "foo")
#'project.core/foo
project.core=> (ns project.core-test
#_=> (:require [project.core :refer :all]))
nil
project.core-test=> (foo)
"foo"

Related

How to implement TemporalAdjuster in Clojure

Clojure newbie, here.
I'm trying to implement a TemporalAdjuster in Clojure. I have the following:
(ns pto-calculator.logic.pay-periods
(:require [java-time :as j]))
(def next-pay-period
(reify java.time.temporal.TemporalAdjuster
(adjustInto [this temporal]
(let [local-date (java.time.LocalDate/from temporal)
day-of-month (.getDayOfMonth local-date)]
(if (< 14 day-of-month)
(j/plus local-date (j/days (- 14 day-of-month)))
(j/adjust local-date :last-day-of-month))))))
(defn get-next-pay-period [date]
(j/adjust date next-pay-period))
And I call it like this:
(ns pto-calculator.core
(:require [pto-calculator.logic.pay-periods :as p]
[java-time :as j])
(:gen-class))
(defn -main
[& args]
(p/get-next-pay-period j/local-date))
Today is March 2nd, so I expect get-next-pay-period to return March 14th, however, I'm getting an exception instead:
Caused by: java.lang.ClassCastException: java_time.local$local_date cannot be cast to java.time.temporal.Temporal
at java_time.adjuster$adjust.invokeStatic(adjuster.clj:64)
at java_time.adjuster$adjust.doInvoke(adjuster.clj:40)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at pto_calculator.logic.pay_periods$get_next_pay_period.invokeStatic(pay_periods.clj:19)
at pto_calculator.logic.pay_periods$get_next_pay_period.invoke(pay_periods.clj:18)
My confusion is this: (j/local-date) returns an instance of java.time.LocalDate, which is a Temporal (according to the docs). So why wouldn't this work?
I've also tried:
(defn get-next-pay-period [^java.time.temporal.Temporal date]
...
But in that case I get this error:
java_time.local$local_date cannot be cast to java.time.temporal.Temporal
Is there a difference between java_time.local$local_date and java.time.LocalDate?
You aren't invoking java-time/local-date in your core namespace. You are passing a function to get-next-pay-period.
Instead of:
(defn -main
[& args]
(p/get-next-pay-period j/local-date))
Try:
(defn -main
[& args]
(p/get-next-pay-period (j/local-date)))

Unittest in Clojure doesn't compile when I test my own functions

I am new to Clojure and now I'm trying to use some unittesting.
I have a sample project with this structure:
core.clj in src/hello contains
(ns hello.core
(:gen-class))
(defn side-eq [x]
(if (< 0 (count x))
(= (.charAt x 0) (.charAt x (- (count x) 1)))
false))
core.clj in test/hello contains
(ns hello.core
(:use clojure.test)
(:require [hello.core :refer :all])
(:gen-class))
(use 'clojure.test)
(deftest side-eq-tests (
is (= false (side-eq "aws"))))
(run-tests)
when I execute tests, it throws
java.lang.RuntimeException: Unable to resolve symbol: side-eq in this context
When I test something like
is (= 1 1)
then everything works fine.
What is going on?
You should not have multiple files with the same namespace. Rename your tests to something else. The idiomatic name here would be hello.core-test In test/hello/core_test.clj
A variation which I prefer is to begin all testing namespacese with the tst.* prefix, which avoids the hyphen-underscore conversion & confusion (e.g. demo.core-test vs demo.core_test.clj. So your files look like so:
> d **/*.clj
-rwxrwxr-x 1 alan alan 1024 Jan 5 19:00 project.clj*
-rwxrwxr-x 1 alan alan 84 Jan 5 15:29 src/demo/core.clj*
-rwxrwxr-x 1 alan alan 248 Jan 7 12:42 test/tst/demo/core.clj*
and the code looks like:
(ns demo.core)
(defn -main []
(println "main - enter")
)
and
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.misc :as tm] ))
....

clojure unit-testing with-redefs

I have something like this:
(ns server.core
(:require [db.api :as d]))
(defrecord Server [host port instance]
(start [c]
(let [connection (d/connect (:host c) (:port c))]
(assoc c :instance connection)))
(stop [c]
;; close the connection
))
(defn new-server
[host port]
(map->Server {:host host
:port port}))
And the unit-tests code
(ns server.core_test
(:require [server.core :refer :all]
[clojure.test :refer :all]))
(deftest server-test
(testing "Calling start should populate :instance"
(with-redefs [d/connect (fn [h p] [h p])]
(let [server (start (new-server "foobar" 12313123))]
(is (-> server :instance nil? not))))))
Running the code above with boot watch test throws an error similar to:
Unable to resolve var: d/connect in this context
And then I modify the test code so it requires the db.api
(ns server.core_test
(:require [server.core :refer :all]
[clojure.test :refer :all]
[db.api :as d]))
I ran the tests again, this time d/connect still refers to db.api.
Any advice?
You need to require db.api in your test code so the var d/connect is accessible:
(ns server.core_test
(:require [server.core :refer :all]
[db.api :as d]
[clojure.test :refer :all]))
(deftest server-test
(testing "Calling start should populate :instance"
(with-redefs [d/connect (fn [h p] [h p])]
(let [server (start (new-server "foobar" 12313123))]
(is (-> server :instance nil? not))))))

Trouble referring to defrecord symbols in Clojure unit test?

Consider the following leiningen project core.clj file
(ns records.core)
(defn hello [] "hello")
(defprotocol my-sequence
(add [seqq item]))
(defrecord my-vector [coll]
my-sequence
(add [_ item] (conj coll item)))
I can compile this and test it in the REPL as follows:
records.core> (hello)
"hello"
records.core> (add (my-vector. []) 42)
[42]
But when I transcribe this into the leiningen unit-test file as follows:
(ns records.core-test
(:require [clojure.test :refer :all]
[records.core :refer :all]))
(deftest a-test
(testing "adding to a my-vector"
(is (= (hello) "hello"))
#_(is (= [42] (add (my-vector. []) 42)))))
The first test succeeds, showing that symbol hello is correctly moved into the records.core-test namespace, but the test of my-vector throws a compiler error (remove the #_ on the second line above):
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException:
Unable to resolve classname: my-vector, compiling:
(.../records/test/records/core_test.clj:8:22)
This does not seem to be a duplicate of this SO question because I am using require and refer, as the answer to that question suggested.
EDIT: the following also do not help
(add (records.core/my-vector. []) 42)
(add (#'records.core/my-vector. []) 42)
(add (##'records.core/my-vector. []) 42)
Unless my defrecord skills become rusty, you should first require the namespace and then import the defrecord.
(:require records.core)
(:import [records.core my-vector])

Don't know how to create ISeq from: clojure.lang.Symbol

I have the following clojure code (trying to write / get entries from redis):
(ns hello.core2)
(ns h hello.core2 (:require [taoensso.carmine :as car]))
(def pool (car/make-conn-pool))
(def spec-server1 (car/make-conn-spec))
(defmacro wcar [& body] `(car/with-conn pool spec-server1 ~#body))`
(defn -main
(wcar (car/ping)
(car/set "foo" "bar")
(car/get "foo")))
I get the following error:
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
at clojure.lang.RT.seqFrom(RT.java:487)
at clojure.lang.RT.seq(RT.java:468)
Any idea what im doing wrong?
(defn -main ...): you forgot the argument vector for the function -main; it should be (defn -main [& args] ...).
The problem is probably this:
(ns h hello.core2 (:require [taoensso.carmine :as car]))
There should be only one namespace declaration, like this (so also move the first ns expression):
(ns hello.core2
(:require [taoensso.carmine :as car]))