Shortcut for Requiring Several 'sub-namespaces' with :as - clojure

Consider this at the beginning of a Clojure program:
(ns my.core
(:require [lib.a :as my1]
[lib.b :as my2]
[lib.c :as my3]
[lib.d :as my4]))
is there a shortcut for this?
something like:
(ns my.core
(:require [lib [[a :as my1]
[b :as my2]
[c :as my3]
[d :as my4]]]))

Yep, it's called a prefix list:
(ns my.core
(:require (lib [a :as my1]
[b :as my2]
[c :as my3]
[d :as my4])))

Related

(:gen-class) and fully qualified symbol

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"

Accessing clojure macro via namespace in which it was not defined

I've defined a macro in func.clj:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
and I want to bind it to a variable in core.clj:
(ns my-library.core
(:require [my-library.func :as func])
(def my-macro-core func/my-macro)
so that I can do something like the following:
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro) ; -> :boring-macro
However, I can't do that because macros don't evaluate to a value. The following works:
(ns my-library.core
(:require [my-library.func :as func])
(defmacro my-macro-core [arg]
`(func/my-macro ~arg))
but it's redundant, so I was wondering if there's a cleaner way to do this.
It's a bit of a hack, but it can be done:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
(ns my-library.core
(:require [my-library.func :as func]))
(def ^:macro my-macro-core ##'func/my-macro)
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro)
The trick is to set the new var being defined to the raw function value of the macro being aliased, while setting the :macro metadata flag so the compiler recognizes it and calls it appropriately.
Potemkin's import-vars supports macros:
(require '[potemkin :refer [import-vars]])
(import-vars [my-library.core my-macro])

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))))))

clojure - simple map function returns list of nils

This function takes a list of a files and is supposed to return a list of artists:
(defn get-artists [files]
(map #(.get-artist (->Mp3 %)) files))
Here the rest of the code:
(ns musicdb.filesystem
(:use [green-tags.core]))
(import '(java.io.File) '(java.net.url) '(java.io))
(require '[clojure.string :as str])
(defn get-files [search-path]
(let [directory (clojure.java.io/file search-path)
files (file-seq directory)
fonly (filter #(.isFile %) files)]
(map #(last (str/split (.toString %) #"/")) fonly)))
(defprotocol MusicFile
(get-artist [this])
(get-song [this])
(get-album [this]))
(defrecord Mp3 [filename]
MusicFile
(get-artist [this]
(:artist (get-all-info filename)))
(get-song [this]
(:title (get-all-info filename)))
(get-album [this]
(:album (get-all-info filename))))
And here are my tests:
(ns musicdb.core-test
(:require [clojure.test :refer :all]
[musicdb.core :refer :all]
[musicdb.filesystem :refer :all]
[clojure.pprint :refer :all]
))
(deftest test_0
(testing "getFiles returns valid result"
(is (> (count (get-files "/home/ls/books/books")) 50))))
(deftest test_1
(testing "check for file included"
(is (some #{"02 Backlit.mp3"} (get-files "/home/ls/Musik")))))
(deftest test_2
(testing "creating music file record"
(let [myfile (->Mp3 "/home/ls/Musik/Panopticon/02 Backlit.mp3")]
(is (= "Isis" (.get-artist myfile)))
(is (= "Backlit" (.get-song myfile))))))
(deftest test_3
(testing "testing get-artists"
(let [artists (get-artists (get-files "/home/ls/Musik"))
]
(is (> (count artists) 10)))))
(deftest test_4
(testing "testing get-artists check for artist"
(let [artists (get-artists (get-files "/home/ls/Musik"))
]
(is (some #{"Isis"} artists))))) ;artists is [nil nil nil ...]
From this tests only the last fails, which returns a list of nils.
If you want to reproduce ths be sure to include the green-tags dependency in your leiningen project.clj:
[green-tags "0.3.0-alpha"]
Your get-files function doesn't return the full path of the file so get-all-info just returns nil (https://github.com/DanPallas/green-tags/blob/master/src/green_tags/core.clj#L59 in combination with https://github.com/DanPallas/green-tags/blob/master/src/green_tags/core.clj#L120).
Here is a simple example that works:
(map (comp :artist get-all-info)
(filter #(.isFile %)
(file-seq (java.io.File. "/home/vema/Downloads/mp3"))))
;=> ("Yo Yo Honey Singh (DJJOhAL.Com)")
(Humoristic?) disclaimer: The MP3 should not be taken as an example of my musical taste, it was just the first free MP3 I found online.

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]))