I have a project with following ns:
in processor.pubsub I have following:
(ns processor.pubsub
(:gen-class))
(defrecord PubsubBus [client])
; + other stuff related to this implementation
In processor.bus I have:
(ns tiptop.processor.bus
(:gen-class)
(:import [processor.pubsub PubsubBus]))
(defprotocol SendToBus
(send-line! [self json]))
(extend-type PubsubBus
SendToBus
....)
The problem that Lein doesn't compile namespaces in right order. I get following error:
$ lein compile
Compiling user
Compiling processor.auth
Compiling processor.bus
java.lang.ClassNotFoundException: processor.pubsub.PubsubBus, compiling:(bus.clj:1:1)
Exception in thread "main" java.lang.ClassNotFoundException: processor.pubsub.PubsubBus, compiling:(bus.clj:1:1)
Notice it tries to compile my ns in alphabetical order (auth -> bus -> pubsub), instead if dependency order.
Of course I could precompile pubsub.clj before, like:
$ lein compile processor.pubsub
$ lein compile processor.bus
$ lein compile
But it doesn't seems quite right for me. What if I'll have more such dependent namespaces?
How I can tell Lein in which order it should compile my namespaces? Or maybe i'm missing something to configure in project.clj
? I have :aot :all
if it matters
Leiningen does not do anything to determine namespace dependencies - it simply compiles the namespaces that you tell it to. It is the Clojure compiler that handles namespace dependencies via the require
(and obsolete use
) built-ins.
In this case, you need to :require
the namespace that defines the generated class before you can import it. Otherwise, you're relying on the imported class being present on the classpath as a side effect of some other operation (loading the namespace that defines it in the REPL, a previous lein compile
command, etc). Adding an explicit :require
in the namespace definition ensures the class is defined before it's imported:
(ns processor.bus
(:gen-class)
(:require [processor.pubsub])
(:import [processor.pubsub PubsubBus]))
A couple of other notes:
:gen-class
is doing what you think it's doing in these namespace declarations. It does not cause class files to get written for the protocol and records defined in these namespaces; that's what the :aot
key is for in the Leiningen project.clj
. The :gen-class
flag as it's used here will cause classes named processor.bus
and processor.pubsub
to be generated.extend-type
in the same namespace where the protocol is defined. The typical use case for extend-type
is to extend your protocol to work with types that are beyond your control, like those built in to Clojure or defined in third-party libraries. When the protocol and record are defined in the same project, it's more common to see the protocol implementation defined inline as part of the defrecord
body.Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments